diff --git a/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h b/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h --- a/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h +++ b/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 @@ -31,6 +32,8 @@ #include #include +// 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 using MatcherT = ast_matchers::internal::Matcher; + +template +using ActionT = std::function; + +template +using ASTMatchSwitch = + std::function; + template -using MatchSwitch = std::function; - -/// 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 BuildSwitch() { -/// return MatchSwitchBuilder>() -/// .CaseOf(callExpr(callee(functionDecl(hasName("foo")))), TransferFooCall) -/// .CaseOf(callExpr(argumentCountIs(2), -/// callee(functionDecl(hasName("bar")))), -/// TransferBarCall) -/// .Build(); -/// } -/// \endcode -template class MatchSwitchBuilder { +using CFGMatchSwitch = + std::function; + +// Alias for the previous implementation under the name of `MatchSwitch`. +template +using MatchSwitch = ASTMatchSwitch; + +template +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 - MatchSwitchBuilder && - CaseOf(ast_matchers::internal::Matcher M, - std::function - 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(Stmt), R, S); }); + template + ASTMatchSwitchBuilder CaseOf(MatcherT Matcher, + ActionT 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(Node), R, S); }); return std::move(*this); } - MatchSwitch Build() && { + ASTMatchSwitch 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(), + DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind(), std::move(Matchers)); } +public: std::vector Matchers; - std::vector> - Actions; + std::vector> Actions; }; + +// Alias for the previous implementation under the name of `MatchSwitchBuilder`. +template +using MatchSwitchBuilder = ASTMatchSwitchBuilder; + +template class CFGMatchSwitchBuilder { +public: + template + CFGMatchSwitchBuilder CaseOfCFGStmt(MatcherT M, + ActionT A) { + StmtBuilder = StmtBuilder.template CaseOf(M, A); + return std::move(*this); + } + + template + CFGMatchSwitchBuilder CaseOfCFGInit(MatcherT M, + ActionT A) { + InitBuilder = InitBuilder.template CaseOf(M, A); + return std::move(*this); + } + + CFGMatchSwitch 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()->getInitializer(), + Context, S); + case CFGElement::Statement: + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + return StmtMS(*Element.getAs()->getStmt(), Context, S); + default: + // FIXME: handle other kinds of CFGElement + return Result(); + } + }; + } + +private: + ASTMatchSwitchBuilder StmtBuilder; + ASTMatchSwitchBuilder InitBuilder; +}; + } // namespace dataflow } // namespace clang -#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_ +#endif \ No newline at end of file