clang-tools 10.0.0

ExpandMacro.cpp
Go to the documentation of this file.
1 //===--- ExpandMacro.cpp -----------------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "refactor/Tweak.h"
10 #include "clang/Basic/SourceLocation.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Basic/TokenKinds.h"
13 #include "clang/Tooling/Core/Replacement.h"
14 #include "clang/Tooling/Syntax/Tokens.h"
15 #include "llvm/ADT/ArrayRef.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/Support/Error.h"
18 #include <string>
19 namespace clang {
20 namespace clangd {
21 namespace {
22
23 /// Replaces a reference to a macro under the cursor with its expansion.
24 /// Before:
25 /// #define FOO(X) X+X
26 /// FOO(10*a)
27 /// ^^^
28 /// After:
29 /// #define FOO(X) X+X
30 /// 10*a+10*a
31 class ExpandMacro : public Tweak {
32 public:
33 const char *id() const override final;
34 Intent intent() const override { return Intent::Refactor; }
35
36 bool prepare(const Selection &Inputs) override;
37 Expected<Tweak::Effect> apply(const Selection &Inputs) override;
38 std::string title() const override;
39
40 private:
41 syntax::TokenBuffer::Expansion Expansion;
42 std::string MacroName;
43 };
44
45 REGISTER_TWEAK(ExpandMacro)
46
47 /// Finds a spelled token that the cursor is pointing at.
48 static const syntax::Token *
49 findTokenUnderCursor(const SourceManager &SM,
50 llvm::ArrayRef<syntax::Token> Spelled,
51 unsigned CursorOffset) {
52 // Find the token that strats after the offset, then look at a previous one.
53 auto It = llvm::partition_point(Spelled, [&](const syntax::Token &T) {
54 assert(T.location().isFileID());
55 return SM.getFileOffset(T.location()) <= CursorOffset;
56 });
57 if (It == Spelled.begin())
58 return nullptr;
59 // Check the token we found actually touches the cursor position.
60 --It;
61 return It->range(SM).touches(CursorOffset) ? It : nullptr;
62 }
63
64 static const syntax::Token *
65 findIdentifierUnderCursor(const syntax::TokenBuffer &Tokens,
66 SourceLocation Cursor) {
67 assert(Cursor.isFileID());
68
69 auto &SM = Tokens.sourceManager();
70 auto Spelled = Tokens.spelledTokens(SM.getFileID(Cursor));
71
72 auto *T = findTokenUnderCursor(SM, Spelled, SM.getFileOffset(Cursor));
73 if (!T)
74 return nullptr;
75 if (T->kind() == tok::identifier)
76 return T;
77 // Also try the previous token when the cursor is at the boundary, e.g.
78 // FOO^()
79 // FOO^+
80 if (T == Spelled.begin())
81 return nullptr;
82 --T;
83 if (T->endLocation() != Cursor || T->kind() != tok::identifier)
84 return nullptr;
85 return T;
86 }
87
88 bool ExpandMacro::prepare(const Selection &Inputs) {
89 // FIXME: we currently succeed on selection at the end of the token, e.g.
90 // 'FOO[[ ]]BAR'. We should not trigger in that case.
91
92 // Find a token under the cursor.
93 auto *T = findIdentifierUnderCursor(Inputs.AST->getTokens(), Inputs.Cursor);
94 // We are interested only in identifiers, other tokens can't be macro names.
95 if (!T)
96 return false;
97 // If the identifier is a macro we will find the corresponding expansion.
98 auto Expansion = Inputs.AST->getTokens().expansionStartingAt(T);
99 if (!Expansion)
100 return false;
101 this->MacroName = T->text(Inputs.AST->getSourceManager());
102 this->Expansion = *Expansion;
103 return true;
104 }
105
106 Expected<Tweak::Effect> ExpandMacro::apply(const Selection &Inputs) {
107 auto &SM = Inputs.AST->getSourceManager();
108
109 std::string Replacement;
110 for (const syntax::Token &T : Expansion.Expanded) {
111 Replacement += T.text(SM);
112 Replacement += " ";
113 }
114 if (!Replacement.empty()) {
115 assert(Replacement.back() == ' ');
116 Replacement.pop_back();
117 }
118
119 CharSourceRange MacroRange =
120 CharSourceRange::getCharRange(Expansion.Spelled.front().location(),
121 Expansion.Spelled.back().endLocation());
122
123 tooling::Replacements Reps;
124 llvm::cantFail(Reps.add(tooling::Replacement(SM, MacroRange, Replacement)));
125 return Effect::mainFileEdit(SM, std::move(Reps));
126 }
127
128 std::string ExpandMacro::title() const {
129 return llvm::formatv("Expand macro '{0}'", MacroName);
130 }
131
132 } // namespace
133 } // namespace clangd
134 } // namespace clang
REGISTER_TWEAK
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
Tweak.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27