xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp (revision 4eecd194b073492a309b87c8f60da6614bba9153)
1 //===- unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp ---------------===//
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 a simplistic version of Constant Propagation as an example
10 //  of a forward, monotonic dataflow analysis. The analysis tracks all
11 //  variables in the scope, but lacks escape analysis.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
16 #include "TestingSupport.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/Stmt.h"
21 #include "clang/ASTMatchers/ASTMatchFinder.h"
22 #include "clang/ASTMatchers/ASTMatchers.h"
23 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
24 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
25 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
26 #include "clang/Analysis/FlowSensitive/MapLattice.h"
27 #include "clang/Tooling/Tooling.h"
28 #include "llvm/ADT/None.h"
29 #include "llvm/ADT/Optional.h"
30 #include "llvm/ADT/StringRef.h"
31 #include "llvm/ADT/Twine.h"
32 #include "llvm/Support/Error.h"
33 #include "llvm/Testing/Support/Annotations.h"
34 #include "llvm/Testing/Support/Error.h"
35 #include "gmock/gmock.h"
36 #include "gtest/gtest.h"
37 #include <cstdint>
38 #include <memory>
39 #include <ostream>
40 #include <string>
41 #include <utility>
42 
43 using namespace clang;
44 using namespace dataflow;
45 
46 namespace {
47 using ::testing::Pair;
48 using ::testing::UnorderedElementsAre;
49 
50 class BooleanLattice {
51 public:
52   BooleanLattice() : Value(false) {}
53   explicit BooleanLattice(bool B) : Value(B) {}
54 
55   static BooleanLattice bottom() { return BooleanLattice(false); }
56 
57   static BooleanLattice top() { return BooleanLattice(true); }
58 
59   LatticeJoinEffect join(BooleanLattice Other) {
60     auto Prev = Value;
61     Value = Value || Other.Value;
62     return Prev == Value ? LatticeJoinEffect::Unchanged
63                          : LatticeJoinEffect::Changed;
64   }
65 
66   friend bool operator==(BooleanLattice LHS, BooleanLattice RHS) {
67     return LHS.Value == RHS.Value;
68   }
69 
70   friend std::ostream &operator<<(std::ostream &Os, const BooleanLattice &B) {
71     Os << B.Value;
72     return Os;
73   }
74 
75   bool value() const { return Value; }
76 
77 private:
78   bool Value;
79 };
80 } // namespace
81 
82 MATCHER_P(Holds, m,
83           ((negation ? "doesn't hold" : "holds") +
84            llvm::StringRef(" a lattice element that ") +
85            ::testing::DescribeMatcher<BooleanLattice>(m, negation))
86               .str()) {
87   return ExplainMatchResult(m, arg.Lattice, result_listener);
88 }
89 
90 void TransferSetTrue(const DeclRefExpr *,
91                      const ast_matchers::MatchFinder::MatchResult &,
92                      TransferState<BooleanLattice> &State) {
93   State.Lattice = BooleanLattice(true);
94 }
95 
96 void TransferSetFalse(const Stmt *,
97                       const ast_matchers::MatchFinder::MatchResult &,
98                       TransferState<BooleanLattice> &State) {
99   State.Lattice = BooleanLattice(false);
100 }
101 
102 class TestAnalysis : public DataflowAnalysis<TestAnalysis, BooleanLattice> {
103   MatchSwitch<TransferState<BooleanLattice>> TransferSwitch;
104 
105 public:
106   explicit TestAnalysis(ASTContext &Context)
107       : DataflowAnalysis<TestAnalysis, BooleanLattice>(Context) {
108     using namespace ast_matchers;
109     TransferSwitch =
110         MatchSwitchBuilder<TransferState<BooleanLattice>>()
111             .CaseOf<DeclRefExpr>(declRefExpr(to(varDecl(hasName("X")))),
112                                  TransferSetTrue)
113             .CaseOf<Stmt>(callExpr(callee(functionDecl(hasName("Foo")))),
114                           TransferSetFalse)
115             .Build();
116   }
117 
118   static BooleanLattice initialElement() { return BooleanLattice::bottom(); }
119 
120   void transfer(const Stmt *S, BooleanLattice &L, Environment &Env) {
121     TransferState<BooleanLattice> State(L, Env);
122     TransferSwitch(*S, getASTContext(), State);
123   }
124 };
125 
126 class MatchSwitchTest : public ::testing::Test {
127 protected:
128   template <typename Matcher>
129   void RunDataflow(llvm::StringRef Code, Matcher Expectations) {
130     ASSERT_THAT_ERROR(
131         test::checkDataflow<TestAnalysis>(
132             Code, "fun",
133             [](ASTContext &C, Environment &) { return TestAnalysis(C); },
134             [&Expectations](
135                 llvm::ArrayRef<std::pair<
136                     std::string, DataflowAnalysisState<TestAnalysis::Lattice>>>
137                     Results,
138                 ASTContext &) { EXPECT_THAT(Results, Expectations); },
139             {"-fsyntax-only", "-std=c++17"}),
140         llvm::Succeeded());
141   }
142 };
143 
144 TEST_F(MatchSwitchTest, JustX) {
145   std::string Code = R"(
146     void fun() {
147       int X = 1;
148       (void)X;
149       // [[p]]
150     }
151   )";
152   RunDataflow(Code,
153               UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true)))));
154 }
155 
156 TEST_F(MatchSwitchTest, JustFoo) {
157   std::string Code = R"(
158     void Foo();
159     void fun() {
160       Foo();
161       // [[p]]
162     }
163   )";
164   RunDataflow(Code,
165               UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false)))));
166 }
167 
168 TEST_F(MatchSwitchTest, XThenFoo) {
169   std::string Code = R"(
170     void Foo();
171     void fun() {
172       int X = 1;
173       (void)X;
174       Foo();
175       // [[p]]
176     }
177   )";
178   RunDataflow(Code,
179               UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false)))));
180 }
181 
182 TEST_F(MatchSwitchTest, FooThenX) {
183   std::string Code = R"(
184     void Foo();
185     void fun() {
186       Foo();
187       int X = 1;
188       (void)X;
189       // [[p]]
190     }
191   )";
192   RunDataflow(Code,
193               UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true)))));
194 }
195 
196 TEST_F(MatchSwitchTest, Neither) {
197   std::string Code = R"(
198     void Bar();
199     void fun(bool b) {
200       Bar();
201       // [[p]]
202     }
203   )";
204   RunDataflow(Code,
205               UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false)))));
206 }
207 
208 TEST_F(MatchSwitchTest, ReturnNonVoid) {
209   using namespace ast_matchers;
210 
211   auto Unit =
212       tooling::buildASTFromCode("void f() { int x = 42; }", "input.cc",
213                                 std::make_shared<PCHContainerOperations>());
214   auto &Context = Unit->getASTContext();
215   const auto *S =
216       selectFirst<FunctionDecl>(
217           "f",
218           match(functionDecl(isDefinition(), hasName("f")).bind("f"), Context))
219           ->getBody();
220 
221   MatchSwitch<const int, std::vector<int>> Switch =
222       MatchSwitchBuilder<const int, std::vector<int>>()
223           .CaseOf<Stmt>(stmt(),
224                         [](const Stmt *, const MatchFinder::MatchResult &,
225                            const int &State) -> std::vector<int> {
226                           return {1, State, 3};
227                         })
228           .Build();
229   std::vector<int> Actual = Switch(*S, Context, 7);
230   std::vector<int> Expected{1, 7, 3};
231   EXPECT_EQ(Actual, Expected);
232 }
233