MatchSwitch.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===---- MatchSwitch.h -----------------------------------------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This file defines the `MatchSwitch` abstraction for building a "switch"
  15. // statement, where each case of the switch is defined by an AST matcher. The
  16. // cases are considered in order, like pattern matching in functional
  17. // languages.
  18. //
  19. // Currently, the design is catered towards simplifying the implementation of
  20. // `DataflowAnalysis` transfer functions. Based on experience here, this
  21. // library may be generalized and moved to ASTMatchers.
  22. //
  23. //===----------------------------------------------------------------------===//
  24. //
  25. // FIXME: Rename to ASTMatchSwitch.h and update documentation when all usages of
  26. // `MatchSwitch` are updated to `ASTMatchSwitch<Stmt>`
  27. #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
  28. #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
  29. #include "clang/AST/ASTContext.h"
  30. #include "clang/AST/Stmt.h"
  31. #include "clang/ASTMatchers/ASTMatchFinder.h"
  32. #include "clang/ASTMatchers/ASTMatchers.h"
  33. #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
  34. #include "llvm/ADT/StringRef.h"
  35. #include <functional>
  36. #include <string>
  37. #include <type_traits>
  38. #include <utility>
  39. #include <vector>
  40. namespace clang {
  41. namespace dataflow {
  42. /// A common form of state shared between the cases of a transfer function.
  43. template <typename LatticeT> struct TransferState {
  44. TransferState(LatticeT &Lattice, Environment &Env)
  45. : Lattice(Lattice), Env(Env) {}
  46. /// Current lattice element.
  47. LatticeT &Lattice;
  48. Environment &Env;
  49. };
  50. /// A read-only version of TransferState.
  51. template <typename LatticeT> struct TransferStateForDiagnostics {
  52. TransferStateForDiagnostics(const LatticeT &Lattice, const Environment &Env)
  53. : Lattice(Lattice), Env(Env) {}
  54. /// Current lattice element.
  55. const LatticeT &Lattice;
  56. const Environment &Env;
  57. };
  58. template <typename T>
  59. using MatchSwitchMatcher = ast_matchers::internal::Matcher<T>;
  60. template <typename T, typename State, typename Result = void>
  61. using MatchSwitchAction = std::function<Result(
  62. const T *, const ast_matchers::MatchFinder::MatchResult &, State &)>;
  63. template <typename BaseT, typename State, typename Result = void>
  64. using ASTMatchSwitch =
  65. std::function<Result(const BaseT &, ASTContext &, State &)>;
  66. // FIXME: Remove this alias when all usages of `MatchSwitch` are updated to
  67. // `ASTMatchSwitch<Stmt>`.
  68. template <typename State, typename Result = void>
  69. using MatchSwitch = ASTMatchSwitch<Stmt, State, Result>;
  70. /// Collects cases of a "match switch": a collection of matchers paired with
  71. /// callbacks, which together define a switch that can be applied to a node
  72. /// whose type derives from `BaseT`. This structure can simplify the definition
  73. /// of `transfer` functions that rely on pattern-matching.
  74. ///
  75. /// For example, consider an analysis that handles particular function calls. It
  76. /// can define the `ASTMatchSwitch` once, in the constructor of the analysis,
  77. /// and then reuse it each time that `transfer` is called, with a fresh state
  78. /// value.
  79. ///
  80. /// \code
  81. /// ASTMatchSwitch<Stmt, TransferState<MyLattice> BuildSwitch() {
  82. /// return ASTMatchSwitchBuilder<TransferState<MyLattice>>()
  83. /// .CaseOf(callExpr(callee(functionDecl(hasName("foo")))), TransferFooCall)
  84. /// .CaseOf(callExpr(argumentCountIs(2),
  85. /// callee(functionDecl(hasName("bar")))),
  86. /// TransferBarCall)
  87. /// .Build();
  88. /// }
  89. /// \endcode
  90. template <typename BaseT, typename State, typename Result = void>
  91. class ASTMatchSwitchBuilder {
  92. public:
  93. /// Registers an action that will be triggered by the match of a pattern
  94. /// against the input statement.
  95. ///
  96. /// Requirements:
  97. ///
  98. /// `NodeT` should be derived from `BaseT`.
  99. template <typename NodeT>
  100. ASTMatchSwitchBuilder &&CaseOf(MatchSwitchMatcher<BaseT> M,
  101. MatchSwitchAction<NodeT, State, Result> A) && {
  102. static_assert(std::is_base_of<BaseT, NodeT>::value,
  103. "NodeT must be derived from BaseT.");
  104. Matchers.push_back(std::move(M));
  105. Actions.push_back(
  106. [A = std::move(A)](const BaseT *Node,
  107. const ast_matchers::MatchFinder::MatchResult &R,
  108. State &S) { return A(cast<NodeT>(Node), R, S); });
  109. return std::move(*this);
  110. }
  111. ASTMatchSwitch<BaseT, State, Result> Build() && {
  112. return [Matcher = BuildMatcher(), Actions = std::move(Actions)](
  113. const BaseT &Node, ASTContext &Context, State &S) -> Result {
  114. auto Results = ast_matchers::matchDynamic(Matcher, Node, Context);
  115. if (Results.empty()) {
  116. return Result();
  117. }
  118. // Look through the map for the first binding of the form "TagN..." use
  119. // that to select the action.
  120. for (const auto &Element : Results[0].getMap()) {
  121. llvm::StringRef ID(Element.first);
  122. size_t Index = 0;
  123. if (ID.consume_front("Tag") && !ID.getAsInteger(10, Index) &&
  124. Index < Actions.size()) {
  125. return Actions[Index](
  126. &Node,
  127. ast_matchers::MatchFinder::MatchResult(Results[0], &Context), S);
  128. }
  129. }
  130. return Result();
  131. };
  132. }
  133. private:
  134. ast_matchers::internal::DynTypedMatcher BuildMatcher() {
  135. using ast_matchers::anything;
  136. using ast_matchers::stmt;
  137. using ast_matchers::unless;
  138. using ast_matchers::internal::DynTypedMatcher;
  139. if (Matchers.empty())
  140. return stmt(unless(anything()));
  141. for (int I = 0, N = Matchers.size(); I < N; ++I) {
  142. std::string Tag = ("Tag" + llvm::Twine(I)).str();
  143. // Many matchers are not bindable, so ensure that tryBind will work.
  144. Matchers[I].setAllowBind(true);
  145. auto M = *Matchers[I].tryBind(Tag);
  146. // Each anyOf explicitly controls the traversal kind. The anyOf itself is
  147. // set to `TK_AsIs` to ensure no nodes are skipped, thereby deferring to
  148. // the kind of the branches. Then, each branch is either left as is, if
  149. // the kind is already set, or explicitly set to `TK_AsIs`. We choose this
  150. // setting because it is the default interpretation of matchers.
  151. Matchers[I] =
  152. !M.getTraversalKind() ? M.withTraversalKind(TK_AsIs) : std::move(M);
  153. }
  154. // The matcher type on the cases ensures that `Expr` kind is compatible with
  155. // all of the matchers.
  156. return DynTypedMatcher::constructVariadic(
  157. DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind<BaseT>(),
  158. std::move(Matchers));
  159. }
  160. std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
  161. std::vector<MatchSwitchAction<BaseT, State, Result>> Actions;
  162. };
  163. // FIXME: Remove this alias when all usages of `MatchSwitchBuilder` are updated
  164. // to `ASTMatchSwitchBuilder<Stmt>`.
  165. template <typename State, typename Result = void>
  166. using MatchSwitchBuilder = ASTMatchSwitchBuilder<Stmt, State, Result>;
  167. } // namespace dataflow
  168. } // namespace clang
  169. #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_MATCHSWITCH_H_
  170. #ifdef __GNUC__
  171. #pragma GCC diagnostic pop
  172. #endif