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" 10*fcaf7f86SDimitry Andric #include "clang/Tooling/Syntax/TokenBufferTokenManager.h" 11480093f4SDimitry Andric #include "clang/Tooling/Syntax/Tokens.h" 12*fcaf7f86SDimitry 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. 21*fcaf7f86SDimitry Andric void enumerateTokenSpans(const syntax::Tree *Root, 22*fcaf7f86SDimitry Andric const syntax::TokenBufferTokenManager &STM, 23*fcaf7f86SDimitry Andric ProcessTokensFn Callback) { 24480093f4SDimitry Andric struct Enumerator { 25*fcaf7f86SDimitry Andric Enumerator(const syntax::TokenBufferTokenManager &STM, 26*fcaf7f86SDimitry Andric ProcessTokensFn Callback) 27*fcaf7f86SDimitry 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) 34480093f4SDimitry Andric Callback(llvm::makeArrayRef(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); 47*fcaf7f86SDimitry Andric if (SpanEnd == STM.getToken(L->getTokenKey()) && 48*fcaf7f86SDimitry 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) 55480093f4SDimitry Andric Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal); 56480093f4SDimitry Andric // Start recording a new span. 57*fcaf7f86SDimitry Andric SpanBegin = STM.getToken(L->getTokenKey()); 58480093f4SDimitry Andric SpanEnd = SpanBegin + 1; 59480093f4SDimitry Andric SpanIsOriginal = L->isOriginal(); 60480093f4SDimitry Andric } 61480093f4SDimitry Andric 62*fcaf7f86SDimitry 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 69*fcaf7f86SDimitry Andric return Enumerator(STM, Callback).run(Root); 70480093f4SDimitry Andric } 71480093f4SDimitry Andric 72*fcaf7f86SDimitry Andric syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM, 73480093f4SDimitry Andric llvm::ArrayRef<syntax::Token> Expanded) { 74*fcaf7f86SDimitry Andric const auto &Buffer = STM.tokenBuffer(); 75*fcaf7f86SDimitry 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 93*fcaf7f86SDimitry Andric syntax::computeReplacements(const TokenBufferTokenManager &TBTM, 94480093f4SDimitry Andric const syntax::TranslationUnit &TU) { 95*fcaf7f86SDimitry Andric const auto &Buffer = TBTM.tokenBuffer(); 96*fcaf7f86SDimitry 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( 105*fcaf7f86SDimitry Andric SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM), 106*fcaf7f86SDimitry Andric Replacement))); 107480093f4SDimitry Andric Replacement = ""; 108480093f4SDimitry Andric }; 109480093f4SDimitry Andric const syntax::Token *NextOriginal = Buffer.expandedTokens().begin(); 110480093f4SDimitry Andric enumerateTokenSpans( 111*fcaf7f86SDimitry 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. 121480093f4SDimitry Andric emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin())); 122480093f4SDimitry Andric } else { 123480093f4SDimitry Andric // No gap, but we may have pending insertions. Emit them now. 124480093f4SDimitry Andric emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/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. 130480093f4SDimitry Andric emitReplacement(llvm::makeArrayRef( 131480093f4SDimitry Andric NextOriginal, Buffer.expandedTokens().drop_back().end())); 132480093f4SDimitry Andric 133480093f4SDimitry Andric return Replacements; 134480093f4SDimitry Andric } 135