Viewing file: ASTMatchFinder.h (12.28 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
//===--- ASTMatchFinder.h - Structural query framework ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Provides a way to construct an ASTConsumer that runs given matchers // over the AST and invokes a given callback on every match. // // The general idea is to construct a matcher expression that describes a // subtree match on the AST. Next, a callback that is executed every time the // expression matches is registered, and the matcher is run over the AST of // some code. Matched subexpressions can be bound to string IDs and easily // be accessed from the registered callback. The callback can than use the // AST nodes that the subexpressions matched on to output information about // the match or construct changes that can be applied to the code. // // Example: // class HandleMatch : public MatchFinder::MatchCallback { // public: // virtual void Run(const MatchFinder::MatchResult &Result) { // const CXXRecordDecl *Class = // Result.Nodes.GetDeclAs<CXXRecordDecl>("id"); // ... // } // }; // // int main(int argc, char **argv) { // ClangTool Tool(argc, argv); // MatchFinder finder; // finder.AddMatcher(Id("id", record(hasName("::a_namespace::AClass"))), // new HandleMatch); // return Tool.Run(newFrontendActionFactory(&finder)); // } // //===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ASTMATCHERS_ASTMATCHFINDER_H #define LLVM_CLANG_ASTMATCHERS_ASTMATCHFINDER_H
#include "clang/ASTMatchers/ASTMatchers.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Timer.h" #include <optional>
namespace clang {
namespace ast_matchers {
/// A class to allow finding matches over the Clang AST. /// /// After creation, you can add multiple matchers to the MatchFinder via /// calls to addMatcher(...). /// /// Once all matchers are added, newASTConsumer() returns an ASTConsumer /// that will trigger the callbacks specified via addMatcher(...) when a match /// is found. /// /// The order of matches is guaranteed to be equivalent to doing a pre-order /// traversal on the AST, and applying the matchers in the order in which they /// were added to the MatchFinder. /// /// See ASTMatchers.h for more information about how to create matchers. /// /// Not intended to be subclassed. class MatchFinder { public: /// Contains all information for a given match. /// /// Every time a match is found, the MatchFinder will invoke the registered /// MatchCallback with a MatchResult containing information about the match. struct MatchResult { MatchResult(const BoundNodes &Nodes, clang::ASTContext *Context);
/// Contains the nodes bound on the current match. /// /// This allows user code to easily extract matched AST nodes. const BoundNodes Nodes;
/// Utilities for interpreting the matched AST structures. /// @{ clang::ASTContext * const Context; clang::SourceManager * const SourceManager; /// @} };
/// Called when the Match registered for it was successfully found /// in the AST. class MatchCallback { public: virtual ~MatchCallback();
/// Called on every match by the \c MatchFinder. virtual void run(const MatchResult &Result) = 0;
/// Called at the start of each translation unit. /// /// Optionally override to do per translation unit tasks. virtual void onStartOfTranslationUnit() {}
/// Called at the end of each translation unit. /// /// Optionally override to do per translation unit tasks. virtual void onEndOfTranslationUnit() {}
/// An id used to group the matchers. /// /// This id is used, for example, for the profiling output. /// It defaults to "<unknown>". virtual StringRef getID() const;
/// TraversalKind to use while matching and processing /// the result nodes. This API is temporary to facilitate /// third parties porting existing code to the default /// behavior of clang-tidy. virtual std::optional<TraversalKind> getCheckTraversalKind() const; };
/// Called when parsing is finished. Intended for testing only. class ParsingDoneTestCallback { public: virtual ~ParsingDoneTestCallback(); virtual void run() = 0; };
struct MatchFinderOptions { struct Profiling { Profiling(llvm::StringMap<llvm::TimeRecord> &Records) : Records(Records) {}
/// Per bucket timing information. llvm::StringMap<llvm::TimeRecord> &Records; };
/// Enables per-check timers. /// /// It prints a report after match. std::optional<Profiling> CheckProfiling; };
MatchFinder(MatchFinderOptions Options = MatchFinderOptions()); ~MatchFinder();
/// Adds a matcher to execute when running over the AST. /// /// Calls 'Action' with the BoundNodes on every match. /// Adding more than one 'NodeMatch' allows finding different matches in a /// single pass over the AST. /// /// Does not take ownership of 'Action'. /// @{ void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const TypeMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const StatementMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const NestedNameSpecifierMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const TypeLocMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const CXXCtorInitializerMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const TemplateArgumentLocMatcher &NodeMatch, MatchCallback *Action); void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action); /// @}
/// Adds a matcher to execute when running over the AST. /// /// This is similar to \c addMatcher(), but it uses the dynamic interface. It /// is more flexible, but the lost type information enables a caller to pass /// a matcher that cannot match anything. /// /// \returns \c true if the matcher is a valid top-level matcher, \c false /// otherwise. bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, MatchCallback *Action);
/// Creates a clang ASTConsumer that finds all matches. std::unique_ptr<clang::ASTConsumer> newASTConsumer();
/// Calls the registered callbacks on all matches on the given \p Node. /// /// Note that there can be multiple matches on a single node, for /// example when using decl(forEachDescendant(stmt())). /// /// @{ template <typename T> void match(const T &Node, ASTContext &Context) { match(clang::DynTypedNode::create(Node), Context); } void match(const clang::DynTypedNode &Node, ASTContext &Context); /// @}
/// Finds all matches in the given AST. void matchAST(ASTContext &Context);
/// Registers a callback to notify the end of parsing. /// /// The provided closure is called after parsing is done, before the AST is /// traversed. Useful for benchmarking. /// Each call to FindAll(...) will call the closure once. void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone);
/// For each \c Matcher<> a \c MatchCallback that will be called /// when it matches. struct MatchersByType { std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>> DeclOrStmt; std::vector<std::pair<TypeMatcher, MatchCallback *>> Type; std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>> NestedNameSpecifier; std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>> NestedNameSpecifierLoc; std::vector<std::pair<TypeLocMatcher, MatchCallback *>> TypeLoc; std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit; std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>> TemplateArgumentLoc; std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr; /// All the callbacks in one container to simplify iteration. llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks; };
private: MatchersByType Matchers;
MatchFinderOptions Options;
/// Called when parsing is done. ParsingDoneTestCallback *ParsingDone; };
/// Returns the results of matching \p Matcher on \p Node. /// /// Collects the \c BoundNodes of all callback invocations when matching /// \p Matcher on \p Node and returns the collected results. /// /// Multiple results occur when using matchers like \c forEachDescendant, /// which generate a result for each sub-match. /// /// If you want to find all matches on the sub-tree rooted at \c Node (rather /// than only the matches on \c Node itself), surround the \c Matcher with a /// \c findAll(). /// /// \see selectFirst /// @{ template <typename MatcherT, typename NodeT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, const NodeT &Node, ASTContext &Context);
template <typename MatcherT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node, ASTContext &Context); /// @}
/// Returns the results of matching \p Matcher on the translation unit of /// \p Context and collects the \c BoundNodes of all callback invocations. template <typename MatcherT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context);
/// Returns the first result of type \c NodeT bound to \p BoundTo. /// /// Returns \c NULL if there is no match, or if the matching node cannot be /// casted to \c NodeT. /// /// This is useful in combanation with \c match(): /// \code /// const Decl *D = selectFirst<Decl>("id", match(Matcher.bind("id"), /// Node, Context)); /// \endcode template <typename NodeT> const NodeT * selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) { for (const BoundNodes &N : Results) { if (const NodeT *Node = N.getNodeAs<NodeT>(BoundTo)) return Node; } return nullptr; }
namespace internal { class CollectMatchesCallback : public MatchFinder::MatchCallback { public: void run(const MatchFinder::MatchResult &Result) override { Nodes.push_back(Result.Nodes); }
std::optional<TraversalKind> getCheckTraversalKind() const override { return std::nullopt; }
SmallVector<BoundNodes, 1> Nodes; }; }
template <typename MatcherT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node, ASTContext &Context) { internal::CollectMatchesCallback Callback; MatchFinder Finder; Finder.addMatcher(Matcher, &Callback); Finder.match(Node, Context); return std::move(Callback.Nodes); }
template <typename MatcherT, typename NodeT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) { return match(Matcher, DynTypedNode::create(Node), Context); }
template <typename MatcherT> SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context) { internal::CollectMatchesCallback Callback; MatchFinder Finder; Finder.addMatcher(Matcher, &Callback); Finder.matchAST(Context); return std::move(Callback.Nodes); }
inline SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher, const DynTypedNode &Node, ASTContext &Context) { internal::CollectMatchesCallback Callback; MatchFinder Finder; Finder.addDynamicMatcher(Matcher, &Callback); Finder.match(Node, Context); return std::move(Callback.Nodes); }
template <typename NodeT> SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher, const NodeT &Node, ASTContext &Context) { return matchDynamic(Matcher, DynTypedNode::create(Node), Context); }
inline SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher, ASTContext &Context) { internal::CollectMatchesCallback Callback; MatchFinder Finder; Finder.addDynamicMatcher(Matcher, &Callback); Finder.matchAST(Context); return std::move(Callback.Nodes); }
} // end namespace ast_matchers } // end namespace clang
#endif
|