/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 42 | ProcessTokensFn Callback) { |
24 | 42 | struct Enumerator { |
25 | 42 | Enumerator(const syntax::TokenBufferTokenManager &STM, |
26 | 42 | ProcessTokensFn Callback) |
27 | 42 | : STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false), |
28 | 42 | Callback(Callback) {} |
29 | | |
30 | 42 | void run(const syntax::Tree *Root) { |
31 | 42 | process(Root); |
32 | | // Report the last span to the user. |
33 | 42 | if (SpanBegin) |
34 | 42 | Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal); |
35 | 42 | } |
36 | | |
37 | 42 | private: |
38 | 798 | void process(const syntax::Node *N) { |
39 | 798 | if (auto *T = dyn_cast<syntax::Tree>(N)) { |
40 | 1.13k | for (const auto *C = T->getFirstChild(); C != nullptr; |
41 | 756 | C = C->getNextSibling()) |
42 | 756 | process(C); |
43 | 378 | return; |
44 | 378 | } |
45 | | |
46 | 420 | auto *L = cast<syntax::Leaf>(N); |
47 | 420 | if (SpanEnd == STM.getToken(L->getTokenKey()) && |
48 | 420 | SpanIsOriginal == L->isOriginal()322 ) { |
49 | | // Extend the current span. |
50 | 322 | ++SpanEnd; |
51 | 322 | return; |
52 | 322 | } |
53 | | // Report the current span to the user. |
54 | 98 | if (SpanBegin) |
55 | 56 | Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal); |
56 | | // Start recording a new span. |
57 | 98 | SpanBegin = STM.getToken(L->getTokenKey()); |
58 | 98 | SpanEnd = SpanBegin + 1; |
59 | 98 | SpanIsOriginal = L->isOriginal(); |
60 | 98 | } |
61 | | |
62 | 42 | const syntax::TokenBufferTokenManager &STM; |
63 | 42 | const syntax::Token *SpanBegin; |
64 | 42 | const syntax::Token *SpanEnd; |
65 | 42 | bool SpanIsOriginal; |
66 | 42 | ProcessTokensFn Callback; |
67 | 42 | }; |
68 | | |
69 | 42 | return Enumerator(STM, Callback).run(Root); |
70 | 42 | } |
71 | | |
72 | | syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM, |
73 | 42 | llvm::ArrayRef<syntax::Token> Expanded) { |
74 | 42 | const auto &Buffer = STM.tokenBuffer(); |
75 | 42 | const auto &SM = STM.sourceManager(); |
76 | | |
77 | | // Check that \p Expanded actually points into expanded tokens. |
78 | 42 | assert(Buffer.expandedTokens().begin() <= Expanded.begin()); |
79 | 0 | assert(Expanded.end() < Buffer.expandedTokens().end()); |
80 | | |
81 | 42 | if (Expanded.empty()) |
82 | | // (!) empty tokens must always point before end(). |
83 | 0 | return syntax::FileRange( |
84 | 0 | SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0); |
85 | | |
86 | 42 | auto Spelled = Buffer.spelledForExpanded(Expanded); |
87 | 42 | assert(Spelled && "could not find spelled tokens for expanded"); |
88 | 0 | return syntax::Token::range(SM, Spelled->front(), Spelled->back()); |
89 | 42 | } |
90 | | } // namespace |
91 | | |
92 | | tooling::Replacements |
93 | | syntax::computeReplacements(const TokenBufferTokenManager &TBTM, |
94 | 42 | const syntax::TranslationUnit &TU) { |
95 | 42 | const auto &Buffer = TBTM.tokenBuffer(); |
96 | 42 | const auto &SM = TBTM.sourceManager(); |
97 | | |
98 | 42 | tooling::Replacements Replacements; |
99 | | // Text inserted by the replacement we are building now. |
100 | 42 | std::string Replacement; |
101 | 126 | auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) { |
102 | 126 | if (ReplacedRange.empty() && Replacement.empty()84 ) |
103 | 84 | return; |
104 | 42 | llvm::cantFail(Replacements.add(tooling::Replacement( |
105 | 42 | SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM), |
106 | 42 | Replacement))); |
107 | 42 | Replacement = ""; |
108 | 42 | }; |
109 | 42 | const syntax::Token *NextOriginal = Buffer.expandedTokens().begin(); |
110 | 42 | enumerateTokenSpans( |
111 | 98 | &TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) { |
112 | 98 | if (!IsOriginal) { |
113 | 14 | Replacement += |
114 | 14 | syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM); |
115 | 14 | return; |
116 | 14 | } |
117 | 84 | assert(NextOriginal <= Tokens.begin()); |
118 | | // We are looking at a span of original tokens. |
119 | 84 | if (NextOriginal != Tokens.begin()) { |
120 | | // There is a gap, record a replacement or deletion. |
121 | 42 | emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin())); |
122 | 42 | } else { |
123 | | // No gap, but we may have pending insertions. Emit them now. |
124 | 42 | emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0)); |
125 | 42 | } |
126 | 84 | NextOriginal = Tokens.end(); |
127 | 84 | }); |
128 | | |
129 | | // We might have pending replacements at the end of file. If so, emit them. |
130 | 42 | emitReplacement(llvm::makeArrayRef( |
131 | 42 | NextOriginal, Buffer.expandedTokens().drop_back().end())); |
132 | | |
133 | 42 | return Replacements; |
134 | 42 | } |