1 //===- ComputeReplacements.cpp --------------------------------*- C++ -*-=====//
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
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"
15 using namespace clang
;
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
) {
25 Enumerator(const syntax::TokenBufferTokenManager
&STM
,
26 ProcessTokensFn Callback
)
27 : STM(STM
), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
30 void run(const syntax::Tree
*Root
) {
32 // Report the last span to the user.
34 Callback(llvm::ArrayRef(SpanBegin
, SpanEnd
), SpanIsOriginal
);
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())
46 auto *L
= cast
<syntax::Leaf
>(N
);
47 if (SpanEnd
== STM
.getToken(L
->getTokenKey()) &&
48 SpanIsOriginal
== L
->isOriginal()) {
49 // Extend the current span.
53 // Report the current span to the user.
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();
62 const syntax::TokenBufferTokenManager
&STM
;
63 const syntax::Token
*SpanBegin
;
64 const syntax::Token
*SpanEnd
;
66 ProcessTokensFn Callback
;
69 return Enumerator(STM
, Callback
).run(Root
);
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();
77 // Check that \p Expanded actually points into expanded tokens.
78 assert(Buffer
.expandedTokens().begin() <= Expanded
.begin());
79 assert(Expanded
.end() < Buffer
.expandedTokens().end());
82 // (!) empty tokens must always point before end().
83 return syntax::FileRange(
84 SM
, SM
.getExpansionLoc(Expanded
.begin()->location()), /*Length=*/0);
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());
93 syntax::computeReplacements(const TokenBufferTokenManager
&TBTM
,
94 const syntax::TranslationUnit
&TU
) {
95 const auto &Buffer
= TBTM
.tokenBuffer();
96 const auto &SM
= TBTM
.sourceManager();
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())
104 llvm::cantFail(Replacements
.add(tooling::Replacement(
105 SM
, rangeOfExpanded(TBTM
, ReplacedRange
).toCharRange(SM
),
109 const syntax::Token
*NextOriginal
= Buffer
.expandedTokens().begin();
111 &TU
, TBTM
, [&](llvm::ArrayRef
<syntax::Token
> Tokens
, bool IsOriginal
) {
114 syntax::Token::range(SM
, Tokens
.front(), Tokens
.back()).text(SM
);
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()));
123 // No gap, but we may have pending insertions. Emit them now.
124 emitReplacement(llvm::ArrayRef(NextOriginal
, /*Length=*/(size_t)0));
126 NextOriginal
= Tokens
.end();
129 // We might have pending replacements at the end of file. If so, emit them.
131 llvm::ArrayRef(NextOriginal
, Buffer
.expandedTokens().drop_back().end()));