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