[clang-tools-extra] r365331 - [clangd] A code tweak to expand a macro
Ilya Biryukov via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 8 08:25:16 PDT 2019
Author: ibiryukov
Date: Mon Jul 8 08:25:16 2019
New Revision: 365331
URL: http://llvm.org/viewvc/llvm-project?rev=365331&view=rev
Log:
[clangd] A code tweak to expand a macro
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgorny, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D61681
Added:
clang-tools-extra/trunk/clangd/refactor/tweaks/ExpandMacro.cpp
Modified:
clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt
clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp
Modified: clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt?rev=365331&r1=365330&r2=365331&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt Mon Jul 8 08:25:16 2019
@@ -14,6 +14,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangDaemonTweaks OBJECT
AnnotateHighlightings.cpp
DumpAST.cpp
+ ExpandMacro.cpp
RawStringLiteral.cpp
SwapIfBranches.cpp
@@ -22,4 +23,5 @@ add_clang_library(clangDaemonTweaks OBJE
clangBasic
clangDaemon
clangToolingCore
+ clangToolingSyntax
)
Added: clang-tools-extra/trunk/clangd/refactor/tweaks/ExpandMacro.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/ExpandMacro.cpp?rev=365331&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/ExpandMacro.cpp (added)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/ExpandMacro.cpp Mon Jul 8 08:25:16 2019
@@ -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
Modified: clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp?rev=365331&r1=365330&r2=365331&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp (original)
+++ clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp Mon Jul 8 08:25:16 2019
@@ -286,6 +286,99 @@ TEST(TweakTest, AnnotateHighlightings) {
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
More information about the cfe-commits
mailing list