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