ilya-biryukov updated this revision to Diff 208425.
ilya-biryukov marked 5 inline comments as done.
ilya-biryukov added a comment.

- Replace bsearch with partition_point.
- Include macro name in the title.
- Added a FIXME for empty selection case.
- Return null when no token is found.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D61681/new/

https://reviews.llvm.org/D61681

Files:
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
  clang-tools-extra/clangd/unittests/TweakTests.cpp
  clang/include/clang/Tooling/Syntax/Tokens.h

Index: clang/include/clang/Tooling/Syntax/Tokens.h
===================================================================
--- clang/include/clang/Tooling/Syntax/Tokens.h
+++ clang/include/clang/Tooling/Syntax/Tokens.h
@@ -66,6 +66,15 @@
 
   unsigned length() const { return End - Begin; }
 
+  /// Check if \p Offset is inside the range.
+  bool contains(unsigned Offset) const {
+    return Begin <= Offset && Offset < End;
+  }
+  /// Check \p Offset is inside the range or equal to its endpoint.
+  bool touches(unsigned Offset) const {
+    return Begin <= Offset && Offset <= End;
+  }
+
   /// Gets the substring that this FileRange refers to.
   llvm::StringRef text(const SourceManager &SM) const;
 
Index: clang-tools-extra/clangd/unittests/TweakTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -286,6 +286,99 @@
   checkTransform(ID, Input, Output);
 }
 
