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