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 |