xref: /freebsd/contrib/llvm-project/clang/include/clang/ASTMatchers/ASTMatchFinder.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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.
onStartOfTranslationUnit()102     virtual void onStartOfTranslationUnit() {}
103 
104     /// Called at the end of each translation unit.
105     ///
106     /// Optionally override to do per translation unit tasks.
onEndOfTranslationUnit()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 {
ProfilingMatchFinderOptions::Profiling131       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   /// @{
match(const T & Node,ASTContext & Context)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 *
selectFirst(StringRef BoundTo,const SmallVectorImpl<BoundNodes> & Results)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:
run(const MatchFinder::MatchResult & Result)289   void run(const MatchFinder::MatchResult &Result) override {
290     Nodes.push_back(Result.Nodes);
291   }
292 
getCheckTraversalKind()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>
match(MatcherT Matcher,const DynTypedNode & Node,ASTContext & Context)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>
match(MatcherT Matcher,const NodeT & Node,ASTContext & Context)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>
match(MatcherT Matcher,ASTContext & Context)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>
matchDynamic(internal::DynTypedMatcher Matcher,const DynTypedNode & Node,ASTContext & Context)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>
matchDynamic(internal::DynTypedMatcher Matcher,const NodeT & Node,ASTContext & Context)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>
matchDynamic(internal::DynTypedMatcher Matcher,ASTContext & Context)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
357