Home | History | Annotate | Line # | Download | only in ASTMatchers
      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.
    101     virtual void onStartOfTranslationUnit() {}
    102 
    103     /// Called at the end of each translation unit.
    104     ///
    105     /// Optionally override to do per translation unit tasks.
    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 {
    130       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   /// @}
    171 
    172   /// Adds a matcher to execute when running over the AST.
    173   ///
    174   /// This is similar to \c addMatcher(), but it uses the dynamic interface. It
    175   /// is more flexible, but the lost type information enables a caller to pass
    176   /// a matcher that cannot match anything.
    177   ///
    178   /// \returns \c true if the matcher is a valid top-level matcher, \c false
    179   ///   otherwise.
    180   bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
    181                          MatchCallback *Action);
    182 
    183   /// Creates a clang ASTConsumer that finds all matches.
    184   std::unique_ptr<clang::ASTConsumer> newASTConsumer();
    185 
    186   /// Calls the registered callbacks on all matches on the given \p Node.
    187   ///
    188   /// Note that there can be multiple matches on a single node, for
    189   /// example when using decl(forEachDescendant(stmt())).
    190   ///
    191   /// @{
    192   template <typename T> void match(const T &Node, ASTContext &Context) {
    193     match(clang::DynTypedNode::create(Node), Context);
    194   }
    195   void match(const clang::DynTypedNode &Node, ASTContext &Context);
    196   /// @}
    197 
    198   /// Finds all matches in the given AST.
    199   void matchAST(ASTContext &Context);
    200 
    201   /// Registers a callback to notify the end of parsing.
    202   ///
    203   /// The provided closure is called after parsing is done, before the AST is
    204   /// traversed. Useful for benchmarking.
    205   /// Each call to FindAll(...) will call the closure once.
    206   void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone);
    207 
    208   /// For each \c Matcher<> a \c MatchCallback that will be called
    209   /// when it matches.
    210   struct MatchersByType {
    211     std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>>
    212         DeclOrStmt;
    213     std::vector<std::pair<TypeMatcher, MatchCallback *>> Type;
    214     std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>>
    215         NestedNameSpecifier;
    216     std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>>
    217         NestedNameSpecifierLoc;
    218     std::vector<std::pair<TypeLocMatcher, MatchCallback *>> TypeLoc;
    219     std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit;
    220     std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>>
    221         TemplateArgumentLoc;
    222     /// All the callbacks in one container to simplify iteration.
    223     llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks;
    224   };
    225 
    226 private:
    227   MatchersByType Matchers;
    228 
    229   MatchFinderOptions Options;
    230 
    231   /// Called when parsing is done.
    232   ParsingDoneTestCallback *ParsingDone;
    233 };
    234 
    235 /// Returns the results of matching \p Matcher on \p Node.
    236 ///
    237 /// Collects the \c BoundNodes of all callback invocations when matching
    238 /// \p Matcher on \p Node and returns the collected results.
    239 ///
    240 /// Multiple results occur when using matchers like \c forEachDescendant,
    241 /// which generate a result for each sub-match.
    242 ///
    243 /// If you want to find all matches on the sub-tree rooted at \c Node (rather
    244 /// than only the matches on \c Node itself), surround the \c Matcher with a
    245 /// \c findAll().
    246 ///
    247 /// \see selectFirst
    248 /// @{
    249 template <typename MatcherT, typename NodeT>
    250 SmallVector<BoundNodes, 1>
    251 match(MatcherT Matcher, const NodeT &Node, ASTContext &Context);
    252 
    253 template <typename MatcherT>
    254 SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node,
    255                                  ASTContext &Context);
    256 /// @}
    257 
    258 /// Returns the results of matching \p Matcher on the translation unit of
    259 /// \p Context and collects the \c BoundNodes of all callback invocations.
    260 template <typename MatcherT>
    261 SmallVector<BoundNodes, 1> match(MatcherT Matcher, ASTContext &Context);
    262 
    263 /// Returns the first result of type \c NodeT bound to \p BoundTo.
    264 ///
    265 /// Returns \c NULL if there is no match, or if the matching node cannot be
    266 /// casted to \c NodeT.
    267 ///
    268 /// This is useful in combanation with \c match():
    269 /// \code
    270 ///   const Decl *D = selectFirst<Decl>("id", match(Matcher.bind("id"),
    271 ///                                                 Node, Context));
    272 /// \endcode
    273 template <typename NodeT>
    274 const NodeT *
    275 selectFirst(StringRef BoundTo, const SmallVectorImpl<BoundNodes> &Results) {
    276   for (const BoundNodes &N : Results) {
    277     if (const NodeT *Node = N.getNodeAs<NodeT>(BoundTo))
    278       return Node;
    279   }
    280   return nullptr;
    281 }
    282 
    283 namespace internal {
    284 class CollectMatchesCallback : public MatchFinder::MatchCallback {
    285 public:
    286   void run(const MatchFinder::MatchResult &Result) override {
    287     Nodes.push_back(Result.Nodes);
    288   }
    289 
    290   llvm::Optional<TraversalKind> getCheckTraversalKind() const override {
    291     return llvm::None;
    292   }
    293 
    294   SmallVector<BoundNodes, 1> Nodes;
    295 };
    296 }
    297 
    298 template <typename MatcherT>
    299 SmallVector<BoundNodes, 1> match(MatcherT Matcher, const DynTypedNode &Node,
    300                                  ASTContext &Context) {
    301   internal::CollectMatchesCallback Callback;
    302   MatchFinder Finder;
    303   Finder.addMatcher(Matcher, &Callback);
    304   Finder.match(Node, Context);
    305   return std::move(Callback.Nodes);
    306 }
    307 
    308 template <typename MatcherT, typename NodeT>
    309 SmallVector<BoundNodes, 1>
    310 match(MatcherT Matcher, const NodeT &Node, ASTContext &Context) {
    311   return match(Matcher, DynTypedNode::create(Node), Context);
    312 }
    313 
    314 template <typename MatcherT>
    315 SmallVector<BoundNodes, 1>
    316 match(MatcherT Matcher, ASTContext &Context) {
    317   internal::CollectMatchesCallback Callback;
    318   MatchFinder Finder;
    319   Finder.addMatcher(Matcher, &Callback);
    320   Finder.matchAST(Context);
    321   return std::move(Callback.Nodes);
    322 }
    323 
    324 inline SmallVector<BoundNodes, 1>
    325 matchDynamic(internal::DynTypedMatcher Matcher, const DynTypedNode &Node,
    326              ASTContext &Context) {
    327   internal::CollectMatchesCallback Callback;
    328   MatchFinder Finder;
    329   Finder.addDynamicMatcher(Matcher, &Callback);
    330   Finder.match(Node, Context);
    331   return std::move(Callback.Nodes);
    332 }
    333 
    334 template <typename NodeT>
    335 SmallVector<BoundNodes, 1> matchDynamic(internal::DynTypedMatcher Matcher,
    336                                         const NodeT &Node,
    337                                         ASTContext &Context) {
    338   return matchDynamic(Matcher, DynTypedNode::create(Node), Context);
    339 }
    340 
    341 inline SmallVector<BoundNodes, 1>
    342 matchDynamic(internal::DynTypedMatcher Matcher, ASTContext &Context) {
    343   internal::CollectMatchesCallback Callback;
    344   MatchFinder Finder;
    345   Finder.addDynamicMatcher(Matcher, &Callback);
    346   Finder.matchAST(Context);
    347   return std::move(Callback.Nodes);
    348 }
    349 
    350 } // end namespace ast_matchers
    351 } // end namespace clang
    352 
    353 #endif
    354