clang  10.0.0svn
RefactoringCallbacks.cpp
Go to the documentation of this file.
1 //===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
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 //===----------------------------------------------------------------------===//
8 //
9 //
10 //===----------------------------------------------------------------------===//
14 #include "clang/Lex/Lexer.h"
15 
16 using llvm::StringError;
17 using llvm::make_error;
18 
19 namespace clang {
20 namespace tooling {
21 
24  return Replace;
25 }
26 
28  std::map<std::string, Replacements> &FileToReplaces)
29  : FileToReplaces(FileToReplaces) {}
30 
32  const ast_matchers::internal::DynTypedMatcher &Matcher,
33  RefactoringCallback *Callback) {
34  MatchFinder.addDynamicMatcher(Matcher, Callback);
35  Callbacks.push_back(Callback);
36 }
37 
39 public:
41  : Refactoring(Refactoring) {}
42 
43  void HandleTranslationUnit(ASTContext &Context) override {
44  // The ASTMatchRefactorer is re-used between translation units.
45  // Clear the matchers so that each Replacement is only emitted once.
46  for (const auto &Callback : Refactoring.Callbacks) {
47  Callback->getReplacements().clear();
48  }
49  Refactoring.MatchFinder.matchAST(Context);
50  for (const auto &Callback : Refactoring.Callbacks) {
51  for (const auto &Replacement : Callback->getReplacements()) {
52  llvm::Error Err =
53  Refactoring.FileToReplaces[Replacement.getFilePath()].add(
54  Replacement);
55  if (Err) {
56  llvm::errs() << "Skipping replacement " << Replacement.toString()
57  << " due to this error:\n"
58  << toString(std::move(Err)) << "\n";
59  }
60  }
61  }
62  }
63 
64 private:
65  ASTMatchRefactorer &Refactoring;
66 };
67 
68 std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
69  return std::make_unique<RefactoringASTConsumer>(*this);
70 }
71 
72 static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
73  StringRef Text) {
74  return tooling::Replacement(
75  Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
76 }
77 static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
78  const Stmt &To) {
79  return replaceStmtWithText(
80  Sources, From,
82  Sources, LangOptions()));
83 }
84 
85 ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
86  : FromId(FromId), ToText(ToText) {}
87 
90  if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
91  auto Err = Replace.add(tooling::Replacement(
92  *Result.SourceManager,
93  CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
94  // FIXME: better error handling. For now, just print error message in the
95  // release version.
96  if (Err) {
97  llvm::errs() << llvm::toString(std::move(Err)) << "\n";
98  assert(false);
99  }
100  }
101 }
102 
103 ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
104  : FromId(FromId), ToId(ToId) {}
105 
108  const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
109  const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
110  if (FromMatch && ToMatch) {
111  auto Err = Replace.add(
112  replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
113  // FIXME: better error handling. For now, just print error message in the
114  // release version.
115  if (Err) {
116  llvm::errs() << llvm::toString(std::move(Err)) << "\n";
117  assert(false);
118  }
119  }
120 }
121 
123  bool PickTrueBranch)
124  : Id(Id), PickTrueBranch(PickTrueBranch) {}
125 
128  if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
129  const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
130  if (Body) {
131  auto Err =
132  Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
133  // FIXME: better error handling. For now, just print error message in the
134  // release version.
135  if (Err) {
136  llvm::errs() << llvm::toString(std::move(Err)) << "\n";
137  assert(false);
138  }
139  } else if (!PickTrueBranch) {
140  // If we want to use the 'else'-branch, but it doesn't exist, delete
141  // the whole 'if'.
142  auto Err =
144  // FIXME: better error handling. For now, just print error message in the
145  // release version.
146  if (Err) {
147  llvm::errs() << llvm::toString(std::move(Err)) << "\n";
148  assert(false);
149  }
150  }
151  }
152 }
153 
154 ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
155  llvm::StringRef FromId, std::vector<TemplateElement> Template)
156  : FromId(FromId), Template(std::move(Template)) {}
157 
159 ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
160  std::vector<TemplateElement> ParsedTemplate;
161  for (size_t Index = 0; Index < ToTemplate.size();) {
162  if (ToTemplate[Index] == '$') {
163  if (ToTemplate.substr(Index, 2) == "$$") {
164  Index += 2;
165  ParsedTemplate.push_back(
166  TemplateElement{TemplateElement::Literal, "$"});
167  } else if (ToTemplate.substr(Index, 2) == "${") {
168  size_t EndOfIdentifier = ToTemplate.find("}", Index);
169  if (EndOfIdentifier == std::string::npos) {
170  return make_error<StringError>(
171  "Unterminated ${...} in replacement template near " +
172  ToTemplate.substr(Index),
173  llvm::inconvertibleErrorCode());
174  }
175  std::string SourceNodeName =
176  ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2);
177  ParsedTemplate.push_back(
178  TemplateElement{TemplateElement::Identifier, SourceNodeName});
179  Index = EndOfIdentifier + 1;
180  } else {
181  return make_error<StringError>(
182  "Invalid $ in replacement template near " +
183  ToTemplate.substr(Index),
184  llvm::inconvertibleErrorCode());
185  }
186  } else {
187  size_t NextIndex = ToTemplate.find('$', Index + 1);
188  ParsedTemplate.push_back(
189  TemplateElement{TemplateElement::Literal,
190  ToTemplate.substr(Index, NextIndex - Index)});
191  Index = NextIndex;
192  }
193  }
194  return std::unique_ptr<ReplaceNodeWithTemplate>(
195  new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
196 }
197 
200  const auto &NodeMap = Result.Nodes.getMap();
201 
202  std::string ToText;
203  for (const auto &Element : Template) {
204  switch (Element.Type) {
205  case TemplateElement::Literal:
206  ToText += Element.Value;
207  break;
209  auto NodeIter = NodeMap.find(Element.Value);
210  if (NodeIter == NodeMap.end()) {
211  llvm::errs() << "Node " << Element.Value
212  << " used in replacement template not bound in Matcher \n";
213  llvm::report_fatal_error("Unbound node in replacement template.");
214  }
215  CharSourceRange Source =
216  CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
217  ToText += Lexer::getSourceText(Source, *Result.SourceManager,
218  Result.Context->getLangOpts());
219  break;
220  }
221  }
222  }
223  if (NodeMap.count(FromId) == 0) {
224  llvm::errs() << "Node to be replaced " << FromId
225  << " not bound in query.\n";
226  llvm::report_fatal_error("FromId node not bound in MatchResult");
227  }
228  auto Replacement =
229  tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
230  Result.Context->getLangOpts());
232  if (Err) {
233  llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
234  << "! " << llvm::toString(std::move(Err)) << "\n";
235  llvm::report_fatal_error("Replacement failed");
236  }
237 }
238 
239 } // end namespace tooling
240 } // end namespace clang
RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
StringRef getFilePath() const
Accessors.
Definition: Replacement.h:120
A class to allow finding matches over the Clang AST.
StringRef Identifier
Definition: Format.cpp:1815
static llvm::Expected< std::unique_ptr< ReplaceNodeWithTemplate > > create(StringRef FromId, StringRef ToTemplate)
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs...
Definition: ASTConsumer.h:33
static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From, StringRef Text)
Stmt - This represents one statement.
Definition: Stmt.h:66
IfStmt - This represents an if/then/else.
Definition: Stmt.h:1822
static CharSourceRange getTokenRange(SourceRange R)
llvm::Error add(const Replacement &R)
Adds a new replacement R to the current set of replacements.
void run(const ast_matchers::MatchFinder::MatchResult &Result) override
Called on every match by the MatchFinder.
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:209
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
const IDToNodeMap & getMap() const
Retrieve mapping from binding identifiers to bound nodes.
Definition: ASTMatchers.h:120
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
void run(const ast_matchers::MatchFinder::MatchResult &Result) override
Called on every match by the MatchFinder.
ReplaceIfStmtWithItsBody(StringRef Id, bool PickTrueBranch)
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
Definition: ASTMatchers.h:110
A text replacement.
Definition: Replacement.h:83
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
Definition: Lexer.cpp:939
Represents a character-granular source range.
Adaptor between ast_matchers::MatchFinder and tooling::RefactoringTool.
int Id
Definition: ASTDiff.cpp:190
void addDynamicMatcher(const ast_matchers::internal::DynTypedMatcher &Matcher, RefactoringCallback *Callback)
Base class for RefactoringCallbacks.
Contains all information for a given match.
ASTMatchRefactorer(std::map< std::string, Replacements > &FileToReplaces)
ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
ReplaceStmtWithText(StringRef FromId, StringRef ToText)
void run(const ast_matchers::MatchFinder::MatchResult &Result) override
Called on every match by the MatchFinder.
clang::ASTContext *const Context
Utilities for interpreting the matched AST structures.
Replace the text of an AST node bound to FromId with the result of evaluating the template in ToTempl...
void HandleTranslationUnit(ASTContext &Context) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
const BoundNodes Nodes
Contains the nodes bound on the current match.
ast_type_traits::DynTypedNode Node
Dataflow Directional Tag Classes.
std::string toString() const
Returns a human readable string representation.
Definition: Replacement.cpp:87
void run(const ast_matchers::MatchFinder::MatchResult &Result) override
Called on every match by the MatchFinder.
Defines the clang::SourceLocation class and associated facilities.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:262
clang::SourceManager *const SourceManager
static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From, const Stmt &To)
StringRef Text
Definition: Format.cpp:1808
const LangOptions & getLangOpts() const
Definition: ASTContext.h:723
This class handles loading and caching of source files into memory.
bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, MatchCallback *Action)
Adds a matcher to execute when running over the AST.
std::unique_ptr< ASTConsumer > newASTConsumer()