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