clang 20.0.0git
ComputeReplacements.cpp
Go to the documentation of this file.
1//===- ComputeReplacements.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//===----------------------------------------------------------------------===//
13#include "llvm/Support/Error.h"
14
15using namespace clang;
16
17namespace {
18using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
19 bool /*IsOriginal*/)>;
20/// Enumerates spans of tokens from the tree consecutively laid out in memory.
21void enumerateTokenSpans(const syntax::Tree *Root,
23 ProcessTokensFn Callback) {
24 struct Enumerator {
25 Enumerator(const syntax::TokenBufferTokenManager &STM,
26 ProcessTokensFn Callback)
27 : STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
28 Callback(Callback) {}
29
30 void run(const syntax::Tree *Root) {
31 process(Root);
32 // Report the last span to the user.
33 if (SpanBegin)
34 Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
35 }
36
37 private:
38 void process(const syntax::Node *N) {
39 if (auto *T = dyn_cast<syntax::Tree>(N)) {
40 for (const auto *C = T->getFirstChild(); C != nullptr;
41 C = C->getNextSibling())
42 process(C);
43 return;
44 }
45
46 auto *L = cast<syntax::Leaf>(N);
47 if (SpanEnd == STM.getToken(L->getTokenKey()) &&
48 SpanIsOriginal == L->isOriginal()) {
49 // Extend the current span.
50 ++SpanEnd;
51 return;
52 }
53 // Report the current span to the user.
54 if (SpanBegin)
55 Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
56 // Start recording a new span.
57 SpanBegin = STM.getToken(L->getTokenKey());
58 SpanEnd = SpanBegin + 1;
59 SpanIsOriginal = L->isOriginal();
60 }
61
63 const syntax::Token *SpanBegin;
64 const syntax::Token *SpanEnd;
65 bool SpanIsOriginal;
66 ProcessTokensFn Callback;
67 };
68
69 return Enumerator(STM, Callback).run(Root);
70}
71
72syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
74 const auto &Buffer = STM.tokenBuffer();
75 const auto &SM = STM.sourceManager();
76
77 // Check that \p Expanded actually points into expanded tokens.
78 assert(Buffer.expandedTokens().begin() <= Expanded.begin());
79 assert(Expanded.end() < Buffer.expandedTokens().end());
80
81 if (Expanded.empty())
82 // (!) empty tokens must always point before end().
83 return syntax::FileRange(
84 SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
85
86 auto Spelled = Buffer.spelledForExpanded(Expanded);
87 assert(Spelled && "could not find spelled tokens for expanded");
88 return syntax::Token::range(SM, Spelled->front(), Spelled->back());
89}
90} // namespace
91
94 const syntax::TranslationUnit &TU) {
95 const auto &Buffer = TBTM.tokenBuffer();
96 const auto &SM = TBTM.sourceManager();
97
98 tooling::Replacements Replacements;
99 // Text inserted by the replacement we are building now.
100 std::string Replacement;
101 auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
102 if (ReplacedRange.empty() && Replacement.empty())
103 return;
104 llvm::cantFail(Replacements.add(tooling::Replacement(
105 SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM),
106 Replacement)));
107 Replacement = "";
108 };
109 const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
110 enumerateTokenSpans(
111 &TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
112 if (!IsOriginal) {
113 Replacement +=
114 syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
115 return;
116 }
117 assert(NextOriginal <= Tokens.begin());
118 // We are looking at a span of original tokens.
119 if (NextOriginal != Tokens.begin()) {
120 // There is a gap, record a replacement or deletion.
121 emitReplacement(llvm::ArrayRef(NextOriginal, Tokens.begin()));
122 } else {
123 // No gap, but we may have pending insertions. Emit them now.
124 emitReplacement(llvm::ArrayRef(NextOriginal, /*Length=*/(size_t)0));
125 }
126 NextOriginal = Tokens.end();
127 });
128
129 // We might have pending replacements at the end of file. If so, emit them.
131 llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
132
133 return Replacements;
134}
#define SM(sm)
Definition: Cuda.cpp:83
static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range, unsigned AbsKind, QualType ArgType)
A node in a syntax tree.
Definition: Tree.h:54
A TokenBuffer-powered token manager.
const syntax::Token * getToken(Key I) const
A token coming directly from a file or from a macro invocation.
Definition: Tokens.h:103
FileRange range(const SourceManager &SM) const
Gets a range of this token.
Definition: Tokens.cpp:161
A node that has children and represents a syntactic language construct.
Definition: Tree.h:144
A text replacement.
Definition: Replacement.h:83
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:212
llvm::Error add(const Replacement &R)
Adds a new replacement R to the current set of replacements.
tooling::Replacements computeReplacements(const TokenBufferTokenManager &TBTM, const syntax::TranslationUnit &TU)
Computes textual replacements required to mimic the tree modifications made to the syntax tree.
Stencil run(MatchConsumer< std::string > C)
Wraps a MatchConsumer in a Stencil, so that it can be used in a Stencil.
Definition: Stencil.cpp:485
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
#define false
Definition: stdbool.h:26
A half-open character range inside a particular file, the start offset is included and the end offset...
Definition: Tokens.h:50
llvm::StringRef text(const SourceManager &SM) const
Gets the substring that this FileRange refers to.
Definition: Tokens.cpp:218