Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
35271 views
//===- ComputeReplacements.cpp --------------------------------*- C++ -*-=====//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "clang/Tooling/Core/Replacement.h"8#include "clang/Tooling/Syntax/Mutations.h"9#include "clang/Tooling/Syntax/TokenBufferTokenManager.h"10#include "clang/Tooling/Syntax/Tokens.h"11#include "clang/Tooling/Syntax/Tree.h"12#include "llvm/Support/Error.h"1314using namespace clang;1516namespace {17using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,18bool /*IsOriginal*/)>;19/// Enumerates spans of tokens from the tree consecutively laid out in memory.20void enumerateTokenSpans(const syntax::Tree *Root,21const syntax::TokenBufferTokenManager &STM,22ProcessTokensFn Callback) {23struct Enumerator {24Enumerator(const syntax::TokenBufferTokenManager &STM,25ProcessTokensFn Callback)26: STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),27Callback(Callback) {}2829void run(const syntax::Tree *Root) {30process(Root);31// Report the last span to the user.32if (SpanBegin)33Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);34}3536private:37void process(const syntax::Node *N) {38if (auto *T = dyn_cast<syntax::Tree>(N)) {39for (const auto *C = T->getFirstChild(); C != nullptr;40C = C->getNextSibling())41process(C);42return;43}4445auto *L = cast<syntax::Leaf>(N);46if (SpanEnd == STM.getToken(L->getTokenKey()) &&47SpanIsOriginal == L->isOriginal()) {48// Extend the current span.49++SpanEnd;50return;51}52// Report the current span to the user.53if (SpanBegin)54Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);55// Start recording a new span.56SpanBegin = STM.getToken(L->getTokenKey());57SpanEnd = SpanBegin + 1;58SpanIsOriginal = L->isOriginal();59}6061const syntax::TokenBufferTokenManager &STM;62const syntax::Token *SpanBegin;63const syntax::Token *SpanEnd;64bool SpanIsOriginal;65ProcessTokensFn Callback;66};6768return Enumerator(STM, Callback).run(Root);69}7071syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,72llvm::ArrayRef<syntax::Token> Expanded) {73const auto &Buffer = STM.tokenBuffer();74const auto &SM = STM.sourceManager();7576// Check that \p Expanded actually points into expanded tokens.77assert(Buffer.expandedTokens().begin() <= Expanded.begin());78assert(Expanded.end() < Buffer.expandedTokens().end());7980if (Expanded.empty())81// (!) empty tokens must always point before end().82return syntax::FileRange(83SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);8485auto Spelled = Buffer.spelledForExpanded(Expanded);86assert(Spelled && "could not find spelled tokens for expanded");87return syntax::Token::range(SM, Spelled->front(), Spelled->back());88}89} // namespace9091tooling::Replacements92syntax::computeReplacements(const TokenBufferTokenManager &TBTM,93const syntax::TranslationUnit &TU) {94const auto &Buffer = TBTM.tokenBuffer();95const auto &SM = TBTM.sourceManager();9697tooling::Replacements Replacements;98// Text inserted by the replacement we are building now.99std::string Replacement;100auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {101if (ReplacedRange.empty() && Replacement.empty())102return;103llvm::cantFail(Replacements.add(tooling::Replacement(104SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM),105Replacement)));106Replacement = "";107};108const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();109enumerateTokenSpans(110&TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {111if (!IsOriginal) {112Replacement +=113syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);114return;115}116assert(NextOriginal <= Tokens.begin());117// We are looking at a span of original tokens.118if (NextOriginal != Tokens.begin()) {119// There is a gap, record a replacement or deletion.120emitReplacement(llvm::ArrayRef(NextOriginal, Tokens.begin()));121} else {122// No gap, but we may have pending insertions. Emit them now.123emitReplacement(llvm::ArrayRef(NextOriginal, /*Length=*/(size_t)0));124}125NextOriginal = Tokens.end();126});127128// We might have pending replacements at the end of file. If so, emit them.129emitReplacement(130llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));131132return Replacements;133}134135136