/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/Tokens.h" |
11 | | #include "llvm/Support/Error.h" |
12 | | |
13 | | using namespace clang; |
14 | | |
15 | | namespace { |
16 | | using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>, |
17 | | bool /*IsOriginal*/)>; |
18 | | /// Enumerates spans of tokens from the tree consecutively laid out in memory. |
19 | 42 | void enumerateTokenSpans(const syntax::Tree *Root, ProcessTokensFn Callback) { |
20 | 42 | struct Enumerator { |
21 | 42 | Enumerator(ProcessTokensFn Callback) |
22 | 42 | : SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false), |
23 | 42 | Callback(Callback) {} |
24 | | |
25 | 42 | void run(const syntax::Tree *Root) { |
26 | 42 | process(Root); |
27 | | // Report the last span to the user. |
28 | 42 | if (SpanBegin) |
29 | 42 | Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal); |
30 | 42 | } |
31 | | |
32 | 42 | private: |
33 | 784 | void process(const syntax::Node *N) { |
34 | 784 | if (auto *T = dyn_cast<syntax::Tree>(N)) { |
35 | 1.10k | for (const auto *C = T->getFirstChild(); C != nullptr; |
36 | 742 | C = C->getNextSibling()) |
37 | 742 | process(C); |
38 | 364 | return; |
39 | 364 | } |
40 | | |
41 | 420 | auto *L = cast<syntax::Leaf>(N); |
42 | 420 | if (SpanEnd == L->getToken() && SpanIsOriginal == L->isOriginal()322 ) { |
43 | | // Extend the current span. |
44 | 322 | ++SpanEnd; |
45 | 322 | return; |
46 | 322 | } |
47 | | // Report the current span to the user. |
48 | 98 | if (SpanBegin) |
49 | 56 | Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal); |
50 | | // Start recording a new span. |
51 | 98 | SpanBegin = L->getToken(); |
52 | 98 | SpanEnd = SpanBegin + 1; |
53 | 98 | SpanIsOriginal = L->isOriginal(); |
54 | 98 | } |
55 | | |
56 | 42 | const syntax::Token *SpanBegin; |
57 | 42 | const syntax::Token *SpanEnd; |
58 | 42 | bool SpanIsOriginal; |
59 | 42 | ProcessTokensFn Callback; |
60 | 42 | }; |
61 | | |
62 | 42 | return Enumerator(Callback).run(Root); |
63 | 42 | } |
64 | | |
65 | | syntax::FileRange rangeOfExpanded(const syntax::Arena &A, |
66 | 42 | llvm::ArrayRef<syntax::Token> Expanded) { |
67 | 42 | const auto &Buffer = A.getTokenBuffer(); |
68 | 42 | const auto &SM = A.getSourceManager(); |
69 | | |
70 | | // Check that \p Expanded actually points into expanded tokens. |
71 | 42 | assert(Buffer.expandedTokens().begin() <= Expanded.begin()); |
72 | 42 | assert(Expanded.end() < Buffer.expandedTokens().end()); |
73 | | |
74 | 42 | if (Expanded.empty()) |
75 | | // (!) empty tokens must always point before end(). |
76 | 0 | return syntax::FileRange( |
77 | 0 | SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0); |
78 | | |
79 | 42 | auto Spelled = Buffer.spelledForExpanded(Expanded); |
80 | 42 | assert(Spelled && "could not find spelled tokens for expanded"); |
81 | 42 | return syntax::Token::range(SM, Spelled->front(), Spelled->back()); |
82 | 42 | } |
83 | | } // namespace |
84 | | |
85 | | tooling::Replacements |
86 | | syntax::computeReplacements(const syntax::Arena &A, |
87 | 42 | const syntax::TranslationUnit &TU) { |
88 | 42 | const auto &Buffer = A.getTokenBuffer(); |
89 | 42 | const auto &SM = A.getSourceManager(); |
90 | | |
91 | 42 | tooling::Replacements Replacements; |
92 | | // Text inserted by the replacement we are building now. |
93 | 42 | std::string Replacement; |
94 | 126 | auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) { |
95 | 126 | if (ReplacedRange.empty() && Replacement.empty()84 ) |
96 | 84 | return; |
97 | 42 | llvm::cantFail(Replacements.add(tooling::Replacement( |
98 | 42 | SM, rangeOfExpanded(A, ReplacedRange).toCharRange(SM), Replacement))); |
99 | 42 | Replacement = ""; |
100 | 42 | }; |
101 | | |
102 | 42 | const syntax::Token *NextOriginal = Buffer.expandedTokens().begin(); |
103 | 42 | enumerateTokenSpans( |
104 | 98 | &TU, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) { |
105 | 98 | if (!IsOriginal) { |
106 | 14 | Replacement += |
107 | 14 | syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM); |
108 | 14 | return; |
109 | 14 | } |
110 | 84 | assert(NextOriginal <= Tokens.begin()); |
111 | | // We are looking at a span of original tokens. |
112 | 84 | if (NextOriginal != Tokens.begin()) { |
113 | | // There is a gap, record a replacement or deletion. |
114 | 42 | emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin())); |
115 | 42 | } else { |
116 | | // No gap, but we may have pending insertions. Emit them now. |
117 | 42 | emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0)); |
118 | 42 | } |
119 | 84 | NextOriginal = Tokens.end(); |
120 | 84 | }); |
121 | | |
122 | | // We might have pending replacements at the end of file. If so, emit them. |
123 | 42 | emitReplacement(llvm::makeArrayRef( |
124 | 42 | NextOriginal, Buffer.expandedTokens().drop_back().end())); |
125 | | |
126 | 42 | return Replacements; |
127 | 42 | } |