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