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"
31 class ExpandMacro :
public Tweak {
33 const char *id() const override final;
34 Intent intent()
const override {
return Intent::Refactor; }
36 bool prepare(
const Selection &
Inputs)
override;
37 Expected<Tweak::Effect> apply(
const Selection &
Inputs)
override;
38 std::string title()
const override;
41 syntax::TokenBuffer::Expansion Expansion;
42 std::string MacroName;
48 static const syntax::Token *
49 findTokenUnderCursor(
const SourceManager &SM,
50 llvm::ArrayRef<syntax::Token> Spelled,
51 unsigned CursorOffset) {
53 auto It = llvm::partition_point(Spelled, [&](
const syntax::Token &T) {
54 assert(T.location().isFileID());
55 return SM.getFileOffset(T.location()) <= CursorOffset;
61 return It->range(SM).touches(CursorOffset) ? It :
nullptr;
64 static const syntax::Token *
65 findIdentifierUnderCursor(
const syntax::TokenBuffer &Tokens,
66 SourceLocation Cursor) {
67 assert(Cursor.isFileID());
69 auto &SM = Tokens.sourceManager();
70 auto Spelled = Tokens.spelledTokens(SM.getFileID(Cursor));
72 auto *T = findTokenUnderCursor(SM, Spelled, SM.getFileOffset(Cursor));
75 if (T->kind() == tok::identifier)
83 if (T->endLocation() != Cursor || T->kind() != tok::identifier)
88 bool ExpandMacro::prepare(
const Selection &
Inputs) {
93 auto *T = findIdentifierUnderCursor(
Inputs.AST->getTokens(),
Inputs.Cursor);
98 auto Expansion =
Inputs.AST->getTokens().expansionStartingAt(T);
101 this->MacroName = std::string(T->text(
Inputs.AST->getSourceManager()));
102 this->Expansion = *Expansion;
106 Expected<Tweak::Effect> ExpandMacro::apply(
const Selection &
Inputs) {
107 auto &SM =
Inputs.AST->getSourceManager();
109 std::string Replacement;
110 for (
const syntax::Token &T : Expansion.Expanded) {
111 Replacement += T.text(SM);
114 if (!Replacement.empty()) {
115 assert(Replacement.back() ==
' ');
116 Replacement.pop_back();
119 CharSourceRange MacroRange =
120 CharSourceRange::getCharRange(Expansion.Spelled.front().location(),
121 Expansion.Spelled.back().endLocation());
123 tooling::Replacements Reps;
124 llvm::cantFail(Reps.add(tooling::Replacement(SM, MacroRange, Replacement)));
125 return Effect::mainFileEdit(SM, std::move(Reps));
128 std::string ExpandMacro::title()
const {
129 return std::string(llvm::formatv(
"Expand macro '{0}'", MacroName));