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