+TEST(TweakTest, ExpandMacro) {
+  llvm::StringLiteral ID = "ExpandMacro";
+
+  // Available on macro names, not available anywhere else.
+  checkAvailable(ID, R"cpp(
+#define FOO 1 2 3
+#define FUNC(X) X+X+X
+^F^O^O^ BAR ^F^O^O^
+^F^U^N^C^(1)
+)cpp");
+  checkNotAvailable(ID, R"cpp(
+^#^d^efine^ ^FO^O 1 ^2 ^3^
+FOO ^B^A^R^ FOO ^
+FUNC(^1^)^
+)cpp");
+
+  // Works as expected on object-like macros.
+  checkTransform(ID, R"cpp(
+#define FOO 1 2 3
+^FOO BAR FOO
+)cpp",
+                 R"cpp(
+#define FOO 1 2 3
+1 2 3 BAR FOO
+)cpp");
+  checkTransform(ID, R"cpp(
+#define FOO 1 2 3
+FOO BAR ^FOO
+)cpp",
+                 R"cpp(
+#define FOO 1 2 3
+FOO BAR 1 2 3
+)cpp");
+
+  // And function-like macros.
+  checkTransform(ID, R"cpp(
+#define FUNC(X) X+X+X
+F^UNC(2)
+)cpp",
+                 R"cpp(
+#define FUNC(X) X+X+X
+2 + 2 + 2
+)cpp");
+
+  // Works on empty macros.
+  checkTransform(ID, R"cpp(
+#define EMPTY
+int a ^EMPTY;
+  )cpp",
+                 R"cpp(
+#define EMPTY
+int a ;
+  )cpp");
+  checkTransform(ID, R"cpp(
+#define EMPTY_FN(X)
+int a ^EMPTY_FN(1 2 3);
+  )cpp",
+                 R"cpp(
+#define EMPTY_FN(X)
+int a ;
+  )cpp");
+  checkTransform(ID, R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123 ^EMPTY EMPTY_FN(1);
+  )cpp",
+                 R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123  EMPTY_FN(1);
+  )cpp");
+  checkTransform(ID, R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123 ^EMPTY_FN(1) EMPTY;
+  )cpp",
+                 R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123  EMPTY;
+  )cpp");
+  checkTransform(ID, R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123 EMPTY_FN(1) ^EMPTY;
+  )cpp",
+                 R"cpp(
+#define EMPTY
+#define EMPTY_FN(X)
+int a = 123 EMPTY_FN(1) ;
+  )cpp");
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
@@ -0,0 +1,136 @@
+//===--- ExpandMacro.cpp -----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "refactor/Tweak.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Syntax/Tokens.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include <string>
+namespace clang {
+namespace clangd {
+namespace {
+
+/// Replaces a reference to a macro under the cursor with its expansion.
+/// Before:
+///   #define FOO(X) X+X
+///   FOO(10*a)
+///   ^^^
+/// After:
+///   #define FOO(X) X+X
+///   10*a+10*a
+class ExpandMacro : public Tweak {
+public:
+  const char *id() const override final;
+  Intent intent() const override { return Intent::Refactor; }
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<Tweak::Effect> apply(const Selection &Inputs) override;
+  std::string title() const override;
+
+private:
+  syntax::TokenBuffer::Expansion Expansion;
+  std::string MacroName;
+};
+
+REGISTER_TWEAK(ExpandMacro)
+
+/// Finds a spelled token that the cursor is pointing at.
+static const syntax::Token *
+findTokenUnderCursor(const SourceManager &SM,
+                     llvm::ArrayRef<syntax::Token> Spelled,
+                     unsigned CursorOffset) {
+  // Find the token that strats after the offset, then look at a previous one.
+  auto It = llvm::partition_point(Spelled, [&](const syntax::Token &T) {
+    assert(T.location().isFileID());
+    return SM.getFileOffset(T.location()) <= CursorOffset;
+  });
+  if (It == Spelled.begin())
+    return nullptr;
+  // Check the token we found actually touches the cursor position.
+  --It;
+  return It->range(SM).touches(CursorOffset) ? It : nullptr;
+}
+
+static const syntax::Token *
+findIdentifierUnderCursor(const syntax::TokenBuffer &Tokens,
+                          SourceLocation Cursor) {
+  assert(Cursor.isFileID());
+
+  auto &SM = Tokens.sourceManager();
+  auto Spelled = Tokens.spelledTokens(SM.getFileID(Cursor));
+
+  auto *T = findTokenUnderCursor(SM, Spelled, SM.getFileOffset(Cursor));
+  if (!T)
+    return nullptr;
+  if (T->kind() == tok::identifier)
+    return T;
+  // Also try the previous token when the cursor is at the boundary, e.g.
+  //   FOO^()
+  //   FOO^+
+  if (T == Spelled.begin())
+    return nullptr;
+  --T;
+  if (T->endLocation() != Cursor || T->kind() != tok::identifier)
+    return nullptr;
+  return T;
+}
+
+bool ExpandMacro::prepare(const Selection &Inputs) {
+  // FIXME: we currently succeed on selection at the end of the token, e.g.
+  //        'FOO[[ ]]BAR'. We should not trigger in that case.
+
+  // Find a token under the cursor.
+  auto *T = findIdentifierUnderCursor(Inputs.AST.getTokens(), Inputs.Cursor);
+  // We are interested only in identifiers, other tokens can't be macro names.
+  if (!T)
+    return false;
+  // If the identifier is a macro we will find the corresponding expansion.
+  auto Expansion = Inputs.AST.getTokens().expansionStartingAt(T);
+  if (!Expansion)
+    return false;
+  this->MacroName = T->text(Inputs.AST.getSourceManager());
+  this->Expansion = *Expansion;
+  return true;
+}
+
+Expected<Tweak::Effect> ExpandMacro::apply(const Selection &Inputs) {
+  auto &SM = Inputs.AST.getASTContext().getSourceManager();
+
+  std::string Replacement;
+  for (const syntax::Token &T : Expansion.Expanded) {
+    Replacement += T.text(SM);
+    Replacement += " ";
+  }
+  if (!Replacement.empty()) {
+    assert(Replacement.back() == ' ');
+    Replacement.pop_back();
+  }
+
+  CharSourceRange MacroRange =
+      CharSourceRange::getCharRange(Expansion.Spelled.front().location(),
+                                    Expansion.Spelled.back().endLocation());
+
+  Tweak::Effect E;
+  E.ApplyEdit.emplace();
+  llvm::cantFail(
+      E.ApplyEdit->add(tooling::Replacement(SM, MacroRange, Replacement)));
+  return E;
+}
+
+std::string ExpandMacro::title() const {
+  return llvm::formatv("Expand macro '{0}'", MacroName);
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -14,6 +14,7 @@
 add_clang_library(clangDaemonTweaks OBJECT
   AnnotateHighlightings.cpp
   DumpAST.cpp
+  ExpandMacro.cpp
   RawStringLiteral.cpp
   SwapIfBranches.cpp
 
@@ -22,4 +23,5 @@
   clangBasic
   clangDaemon
   clangToolingCore
+  clangToolingSyntax
   )
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to