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.
onStartOfTranslationUnit()101 virtual void onStartOfTranslationUnit() {}
102
103 /// Called at the end of each translation unit.
104 ///
105 /// Optionally override to do per translation unit tasks.
onEndOfTranslationUnit()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 /// TraversalKind to use while matching and processing
115 /// the result nodes. This API is temporary to facilitate
116 /// third parties porting existing code to the default
117 /// behavior of clang-tidy.
118 virtual llvm::Optional<TraversalKind> getCheckTraversalKind() const;
119 };
120
121 /// Called when parsing is finished. Intended for testing only.
122 class ParsingDoneTestCallback {
123 public:
124 virtual ~ParsingDoneTestCallback();
125 virtual void run() = 0;
126 };
127
128 struct MatchFinderOptions {
129 struct Profiling {
ProfilingMatchFinderOptions::Profiling130 Profiling(llvm::StringMap<llvm::TimeRecord> &Records)
131 : Records(Records) {}
132
133 /// Per bucket timing information.
134 llvm::StringMap<llvm::TimeRecord> &Records;
135 };
136
137 /// Enables per-check timers.
138 ///
139 /// It prints a report after match.
140 llvm::Optional<Profiling> CheckProfiling;
141 };
142
143 MatchFinder(MatchFinderOptions Options = MatchFinderOptions());
144 ~MatchFinder();
145
146 /// Adds a matcher to execute when running over the AST.
147 ///
148 /// Calls 'Action' with the BoundNodes on every match.
149 /// Adding more than one 'NodeMatch' allows finding different matches in a
150 /// single pass over the AST.
151 ///
152 /// Does not take ownership of 'Action'.
153 /// @{
154 void addMatcher(const DeclarationMatcher &NodeMatch,
155 MatchCallback *Action);
156 void addMatcher(const TypeMatcher &NodeMatch,
157 MatchCallback *Action);
158 void addMatcher(const StatementMatcher &NodeMatch,
159 MatchCallback *Action);
160 void addMatcher(const NestedNameSpecifierMatcher &NodeMatch,
161 MatchCallback *Action);
162 void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch,
163 MatchCallback *Action);
164 void addMatcher(const TypeLocMatcher &NodeMatch,
165 MatchCallback *Action);
166 void addMatcher(const CXXCtorInitializerMatcher &NodeMatch,
167 MatchCallback *Action);
168 void addMatcher(const TemplateArgumentLocMatcher &NodeMatch,
169 MatchCallback *Action);
170 void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action);
171 /// @}
172
173 /// Adds a matcher to execute when running over the AST.
174 ///
175 /// This is similar to \c addMatcher(), but it uses the dynamic interface. It
176 /// is more flexible, but the lost type information enables a caller to pass
177 /// a matcher that cannot match anything.
178 ///
179 /// \returns \c true if the matcher is a valid top-level matcher, \c false
180 /// otherwise.
181 bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
182 MatchCallback *Action);
183
184 /// Creates a clang ASTConsumer that finds all matches.
185 std::unique_ptr<clang::ASTConsumer> newASTConsumer();
186
187 /// Calls the registered callbacks on all matches on the given \p Node.
188 ///
189 /// Note that there can be multiple matches on a single node, for
190 /// example when using decl(forEachDescendant(stmt())).
191 ///
192 /// @{
match(const T & Node,ASTContext & Context)193 template <typename T> void match(const T &Node, ASTContext &Context) {
194 match(clang::DynTypedNode::create(Node), Context);
195 }
196 void match(const clang::DynTypedNode &Node, ASTContext &Context);
197 /// @}
198
199 /// Finds all matches in the given AST.
200 void matchAST(ASTContext &Context);
201
202 /// Registers a callback to notify the end of parsing.
203 ///
204 /// The provided closure is called after parsing is done, before the AST is
205 /// traversed. Useful for benchmarking.
206 /// Each call to FindAll(...) will call the closure once.
207 void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone);
208
209 /// For each \c Matcher<> a \c MatchCallback that will be called
210 /// when it matches.
211 struct MatchersByType {
212 std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>>
213 DeclOrStmt;
214 std::vector<std::pair<TypeMatcher, MatchCallback *>> Type;
215 std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>>
216 NestedNameSpecifier;
217 std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>>
218 NestedNameSpecifierLoc;
219 std::vector<std::pair<TypeLocMatcher, MatchCallback *>> TypeLoc;
220 std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit;
221 std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>>
222 TemplateArgumentLoc;
223 std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr;
224 /// All the callbacks in one container to simplify iteration.
225 llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks;
226 };
227
228 private:
229 MatchersByType Matchers;
230
231 MatchFinderOptions Options;
232
233 /// Called when parsing is done.
234 ParsingDoneTestCallback *ParsingDone;
235 };
236
237 /// Returns the results of matching \p Matcher on \p Node.
238 ///
239 /// Collects the \c BoundNodes of all callback invocations when matching
240 /// \p Matcher on \p Node and returns the collected results.
241 ///
242 /// Multiple results occur when using matchers like \c forEachDescendant,
243 /// which generate a result for each sub-match.
244 ///
245 /// If you want to find all matches on the sub-tree rooted at \c Node (rather
246 /// than only the matches on \c Node itself), surround the \c Matcher with a
247 /// \c findAll().
248 ///
249 /// \see selectFirst
250 /// @{
251 template <typename MatcherT, typename NodeT>
252 SmallVector<BoundNodes, 1>
253 match(MatcherT Matcher, const NodeT &Node, ASTContext &Context);
254
255 template <typename MatcherT>
256 SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node,
257 ASTContext &Context);
258 /// @}
259
260 /// Returns the results of matching \p Matcher on the translation unit of
261 /// \p Context and collects the \c BoundNodes of all callback invocations.
262 template <typename MatcherT>
263 SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context);
264
265 /// Returns the first result of type \c NodeT bound to \p BoundTo.
266 ///
267 /// Returns \c NULL if there is no match, or if the matching node cannot be
268 /// casted to \c NodeT.
269 ///
270 /// This is useful in combanation with \c match():
271 /// \code
272 /// const Decl *D = selectFirst<Decl>("id", match(Matcher.bind("id"),
273 /// Node, Context));
274 /// \endcode
275 template <typename NodeT>
276 const NodeT *
selectFirst(StringRef BoundTo,const SmallVectorImpl<BoundNodes> & Results)277 selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) {
278 for (const BoundNodes &N : Results) {
279 if (const NodeT *Node = N.getNodeAs<NodeT>(BoundTo))
280 return Node;
281 }
282 return nullptr;
283 }
284
285 namespace internal {
286 class CollectMatchesCallback : public MatchFinder::MatchCallback {
287 public:
run(const MatchFinder::MatchResult & Result)288 void run(const MatchFinder::MatchResult &Result) override {
289 Nodes.push_back(Result.Nodes);
290 }
291
getCheckTraversalKind()292 llvm::Optional<TraversalKind> getCheckTraversalKind() const override {
293 return llvm::None;
294 }
295
296 SmallVector<BoundNodes, 1> Nodes;
297 };
298 }
299
300 template <typename MatcherT>
match(MatcherT Matcher,const DynTypedNode & Node,ASTContext & Context)301 SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node,
302 ASTContext &Context) {
303 internal::CollectMatchesCallback Callback;
304 MatchFinder Finder;
305 Finder.addMatcher(Matcher, &Callback);
306 Finder.match(Node, Context);
307 return std::move(Callback.Nodes);
308 }
309
310 template <typename MatcherT, typename NodeT>
311 SmallVector<BoundNodes, 1>
match(MatcherT Matcher,const NodeT & Node,ASTContext & Context)312 match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) {
313 return match(Matcher, DynTypedNode::create(Node), Context);
314 }
315
316 template <typename MatcherT>
317 SmallVector<BoundNodes, 1>
match(MatcherT Matcher,ASTContext & Context)318 match(MatcherT Matcher, ASTContext &Context) {
319 internal::CollectMatchesCallback Callback;
320 MatchFinder Finder;
321 Finder.addMatcher(Matcher, &Callback);
322 Finder.matchAST(Context);
323 return std::move(Callback.Nodes);
324 }
325
326 inline SmallVector<BoundNodes, 1>
matchDynamic(internal::DynTypedMatcher Matcher,const DynTypedNode & Node,ASTContext & Context)327 matchDynamic(internal::DynTypedMatcher Matcher, const DynTypedNode &Node,
328 ASTContext &Context) {
329 internal::CollectMatchesCallback Callback;
330 MatchFinder Finder;
331 Finder.addDynamicMatcher(Matcher, &Callback);
332 Finder.match(Node, Context);
333 return std::move(Callback.Nodes);
334 }
335
336 template <typename NodeT>
matchDynamic(internal::DynTypedMatcher Matcher,const NodeT & Node,ASTContext & Context)337 SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher,
338 const NodeT &Node,
339 ASTContext &Context) {
340 return matchDynamic(Matcher, DynTypedNode::create(Node), Context);
341 }
342
343 inline SmallVector<BoundNodes, 1>
matchDynamic(internal::DynTypedMatcher Matcher,ASTContext & Context)344 matchDynamic(internal::DynTypedMatcher Matcher, ASTContext &Context) {
345 internal::CollectMatchesCallback Callback;
346 MatchFinder Finder;
347 Finder.addDynamicMatcher(Matcher, &Callback);
348 Finder.matchAST(Context);
349 return std::move(Callback.Nodes);
350 }
351
352 } // end namespace ast_matchers
353 } // end namespace clang
354
355 #endif
356