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