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