| 1 | //===--- ASTMatchFinder.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 a way to construct an ASTConsumer that runs given matchers |
| 10 | // over the AST and invokes a given callback on every match. |
| 11 | // |
| 12 | // The general idea is to construct a matcher expression that describes a |
| 13 | // subtree match on the AST. Next, a callback that is executed every time the |
| 14 | // expression matches is registered, and the matcher is run over the AST of |
| 15 | // some code. Matched subexpressions can be bound to string IDs and easily |
| 16 | // be accessed from the registered callback. The callback can than use the |
| 17 | // AST nodes that the subexpressions matched on to output information about |
| 18 | // the match or construct changes that can be applied to the code. |
| 19 | // |
| 20 | // Example: |
| 21 | // class HandleMatch : public MatchFinder::MatchCallback { |
| 22 | // public: |
| 23 | // virtual void Run(const MatchFinder::MatchResult &Result) { |
| 24 | // const CXXRecordDecl *Class = |
| 25 | // Result.Nodes.GetDeclAs<CXXRecordDecl>("id"); |
| 26 | // ... |
| 27 | // } |
| 28 | // }; |
| 29 | // |
| 30 | // int main(int argc, char **argv) { |
| 31 | // ClangTool Tool(argc, argv); |
| 32 | // MatchFinder finder; |
| 33 | // finder.AddMatcher(Id("id", record(hasName("::a_namespace::AClass"))), |
| 34 | // new HandleMatch); |
| 35 | // return Tool.Run(newFrontendActionFactory(&finder)); |
| 36 | // } |
| 37 | // |
| 38 | //===----------------------------------------------------------------------===// |
| 39 | |
| 40 | #ifndef LLVM_CLANG_ASTMATCHERS_ASTMATCHFINDER_H |
| 41 | #define LLVM_CLANG_ASTMATCHERS_ASTMATCHFINDER_H |
| 42 | |
| 43 | #include "clang/ASTMatchers/ASTMatchers.h" |
| 44 | #include "llvm/ADT/SmallPtrSet.h" |
| 45 | #include "llvm/ADT/StringMap.h" |
| 46 | #include "llvm/Support/Timer.h" |
| 47 | |
| 48 | namespace clang { |
| 49 | |
| 50 | namespace ast_matchers { |
| 51 | |
| 52 | /// A class to allow finding matches over the Clang AST. |
| 53 | /// |
| 54 | /// After creation, you can add multiple matchers to the MatchFinder via |
| 55 | /// calls to addMatcher(...). |
| 56 | /// |
| 57 | /// Once all matchers are added, newASTConsumer() returns an ASTConsumer |
| 58 | /// that will trigger the callbacks specified via addMatcher(...) when a match |
| 59 | /// is found. |
| 60 | /// |
| 61 | /// The order of matches is guaranteed to be equivalent to doing a pre-order |
| 62 | /// traversal on the AST, and applying the matchers in the order in which they |
| 63 | /// were added to the MatchFinder. |
| 64 | /// |
| 65 | /// See ASTMatchers.h for more information about how to create matchers. |
| 66 | /// |
| 67 | /// Not intended to be subclassed. |
| 68 | class MatchFinder { |
| 69 | public: |
| 70 | /// Contains all information for a given match. |
| 71 | /// |
| 72 | /// Every time a match is found, the MatchFinder will invoke the registered |
| 73 | /// MatchCallback with a MatchResult containing information about the match. |
| 74 | struct MatchResult { |
| 75 | MatchResult(const BoundNodes &Nodes, clang::ASTContext *Context); |
| 76 | |
| 77 | /// Contains the nodes bound on the current match. |
| 78 | /// |
| 79 | /// This allows user code to easily extract matched AST nodes. |
| 80 | const BoundNodes Nodes; |
| 81 | |
| 82 | /// Utilities for interpreting the matched AST structures. |
| 83 | /// @{ |
| 84 | clang::ASTContext * const Context; |
| 85 | clang::SourceManager * const SourceManager; |
| 86 | /// @} |
| 87 | }; |
| 88 | |
| 89 | /// Called when the Match registered for it was successfully found |
| 90 | /// in the AST. |
| 91 | class MatchCallback { |
| 92 | public: |
| 93 | virtual ~MatchCallback(); |
| 94 | |
| 95 | /// Called on every match by the \c MatchFinder. |
| 96 | virtual void run(const MatchResult &Result) = 0; |
| 97 | |
| 98 | /// Called at the start of each translation unit. |
| 99 | /// |
| 100 | /// Optionally override to do per translation unit tasks. |
| 101 | virtual void onStartOfTranslationUnit() {} |
| 102 | |
| 103 | /// Called at the end of each translation unit. |
| 104 | /// |
| 105 | /// Optionally override to do per translation unit tasks. |
| 106 | virtual void onEndOfTranslationUnit() {} |
| 107 | |
| 108 | /// An id used to group the matchers. |
| 109 | /// |
| 110 | /// This id is used, for example, for the profiling output. |
| 111 | /// It defaults to "<unknown>". |
| 112 | virtual StringRef getID() const; |
| 113 | }; |
| 114 | |
| 115 | /// Called when parsing is finished. Intended for testing only. |
| 116 | class ParsingDoneTestCallback { |
| 117 | public: |
| 118 | virtual ~ParsingDoneTestCallback(); |
| 119 | virtual void run() = 0; |
| 120 | }; |
| 121 | |
| 122 | struct MatchFinderOptions { |
| 123 | struct Profiling { |
| 124 | Profiling(llvm::StringMap<llvm::TimeRecord> &Records) |
| 125 | : Records(Records) {} |
| 126 | |
| 127 | /// Per bucket timing information. |
| 128 | llvm::StringMap<llvm::TimeRecord> &Records; |
| 129 | }; |
| 130 | |
| 131 | /// Enables per-check timers. |
| 132 | /// |
| 133 | /// It prints a report after match. |
| 134 | llvm::Optional<Profiling> CheckProfiling; |
| 135 | }; |
| 136 | |
| 137 | MatchFinder(MatchFinderOptions Options = MatchFinderOptions()); |
| 138 | ~MatchFinder(); |
| 139 | |
| 140 | /// Adds a matcher to execute when running over the AST. |
| 141 | /// |
| 142 | /// Calls 'Action' with the BoundNodes on every match. |
| 143 | /// Adding more than one 'NodeMatch' allows finding different matches in a |
| 144 | /// single pass over the AST. |
| 145 | /// |
| 146 | /// Does not take ownership of 'Action'. |
| 147 | /// @{ |
| 148 | void addMatcher(const DeclarationMatcher &NodeMatch, |
| 149 | MatchCallback *Action); |
| 150 | void addMatcher(const TypeMatcher &NodeMatch, |
| 151 | MatchCallback *Action); |
| 152 | void addMatcher(const StatementMatcher &NodeMatch, |
| 153 | MatchCallback *Action); |
| 154 | void addMatcher(const NestedNameSpecifierMatcher &NodeMatch, |
| 155 | MatchCallback *Action); |
| 156 | void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, |
| 157 | MatchCallback *Action); |
| 158 | void addMatcher(const TypeLocMatcher &NodeMatch, |
| 159 | MatchCallback *Action); |
| 160 | void addMatcher(const CXXCtorInitializerMatcher &NodeMatch, |
| 161 | MatchCallback *Action); |
| 162 | /// @} |
| 163 | |
| 164 | /// Adds a matcher to execute when running over the AST. |
| 165 | /// |
| 166 | /// This is similar to \c addMatcher(), but it uses the dynamic interface. It |
| 167 | /// is more flexible, but the lost type information enables a caller to pass |
| 168 | /// a matcher that cannot match anything. |
| 169 | /// |
| 170 | /// \returns \c true if the matcher is a valid top-level matcher, \c false |
| 171 | /// otherwise. |
| 172 | bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, |
| 173 | MatchCallback *Action); |
| 174 | |
| 175 | /// Creates a clang ASTConsumer that finds all matches. |
| 176 | std::unique_ptr<clang::ASTConsumer> newASTConsumer(); |
| 177 | |
| 178 | /// Calls the registered callbacks on all matches on the given \p Node. |
| 179 | /// |
| 180 | /// Note that there can be multiple matches on a single node, for |
| 181 | /// example when using decl(forEachDescendant(stmt())). |
| 182 | /// |
| 183 | /// @{ |
| 184 | template <typename T> void match(const T &Node, ASTContext &Context) { |
| 185 | match(clang::ast_type_traits::DynTypedNode::create(Node), Context); |
| 186 | } |
| 187 | void match(const clang::ast_type_traits::DynTypedNode &Node, |
| 188 | ASTContext &Context); |
| 189 | /// @} |
| 190 | |
| 191 | /// Finds all matches in the given AST. |
| 192 | void matchAST(ASTContext &Context); |
| 193 | |
| 194 | /// Registers a callback to notify the end of parsing. |
| 195 | /// |
| 196 | /// The provided closure is called after parsing is done, before the AST is |
| 197 | /// traversed. Useful for benchmarking. |
| 198 | /// Each call to FindAll(...) will call the closure once. |
| 199 | void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone); |
| 200 | |
| 201 | /// For each \c Matcher<> a \c MatchCallback that will be called |
| 202 | /// when it matches. |
| 203 | struct MatchersByType { |
| 204 | std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>> |
| 205 | DeclOrStmt; |
| 206 | std::vector<std::pair<TypeMatcher, MatchCallback *>> Type; |
| 207 | std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>> |
| 208 | NestedNameSpecifier; |
| 209 | std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>> |
| 210 | NestedNameSpecifierLoc; |
| 211 | std::vector<std::pair<TypeLocMatcher, MatchCallback *>> TypeLoc; |
| 212 | std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit; |
| 213 | /// All the callbacks in one container to simplify iteration. |
| 214 | llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks; |
| 215 | }; |
| 216 | |
| 217 | private: |
| 218 | MatchersByType Matchers; |
| 219 | |
| 220 | MatchFinderOptions Options; |
| 221 | |
| 222 | /// Called when parsing is done. |
| 223 | ParsingDoneTestCallback *ParsingDone; |
| 224 | }; |
| 225 | |
| 226 | /// Returns the results of matching \p Matcher on \p Node. |
| 227 | /// |
| 228 | /// Collects the \c BoundNodes of all callback invocations when matching |
| 229 | /// \p Matcher on \p Node and returns the collected results. |
| 230 | /// |
| 231 | /// Multiple results occur when using matchers like \c forEachDescendant, |
| 232 | /// which generate a result for each sub-match. |
| 233 | /// |
| 234 | /// If you want to find all matches on the sub-tree rooted at \c Node (rather |
| 235 | /// than only the matches on \c Node itself), surround the \c Matcher with a |
| 236 | /// \c findAll(). |
| 237 | /// |
| 238 | /// \see selectFirst |
| 239 | /// @{ |
| 240 | template <typename MatcherT, typename NodeT> |
| 241 | SmallVector<BoundNodes, 1> |
| 242 | match(MatcherT Matcher, const NodeT &Node, ASTContext &Context); |
| 243 | |
| 244 | template <typename MatcherT> |
| 245 | SmallVector<BoundNodes, 1> |
| 246 | match(MatcherT Matcher, const ast_type_traits::DynTypedNode &Node, |
| 247 | ASTContext &Context); |
| 248 | /// @} |
| 249 | |
| 250 | /// Returns the results of matching \p Matcher on the translation unit of |
| 251 | /// \p Context and collects the \c BoundNodes of all callback invocations. |
| 252 | template <typename MatcherT> |
| 253 | SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context); |
| 254 | |
| 255 | /// Returns the first result of type \c NodeT bound to \p BoundTo. |
| 256 | /// |
| 257 | /// Returns \c NULL if there is no match, or if the matching node cannot be |
| 258 | /// casted to \c NodeT. |
| 259 | /// |
| 260 | /// This is useful in combanation with \c match(): |
| 261 | /// \code |
| 262 | /// const Decl *D = selectFirst<Decl>("id", match(Matcher.bind("id"), |
| 263 | /// Node, Context)); |
| 264 | /// \endcode |
| 265 | template <typename NodeT> |
| 266 | const NodeT * |
| 267 | selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) { |
| 268 | for (const BoundNodes &N : Results) { |
| 269 | if (const NodeT *Node = N.getNodeAs<NodeT>(BoundTo)) |
| 270 | return Node; |
| 271 | } |
| 272 | return nullptr; |
| 273 | } |
| 274 | |
| 275 | namespace internal { |
| 276 | class CollectMatchesCallback : public MatchFinder::MatchCallback { |
| 277 | public: |
| 278 | void run(const MatchFinder::MatchResult &Result) override { |
| 279 | Nodes.push_back(Result.Nodes); |
| 280 | } |
| 281 | SmallVector<BoundNodes, 1> Nodes; |
| 282 | }; |
| 283 | } |
| 284 | |
| 285 | template <typename MatcherT> |
| 286 | SmallVector<BoundNodes, 1> |
| 287 | match(MatcherT Matcher, const ast_type_traits::DynTypedNode &Node, |
| 288 | ASTContext &Context) { |
| 289 | internal::CollectMatchesCallback Callback; |
| 290 | MatchFinder Finder; |
| 291 | Finder.addMatcher(Matcher, &Callback); |
| 292 | Finder.match(Node, Context); |
| 293 | return std::move(Callback.Nodes); |
| 294 | } |
| 295 | |
| 296 | template <typename MatcherT, typename NodeT> |
| 297 | SmallVector<BoundNodes, 1> |
| 298 | match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) { |
| 299 | return match(Matcher, ast_type_traits::DynTypedNode::create(Node), Context); |
| 300 | } |
| 301 | |
| 302 | template <typename MatcherT> |
| 303 | SmallVector<BoundNodes, 1> |
| 304 | match(MatcherT Matcher, ASTContext &Context) { |
| 305 | internal::CollectMatchesCallback Callback; |
| 306 | MatchFinder Finder; |
| 307 | Finder.addMatcher(Matcher, &Callback); |
| 308 | Finder.matchAST(Context); |
| 309 | return std::move(Callback.Nodes); |
| 310 | } |
| 311 | |
| 312 | } // end namespace ast_matchers |
| 313 | } // end namespace clang |
| 314 | |
| 315 | #endif |
| 316 |