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
49namespace clang {
50
51namespace 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.
69class MatchFinder {
70public:
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
229private:
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/// @{
252template <typename MatcherT, typename NodeT>
253SmallVector<BoundNodes, 1>
254match(MatcherT Matcher, const NodeT &Node, ASTContext &Context);
255
256template <typename MatcherT>
257SmallVector<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.
263template <typename MatcherT>
264SmallVector<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
276template <typename NodeT>
277const NodeT *
278selectFirst(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
286namespace internal {
287class CollectMatchesCallback : public MatchFinder::MatchCallback {
288public:
289 void run(const MatchFinder::MatchResult &Result) override {
290 Nodes.push_back(Elt: 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
301template <typename MatcherT>
302SmallVector<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
311template <typename MatcherT, typename NodeT>
312SmallVector<BoundNodes, 1>
313match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) {
314 return match(Matcher, DynTypedNode::create(Node), Context);
315}
316
317template <typename MatcherT>
318SmallVector<BoundNodes, 1>
319match(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
327inline SmallVector<BoundNodes, 1>
328matchDynamic(internal::DynTypedMatcher Matcher, const DynTypedNode &Node,
329 ASTContext &Context) {
330 internal::CollectMatchesCallback Callback;
331 MatchFinder Finder;
332 Finder.addDynamicMatcher(NodeMatch: Matcher, Action: &Callback);
333 Finder.match(Node, Context);
334 return std::move(Callback.Nodes);
335}
336
337template <typename NodeT>
338SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher,
339 const NodeT &Node,
340 ASTContext &Context) {
341 return matchDynamic(Matcher, DynTypedNode::create(Node), Context);
342}
343
344inline SmallVector<BoundNodes, 1>
345matchDynamic(internal::DynTypedMatcher Matcher, ASTContext &Context) {
346 internal::CollectMatchesCallback Callback;
347 MatchFinder Finder;
348 Finder.addDynamicMatcher(NodeMatch: Matcher, Action: &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

source code of clang/include/clang/ASTMatchers/ASTMatchFinder.h