xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1480093f4SDimitry Andric //===- ComputeReplacements.cpp --------------------------------*- C++ -*-=====//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric #include "clang/Tooling/Core/Replacement.h"
9480093f4SDimitry Andric #include "clang/Tooling/Syntax/Mutations.h"
10fcaf7f86SDimitry Andric #include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
11480093f4SDimitry Andric #include "clang/Tooling/Syntax/Tokens.h"
12fcaf7f86SDimitry Andric #include "clang/Tooling/Syntax/Tree.h"
13480093f4SDimitry Andric #include "llvm/Support/Error.h"
14480093f4SDimitry Andric 
15480093f4SDimitry Andric using namespace clang;
16480093f4SDimitry Andric 
17480093f4SDimitry Andric namespace {
18480093f4SDimitry Andric using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
19480093f4SDimitry Andric                                                 bool /*IsOriginal*/)>;
20480093f4SDimitry Andric /// Enumerates spans of tokens from the tree consecutively laid out in memory.
enumerateTokenSpans(const syntax::Tree * Root,const syntax::TokenBufferTokenManager & STM,ProcessTokensFn Callback)21fcaf7f86SDimitry Andric void enumerateTokenSpans(const syntax::Tree *Root,
22fcaf7f86SDimitry Andric                          const syntax::TokenBufferTokenManager &STM,
23fcaf7f86SDimitry Andric                          ProcessTokensFn Callback) {
24480093f4SDimitry Andric   struct Enumerator {
25fcaf7f86SDimitry Andric     Enumerator(const syntax::TokenBufferTokenManager &STM,
26fcaf7f86SDimitry Andric                ProcessTokensFn Callback)
27fcaf7f86SDimitry Andric         : STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
28480093f4SDimitry Andric           Callback(Callback) {}
29480093f4SDimitry Andric 
30480093f4SDimitry Andric     void run(const syntax::Tree *Root) {
31480093f4SDimitry Andric       process(Root);
32480093f4SDimitry Andric       // Report the last span to the user.
33480093f4SDimitry Andric       if (SpanBegin)
34*bdd1243dSDimitry Andric         Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
35480093f4SDimitry Andric     }
36480093f4SDimitry Andric 
37480093f4SDimitry Andric   private:
38480093f4SDimitry Andric     void process(const syntax::Node *N) {
39480093f4SDimitry Andric       if (auto *T = dyn_cast<syntax::Tree>(N)) {
40e8d8bef9SDimitry Andric         for (const auto *C = T->getFirstChild(); C != nullptr;
41e8d8bef9SDimitry Andric              C = C->getNextSibling())
42480093f4SDimitry Andric           process(C);
43480093f4SDimitry Andric         return;
44480093f4SDimitry Andric       }
45480093f4SDimitry Andric 
46480093f4SDimitry Andric       auto *L = cast<syntax::Leaf>(N);
47fcaf7f86SDimitry Andric       if (SpanEnd == STM.getToken(L->getTokenKey()) &&
48fcaf7f86SDimitry Andric           SpanIsOriginal == L->isOriginal()) {
49480093f4SDimitry Andric         // Extend the current span.
50480093f4SDimitry Andric         ++SpanEnd;
51480093f4SDimitry Andric         return;
52480093f4SDimitry Andric       }
53480093f4SDimitry Andric       // Report the current span to the user.
54480093f4SDimitry Andric       if (SpanBegin)
55*bdd1243dSDimitry Andric         Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
56480093f4SDimitry Andric       // Start recording a new span.
57fcaf7f86SDimitry Andric       SpanBegin = STM.getToken(L->getTokenKey());
58480093f4SDimitry Andric       SpanEnd = SpanBegin + 1;
59480093f4SDimitry Andric       SpanIsOriginal = L->isOriginal();
60480093f4SDimitry Andric     }
61480093f4SDimitry Andric 
62fcaf7f86SDimitry Andric     const syntax::TokenBufferTokenManager &STM;
63480093f4SDimitry Andric     const syntax::Token *SpanBegin;
64480093f4SDimitry Andric     const syntax::Token *SpanEnd;
65480093f4SDimitry Andric     bool SpanIsOriginal;
66480093f4SDimitry Andric     ProcessTokensFn Callback;
67480093f4SDimitry Andric   };
68480093f4SDimitry Andric 
69fcaf7f86SDimitry Andric   return Enumerator(STM, Callback).run(Root);
70480093f4SDimitry Andric }
71480093f4SDimitry Andric 
rangeOfExpanded(const syntax::TokenBufferTokenManager & STM,llvm::ArrayRef<syntax::Token> Expanded)72fcaf7f86SDimitry Andric syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
73480093f4SDimitry Andric                                   llvm::ArrayRef<syntax::Token> Expanded) {
74fcaf7f86SDimitry Andric   const auto &Buffer = STM.tokenBuffer();
75fcaf7f86SDimitry Andric   const auto &SM = STM.sourceManager();
76480093f4SDimitry Andric 
77480093f4SDimitry Andric   // Check that \p Expanded actually points into expanded tokens.
78480093f4SDimitry Andric   assert(Buffer.expandedTokens().begin() <= Expanded.begin());
79480093f4SDimitry Andric   assert(Expanded.end() < Buffer.expandedTokens().end());
80480093f4SDimitry Andric 
81480093f4SDimitry Andric   if (Expanded.empty())
82480093f4SDimitry Andric     // (!) empty tokens must always point before end().
83480093f4SDimitry Andric     return syntax::FileRange(
84480093f4SDimitry Andric         SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
85480093f4SDimitry Andric 
86480093f4SDimitry Andric   auto Spelled = Buffer.spelledForExpanded(Expanded);
87480093f4SDimitry Andric   assert(Spelled && "could not find spelled tokens for expanded");
88480093f4SDimitry Andric   return syntax::Token::range(SM, Spelled->front(), Spelled->back());
89480093f4SDimitry Andric }
90480093f4SDimitry Andric } // namespace
91480093f4SDimitry Andric 
92480093f4SDimitry Andric tooling::Replacements
computeReplacements(const TokenBufferTokenManager & TBTM,const syntax::TranslationUnit & TU)93fcaf7f86SDimitry Andric syntax::computeReplacements(const TokenBufferTokenManager &TBTM,
94480093f4SDimitry Andric                             const syntax::TranslationUnit &TU) {
95fcaf7f86SDimitry Andric   const auto &Buffer = TBTM.tokenBuffer();
96fcaf7f86SDimitry Andric   const auto &SM = TBTM.sourceManager();
97480093f4SDimitry Andric 
98480093f4SDimitry Andric   tooling::Replacements Replacements;
99480093f4SDimitry Andric   // Text inserted by the replacement we are building now.
100480093f4SDimitry Andric   std::string Replacement;
101480093f4SDimitry Andric   auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
102480093f4SDimitry Andric     if (ReplacedRange.empty() && Replacement.empty())
103480093f4SDimitry Andric       return;
104480093f4SDimitry Andric     llvm::cantFail(Replacements.add(tooling::Replacement(
105fcaf7f86SDimitry Andric         SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM),
106fcaf7f86SDimitry Andric         Replacement)));
107480093f4SDimitry Andric     Replacement = "";
108480093f4SDimitry Andric   };
109480093f4SDimitry Andric   const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
110480093f4SDimitry Andric   enumerateTokenSpans(
111fcaf7f86SDimitry Andric       &TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
112480093f4SDimitry Andric         if (!IsOriginal) {
113480093f4SDimitry Andric           Replacement +=
114480093f4SDimitry Andric               syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
115480093f4SDimitry Andric           return;
116480093f4SDimitry Andric         }
117480093f4SDimitry Andric         assert(NextOriginal <= Tokens.begin());
118480093f4SDimitry Andric         // We are looking at a span of original tokens.
119480093f4SDimitry Andric         if (NextOriginal != Tokens.begin()) {
120480093f4SDimitry Andric           // There is a gap, record a replacement or deletion.
121*bdd1243dSDimitry Andric           emitReplacement(llvm::ArrayRef(NextOriginal, Tokens.begin()));
122480093f4SDimitry Andric         } else {
123480093f4SDimitry Andric           // No gap, but we may have pending insertions. Emit them now.
124*bdd1243dSDimitry Andric           emitReplacement(llvm::ArrayRef(NextOriginal, /*Length=*/(size_t)0));
125480093f4SDimitry Andric         }
126480093f4SDimitry Andric         NextOriginal = Tokens.end();
127480093f4SDimitry Andric       });
128480093f4SDimitry Andric 
129480093f4SDimitry Andric   // We might have pending replacements at the end of file. If so, emit them.
130*bdd1243dSDimitry Andric   emitReplacement(
131*bdd1243dSDimitry Andric       llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
132480093f4SDimitry Andric 
133480093f4SDimitry Andric   return Replacements;
134480093f4SDimitry Andric }
135