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