Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 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 | #include <optional> |
||
| 48 | |||
| 49 | namespace clang { |
||
| 50 | |||
| 51 | namespace ast_matchers { |
||
| 52 | |||
| 53 | /// A class to allow finding matches over the Clang AST. |
||
| 54 | /// |
||
| 55 | /// After creation, you can add multiple matchers to the MatchFinder via |
||
| 56 | /// calls to addMatcher(...). |
||
| 57 | /// |
||
| 58 | /// Once all matchers are added, newASTConsumer() returns an ASTConsumer |
||
| 59 | /// that will trigger the callbacks specified via addMatcher(...) when a match |
||
| 60 | /// is found. |
||
| 61 | /// |
||
| 62 | /// The order of matches is guaranteed to be equivalent to doing a pre-order |
||
| 63 | /// traversal on the AST, and applying the matchers in the order in which they |
||
| 64 | /// were added to the MatchFinder. |
||
| 65 | /// |
||
| 66 | /// See ASTMatchers.h for more information about how to create matchers. |
||
| 67 | /// |
||
| 68 | /// Not intended to be subclassed. |
||
| 69 | class MatchFinder { |
||
| 70 | public: |
||
| 71 | /// Contains all information for a given match. |
||
| 72 | /// |
||
| 73 | /// Every time a match is found, the MatchFinder will invoke the registered |
||
| 74 | /// MatchCallback with a MatchResult containing information about the match. |
||
| 75 | struct MatchResult { |
||
| 76 | MatchResult(const BoundNodes &Nodes, clang::ASTContext *Context); |
||
| 77 | |||
| 78 | /// Contains the nodes bound on the current match. |
||
| 79 | /// |
||
| 80 | /// This allows user code to easily extract matched AST nodes. |
||
| 81 | const BoundNodes Nodes; |
||
| 82 | |||
| 83 | /// Utilities for interpreting the matched AST structures. |
||
| 84 | /// @{ |
||
| 85 | clang::ASTContext * const Context; |
||
| 86 | clang::SourceManager * const SourceManager; |
||
| 87 | /// @} |
||
| 88 | }; |
||
| 89 | |||
| 90 | /// Called when the Match registered for it was successfully found |
||
| 91 | /// in the AST. |
||
| 92 | class MatchCallback { |
||
| 93 | public: |
||
| 94 | virtual ~MatchCallback(); |
||
| 95 | |||
| 96 | /// Called on every match by the \c MatchFinder. |
||
| 97 | virtual void run(const MatchResult &Result) = 0; |
||
| 98 | |||
| 99 | /// Called at the start of each translation unit. |
||
| 100 | /// |
||
| 101 | /// Optionally override to do per translation unit tasks. |
||
| 102 | virtual void onStartOfTranslationUnit() {} |
||
| 103 | |||
| 104 | /// Called at the end of each translation unit. |
||
| 105 | /// |
||
| 106 | /// Optionally override to do per translation unit tasks. |
||
| 107 | virtual void onEndOfTranslationUnit() {} |
||
| 108 | |||
| 109 | /// An id used to group the matchers. |
||
| 110 | /// |
||
| 111 | /// This id is used, for example, for the profiling output. |
||
| 112 | /// It defaults to "<unknown>". |
||
| 113 | virtual StringRef getID() const; |
||
| 114 | |||
| 115 | /// TraversalKind to use while matching and processing |
||
| 116 | /// the result nodes. This API is temporary to facilitate |
||
| 117 | /// third parties porting existing code to the default |
||
| 118 | /// behavior of clang-tidy. |
||
| 119 | virtual std::optional<TraversalKind> getCheckTraversalKind() const; |
||
| 120 | }; |
||
| 121 | |||
| 122 | /// Called when parsing is finished. Intended for testing only. |
||
| 123 | class ParsingDoneTestCallback { |
||
| 124 | public: |
||
| 125 | virtual ~ParsingDoneTestCallback(); |
||
| 126 | virtual void run() = 0; |
||
| 127 | }; |
||
| 128 | |||
| 129 | struct MatchFinderOptions { |
||
| 130 | struct Profiling { |
||
| 131 | Profiling(llvm::StringMap<llvm::TimeRecord> &Records) |
||
| 132 | : Records(Records) {} |
||
| 133 | |||
| 134 | /// Per bucket timing information. |
||
| 135 | llvm::StringMap<llvm::TimeRecord> &Records; |
||
| 136 | }; |
||
| 137 | |||
| 138 | /// Enables per-check timers. |
||
| 139 | /// |
||
| 140 | /// It prints a report after match. |
||
| 141 | std::optional<Profiling> CheckProfiling; |
||
| 142 | }; |
||
| 143 | |||
| 144 | MatchFinder(MatchFinderOptions Options = MatchFinderOptions()); |
||
| 145 | ~MatchFinder(); |
||
| 146 | |||
| 147 | /// Adds a matcher to execute when running over the AST. |
||
| 148 | /// |
||
| 149 | /// Calls 'Action' with the BoundNodes on every match. |
||
| 150 | /// Adding more than one 'NodeMatch' allows finding different matches in a |
||
| 151 | /// single pass over the AST. |
||
| 152 | /// |
||
| 153 | /// Does not take ownership of 'Action'. |
||
| 154 | /// @{ |
||
| 155 | void addMatcher(const DeclarationMatcher &NodeMatch, |
||
| 156 | MatchCallback *Action); |
||
| 157 | void addMatcher(const TypeMatcher &NodeMatch, |
||
| 158 | MatchCallback *Action); |
||
| 159 | void addMatcher(const StatementMatcher &NodeMatch, |
||
| 160 | MatchCallback *Action); |
||
| 161 | void addMatcher(const NestedNameSpecifierMatcher &NodeMatch, |
||
| 162 | MatchCallback *Action); |
||
| 163 | void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, |
||
| 164 | MatchCallback *Action); |
||
| 165 | void addMatcher(const TypeLocMatcher &NodeMatch, |
||
| 166 | MatchCallback *Action); |
||
| 167 | void addMatcher(const CXXCtorInitializerMatcher &NodeMatch, |
||
| 168 | MatchCallback *Action); |
||
| 169 | void addMatcher(const TemplateArgumentLocMatcher &NodeMatch, |
||
| 170 | MatchCallback *Action); |
||
| 171 | void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action); |
||
| 172 | /// @} |
||
| 173 | |||
| 174 | /// Adds a matcher to execute when running over the AST. |
||
| 175 | /// |
||
| 176 | /// This is similar to \c addMatcher(), but it uses the dynamic interface. It |
||
| 177 | /// is more flexible, but the lost type information enables a caller to pass |
||
| 178 | /// a matcher that cannot match anything. |
||
| 179 | /// |
||
| 180 | /// \returns \c true if the matcher is a valid top-level matcher, \c false |
||
| 181 | /// otherwise. |
||
| 182 | bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, |
||
| 183 | MatchCallback *Action); |
||
| 184 | |||
| 185 | /// Creates a clang ASTConsumer that finds all matches. |
||
| 186 | std::unique_ptr<clang::ASTConsumer> newASTConsumer(); |
||
| 187 | |||
| 188 | /// Calls the registered callbacks on all matches on the given \p Node. |
||
| 189 | /// |
||
| 190 | /// Note that there can be multiple matches on a single node, for |
||
| 191 | /// example when using decl(forEachDescendant(stmt())). |
||
| 192 | /// |
||
| 193 | /// @{ |
||
| 194 | template <typename T> void match(const T &Node, ASTContext &Context) { |
||
| 195 | match(clang::DynTypedNode::create(Node), Context); |
||
| 196 | } |
||
| 197 | void match(const clang::DynTypedNode &Node, ASTContext &Context); |
||
| 198 | /// @} |
||
| 199 | |||
| 200 | /// Finds all matches in the given AST. |
||
| 201 | void matchAST(ASTContext &Context); |
||
| 202 | |||
| 203 | /// Registers a callback to notify the end of parsing. |
||
| 204 | /// |
||
| 205 | /// The provided closure is called after parsing is done, before the AST is |
||
| 206 | /// traversed. Useful for benchmarking. |
||
| 207 | /// Each call to FindAll(...) will call the closure once. |
||
| 208 | void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone); |
||
| 209 | |||
| 210 | /// For each \c Matcher<> a \c MatchCallback that will be called |
||
| 211 | /// when it matches. |
||
| 212 | struct MatchersByType { |
||
| 213 | std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>> |
||
| 214 | DeclOrStmt; |
||
| 215 | std::vector<std::pair<TypeMatcher, MatchCallback *>> Type; |
||
| 216 | std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>> |
||
| 217 | NestedNameSpecifier; |
||
| 218 | std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>> |
||
| 219 | NestedNameSpecifierLoc; |
||
| 220 | std::vector<std::pair<TypeLocMatcher, MatchCallback *>> TypeLoc; |
||
| 221 | std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit; |
||
| 222 | std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>> |
||
| 223 | TemplateArgumentLoc; |
||
| 224 | std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr; |
||
| 225 | /// All the callbacks in one container to simplify iteration. |
||
| 226 | llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks; |
||
| 227 | }; |
||
| 228 | |||
| 229 | private: |
||
| 230 | MatchersByType Matchers; |
||
| 231 | |||
| 232 | MatchFinderOptions Options; |
||
| 233 | |||
| 234 | /// Called when parsing is done. |
||
| 235 | ParsingDoneTestCallback *ParsingDone; |
||
| 236 | }; |
||
| 237 | |||
| 238 | /// Returns the results of matching \p Matcher on \p Node. |
||
| 239 | /// |
||
| 240 | /// Collects the \c BoundNodes of all callback invocations when matching |
||
| 241 | /// \p Matcher on \p Node and returns the collected results. |
||
| 242 | /// |
||
| 243 | /// Multiple results occur when using matchers like \c forEachDescendant, |
||
| 244 | /// which generate a result for each sub-match. |
||
| 245 | /// |
||
| 246 | /// If you want to find all matches on the sub-tree rooted at \c Node (rather |
||
| 247 | /// than only the matches on \c Node itself), surround the \c Matcher with a |
||
| 248 | /// \c findAll(). |
||
| 249 | /// |
||
| 250 | /// \see selectFirst |
||
| 251 | /// @{ |
||
| 252 | template <typename MatcherT, typename NodeT> |
||
| 253 | SmallVector<BoundNodes, 1> |
||
| 254 | match(MatcherT Matcher, const NodeT &Node, ASTContext &Context); |
||
| 255 | |||
| 256 | template <typename MatcherT> |
||
| 257 | SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node, |
||
| 258 | ASTContext &Context); |
||
| 259 | /// @} |
||
| 260 | |||
| 261 | /// Returns the results of matching \p Matcher on the translation unit of |
||
| 262 | /// \p Context and collects the \c BoundNodes of all callback invocations. |
||
| 263 | template <typename MatcherT> |
||
| 264 | SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context); |
||
| 265 | |||
| 266 | /// Returns the first result of type \c NodeT bound to \p BoundTo. |
||
| 267 | /// |
||
| 268 | /// Returns \c NULL if there is no match, or if the matching node cannot be |
||
| 269 | /// casted to \c NodeT. |
||
| 270 | /// |
||
| 271 | /// This is useful in combanation with \c match(): |
||
| 272 | /// \code |
||
| 273 | /// const Decl *D = selectFirst<Decl>("id", match(Matcher.bind("id"), |
||
| 274 | /// Node, Context)); |
||
| 275 | /// \endcode |
||
| 276 | template <typename NodeT> |
||
| 277 | const NodeT * |
||
| 278 | selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) { |
||
| 279 | for (const BoundNodes &N : Results) { |
||
| 280 | if (const NodeT *Node = N.getNodeAs<NodeT>(BoundTo)) |
||
| 281 | return Node; |
||
| 282 | } |
||
| 283 | return nullptr; |
||
| 284 | } |
||
| 285 | |||
| 286 | namespace internal { |
||
| 287 | class CollectMatchesCallback : public MatchFinder::MatchCallback { |
||
| 288 | public: |
||
| 289 | void run(const MatchFinder::MatchResult &Result) override { |
||
| 290 | Nodes.push_back(Result.Nodes); |
||
| 291 | } |
||
| 292 | |||
| 293 | std::optional<TraversalKind> getCheckTraversalKind() const override { |
||
| 294 | return std::nullopt; |
||
| 295 | } |
||
| 296 | |||
| 297 | SmallVector<BoundNodes, 1> Nodes; |
||
| 298 | }; |
||
| 299 | } |
||
| 300 | |||
| 301 | template <typename MatcherT> |
||
| 302 | SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node, |
||
| 303 | ASTContext &Context) { |
||
| 304 | internal::CollectMatchesCallback Callback; |
||
| 305 | MatchFinder Finder; |
||
| 306 | Finder.addMatcher(Matcher, &Callback); |
||
| 307 | Finder.match(Node, Context); |
||
| 308 | return std::move(Callback.Nodes); |
||
| 309 | } |
||
| 310 | |||
| 311 | template <typename MatcherT, typename NodeT> |
||
| 312 | SmallVector<BoundNodes, 1> |
||
| 313 | match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) { |
||
| 314 | return match(Matcher, DynTypedNode::create(Node), Context); |
||
| 315 | } |
||
| 316 | |||
| 317 | template <typename MatcherT> |
||
| 318 | SmallVector<BoundNodes, 1> |
||
| 319 | match(MatcherT Matcher, ASTContext &Context) { |
||
| 320 | internal::CollectMatchesCallback Callback; |
||
| 321 | MatchFinder Finder; |
||
| 322 | Finder.addMatcher(Matcher, &Callback); |
||
| 323 | Finder.matchAST(Context); |
||
| 324 | return std::move(Callback.Nodes); |
||
| 325 | } |
||
| 326 | |||
| 327 | inline SmallVector<BoundNodes, 1> |
||
| 328 | matchDynamic(internal::DynTypedMatcher Matcher, const DynTypedNode &Node, |
||
| 329 | ASTContext &Context) { |
||
| 330 | internal::CollectMatchesCallback Callback; |
||
| 331 | MatchFinder Finder; |
||
| 332 | Finder.addDynamicMatcher(Matcher, &Callback); |
||
| 333 | Finder.match(Node, Context); |
||
| 334 | return std::move(Callback.Nodes); |
||
| 335 | } |
||
| 336 | |||
| 337 | template <typename NodeT> |
||
| 338 | SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher, |
||
| 339 | const NodeT &Node, |
||
| 340 | ASTContext &Context) { |
||
| 341 | return matchDynamic(Matcher, DynTypedNode::create(Node), Context); |
||
| 342 | } |
||
| 343 | |||
| 344 | inline SmallVector<BoundNodes, 1> |
||
| 345 | matchDynamic(internal::DynTypedMatcher Matcher, ASTContext &Context) { |
||
| 346 | internal::CollectMatchesCallback Callback; |
||
| 347 | MatchFinder Finder; |
||
| 348 | Finder.addDynamicMatcher(Matcher, &Callback); |
||
| 349 | Finder.matchAST(Context); |
||
| 350 | return std::move(Callback.Nodes); |
||
| 351 | } |
||
| 352 | |||
| 353 | } // end namespace ast_matchers |
||
| 354 | } // end namespace clang |
||
| 355 | |||
| 356 | #endif |