1 //===--- RefactoringCallbacks.h - Structural query framework ----*- 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 //===----------------------------------------------------------------------===//
8 //
9 //  Provides callbacks to make common kinds of refactorings easy.
10 //
11 //  The general idea is to construct a matcher expression that describes a
12 //  subtree match on the AST and then replace the corresponding source code
13 //  either by some specific text or some other AST node.
14 //
15 //  Example:
16 //  int main(int argc, char **argv) {
17 //    ClangTool Tool(argc, argv);
18 //    MatchFinder Finder;
19 //    ReplaceStmtWithText Callback("integer", "42");
20 //    Finder.AddMatcher(id("integer", expression(integerLiteral())), Callback);
21 //    return Tool.run(newFrontendActionFactory(&Finder));
22 //  }
23 //
24 //  This will replace all integer literals with "42".
25 //
26 //===----------------------------------------------------------------------===//
27 
28 #ifndef LLVM_CLANG_TOOLING_REFACTORINGCALLBACKS_H
29 #define LLVM_CLANG_TOOLING_REFACTORINGCALLBACKS_H
30 
31 #include "clang/ASTMatchers/ASTMatchFinder.h"
32 #include "clang/Tooling/Refactoring.h"
33 
34 namespace clang {
35 namespace tooling {
36 
37 /// Base class for RefactoringCallbacks.
38 ///
39 /// Collects \c tooling::Replacements while running.
40 class RefactoringCallback : public ast_matchers::MatchFinder::MatchCallback {
41 public:
42   RefactoringCallback();
43   Replacements &getReplacements();
44 
45 protected:
46   Replacements Replace;
47 };
48 
49 /// Adaptor between \c ast_matchers::MatchFinder and \c
50 /// tooling::RefactoringTool.
51 ///
52 /// Runs AST matchers and stores the \c tooling::Replacements in a map.
53 class ASTMatchRefactorer {
54 public:
55   explicit ASTMatchRefactorer(
56     std::map<std::string, Replacements> &FileToReplaces);
57 
58   template <typename T>
addMatcher(const T & Matcher,RefactoringCallback * Callback)59   void addMatcher(const T &Matcher, RefactoringCallback *Callback) {
60     MatchFinder.addMatcher(Matcher, Callback);
61     Callbacks.push_back(Callback);
62   }
63 
64   void addDynamicMatcher(const ast_matchers::internal::DynTypedMatcher &Matcher,
65                          RefactoringCallback *Callback);
66 
67   std::unique_ptr<ASTConsumer> newASTConsumer();
68 
69 private:
70   friend class RefactoringASTConsumer;
71   std::vector<RefactoringCallback *> Callbacks;
72   ast_matchers::MatchFinder MatchFinder;
73   std::map<std::string, Replacements> &FileToReplaces;
74 };
75 
76 /// Replace the text of the statement bound to \c FromId with the text in
77 /// \c ToText.
78 class ReplaceStmtWithText : public RefactoringCallback {
79 public:
80   ReplaceStmtWithText(StringRef FromId, StringRef ToText);
81   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
82 
83 private:
84   std::string FromId;
85   std::string ToText;
86 };
87 
88 /// Replace the text of an AST node bound to \c FromId with the result of
89 /// evaluating the template in \c ToTemplate.
90 ///
91 /// Expressions of the form ${NodeName} in \c ToTemplate will be
92 /// replaced by the text of the node bound to ${NodeName}. The string
93 /// "$$" will be replaced by "$".
94 class ReplaceNodeWithTemplate : public RefactoringCallback {
95 public:
96   static llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
97   create(StringRef FromId, StringRef ToTemplate);
98   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
99 
100 private:
101   struct TemplateElement {
102     enum { Literal, Identifier } Type;
103     std::string Value;
104   };
105   ReplaceNodeWithTemplate(llvm::StringRef FromId,
106                           std::vector<TemplateElement> Template);
107   std::string FromId;
108   std::vector<TemplateElement> Template;
109 };
110 
111 /// Replace the text of the statement bound to \c FromId with the text of
112 /// the statement bound to \c ToId.
113 class ReplaceStmtWithStmt : public RefactoringCallback {
114 public:
115   ReplaceStmtWithStmt(StringRef FromId, StringRef ToId);
116   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
117 
118 private:
119   std::string FromId;
120   std::string ToId;
121 };
122 
123 /// Replace an if-statement bound to \c Id with the outdented text of its
124 /// body, choosing the consequent or the alternative based on whether
125 /// \c PickTrueBranch is true.
126 class ReplaceIfStmtWithItsBody : public RefactoringCallback {
127 public:
128   ReplaceIfStmtWithItsBody(StringRef Id, bool PickTrueBranch);
129   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
130 
131 private:
132   std::string Id;
133   const bool PickTrueBranch;
134 };
135 
136 } // end namespace tooling
137 } // end namespace clang
138 
139 #endif
140