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