xref: /llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp (revision a3c248db87ebe88084386950846678c9a52dd7c0)
11ad15046SIlya Biryukov //===- ComputeReplacements.cpp --------------------------------*- C++ -*-=====//
21ad15046SIlya Biryukov //
31ad15046SIlya Biryukov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41ad15046SIlya Biryukov // See https://llvm.org/LICENSE.txt for license information.
51ad15046SIlya Biryukov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61ad15046SIlya Biryukov //
71ad15046SIlya Biryukov //===----------------------------------------------------------------------===//
81ad15046SIlya Biryukov #include "clang/Tooling/Core/Replacement.h"
91ad15046SIlya Biryukov #include "clang/Tooling/Syntax/Mutations.h"
10263dcf45SHaojian Wu #include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
111ad15046SIlya Biryukov #include "clang/Tooling/Syntax/Tokens.h"
12263dcf45SHaojian Wu #include "clang/Tooling/Syntax/Tree.h"
131ad15046SIlya Biryukov #include "llvm/Support/Error.h"
141ad15046SIlya Biryukov 
151ad15046SIlya Biryukov using namespace clang;
161ad15046SIlya Biryukov 
171ad15046SIlya Biryukov namespace {
181ad15046SIlya Biryukov using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
191ad15046SIlya Biryukov                                                 bool /*IsOriginal*/)>;
201ad15046SIlya Biryukov /// Enumerates spans of tokens from the tree consecutively laid out in memory.
enumerateTokenSpans(const syntax::Tree * Root,const syntax::TokenBufferTokenManager & STM,ProcessTokensFn Callback)21263dcf45SHaojian Wu void enumerateTokenSpans(const syntax::Tree *Root,
22263dcf45SHaojian Wu                          const syntax::TokenBufferTokenManager &STM,
23263dcf45SHaojian Wu                          ProcessTokensFn Callback) {
241ad15046SIlya Biryukov   struct Enumerator {
25263dcf45SHaojian Wu     Enumerator(const syntax::TokenBufferTokenManager &STM,
26263dcf45SHaojian Wu                ProcessTokensFn Callback)
27263dcf45SHaojian Wu         : STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
281ad15046SIlya Biryukov           Callback(Callback) {}
291ad15046SIlya Biryukov 
301ad15046SIlya Biryukov     void run(const syntax::Tree *Root) {
311ad15046SIlya Biryukov       process(Root);
321ad15046SIlya Biryukov       // Report the last span to the user.
331ad15046SIlya Biryukov       if (SpanBegin)
34*a3c248dbSserge-sans-paille         Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
351ad15046SIlya Biryukov     }
361ad15046SIlya Biryukov 
371ad15046SIlya Biryukov   private:
381ad15046SIlya Biryukov     void process(const syntax::Node *N) {
391ad15046SIlya Biryukov       if (auto *T = dyn_cast<syntax::Tree>(N)) {
40238ae4eeSEduardo Caldas         for (const auto *C = T->getFirstChild(); C != nullptr;
414c14ee61SEduardo Caldas              C = C->getNextSibling())
421ad15046SIlya Biryukov           process(C);
431ad15046SIlya Biryukov         return;
441ad15046SIlya Biryukov       }
451ad15046SIlya Biryukov 
461ad15046SIlya Biryukov       auto *L = cast<syntax::Leaf>(N);
47263dcf45SHaojian Wu       if (SpanEnd == STM.getToken(L->getTokenKey()) &&
48263dcf45SHaojian Wu           SpanIsOriginal == L->isOriginal()) {
491ad15046SIlya Biryukov         // Extend the current span.
501ad15046SIlya Biryukov         ++SpanEnd;
511ad15046SIlya Biryukov         return;
521ad15046SIlya Biryukov       }
531ad15046SIlya Biryukov       // Report the current span to the user.
541ad15046SIlya Biryukov       if (SpanBegin)
55*a3c248dbSserge-sans-paille         Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
561ad15046SIlya Biryukov       // Start recording a new span.
57263dcf45SHaojian Wu       SpanBegin = STM.getToken(L->getTokenKey());
581ad15046SIlya Biryukov       SpanEnd = SpanBegin + 1;
591ad15046SIlya Biryukov       SpanIsOriginal = L->isOriginal();
601ad15046SIlya Biryukov     }
611ad15046SIlya Biryukov 
62263dcf45SHaojian Wu     const syntax::TokenBufferTokenManager &STM;
631ad15046SIlya Biryukov     const syntax::Token *SpanBegin;
641ad15046SIlya Biryukov     const syntax::Token *SpanEnd;
651ad15046SIlya Biryukov     bool SpanIsOriginal;
661ad15046SIlya Biryukov     ProcessTokensFn Callback;
671ad15046SIlya Biryukov   };
681ad15046SIlya Biryukov 
69263dcf45SHaojian Wu   return Enumerator(STM, Callback).run(Root);
701ad15046SIlya Biryukov }
711ad15046SIlya Biryukov 
rangeOfExpanded(const syntax::TokenBufferTokenManager & STM,llvm::ArrayRef<syntax::Token> Expanded)72263dcf45SHaojian Wu syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
731ad15046SIlya Biryukov                                   llvm::ArrayRef<syntax::Token> Expanded) {
74263dcf45SHaojian Wu   const auto &Buffer = STM.tokenBuffer();
75263dcf45SHaojian Wu   const auto &SM = STM.sourceManager();
761ad15046SIlya Biryukov 
771ad15046SIlya Biryukov   // Check that \p Expanded actually points into expanded tokens.
781ad15046SIlya Biryukov   assert(Buffer.expandedTokens().begin() <= Expanded.begin());
791ad15046SIlya Biryukov   assert(Expanded.end() < Buffer.expandedTokens().end());
801ad15046SIlya Biryukov 
811ad15046SIlya Biryukov   if (Expanded.empty())
821ad15046SIlya Biryukov     // (!) empty tokens must always point before end().
831ad15046SIlya Biryukov     return syntax::FileRange(
841ad15046SIlya Biryukov         SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
851ad15046SIlya Biryukov 
861ad15046SIlya Biryukov   auto Spelled = Buffer.spelledForExpanded(Expanded);
871ad15046SIlya Biryukov   assert(Spelled && "could not find spelled tokens for expanded");
881ad15046SIlya Biryukov   return syntax::Token::range(SM, Spelled->front(), Spelled->back());
891ad15046SIlya Biryukov }
901ad15046SIlya Biryukov } // namespace
911ad15046SIlya Biryukov 
921ad15046SIlya Biryukov tooling::Replacements
computeReplacements(const TokenBufferTokenManager & TBTM,const syntax::TranslationUnit & TU)93263dcf45SHaojian Wu syntax::computeReplacements(const TokenBufferTokenManager &TBTM,
941ad15046SIlya Biryukov                             const syntax::TranslationUnit &TU) {
95263dcf45SHaojian Wu   const auto &Buffer = TBTM.tokenBuffer();
96263dcf45SHaojian Wu   const auto &SM = TBTM.sourceManager();
971ad15046SIlya Biryukov 
981ad15046SIlya Biryukov   tooling::Replacements Replacements;
991ad15046SIlya Biryukov   // Text inserted by the replacement we are building now.
1001ad15046SIlya Biryukov   std::string Replacement;
1011ad15046SIlya Biryukov   auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
1021ad15046SIlya Biryukov     if (ReplacedRange.empty() && Replacement.empty())
1031ad15046SIlya Biryukov       return;
1041ad15046SIlya Biryukov     llvm::cantFail(Replacements.add(tooling::Replacement(
105263dcf45SHaojian Wu         SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM),
106263dcf45SHaojian Wu         Replacement)));
1071ad15046SIlya Biryukov     Replacement = "";
1081ad15046SIlya Biryukov   };
1091ad15046SIlya Biryukov   const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
1101ad15046SIlya Biryukov   enumerateTokenSpans(
111263dcf45SHaojian Wu       &TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
1121ad15046SIlya Biryukov         if (!IsOriginal) {
1131ad15046SIlya Biryukov           Replacement +=
1141ad15046SIlya Biryukov               syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
1151ad15046SIlya Biryukov           return;
1161ad15046SIlya Biryukov         }
1171ad15046SIlya Biryukov         assert(NextOriginal <= Tokens.begin());
1181ad15046SIlya Biryukov         // We are looking at a span of original tokens.
1191ad15046SIlya Biryukov         if (NextOriginal != Tokens.begin()) {
1201ad15046SIlya Biryukov           // There is a gap, record a replacement or deletion.
121*a3c248dbSserge-sans-paille           emitReplacement(llvm::ArrayRef(NextOriginal, Tokens.begin()));
1221ad15046SIlya Biryukov         } else {
1231ad15046SIlya Biryukov           // No gap, but we may have pending insertions. Emit them now.
124*a3c248dbSserge-sans-paille           emitReplacement(llvm::ArrayRef(NextOriginal, /*Length=*/(size_t)0));
1251ad15046SIlya Biryukov         }
1261ad15046SIlya Biryukov         NextOriginal = Tokens.end();
1271ad15046SIlya Biryukov       });
1281ad15046SIlya Biryukov 
1291ad15046SIlya Biryukov   // We might have pending replacements at the end of file. If so, emit them.
130*a3c248dbSserge-sans-paille   emitReplacement(
131*a3c248dbSserge-sans-paille       llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
1321ad15046SIlya Biryukov 
1331ad15046SIlya Biryukov   return Replacements;
1341ad15046SIlya Biryukov }
135