Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
35271 views
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
//===----------------------------------------------------------------------===//
8
#include "clang/Tooling/Core/Replacement.h"
9
#include "clang/Tooling/Syntax/Mutations.h"
10
#include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
11
#include "clang/Tooling/Syntax/Tokens.h"
12
#include "clang/Tooling/Syntax/Tree.h"
13
#include "llvm/Support/Error.h"
14
15
using namespace clang;
16
17
namespace {
18
using 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.
21
void enumerateTokenSpans(const syntax::Tree *Root,
22
const syntax::TokenBufferTokenManager &STM,
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
62
const syntax::TokenBufferTokenManager &STM;
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
72
syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
73
llvm::ArrayRef<syntax::Token> Expanded) {
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
92
tooling::Replacements
93
syntax::computeReplacements(const TokenBufferTokenManager &TBTM,
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.
130
emitReplacement(
131
llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
132
133
return Replacements;
134
}
135
136