123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===--- 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"
- 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 llvm::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.
- llvm::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);
- }
- llvm::Optional<TraversalKind> getCheckTraversalKind() const override {
- return llvm::None;
- }
- 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
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|