clang  10.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 //===----------------------------------------------------------------------===//
11 #include "llvm/Support/Error.h"
12 
13 using namespace clang;
14 
15 namespace {
16 using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
17  bool /*IsOriginal*/)>;
18 /// Enumerates spans of tokens from the tree consecutively laid out in memory.
19 void enumerateTokenSpans(const syntax::Tree *Root, ProcessTokensFn Callback) {
20  struct Enumerator {
21  Enumerator(ProcessTokensFn Callback)
22  : SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
23  Callback(Callback) {}
24 
25  void run(const syntax::Tree *Root) {
26  process(Root);
27  // Report the last span to the user.
28  if (SpanBegin)
29  Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
30  }
31 
32  private:
33  void process(const syntax::Node *N) {
34  if (auto *T = dyn_cast<syntax::Tree>(N)) {
35  for (auto *C = T->firstChild(); C != nullptr; C = C->nextSibling())
36  process(C);
37  return;
38  }
39 
40  auto *L = cast<syntax::Leaf>(N);
41  if (SpanEnd == L->token() && SpanIsOriginal == L->isOriginal()) {
42  // Extend the current span.
43  ++SpanEnd;
44  return;
45  }
46  // Report the current span to the user.
47  if (SpanBegin)
48  Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
49  // Start recording a new span.
50  SpanBegin = L->token();
51  SpanEnd = SpanBegin + 1;
52  SpanIsOriginal = L->isOriginal();
53  }
54 
55  const syntax::Token *SpanBegin;
56  const syntax::Token *SpanEnd;
57  bool SpanIsOriginal;
58  ProcessTokensFn Callback;
59  };
60 
61  return Enumerator(Callback).run(Root);
62 }
63 
64 syntax::FileRange rangeOfExpanded(const syntax::Arena &A,
66  auto &Buffer = A.tokenBuffer();
67  auto &SM = A.sourceManager();
68 
69  // Check that \p Expanded actually points into expanded tokens.
70  assert(Buffer.expandedTokens().begin() <= Expanded.begin());
71  assert(Expanded.end() < Buffer.expandedTokens().end());
72 
73  if (Expanded.empty())
74  // (!) empty tokens must always point before end().
75  return syntax::FileRange(
76  SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
77 
78  auto Spelled = Buffer.spelledForExpanded(Expanded);
79  assert(Spelled && "could not find spelled tokens for expanded");
80  return syntax::Token::range(SM, Spelled->front(), Spelled->back());
81 }
82 } // namespace
83 
86  const syntax::TranslationUnit &TU) {
87  auto &Buffer = A.tokenBuffer();
88  auto &SM = A.sourceManager();
89 
90  tooling::Replacements Replacements;
91  // Text inserted by the replacement we are building now.
92  std::string Replacement;
93  auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
94  if (ReplacedRange.empty() && Replacement.empty())
95  return;
96  llvm::cantFail(Replacements.add(tooling::Replacement(
97  SM, rangeOfExpanded(A, ReplacedRange).toCharRange(SM), Replacement)));
98  Replacement = "";
99  };
100 
101  const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
102  enumerateTokenSpans(
103  &TU, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
104  if (!IsOriginal) {
105  Replacement +=
106  syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
107  return;
108  }
109  assert(NextOriginal <= Tokens.begin());
110  // We are looking at a span of original tokens.
111  if (NextOriginal != Tokens.begin()) {
112  // There is a gap, record a replacement or deletion.
113  emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin()));
114  } else {
115  // No gap, but we may have pending insertions. Emit them now.
116  emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0));
117  }
118  NextOriginal = Tokens.end();
119  });
120 
121  // We might have pending replacements at the end of file. If so, emit them.
122  emitReplacement(llvm::makeArrayRef(
123  NextOriginal, Buffer.expandedTokens().drop_back().end()));
124 
125  return Replacements;
126 }
llvm::Error add(const Replacement &R)
Adds a new replacement R to the current set of replacements.
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:209
A token coming directly from a file or from a macro invocation.
Definition: Tokens.h:104
A root node for a translation unit. Parent is always null.
Definition: Nodes.h:110
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
const SourceManager & sourceManager() const
Definition: Tree.h:44
tooling::Replacements computeReplacements(const Arena &A, const syntax::TranslationUnit &TU)
Computes textual replacements required to mimic the tree modifications made to the syntax tree...
const TokenBuffer & tokenBuffer() const
Definition: Tree.cpp:39
A text replacement.
Definition: Replacement.h:83
A memory arena for syntax trees.
Definition: Tree.h:39
const SourceManager & SM
Definition: Format.cpp:1685
#define false
Definition: stdbool.h:17
ast_type_traits::DynTypedNode Node
Dataflow Directional Tag Classes.
Stencil run(MatchConsumer< std::string > C)
Wraps a MatchConsumer in a Stencil, so that it can be used in a Stencil.
Definition: Stencil.cpp:350
SyntaxTree::Impl & Tree
Definition: ASTDiff.cpp:192
static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range, unsigned AbsKind, QualType ArgType)
FileRange range(const SourceManager &SM) const
Gets a range of this token.
Definition: Tokens.cpp:56