brintos

brintos / llvm-project-archived public Read only

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