clang  19.0.0git
ComputeReplacements.cpp
Go to the documentation of this file.
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-exception
6 //
7 //===----------------------------------------------------------------------===//
13 #include "llvm/Support/Error.h"
14 
15 using namespace clang;
16 
17 namespace {
18 using 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.
21 void enumerateTokenSpans(const syntax::Tree *Root,
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 
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 
72 syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
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 } // namespace
91 
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.
131  llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
132 
133  return Replacements;
134 }
#define SM(sm)
Definition: Cuda.cpp:83
static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range, unsigned AbsKind, QualType ArgType)
A node in a syntax tree.
Definition: Tree.h:54
A TokenBuffer-powered token manager.
const syntax::Token * getToken(Key I) const
A token coming directly from a file or from a macro invocation.
Definition: Tokens.h:103
FileRange range(const SourceManager &SM) const
Gets a range of this token.
Definition: Tokens.cpp:161
A node that has children and represents a syntactic language construct.
Definition: Tree.h:144
A text replacement.
Definition: Replacement.h:83
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:212
llvm::Error add(const Replacement &R)
Adds a new replacement R to the current set of replacements.
tooling::Replacements computeReplacements(const TokenBufferTokenManager &TBTM, const syntax::TranslationUnit &TU)
Computes textual replacements required to mimic the tree modifications made to the syntax tree.
Stencil run(MatchConsumer< std::string > C)
Wraps a MatchConsumer in a Stencil, so that it can be used in a Stencil.
Definition: Stencil.cpp:485
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
#define false
Definition: stdbool.h:26
A half-open character range inside a particular file, the start offset is included and the end offset...
Definition: Tokens.h:50
llvm::StringRef text(const SourceManager &SM) const
Gets the substring that this FileRange refers to.
Definition: Tokens.cpp:218