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