Coverage Report

Created: 2022-05-17 06:19

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