123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===---- MatchSwitch.h -----------------------------------------*- 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
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines the `MatchSwitch` abstraction for building a "switch"
- // statement, where each case of the switch is defined by an AST matcher. The
- // cases are considered in order, like pattern matching in functional
- // languages.
- //
- // Currently, the design is catered towards simplifying the implementation of
- // `DataflowAnalysis` transfer functions. Based on experience here, this
- // library may be generalized and moved to ASTMatchers.
- //
- //===----------------------------------------------------------------------===//
- //
- // FIXME: Rename to ASTMatchSwitch.h and update documentation when all usages of
- // `MatchSwitch` are updated to `ASTMatchSwitch<Stmt>`
- #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
- #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/Stmt.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/ASTMatchers/ASTMatchers.h"
- #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
- #include "llvm/ADT/StringRef.h"
- #include <functional>
- #include <string>
- #include <type_traits>
- #include <utility>
- #include <vector>
- namespace clang {
- namespace dataflow {
- /// A common form of state shared between the cases of a transfer function.
- template <typename LatticeT> struct TransferState {
- TransferState(LatticeT &Lattice, Environment &Env)
- : Lattice(Lattice), Env(Env) {}
- /// Current lattice element.
- LatticeT &Lattice;
- Environment &Env;
- };
- /// A read-only version of TransferState.
- template <typename LatticeT> struct TransferStateForDiagnostics {
- TransferStateForDiagnostics(const LatticeT &Lattice, const Environment &Env)
- : Lattice(Lattice), Env(Env) {}
- /// Current lattice element.
- const LatticeT &Lattice;
- const Environment &Env;
- };
- template <typename T>
- using MatchSwitchMatcher = ast_matchers::internal::Matcher<T>;
- template <typename T, typename State, typename Result = void>
- using MatchSwitchAction = 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 &)>;
- // FIXME: Remove this alias when all usages of `MatchSwitch` are updated to
- // `ASTMatchSwitch<Stmt>`.
- template <typename State, typename Result = void>
- using MatchSwitch = ASTMatchSwitch<Stmt, State, Result>;
- /// Collects cases of a "match switch": a collection of matchers paired with
- /// callbacks, which together define a switch that can be applied to a node
- /// whose type derives from `BaseT`. 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 `ASTMatchSwitch` once, in the constructor of the analysis,
- /// and then reuse it each time that `transfer` is called, with a fresh state
- /// value.
- ///
- /// \code
- /// ASTMatchSwitch<Stmt, TransferState<MyLattice> BuildSwitch() {
- /// return ASTMatchSwitchBuilder<TransferState<MyLattice>>()
- /// .CaseOf(callExpr(callee(functionDecl(hasName("foo")))), TransferFooCall)
- /// .CaseOf(callExpr(argumentCountIs(2),
- /// callee(functionDecl(hasName("bar")))),
- /// TransferBarCall)
- /// .Build();
- /// }
- /// \endcode
- 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:
- ///
- /// `NodeT` should be derived from `BaseT`.
- template <typename NodeT>
- ASTMatchSwitchBuilder &&CaseOf(MatchSwitchMatcher<BaseT> M,
- MatchSwitchAction<NodeT, State, Result> A) && {
- static_assert(std::is_base_of<BaseT, NodeT>::value,
- "NodeT must be derived from BaseT.");
- Matchers.push_back(std::move(M));
- Actions.push_back(
- [A = std::move(A)](const BaseT *Node,
- const ast_matchers::MatchFinder::MatchResult &R,
- State &S) { return A(cast<NodeT>(Node), R, S); });
- return std::move(*this);
- }
- ASTMatchSwitch<BaseT, State, Result> Build() && {
- return [Matcher = BuildMatcher(), Actions = std::move(Actions)](
- 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()) {
- llvm::StringRef ID(Element.first);
- size_t Index = 0;
- if (ID.consume_front("Tag") && !ID.getAsInteger(10, Index) &&
- Index < Actions.size()) {
- return Actions[Index](
- &Node,
- ast_matchers::MatchFinder::MatchResult(Results[0], &Context), S);
- }
- }
- return Result();
- };
- }
- private:
- ast_matchers::internal::DynTypedMatcher BuildMatcher() {
- using ast_matchers::anything;
- using ast_matchers::stmt;
- using ast_matchers::unless;
- using ast_matchers::internal::DynTypedMatcher;
- if (Matchers.empty())
- return stmt(unless(anything()));
- for (int I = 0, N = Matchers.size(); I < N; ++I) {
- std::string Tag = ("Tag" + llvm::Twine(I)).str();
- // Many matchers are not bindable, so ensure that tryBind will work.
- Matchers[I].setAllowBind(true);
- auto M = *Matchers[I].tryBind(Tag);
- // Each anyOf explicitly controls the traversal kind. The anyOf itself is
- // set to `TK_AsIs` to ensure no nodes are skipped, thereby deferring to
- // the kind of the branches. Then, each branch is either left as is, if
- // the kind is already set, or explicitly set to `TK_AsIs`. We choose this
- // setting because it is the default interpretation of matchers.
- Matchers[I] =
- !M.getTraversalKind() ? M.withTraversalKind(TK_AsIs) : std::move(M);
- }
- // 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<BaseT>(),
- std::move(Matchers));
- }
- std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
- std::vector<MatchSwitchAction<BaseT, State, Result>> Actions;
- };
- // FIXME: Remove this alias when all usages of `MatchSwitchBuilder` are updated
- // to `ASTMatchSwitchBuilder<Stmt>`.
- template <typename State, typename Result = void>
- using MatchSwitchBuilder = ASTMatchSwitchBuilder<Stmt, State, Result>;
- } // namespace dataflow
- } // namespace clang
- #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|