wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Depends On D131614 <https://reviews.llvm.org/D131614>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h

Index: clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
+++ clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
@@ -24,6 +24,7 @@
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "llvm/ADT/StringRef.h"
 #include <functional>
@@ -31,6 +32,8 @@
 #include <utility>
 #include <vector>
 
+// TODO: add tests and documentation
+
 namespace clang {
 namespace dataflow {
 
@@ -44,59 +47,45 @@
   Environment &Env;
 };
 
-/// Matches against `Stmt` and, based on its structure, dispatches to an
-/// appropriate handler.
+template <typename T> using MatcherT = ast_matchers::internal::Matcher<T>;
+
+template <typename T, typename State, typename Result = void>
+using ActionT = std::function<Result(
+    const T *, const ast_matchers::MatchFinder::MatchResult &, State &)>;
+
+template <typename BaseT, typename State, typename Result = void>
+using ASTMatchSwitch =
+    std::function<Result(const BaseT &, ASTContext &, State &)>;
+
 template <typename State, typename Result = void>
-using MatchSwitch = std::function<Result(const Stmt &, ASTContext &, State &)>;
-
-/// Collects cases of a "match switch": a collection of matchers paired with
-/// callbacks, which together define a switch that can be applied to a
-/// `Stmt`. This structure can simplify the definition of `transfer` functions
-/// that rely on pattern-matching.
-///
-/// For example, consider an analysis that handles particular function calls. It
-/// can define the `MatchSwitch` once, in the constructor of the analysis, and
-/// then reuse it each time that `transfer` is called, with a fresh state value.
-///
-/// \code
-/// MatchSwitch<TransferState<MyLattice> BuildSwitch() {
-///   return MatchSwitchBuilder<TransferState<MyLattice>>()
-///     .CaseOf(callExpr(callee(functionDecl(hasName("foo")))), TransferFooCall)
-///     .CaseOf(callExpr(argumentCountIs(2),
-///                      callee(functionDecl(hasName("bar")))),
-///             TransferBarCall)
-///     .Build();
-/// }
-/// \endcode
-template <typename State, typename Result = void> class MatchSwitchBuilder {
+using CFGMatchSwitch =
+    std::function<Result(const CFGElement &, ASTContext &, State &)>;
+
+// Alias for the previous implementation under the name of `MatchSwitch`.
+template <typename State, typename Result = void>
+using MatchSwitch = ASTMatchSwitch<Stmt, State, Result>;
+
+template <typename BaseT, typename State, typename Result = void>
+class ASTMatchSwitchBuilder {
 public:
-  /// Registers an action that will be triggered by the match of a pattern
-  /// against the input statement.
-  ///
-  /// Requirements:
-  ///
-  ///  `Node` should be a subclass of `Stmt`.
-  template <typename Node>
-  MatchSwitchBuilder &&
-  CaseOf(ast_matchers::internal::Matcher<Stmt> M,
-         std::function<Result(const Node *,
-                              const ast_matchers::MatchFinder::MatchResult &,
-                              State &)>
-             A) && {
-    Matchers.push_back(std::move(M));
-    Actions.push_back(
-        [A = std::move(A)](const Stmt *Stmt,
-                           const ast_matchers::MatchFinder::MatchResult &R,
-                           State &S) { return A(cast<Node>(Stmt), R, S); });
+  template <typename NodeT>
+  ASTMatchSwitchBuilder CaseOf(MatcherT<BaseT> Matcher,
+                               ActionT<NodeT, State, Result> Action) {
+    Matchers.push_back(std::move(Matcher));
+    Actions.push_back([A = std::move(Action)](
+                          const BaseT *Node,
+                          const ast_matchers::MatchFinder::MatchResult &R,
+                          State &S) { return A(cast<NodeT>(Node), R, S); });
     return std::move(*this);
   }
 
-  MatchSwitch<State, Result> Build() && {
+  ASTMatchSwitch<BaseT, State, Result> Build() {
     return [Matcher = BuildMatcher(), Actions = std::move(Actions)](
-               const Stmt &Stmt, ASTContext &Context, State &S) -> Result {
-      auto Results = ast_matchers::matchDynamic(Matcher, Stmt, Context);
-      if (Results.empty())
+               const BaseT &Node, ASTContext &Context, State &S) -> Result {
+      auto Results = ast_matchers::matchDynamic(Matcher, Node, Context);
+      if (Results.empty()) {
         return Result();
+      }
       // Look through the map for the first binding of the form "TagN..." use
       // that to select the action.
       for (const auto &Element : Results[0].getMap()) {
@@ -105,7 +94,7 @@
         if (ID.consume_front("Tag") && !ID.getAsInteger(10, Index) &&
             Index < Actions.size()) {
           return Actions[Index](
-              &Stmt,
+              &Node,
               ast_matchers::MatchFinder::MatchResult(Results[0], &Context), S);
         }
       }
@@ -137,15 +126,59 @@
     // The matcher type on the cases ensures that `Expr` kind is compatible with
     // all of the matchers.
     return DynTypedMatcher::constructVariadic(
-        DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind<Stmt>(),
+        DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind<BaseT>(),
         std::move(Matchers));
   }
 
+public:
   std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
-  std::vector<std::function<Result(
-      const Stmt *, const ast_matchers::MatchFinder::MatchResult &, State &)>>
-      Actions;
+  std::vector<ActionT<BaseT, State, Result>> Actions;
 };
+
+// Alias for the previous implementation under the name of `MatchSwitchBuilder`.
+template <typename State, typename Result = void>
+using MatchSwitchBuilder = ASTMatchSwitchBuilder<Stmt, State, Result>;
+
+template <typename State, typename Result = void> class CFGMatchSwitchBuilder {
+public:
+  template <typename NodeT>
+  CFGMatchSwitchBuilder CaseOfCFGStmt(MatcherT<Stmt> M,
+                                      ActionT<NodeT, State, Result> A) {
+    StmtBuilder = StmtBuilder.template CaseOf<NodeT>(M, A);
+    return std::move(*this);
+  }
+
+  template <typename NodeT>
+  CFGMatchSwitchBuilder CaseOfCFGInit(MatcherT<CXXCtorInitializer> M,
+                                      ActionT<NodeT, State, Result> A) {
+    InitBuilder = InitBuilder.template CaseOf<NodeT>(M, A);
+    return std::move(*this);
+  }
+
+  CFGMatchSwitch<State, Result> Build() && {
+    return [StmtMS = StmtBuilder.Build(), InitMS = InitBuilder.Build()](
+               const CFGElement &Element, ASTContext &Context,
+               State &S) -> Result {
+      switch (Element.getKind()) {
+      case CFGElement::Initializer:
+        return InitMS(*Element.getAs<CFGInitializer>()->getInitializer(),
+                      Context, S);
+      case CFGElement::Statement:
+      case CFGElement::Constructor:
+      case CFGElement::CXXRecordTypedCall:
+        return StmtMS(*Element.getAs<CFGStmt>()->getStmt(), Context, S);
+      default:
+        // FIXME: handle other kinds of CFGElement
+        return Result();
+      }
+    };
+  }
+
+private:
+  ASTMatchSwitchBuilder<Stmt, State, Result> StmtBuilder;
+  ASTMatchSwitchBuilder<CXXCtorInitializer, State, Result> InitBuilder;
+};
+
 } // namespace dataflow
 } // namespace clang
-#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
+#endif
\ No newline at end of file
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to