1 //===- unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.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 "TestingSupport.h" 10 #include "clang/AST/Decl.h" 11 #include "clang/AST/ExprCXX.h" 12 #include "clang/AST/OperationKinds.h" 13 #include "clang/AST/Type.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "clang/ASTMatchers/ASTMatchers.h" 16 #include "clang/Analysis/CFG.h" 17 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" 18 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 19 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 20 #include "clang/Analysis/FlowSensitive/DataflowLattice.h" 21 #include "clang/Analysis/FlowSensitive/DebugSupport.h" 22 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" 23 #include "clang/Analysis/FlowSensitive/Value.h" 24 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" 25 #include "clang/Tooling/Tooling.h" 26 #include "llvm/ADT/STLExtras.h" 27 #include "llvm/ADT/SmallSet.h" 28 #include "llvm/ADT/StringMap.h" 29 #include "llvm/ADT/StringRef.h" 30 #include "llvm/Support/Error.h" 31 #include "llvm/Testing/ADT/StringMapEntry.h" 32 #include "llvm/Testing/Support/Error.h" 33 #include "gmock/gmock.h" 34 #include "gtest/gtest.h" 35 #include <cassert> 36 #include <memory> 37 #include <optional> 38 #include <ostream> 39 #include <string> 40 #include <utility> 41 #include <vector> 42 43 namespace { 44 45 using namespace clang; 46 using namespace dataflow; 47 using namespace test; 48 using namespace ast_matchers; 49 using llvm::IsStringMapEntry; 50 using ::testing::DescribeMatcher; 51 using ::testing::IsEmpty; 52 using ::testing::NotNull; 53 using ::testing::Test; 54 using ::testing::UnorderedElementsAre; 55 56 class DataflowAnalysisTest : public Test { 57 protected: 58 template <typename AnalysisT> 59 llvm::Expected<std::vector< 60 std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>> 61 runAnalysis(llvm::StringRef Code, AnalysisT (*MakeAnalysis)(ASTContext &)) { 62 AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); 63 64 auto *Func = selectFirst<FunctionDecl>( 65 "func", 66 match(functionDecl(ast_matchers::hasName("target")).bind("func"), 67 AST->getASTContext())); 68 assert(Func != nullptr); 69 70 ACFG = 71 std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build(*Func))); 72 73 AnalysisT Analysis = MakeAnalysis(AST->getASTContext()); 74 DACtx = std::make_unique<DataflowAnalysisContext>( 75 std::make_unique<WatchedLiteralsSolver>()); 76 Environment Env(*DACtx, *Func); 77 78 return runDataflowAnalysis(*ACFG, Analysis, Env); 79 } 80 81 /// Returns the `CFGBlock` containing `S` (and asserts that it exists). 82 const CFGBlock *blockForStmt(const Stmt &S) { 83 const CFGBlock *Block = ACFG->blockForStmt(S); 84 assert(Block != nullptr); 85 return Block; 86 } 87 88 template <typename StateT> 89 const StateT & 90 blockStateForStmt(const std::vector<std::optional<StateT>> &BlockStates, 91 const Stmt &S) { 92 const std::optional<StateT> &MaybeState = 93 BlockStates[blockForStmt(S)->getBlockID()]; 94 assert(MaybeState.has_value()); 95 return *MaybeState; 96 } 97 98 /// Returns the first node that matches `Matcher` (and asserts that the match 99 /// was successful, i.e. the returned node is not null). 100 template <typename NodeT, typename MatcherT> 101 const NodeT &matchNode(MatcherT Matcher) { 102 const auto *Node = selectFirst<NodeT>( 103 "node", match(Matcher.bind("node"), AST->getASTContext())); 104 assert(Node != nullptr); 105 return *Node; 106 } 107 108 std::unique_ptr<ASTUnit> AST; 109 std::unique_ptr<AdornedCFG> ACFG; 110 std::unique_ptr<DataflowAnalysisContext> DACtx; 111 }; 112 113 TEST_F(DataflowAnalysisTest, NoopAnalysis) { 114 auto BlockStates = llvm::cantFail( 115 runAnalysis<NoopAnalysis>("void target() {}", [](ASTContext &C) { 116 return NoopAnalysis(C, 117 // Don't use builtin transfer function. 118 DataflowAnalysisOptions{std::nullopt}); 119 })); 120 EXPECT_EQ(BlockStates.size(), 2u); 121 EXPECT_TRUE(BlockStates[0].has_value()); 122 EXPECT_TRUE(BlockStates[1].has_value()); 123 } 124 125 // Basic test that `diagnoseFunction` calls the Diagnoser function for the 126 // number of elements expected. 127 TEST_F(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) { 128 std::string Code = R"(void target() { int x = 0; ++x; })"; 129 std::unique_ptr<ASTUnit> AST = 130 tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); 131 132 auto *Func = 133 cast<FunctionDecl>(findValueDecl(AST->getASTContext(), "target")); 134 auto Diagnoser = [](const CFGElement &Elt, ASTContext &, 135 const TransferStateForDiagnostics<NoopLattice> &) { 136 llvm::SmallVector<std::string> Diagnostics(1); 137 llvm::raw_string_ostream OS(Diagnostics.front()); 138 Elt.dumpToStream(OS); 139 return Diagnostics; 140 }; 141 auto Result = diagnoseFunction<NoopAnalysis, std::string>( 142 *Func, AST->getASTContext(), Diagnoser); 143 // `diagnoseFunction` provides no guarantees about the order in which elements 144 // are visited, so we use `UnorderedElementsAre`. 145 EXPECT_THAT_EXPECTED(Result, llvm::HasValue(UnorderedElementsAre( 146 "0\n", "int x = 0;\n", "x\n", "++x\n", 147 " (Lifetime ends)\n"))); 148 } 149 150 TEST_F(DataflowAnalysisTest, CanAnalyzeStmt) { 151 std::string Code = R"cc( 152 struct S { bool b; }; 153 void foo() { 154 S AnS = S{true}; 155 } 156 )cc"; 157 AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); 158 const auto &DeclStatement = 159 matchNode<DeclStmt>(declStmt(hasSingleDecl(varDecl(hasName("AnS"))))); 160 const auto &Func = matchNode<FunctionDecl>(functionDecl(hasName("foo"))); 161 162 ACFG = std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build( 163 Func, const_cast<DeclStmt &>(DeclStatement), AST->getASTContext()))); 164 165 NoopAnalysis Analysis = NoopAnalysis(AST->getASTContext()); 166 DACtx = std::make_unique<DataflowAnalysisContext>( 167 std::make_unique<WatchedLiteralsSolver>()); 168 Environment Env(*DACtx, const_cast<DeclStmt &>(DeclStatement)); 169 170 llvm::Expected<std::vector<std::optional<DataflowAnalysisState<NoopLattice>>>> 171 Results = runDataflowAnalysis(*ACFG, Analysis, Env); 172 173 ASSERT_THAT_ERROR(Results.takeError(), llvm::Succeeded()); 174 const Environment &ExitBlockEnv = Results->front()->Env; 175 BoolValue *BoolFieldValue = cast<BoolValue>( 176 getFieldValue(ExitBlockEnv.get<RecordStorageLocation>( 177 *cast<VarDecl>((*DeclStatement.decl_begin()))), 178 "b", AST->getASTContext(), ExitBlockEnv)); 179 EXPECT_TRUE(Env.proves(BoolFieldValue->formula())); 180 } 181 182 // Tests for the statement-to-block map. 183 using StmtToBlockTest = DataflowAnalysisTest; 184 185 TEST_F(StmtToBlockTest, ConditionalOperator) { 186 std::string Code = R"( 187 void target(bool b) { 188 int i = b ? 1 : 0; 189 } 190 )"; 191 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>( 192 Code, [](ASTContext &C) { return NoopAnalysis(C); }) 193 .takeError(), 194 llvm::Succeeded()); 195 196 const auto &IDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("i"))))); 197 const auto &ConditionalOp = 198 matchNode<ConditionalOperator>(conditionalOperator()); 199 200 // The conditional operator should be associated with the same block as the 201 // `DeclStmt` for `i`. (Specifically, the conditional operator should not be 202 // associated with the block for which it is the terminator.) 203 EXPECT_EQ(blockForStmt(IDecl), blockForStmt(ConditionalOp)); 204 } 205 206 TEST_F(StmtToBlockTest, LogicalAnd) { 207 std::string Code = R"( 208 void target(bool b1, bool b2) { 209 bool b = b1 && b2; 210 } 211 )"; 212 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>( 213 Code, [](ASTContext &C) { return NoopAnalysis(C); }) 214 .takeError(), 215 llvm::Succeeded()); 216 217 const auto &BDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("b"))))); 218 const auto &AndOp = 219 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&"))); 220 221 // The `&&` operator should be associated with the same block as the 222 // `DeclStmt` for `b`. (Specifically, the `&&` operator should not be 223 // associated with the block for which it is the terminator.) 224 EXPECT_EQ(blockForStmt(BDecl), blockForStmt(AndOp)); 225 } 226 227 TEST_F(StmtToBlockTest, IfStatementWithLogicalAnd) { 228 std::string Code = R"( 229 void target(bool b1, bool b2) { 230 if (b1 && b2) 231 ; 232 } 233 )"; 234 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>( 235 Code, [](ASTContext &C) { return NoopAnalysis(C); }) 236 .takeError(), 237 llvm::Succeeded()); 238 239 const auto &If = matchNode<IfStmt>(ifStmt()); 240 const auto &B2 = 241 matchNode<DeclRefExpr>(declRefExpr(to(varDecl(hasName("b2"))))); 242 const auto &AndOp = 243 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&"))); 244 245 // The if statement is the terminator for the block that contains both `b2` 246 // and the `&&` operator (which appears only as a terminator condition, not 247 // as a regular `CFGElement`). 248 const CFGBlock *IfBlock = blockForStmt(If); 249 const CFGBlock *B2Block = blockForStmt(B2); 250 const CFGBlock *AndOpBlock = blockForStmt(AndOp); 251 EXPECT_EQ(IfBlock, B2Block); 252 EXPECT_EQ(IfBlock, AndOpBlock); 253 } 254 255 // Tests that check we discard state for expressions correctly. 256 using DiscardExprStateTest = DataflowAnalysisTest; 257 258 TEST_F(DiscardExprStateTest, WhileStatement) { 259 std::string Code = R"( 260 void foo(int *p); 261 void target(int *p) { 262 while (p != nullptr) 263 foo(p); 264 } 265 )"; 266 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>( 267 Code, [](ASTContext &C) { return NoopAnalysis(C); })); 268 269 const auto &NotEqOp = 270 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("!="))); 271 const auto &CallFoo = 272 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("foo"))))); 273 274 // In the block that evaluates the expression `p != nullptr`, this expression 275 // is associated with a value. 276 const auto &NotEqOpState = blockStateForStmt(BlockStates, NotEqOp); 277 EXPECT_NE(NotEqOpState.Env.getValue(NotEqOp), nullptr); 278 279 // In the block that calls `foo(p)`, the value for `p != nullptr` is discarded 280 // because it is not consumed outside the block it is in. 281 const auto &CallFooState = blockStateForStmt(BlockStates, CallFoo); 282 EXPECT_EQ(CallFooState.Env.getValue(NotEqOp), nullptr); 283 } 284 285 TEST_F(DiscardExprStateTest, BooleanOperator) { 286 std::string Code = R"( 287 void f(); 288 void target(bool b1, bool b2) { 289 if (b1 && b2) 290 f(); 291 } 292 )"; 293 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>( 294 Code, [](ASTContext &C) { return NoopAnalysis(C); })); 295 296 const auto &AndOp = 297 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&"))); 298 const auto &CallF = 299 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("f"))))); 300 301 // In the block that evaluates the LHS of the `&&` operator, the LHS is 302 // associated with a value, while the right-hand side is not (unsurprisingly, 303 // as it hasn't been evaluated yet). 304 const auto &LHSState = blockStateForStmt(BlockStates, *AndOp.getLHS()); 305 auto *LHSValue = cast<BoolValue>(LHSState.Env.getValue(*AndOp.getLHS())); 306 EXPECT_NE(LHSValue, nullptr); 307 EXPECT_EQ(LHSState.Env.getValue(*AndOp.getRHS()), nullptr); 308 309 // In the block that evaluates the RHS, both the LHS and RHS are associated 310 // with values, as they are both subexpressions of the `&&` operator, which 311 // is evaluated in a later block. 312 const auto &RHSState = blockStateForStmt(BlockStates, *AndOp.getRHS()); 313 EXPECT_EQ(RHSState.Env.getValue(*AndOp.getLHS()), LHSValue); 314 auto *RHSValue = RHSState.Env.get<BoolValue>(*AndOp.getRHS()); 315 EXPECT_NE(RHSValue, nullptr); 316 317 // In the block that evaluates `b1 && b2`, the `&&` as well as its operands 318 // are associated with values. 319 const auto &AndOpState = blockStateForStmt(BlockStates, AndOp); 320 EXPECT_EQ(AndOpState.Env.getValue(*AndOp.getLHS()), LHSValue); 321 EXPECT_EQ(AndOpState.Env.getValue(*AndOp.getRHS()), RHSValue); 322 EXPECT_EQ(AndOpState.Env.getValue(AndOp), 323 &AndOpState.Env.makeAnd(*LHSValue, *RHSValue)); 324 325 // In the block that calls `f()`, none of `b1`, `b2`, or `b1 && b2` should be 326 // associated with values. 327 const auto &CallFState = blockStateForStmt(BlockStates, CallF); 328 EXPECT_EQ(CallFState.Env.getValue(*AndOp.getLHS()), nullptr); 329 EXPECT_EQ(CallFState.Env.getValue(*AndOp.getRHS()), nullptr); 330 EXPECT_EQ(CallFState.Env.getValue(AndOp), nullptr); 331 } 332 333 TEST_F(DiscardExprStateTest, ConditionalOperator) { 334 std::string Code = R"( 335 void f(int*, int); 336 void g(); 337 bool cond(); 338 339 void target() { 340 int i = 0; 341 if (cond()) 342 f(&i, cond() ? 1 : 0); 343 g(); 344 } 345 )"; 346 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>( 347 Code, [](ASTContext &C) { return NoopAnalysis(C); })); 348 349 const auto &AddrOfI = 350 matchNode<UnaryOperator>(unaryOperator(hasOperatorName("&"))); 351 const auto &CallF = 352 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("f"))))); 353 const auto &CallG = 354 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("g"))))); 355 356 // In the block that evaluates `&i`, it should obviously have a value. 357 const auto &AddrOfIState = blockStateForStmt(BlockStates, AddrOfI); 358 auto *AddrOfIVal = AddrOfIState.Env.get<PointerValue>(AddrOfI); 359 EXPECT_NE(AddrOfIVal, nullptr); 360 361 // Because of the conditional operator, the `f(...)` call is evaluated in a 362 // different block than `&i`, but `&i` still needs to have a value here 363 // because it's a subexpression of the call. 364 const auto &CallFState = blockStateForStmt(BlockStates, CallF); 365 EXPECT_NE(&CallFState, &AddrOfIState); 366 EXPECT_EQ(CallFState.Env.get<PointerValue>(AddrOfI), AddrOfIVal); 367 368 // In the block that calls `g()`, `&i` should no longer be associated with a 369 // value. 370 const auto &CallGState = blockStateForStmt(BlockStates, CallG); 371 EXPECT_EQ(CallGState.Env.get<PointerValue>(AddrOfI), nullptr); 372 } 373 374 TEST_F(DiscardExprStateTest, CallWithParenExprTreatedCorrectly) { 375 // This is a regression test. 376 // In the CFG for `target()` below, the expression that evaluates the function 377 // pointer for `expect` and the actual call are separated into different 378 // baseic blocks (because of the control flow introduced by the `||` 379 // operator). 380 // The value for the `expect` function pointer was erroneously discarded 381 // from the environment between these two blocks because the code that 382 // determines whether the expression values for a block need to be preserved 383 // did not ignore the `ParenExpr` around `(i == 1)` (which is not represented 384 // in the CFG). 385 std::string Code = R"( 386 bool expect(bool, bool); 387 void target(int i) { 388 expect(false || (i == 1), false); 389 } 390 )"; 391 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>( 392 Code, [](ASTContext &C) { return NoopAnalysis(C); })); 393 394 const auto &FnToPtrDecay = matchNode<ImplicitCastExpr>( 395 implicitCastExpr(hasCastKind(CK_FunctionToPointerDecay))); 396 const auto &CallExpect = 397 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("expect"))))); 398 399 // In the block that evaluates the implicit cast of `expect` to a pointer, 400 // this expression is associated with a value. 401 const auto &FnToPtrDecayState = blockStateForStmt(BlockStates, FnToPtrDecay); 402 EXPECT_NE(FnToPtrDecayState.Env.getValue(FnToPtrDecay), nullptr); 403 404 // In the block that calls `expect()`, the implicit cast of `expect` to a 405 // pointer is still associated with a value. 406 const auto &CallExpectState = blockStateForStmt(BlockStates, CallExpect); 407 EXPECT_NE(CallExpectState.Env.getValue(FnToPtrDecay), nullptr); 408 } 409 410 struct NonConvergingLattice { 411 int State; 412 413 bool operator==(const NonConvergingLattice &Other) const { 414 return State == Other.State; 415 } 416 417 LatticeJoinEffect join(const NonConvergingLattice &Other) { 418 if (Other.State == 0) 419 return LatticeJoinEffect::Unchanged; 420 State += Other.State; 421 return LatticeJoinEffect::Changed; 422 } 423 }; 424 425 class NonConvergingAnalysis 426 : public DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice> { 427 public: 428 explicit NonConvergingAnalysis(ASTContext &Context) 429 : DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice>( 430 Context, 431 // Don't apply builtin transfer function. 432 DataflowAnalysisOptions{std::nullopt}) {} 433 434 static NonConvergingLattice initialElement() { return {0}; } 435 436 void transfer(const CFGElement &, NonConvergingLattice &E, Environment &) { 437 ++E.State; 438 } 439 }; 440 441 TEST_F(DataflowAnalysisTest, NonConvergingAnalysis) { 442 std::string Code = R"( 443 void target() { 444 while(true) {} 445 } 446 )"; 447 auto Res = runAnalysis<NonConvergingAnalysis>( 448 Code, [](ASTContext &C) { return NonConvergingAnalysis(C); }); 449 EXPECT_EQ(llvm::toString(Res.takeError()), 450 "maximum number of blocks processed"); 451 } 452 453 // Regression test for joins of bool-typed lvalue expressions. The first loop 454 // results in two passes through the code that follows. Each pass results in a 455 // different `StorageLocation` for the pointee of `v`. Then, the second loop 456 // causes a join at the loop head where the two environments map expresssion 457 // `*v` to different `StorageLocation`s. 458 // 459 // An earlier version crashed for this condition (for boolean-typed lvalues), so 460 // this test only verifies that the analysis runs successfully, without 461 // examining any details of the results. 462 TEST_F(DataflowAnalysisTest, JoinBoolLValues) { 463 std::string Code = R"( 464 void target() { 465 for (int x = 1; x; x = 0) 466 (void)x; 467 bool *v; 468 if (*v) 469 for (int x = 1; x; x = 0) 470 (void)x; 471 } 472 )"; 473 ASSERT_THAT_ERROR( 474 runAnalysis<NoopAnalysis>(Code, 475 [](ASTContext &C) { 476 auto EnableBuiltIns = DataflowAnalysisOptions{ 477 DataflowAnalysisContext::Options{}}; 478 return NoopAnalysis(C, EnableBuiltIns); 479 }) 480 .takeError(), 481 llvm::Succeeded()); 482 } 483 484 struct FunctionCallLattice { 485 using FunctionSet = llvm::SmallSet<std::string, 8>; 486 FunctionSet CalledFunctions; 487 488 bool operator==(const FunctionCallLattice &Other) const { 489 return CalledFunctions == Other.CalledFunctions; 490 } 491 492 LatticeJoinEffect join(const FunctionCallLattice &Other) { 493 if (Other.CalledFunctions.empty()) 494 return LatticeJoinEffect::Unchanged; 495 const size_t size_before = CalledFunctions.size(); 496 CalledFunctions.insert(Other.CalledFunctions.begin(), 497 Other.CalledFunctions.end()); 498 return CalledFunctions.size() == size_before ? LatticeJoinEffect::Unchanged 499 : LatticeJoinEffect::Changed; 500 } 501 }; 502 503 std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) { 504 std::string S; 505 llvm::raw_string_ostream ROS(S); 506 llvm::interleaveComma(L.CalledFunctions, ROS); 507 return OS << "{" << S << "}"; 508 } 509 510 class FunctionCallAnalysis 511 : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> { 512 public: 513 explicit FunctionCallAnalysis(ASTContext &Context) 514 : DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice>(Context) {} 515 516 static FunctionCallLattice initialElement() { return {}; } 517 518 void transfer(const CFGElement &Elt, FunctionCallLattice &E, Environment &) { 519 auto CS = Elt.getAs<CFGStmt>(); 520 if (!CS) 521 return; 522 const auto *S = CS->getStmt(); 523 if (auto *C = dyn_cast<CallExpr>(S)) { 524 if (auto *F = dyn_cast<FunctionDecl>(C->getCalleeDecl())) { 525 E.CalledFunctions.insert(F->getNameInfo().getAsString()); 526 } 527 } 528 } 529 }; 530 531 class NoreturnDestructorTest : public Test { 532 protected: 533 template <typename Matcher> 534 void runDataflow(llvm::StringRef Code, Matcher Expectations) { 535 tooling::FileContentMappings FilesContents; 536 FilesContents.push_back(std::make_pair<std::string, std::string>( 537 "noreturn_destructor_test_defs.h", R"( 538 int foo(); 539 540 class Fatal { 541 public: 542 ~Fatal() __attribute__((noreturn)); 543 int bar(); 544 int baz(); 545 }; 546 547 class NonFatal { 548 public: 549 ~NonFatal(); 550 int bar(); 551 }; 552 )")); 553 554 ASSERT_THAT_ERROR( 555 test::checkDataflow<FunctionCallAnalysis>( 556 AnalysisInputs<FunctionCallAnalysis>( 557 Code, ast_matchers::hasName("target"), 558 [](ASTContext &C, Environment &) { 559 return FunctionCallAnalysis(C); 560 }) 561 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}) 562 .withASTBuildVirtualMappedFiles(std::move(FilesContents)), 563 /*VerifyResults=*/ 564 [&Expectations]( 565 const llvm::StringMap< 566 DataflowAnalysisState<FunctionCallLattice>> &Results, 567 const AnalysisOutputs &) { 568 EXPECT_THAT(Results, Expectations); 569 }), 570 llvm::Succeeded()); 571 } 572 }; 573 574 MATCHER_P(HoldsFunctionCallLattice, m, 575 ((negation ? "doesn't hold" : "holds") + 576 llvm::StringRef(" a lattice element that ") + 577 DescribeMatcher<FunctionCallLattice>(m)) 578 .str()) { 579 return ExplainMatchResult(m, arg.Lattice, result_listener); 580 } 581 582 MATCHER_P(HasCalledFunctions, m, 583 ((negation ? "doesn't hold" : "holds") + 584 llvm::StringRef(" a set of called functions that ") + 585 DescribeMatcher<FunctionCallLattice::FunctionSet>(m)) 586 .str()) { 587 return ExplainMatchResult(m, arg.CalledFunctions, result_listener); 588 } 589 590 TEST_F(NoreturnDestructorTest, ConditionalOperatorBothBranchesReturn) { 591 std::string Code = R"( 592 #include "noreturn_destructor_test_defs.h" 593 594 void target(bool b) { 595 int value = b ? foo() : NonFatal().bar(); 596 (void)0; 597 // [[p]] 598 } 599 )"; 600 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 601 "p", HoldsFunctionCallLattice(HasCalledFunctions( 602 UnorderedElementsAre("foo", "bar")))))); 603 } 604 605 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) { 606 std::string Code = R"( 607 #include "noreturn_destructor_test_defs.h" 608 609 void target(bool b) { 610 int value = b ? foo() : Fatal().bar(); 611 (void)0; 612 // [[p]] 613 } 614 )"; 615 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 616 "p", HoldsFunctionCallLattice(HasCalledFunctions( 617 UnorderedElementsAre("foo")))))); 618 } 619 620 TEST_F(NoreturnDestructorTest, 621 ConditionalOperatorConstantCondition_LeftBranchReturns) { 622 std::string Code = R"( 623 #include "noreturn_destructor_test_defs.h" 624 625 void target() { 626 int value = true ? foo() : Fatal().bar(); 627 (void)0; 628 // [[p]] 629 } 630 )"; 631 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 632 "p", HoldsFunctionCallLattice(HasCalledFunctions( 633 UnorderedElementsAre("foo")))))); 634 } 635 636 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) { 637 std::string Code = R"( 638 #include "noreturn_destructor_test_defs.h" 639 640 void target(bool b) { 641 int value = b ? Fatal().bar() : foo(); 642 (void)0; 643 // [[p]] 644 } 645 )"; 646 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 647 "p", HoldsFunctionCallLattice(HasCalledFunctions( 648 UnorderedElementsAre("foo")))))); 649 } 650 651 TEST_F(NoreturnDestructorTest, 652 ConditionalOperatorConstantCondition_RightBranchReturns) { 653 std::string Code = R"( 654 #include "noreturn_destructor_test_defs.h" 655 656 void target() { 657 int value = false ? Fatal().bar() : foo(); 658 (void)0; 659 // [[p]] 660 } 661 )"; 662 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 663 "p", HoldsFunctionCallLattice(HasCalledFunctions( 664 UnorderedElementsAre("foo")))))); 665 } 666 667 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchesDoNotReturn) { 668 std::string Code = R"( 669 #include "noreturn_destructor_test_defs.h" 670 671 void target(bool b1, bool b2) { 672 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz()); 673 (void)0; 674 // [[p]] 675 } 676 )"; 677 runDataflow(Code, IsEmpty()); 678 // FIXME: Called functions at point `p` should contain "foo". 679 } 680 681 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) { 682 std::string Code = R"( 683 #include "noreturn_destructor_test_defs.h" 684 685 void target(bool b1, bool b2) { 686 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo()); 687 (void)0; 688 // [[p]] 689 } 690 )"; 691 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry( 692 "p", HoldsFunctionCallLattice(HasCalledFunctions( 693 UnorderedElementsAre("baz", "foo")))))); 694 // FIXME: Called functions at point `p` should contain only "foo". 695 } 696 697 // Models an analysis that uses flow conditions. 698 class SpecialBoolAnalysis final 699 : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> { 700 public: 701 explicit SpecialBoolAnalysis(ASTContext &Context, Environment &Env) 702 : DataflowAnalysis<SpecialBoolAnalysis, NoopLattice>(Context) { 703 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 704 [](QualType Ty) -> llvm::StringMap<QualType> { 705 RecordDecl *RD = Ty->getAsRecordDecl(); 706 if (RD == nullptr || RD->getIdentifier() == nullptr || 707 RD->getName() != "SpecialBool") 708 return {}; 709 return {{"is_set", RD->getASTContext().BoolTy}}; 710 }); 711 } 712 713 static NoopLattice initialElement() { return {}; } 714 715 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { 716 auto CS = Elt.getAs<CFGStmt>(); 717 if (!CS) 718 return; 719 const auto *S = CS->getStmt(); 720 auto SpecialBoolRecordDecl = recordDecl(hasName("SpecialBool")); 721 auto HasSpecialBoolType = hasType(SpecialBoolRecordDecl); 722 723 if (const auto *E = selectFirst<CXXConstructExpr>( 724 "call", match(cxxConstructExpr(HasSpecialBoolType).bind("call"), *S, 725 getASTContext()))) { 726 Env.setValue(Env.getResultObjectLocation(*E).getSyntheticField("is_set"), 727 Env.getBoolLiteralValue(false)); 728 } else if (const auto *E = selectFirst<CXXMemberCallExpr>( 729 "call", match(cxxMemberCallExpr(callee(cxxMethodDecl(ofClass( 730 SpecialBoolRecordDecl)))) 731 .bind("call"), 732 *S, getASTContext()))) { 733 if (RecordStorageLocation *ObjectLoc = getImplicitObjectLocation(*E, Env)) 734 Env.setValue(ObjectLoc->getSyntheticField("is_set"), 735 Env.getBoolLiteralValue(true)); 736 } 737 } 738 }; 739 740 class JoinFlowConditionsTest : public Test { 741 protected: 742 template <typename Matcher> 743 void runDataflow(llvm::StringRef Code, Matcher Match) { 744 ASSERT_THAT_ERROR( 745 test::checkDataflow<SpecialBoolAnalysis>( 746 AnalysisInputs<SpecialBoolAnalysis>( 747 Code, ast_matchers::hasName("target"), 748 [](ASTContext &Context, Environment &Env) { 749 return SpecialBoolAnalysis(Context, Env); 750 }) 751 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}), 752 /*VerifyResults=*/[&Match](const llvm::StringMap< 753 DataflowAnalysisState<NoopLattice>> 754 &Results, 755 const AnalysisOutputs 756 &AO) { Match(Results, AO.ASTCtx); }), 757 llvm::Succeeded()); 758 } 759 }; 760 761 TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) { 762 std::string Code = R"( 763 struct SpecialBool { 764 SpecialBool() = default; 765 void set(); 766 }; 767 768 void target(bool Cond) { 769 SpecialBool Foo; 770 /*[[p1]]*/ 771 if (Cond) { 772 Foo.set(); 773 /*[[p2]]*/ 774 } else { 775 Foo.set(); 776 /*[[p3]]*/ 777 } 778 (void)0; 779 /*[[p4]]*/ 780 } 781 )"; 782 runDataflow( 783 Code, 784 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 785 ASTContext &ASTCtx) { 786 ASSERT_THAT(Results.keys(), 787 UnorderedElementsAre("p1", "p2", "p3", "p4")); 788 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 789 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 790 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3"); 791 const Environment &Env4 = getEnvironmentAtAnnotation(Results, "p4"); 792 793 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 794 ASSERT_THAT(FooDecl, NotNull()); 795 796 auto GetFoo = [FooDecl](const Environment &Env) -> const Formula & { 797 auto *Loc = 798 cast<RecordStorageLocation>(Env.getStorageLocation(*FooDecl)); 799 return cast<BoolValue>(Env.getValue(Loc->getSyntheticField("is_set"))) 800 ->formula(); 801 }; 802 803 EXPECT_FALSE(Env1.proves(GetFoo(Env1))); 804 EXPECT_TRUE(Env2.proves(GetFoo(Env2))); 805 EXPECT_TRUE(Env3.proves(GetFoo(Env3))); 806 EXPECT_TRUE(Env4.proves(GetFoo(Env4))); 807 }); 808 } 809 810 class NullPointerAnalysis final 811 : public DataflowAnalysis<NullPointerAnalysis, NoopLattice> { 812 public: 813 explicit NullPointerAnalysis(ASTContext &Context) 814 : DataflowAnalysis<NullPointerAnalysis, NoopLattice>(Context) {} 815 816 static NoopLattice initialElement() { return {}; } 817 818 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { 819 auto CS = Elt.getAs<CFGStmt>(); 820 if (!CS) 821 return; 822 const Stmt *S = CS->getStmt(); 823 const Expr *E = dyn_cast<Expr>(S); 824 if (!E) 825 return; 826 827 if (!E->getType()->isPointerType()) 828 return; 829 830 // Make sure we have a `PointerValue` for `E`. 831 auto *PtrVal = cast_or_null<PointerValue>(Env.getValue(*E)); 832 if (PtrVal == nullptr) { 833 PtrVal = cast<PointerValue>(Env.createValue(E->getType())); 834 Env.setValue(*E, *PtrVal); 835 } 836 837 if (auto *Cast = dyn_cast<ImplicitCastExpr>(E); 838 Cast && Cast->getCastKind() == CK_NullToPointer) 839 PtrVal->setProperty("is_null", Env.getBoolLiteralValue(true)); 840 else if (auto *Op = dyn_cast<UnaryOperator>(E); 841 Op && Op->getOpcode() == UO_AddrOf) 842 PtrVal->setProperty("is_null", Env.getBoolLiteralValue(false)); 843 } 844 845 ComparisonResult compare(QualType Type, const Value &Val1, 846 const Environment &Env1, const Value &Val2, 847 const Environment &Env2) override { 848 // Nothing to say about a value that is not a pointer. 849 if (!Type->isPointerType()) 850 return ComparisonResult::Unknown; 851 852 auto *Prop1 = Val1.getProperty("is_null"); 853 auto *Prop2 = Val2.getProperty("is_null"); 854 assert(Prop1 != nullptr && Prop2 != nullptr); 855 return areEquivalentValues(*Prop1, *Prop2) ? ComparisonResult::Same 856 : ComparisonResult::Different; 857 } 858 859 void join(QualType Type, const Value &Val1, const Environment &Env1, 860 const Value &Val2, const Environment &Env2, Value &JoinedVal, 861 Environment &JoinedEnv) override { 862 // Nothing to say about a value that is not a pointer... 863 if (!Type->isPointerType()) 864 return; 865 866 // ... or, a pointer without the `is_null` property. 867 auto *IsNull1 = cast_or_null<BoolValue>(Val1.getProperty("is_null")); 868 auto *IsNull2 = cast_or_null<BoolValue>(Val2.getProperty("is_null")); 869 if (IsNull1 == nullptr || IsNull2 == nullptr) 870 return; 871 872 if (IsNull1 == IsNull2) 873 JoinedVal.setProperty("is_null", *IsNull1); 874 else 875 JoinedVal.setProperty("is_null", JoinedEnv.makeTopBoolValue()); 876 } 877 878 std::optional<WidenResult> widen(QualType Type, Value &Prev, 879 const Environment &PrevEnv, Value &Current, 880 Environment &CurrentEnv) override { 881 switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 882 case ComparisonResult::Same: 883 return WidenResult{&Current, LatticeJoinEffect::Unchanged}; 884 case ComparisonResult::Different: { 885 auto &CurPtr = cast<PointerValue>(Current); 886 auto &WidenedPtr = 887 CurrentEnv.create<PointerValue>(CurPtr.getPointeeLoc()); 888 WidenedPtr.setProperty("is_null", CurrentEnv.makeTopBoolValue()); 889 return WidenResult{&WidenedPtr, LatticeJoinEffect::Changed}; 890 } 891 case ComparisonResult::Unknown: 892 return std::nullopt; 893 } 894 llvm_unreachable("all cases in switch covered"); 895 } 896 }; 897 898 class WideningTest : public Test { 899 protected: 900 template <typename Matcher> 901 void runDataflow(llvm::StringRef Code, Matcher Match) { 902 ASSERT_THAT_ERROR( 903 checkDataflow<NullPointerAnalysis>( 904 AnalysisInputs<NullPointerAnalysis>( 905 Code, ast_matchers::hasName("target"), 906 [](ASTContext &Context, Environment &Env) { 907 return NullPointerAnalysis(Context); 908 }) 909 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}), 910 /*VerifyResults=*/[&Match](const llvm::StringMap< 911 DataflowAnalysisState<NoopLattice>> 912 &Results, 913 const AnalysisOutputs 914 &AO) { Match(Results, AO.ASTCtx); }), 915 llvm::Succeeded()); 916 } 917 }; 918 919 TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) { 920 std::string Code = R"( 921 void target(bool Cond) { 922 int *Foo = nullptr; 923 int i = 0; 924 /*[[p1]]*/ 925 if (Cond) { 926 Foo = &i; 927 /*[[p2]]*/ 928 } 929 (void)0; 930 /*[[p3]]*/ 931 } 932 )"; 933 runDataflow( 934 Code, 935 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 936 ASTContext &ASTCtx) { 937 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 938 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 939 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3"); 940 941 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 942 ASSERT_THAT(FooDecl, NotNull()); 943 944 auto GetFooValue = [FooDecl](const Environment &Env) { 945 return Env.getValue(*FooDecl); 946 }; 947 948 EXPECT_EQ(GetFooValue(Env1)->getProperty("is_null"), 949 &Env1.getBoolLiteralValue(true)); 950 EXPECT_EQ(GetFooValue(Env2)->getProperty("is_null"), 951 &Env2.getBoolLiteralValue(false)); 952 EXPECT_TRUE( 953 isa<TopBoolValue>(GetFooValue(Env3)->getProperty("is_null"))); 954 }); 955 } 956 957 TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) { 958 std::string Code = R"( 959 void target(bool Cond) { 960 int *Foo = nullptr; 961 int i1 = 0; 962 int i2 = 0; 963 /*[[p1]]*/ 964 if (Cond) { 965 Foo = &i1; 966 /*[[p2]]*/ 967 } else { 968 Foo = &i2; 969 /*[[p3]]*/ 970 } 971 (void)0; 972 /*[[p4]]*/ 973 } 974 )"; 975 runDataflow( 976 Code, 977 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 978 ASTContext &ASTCtx) { 979 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 980 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 981 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3"); 982 const Environment &Env4 = getEnvironmentAtAnnotation(Results, "p4"); 983 984 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 985 ASSERT_THAT(FooDecl, NotNull()); 986 987 auto GetFooValue = [FooDecl](const Environment &Env) { 988 return Env.getValue(*FooDecl); 989 }; 990 991 EXPECT_EQ(GetFooValue(Env1)->getProperty("is_null"), 992 &Env1.getBoolLiteralValue(true)); 993 EXPECT_EQ(GetFooValue(Env2)->getProperty("is_null"), 994 &Env2.getBoolLiteralValue(false)); 995 EXPECT_EQ(GetFooValue(Env3)->getProperty("is_null"), 996 &Env3.getBoolLiteralValue(false)); 997 EXPECT_EQ(GetFooValue(Env4)->getProperty("is_null"), 998 &Env4.getBoolLiteralValue(false)); 999 }); 1000 } 1001 1002 TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) { 1003 std::string Code = R"( 1004 void target(int Foo, bool Cond) { 1005 int *Bar = &Foo; 1006 while (Cond) { 1007 Bar = &Foo; 1008 } 1009 (void)0; 1010 // [[p]] 1011 } 1012 )"; 1013 runDataflow( 1014 Code, 1015 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1016 ASTContext &ASTCtx) { 1017 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1018 const auto &FooLoc = 1019 getLocForDecl<ScalarStorageLocation>(ASTCtx, Env, "Foo"); 1020 const auto &BarVal = getValueForDecl<PointerValue>(ASTCtx, Env, "Bar"); 1021 EXPECT_EQ(&BarVal.getPointeeLoc(), &FooLoc); 1022 }); 1023 } 1024 1025 TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) { 1026 std::string Code = R"( 1027 void target(bool Cond) { 1028 int *Foo; 1029 int i1 = 0; 1030 int i2 = 0; 1031 Foo = &i1; 1032 while (Cond) { 1033 Foo = &i2; 1034 } 1035 (void)0; 1036 /*[[p]]*/ 1037 } 1038 )"; 1039 runDataflow( 1040 Code, 1041 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1042 ASTContext &ASTCtx) { 1043 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1044 const auto &FooVal = getValueForDecl<Value>(ASTCtx, Env, "Foo"); 1045 EXPECT_EQ(FooVal.getProperty("is_null"), 1046 &Env.getBoolLiteralValue(false)); 1047 }); 1048 } 1049 1050 TEST_F(WideningTest, DistinctValuesWithDifferentPropertiesWidenedToTop) { 1051 std::string Code = R"( 1052 void target(bool Cond) { 1053 int *Foo; 1054 int i = 0; 1055 Foo = nullptr; 1056 while (Cond) { 1057 Foo = &i; 1058 } 1059 (void)0; 1060 /*[[p]]*/ 1061 } 1062 )"; 1063 runDataflow( 1064 Code, 1065 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1066 ASTContext &ASTCtx) { 1067 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1068 const auto &FooVal = getValueForDecl<Value>(ASTCtx, Env, "Foo"); 1069 ASSERT_THAT(FooVal.getProperty("is_null"), NotNull()); 1070 EXPECT_TRUE(areEquivalentValues(*FooVal.getProperty("is_null"), 1071 Env.makeTopBoolValue())); 1072 }); 1073 } 1074 1075 class FlowConditionTest : public Test { 1076 protected: 1077 template <typename Matcher> 1078 void runDataflow(llvm::StringRef Code, Matcher Match) { 1079 ASSERT_THAT_ERROR( 1080 checkDataflow<NoopAnalysis>( 1081 AnalysisInputs<NoopAnalysis>( 1082 Code, ast_matchers::hasName("target"), 1083 [](ASTContext &Context, Environment &Env) { 1084 return NoopAnalysis(Context); 1085 }) 1086 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}), 1087 /*VerifyResults=*/[&Match](const llvm::StringMap< 1088 DataflowAnalysisState<NoopLattice>> 1089 &Results, 1090 const AnalysisOutputs 1091 &AO) { Match(Results, AO.ASTCtx); }), 1092 llvm::Succeeded()); 1093 } 1094 }; 1095 1096 TEST_F(FlowConditionTest, IfStmtSingleVar) { 1097 std::string Code = R"( 1098 void target(bool Foo) { 1099 if (Foo) { 1100 (void)0; 1101 /*[[p1]]*/ 1102 } else { 1103 (void)1; 1104 /*[[p2]]*/ 1105 } 1106 } 1107 )"; 1108 runDataflow( 1109 Code, 1110 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1111 ASTContext &ASTCtx) { 1112 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1113 ASSERT_THAT(FooDecl, NotNull()); 1114 1115 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1116 1117 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1118 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1119 EXPECT_TRUE(Env1.proves(FooVal1)); 1120 1121 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1122 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1123 EXPECT_FALSE(Env2.proves(FooVal2)); 1124 }); 1125 } 1126 1127 TEST_F(FlowConditionTest, IfStmtSingleNegatedVar) { 1128 std::string Code = R"( 1129 void target(bool Foo) { 1130 if (!Foo) { 1131 (void)0; 1132 /*[[p1]]*/ 1133 } else { 1134 (void)1; 1135 /*[[p2]]*/ 1136 } 1137 } 1138 )"; 1139 runDataflow( 1140 Code, 1141 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1142 ASTContext &ASTCtx) { 1143 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1144 ASSERT_THAT(FooDecl, NotNull()); 1145 1146 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1147 1148 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1149 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1150 EXPECT_FALSE(Env1.proves(FooVal1)); 1151 1152 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1153 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1154 EXPECT_TRUE(Env2.proves(FooVal2)); 1155 }); 1156 } 1157 1158 TEST_F(FlowConditionTest, WhileStmt) { 1159 std::string Code = R"( 1160 void target(bool Foo) { 1161 while (Foo) { 1162 (void)0; 1163 /*[[p]]*/ 1164 } 1165 } 1166 )"; 1167 runDataflow( 1168 Code, 1169 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1170 ASTContext &ASTCtx) { 1171 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1172 ASSERT_THAT(FooDecl, NotNull()); 1173 1174 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1175 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1176 1177 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula(); 1178 EXPECT_TRUE(Env.proves(FooVal)); 1179 }); 1180 } 1181 1182 TEST_F(FlowConditionTest, WhileStmtWithAssignmentInCondition) { 1183 std::string Code = R"( 1184 void target(bool Foo) { 1185 // This test checks whether the analysis preserves the connection between 1186 // the value of `Foo` and the assignment expression, despite widening. 1187 // The equality operator generates a fresh boolean variable on each 1188 // interpretation, which forces use of widening. 1189 while ((Foo = (3 == 4))) { 1190 (void)0; 1191 /*[[p]]*/ 1192 } 1193 } 1194 )"; 1195 runDataflow( 1196 Code, 1197 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1198 ASTContext &ASTCtx) { 1199 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1200 auto &FooVal = getValueForDecl<BoolValue>(ASTCtx, Env, "Foo").formula(); 1201 EXPECT_TRUE(Env.proves(FooVal)); 1202 }); 1203 } 1204 1205 TEST_F(FlowConditionTest, Conjunction) { 1206 std::string Code = R"( 1207 void target(bool Foo, bool Bar) { 1208 if (Foo && Bar) { 1209 (void)0; 1210 /*[[p1]]*/ 1211 } else { 1212 (void)1; 1213 /*[[p2]]*/ 1214 } 1215 } 1216 )"; 1217 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 1218 &Results, 1219 ASTContext &ASTCtx) { 1220 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1221 ASSERT_THAT(FooDecl, NotNull()); 1222 1223 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1224 ASSERT_THAT(BarDecl, NotNull()); 1225 1226 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1227 1228 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1229 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1230 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula(); 1231 EXPECT_TRUE(Env1.proves(FooVal1)); 1232 EXPECT_TRUE(Env1.proves(BarVal1)); 1233 1234 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1235 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1236 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula(); 1237 EXPECT_FALSE(Env2.proves(FooVal2)); 1238 EXPECT_FALSE(Env2.proves(BarVal2)); 1239 }); 1240 } 1241 1242 TEST_F(FlowConditionTest, Disjunction) { 1243 std::string Code = R"( 1244 void target(bool Foo, bool Bar) { 1245 if (Foo || Bar) { 1246 (void)0; 1247 /*[[p1]]*/ 1248 } else { 1249 (void)1; 1250 /*[[p2]]*/ 1251 } 1252 } 1253 )"; 1254 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 1255 &Results, 1256 ASTContext &ASTCtx) { 1257 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1258 ASSERT_THAT(FooDecl, NotNull()); 1259 1260 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1261 ASSERT_THAT(BarDecl, NotNull()); 1262 1263 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1264 1265 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1266 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1267 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula(); 1268 EXPECT_FALSE(Env1.proves(FooVal1)); 1269 EXPECT_FALSE(Env1.proves(BarVal1)); 1270 1271 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1272 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1273 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula(); 1274 EXPECT_FALSE(Env2.proves(FooVal2)); 1275 EXPECT_FALSE(Env2.proves(BarVal2)); 1276 }); 1277 } 1278 1279 TEST_F(FlowConditionTest, NegatedConjunction) { 1280 std::string Code = R"( 1281 void target(bool Foo, bool Bar) { 1282 if (!(Foo && Bar)) { 1283 (void)0; 1284 /*[[p1]]*/ 1285 } else { 1286 (void)1; 1287 /*[[p2]]*/ 1288 } 1289 } 1290 )"; 1291 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 1292 &Results, 1293 ASTContext &ASTCtx) { 1294 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1295 ASSERT_THAT(FooDecl, NotNull()); 1296 1297 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1298 ASSERT_THAT(BarDecl, NotNull()); 1299 1300 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1301 1302 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1303 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1304 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula(); 1305 EXPECT_FALSE(Env1.proves(FooVal1)); 1306 EXPECT_FALSE(Env1.proves(BarVal1)); 1307 1308 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1309 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1310 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula(); 1311 EXPECT_TRUE(Env2.proves(FooVal2)); 1312 EXPECT_TRUE(Env2.proves(BarVal2)); 1313 }); 1314 } 1315 1316 TEST_F(FlowConditionTest, DeMorgan) { 1317 std::string Code = R"( 1318 void target(bool Foo, bool Bar) { 1319 if (!(!Foo || !Bar)) { 1320 (void)0; 1321 /*[[p1]]*/ 1322 } else { 1323 (void)1; 1324 /*[[p2]]*/ 1325 } 1326 } 1327 )"; 1328 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 1329 &Results, 1330 ASTContext &ASTCtx) { 1331 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1332 ASSERT_THAT(FooDecl, NotNull()); 1333 1334 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1335 ASSERT_THAT(BarDecl, NotNull()); 1336 1337 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1338 1339 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1340 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1341 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula(); 1342 EXPECT_TRUE(Env1.proves(FooVal1)); 1343 EXPECT_TRUE(Env1.proves(BarVal1)); 1344 1345 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1346 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1347 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula(); 1348 EXPECT_FALSE(Env2.proves(FooVal2)); 1349 EXPECT_FALSE(Env2.proves(BarVal2)); 1350 }); 1351 } 1352 1353 TEST_F(FlowConditionTest, Join) { 1354 std::string Code = R"( 1355 void target(bool Foo, bool Bar) { 1356 if (Bar) { 1357 if (!Foo) 1358 return; 1359 } else { 1360 if (!Foo) 1361 return; 1362 } 1363 (void)0; 1364 /*[[p]]*/ 1365 } 1366 )"; 1367 runDataflow( 1368 Code, 1369 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1370 ASTContext &ASTCtx) { 1371 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1372 1373 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1374 ASSERT_THAT(FooDecl, NotNull()); 1375 1376 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1377 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula(); 1378 EXPECT_TRUE(Env.proves(FooVal)); 1379 }); 1380 } 1381 1382 // Verifies that flow conditions are properly constructed even when the 1383 // condition is not meaningfully interpreted. 1384 // 1385 // Note: currently, arbitrary function calls are uninterpreted, so the test 1386 // exercises this case. If and when we change that, this test will not add to 1387 // coverage (although it may still test a valuable case). 1388 TEST_F(FlowConditionTest, OpaqueFlowConditionJoinsToOpaqueBool) { 1389 std::string Code = R"( 1390 bool foo(); 1391 1392 void target() { 1393 bool Bar = true; 1394 if (foo()) 1395 Bar = false; 1396 (void)0; 1397 /*[[p]]*/ 1398 } 1399 )"; 1400 runDataflow( 1401 Code, 1402 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1403 ASTContext &ASTCtx) { 1404 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1405 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1406 1407 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1408 ASSERT_THAT(BarDecl, NotNull()); 1409 1410 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula(); 1411 1412 EXPECT_FALSE(Env.proves(BarVal)); 1413 }); 1414 } 1415 1416 // Verifies that flow conditions are properly constructed even when the 1417 // condition is not meaningfully interpreted. 1418 // 1419 // Note: currently, fields with recursive type calls are uninterpreted (beneath 1420 // the first instance), so the test exercises this case. If and when we change 1421 // that, this test will not add to coverage (although it may still test a 1422 // valuable case). 1423 TEST_F(FlowConditionTest, OpaqueFieldFlowConditionJoinsToOpaqueBool) { 1424 std::string Code = R"( 1425 struct Rec { 1426 Rec* Next; 1427 }; 1428 1429 struct Foo { 1430 Rec* X; 1431 }; 1432 1433 void target(Foo F) { 1434 bool Bar = true; 1435 if (F.X->Next) 1436 Bar = false; 1437 (void)0; 1438 /*[[p]]*/ 1439 } 1440 )"; 1441 runDataflow( 1442 Code, 1443 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1444 ASTContext &ASTCtx) { 1445 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1446 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1447 1448 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1449 ASSERT_THAT(BarDecl, NotNull()); 1450 1451 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula(); 1452 1453 EXPECT_FALSE(Env.proves(BarVal)); 1454 }); 1455 } 1456 1457 // Verifies that flow conditions are properly constructed even when the 1458 // condition is not meaningfully interpreted. Adds to above by nesting the 1459 // interestnig case inside a normal branch. This protects against degenerate 1460 // solutions which only test for empty flow conditions, for example. 1461 TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchJoinsToOpaqueBool) { 1462 std::string Code = R"( 1463 bool foo(); 1464 1465 void target(bool Cond) { 1466 bool Bar = true; 1467 if (Cond) { 1468 if (foo()) 1469 Bar = false; 1470 (void)0; 1471 /*[[p]]*/ 1472 } 1473 } 1474 )"; 1475 runDataflow( 1476 Code, 1477 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1478 ASTContext &ASTCtx) { 1479 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1480 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1481 1482 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); 1483 ASSERT_THAT(BarDecl, NotNull()); 1484 1485 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula(); 1486 1487 EXPECT_FALSE(Env.proves(BarVal)); 1488 }); 1489 } 1490 1491 TEST_F(FlowConditionTest, PointerToBoolImplicitCast) { 1492 std::string Code = R"( 1493 void target(int *Ptr) { 1494 bool Foo = false; 1495 if (Ptr) { 1496 Foo = true; 1497 /*[[p1]]*/ 1498 } 1499 1500 (void)0; 1501 /*[[p2]]*/ 1502 } 1503 )"; 1504 runDataflow( 1505 Code, 1506 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1507 ASTContext &ASTCtx) { 1508 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1509 1510 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 1511 ASSERT_THAT(FooDecl, NotNull()); 1512 1513 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1514 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula(); 1515 EXPECT_TRUE(Env1.proves(FooVal1)); 1516 1517 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1518 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula(); 1519 EXPECT_FALSE(Env2.proves(FooVal2)); 1520 }); 1521 } 1522 1523 class TopAnalysis final : public DataflowAnalysis<TopAnalysis, NoopLattice> { 1524 public: 1525 explicit TopAnalysis(ASTContext &Context) 1526 : DataflowAnalysis<TopAnalysis, NoopLattice>(Context) {} 1527 1528 static NoopLattice initialElement() { return {}; } 1529 1530 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { 1531 auto CS = Elt.getAs<CFGStmt>(); 1532 if (!CS) 1533 return; 1534 const Stmt *S = CS->getStmt(); 1535 SmallVector<BoundNodes, 1> Matches = 1536 match(callExpr(callee(functionDecl(hasName("makeTop")))).bind("top"), 1537 *S, getASTContext()); 1538 if (const auto *E = selectFirst<CallExpr>("top", Matches)) { 1539 Env.setValue(*E, Env.makeTopBoolValue()); 1540 } 1541 } 1542 1543 ComparisonResult compare(QualType Type, const Value &Val1, 1544 const Environment &Env1, const Value &Val2, 1545 const Environment &Env2) override { 1546 // Changes to a sound approximation, which allows us to test whether we can 1547 // (soundly) converge for some loops. 1548 return ComparisonResult::Unknown; 1549 } 1550 }; 1551 1552 class TopTest : public Test { 1553 protected: 1554 template <typename Matcher> 1555 void runDataflow(llvm::StringRef Code, Matcher VerifyResults) { 1556 ASSERT_THAT_ERROR( 1557 checkDataflow<TopAnalysis>( 1558 AnalysisInputs<TopAnalysis>( 1559 Code, ast_matchers::hasName("target"), 1560 [](ASTContext &Context, Environment &Env) { 1561 return TopAnalysis(Context); 1562 }) 1563 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}), 1564 VerifyResults), 1565 llvm::Succeeded()); 1566 } 1567 }; 1568 1569 // Tests that when Top is unused it remains Top. 1570 TEST_F(TopTest, UnusedTopInitializer) { 1571 std::string Code = R"( 1572 bool makeTop(); 1573 1574 void target() { 1575 bool Foo = makeTop(); 1576 /*[[p1]]*/ 1577 (void)0; 1578 /*[[p2]]*/ 1579 } 1580 )"; 1581 runDataflow( 1582 Code, 1583 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1584 const AnalysisOutputs &AO) { 1585 ASSERT_THAT(Results.keys(), 1586 UnorderedElementsAre("p1", "p2")); 1587 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1588 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1589 1590 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1591 ASSERT_THAT(FooDecl, NotNull()); 1592 1593 auto GetFooValue = [FooDecl](const Environment &Env) { 1594 return Env.getValue(*FooDecl); 1595 }; 1596 1597 Value *FooVal1 = GetFooValue(Env1); 1598 ASSERT_THAT(FooVal1, NotNull()); 1599 EXPECT_TRUE(isa<TopBoolValue>(FooVal1)) 1600 << debugString(FooVal1->getKind()); 1601 1602 Value *FooVal2 = GetFooValue(Env2); 1603 ASSERT_THAT(FooVal2, NotNull()); 1604 EXPECT_TRUE(isa<TopBoolValue>(FooVal2)) 1605 << debugString(FooVal2->getKind()); 1606 1607 EXPECT_EQ(FooVal1, FooVal2); 1608 }); 1609 } 1610 1611 // Tests that when Top is unused it remains Top. Like above, but uses the 1612 // assignment form rather than initialization, which uses Top as an lvalue that 1613 // is *not* in an rvalue position. 1614 TEST_F(TopTest, UnusedTopAssignment) { 1615 std::string Code = R"( 1616 bool makeTop(); 1617 1618 void target() { 1619 bool Foo; 1620 Foo = makeTop(); 1621 /*[[p1]]*/ 1622 (void)0; 1623 /*[[p2]]*/ 1624 } 1625 )"; 1626 runDataflow( 1627 Code, 1628 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1629 const AnalysisOutputs &AO) { 1630 ASSERT_THAT(Results.keys(), 1631 UnorderedElementsAre("p1", "p2")); 1632 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1633 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1634 1635 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1636 ASSERT_THAT(FooDecl, NotNull()); 1637 1638 auto GetFooValue = [FooDecl](const Environment &Env) { 1639 return Env.getValue(*FooDecl); 1640 }; 1641 1642 Value *FooVal1 = GetFooValue(Env1); 1643 ASSERT_THAT(FooVal1, NotNull()); 1644 EXPECT_TRUE(isa<TopBoolValue>(FooVal1)) 1645 << debugString(FooVal1->getKind()); 1646 1647 Value *FooVal2 = GetFooValue(Env2); 1648 ASSERT_THAT(FooVal2, NotNull()); 1649 EXPECT_TRUE(isa<TopBoolValue>(FooVal2)) 1650 << debugString(FooVal2->getKind()); 1651 1652 EXPECT_EQ(FooVal1, FooVal2); 1653 }); 1654 } 1655 1656 TEST_F(TopTest, UnusedTopJoinsToTop) { 1657 std::string Code = R"( 1658 bool makeTop(); 1659 1660 void target(bool Cond, bool F) { 1661 bool Foo = makeTop(); 1662 // Force a new CFG block. 1663 if (F) return; 1664 (void)0; 1665 /*[[p1]]*/ 1666 1667 bool Zab1; 1668 bool Zab2; 1669 if (Cond) { 1670 Zab1 = true; 1671 } else { 1672 Zab2 = true; 1673 } 1674 (void)0; 1675 /*[[p2]]*/ 1676 } 1677 )"; 1678 runDataflow( 1679 Code, 1680 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1681 const AnalysisOutputs &AO) { 1682 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1683 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1684 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1685 1686 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1687 ASSERT_THAT(FooDecl, NotNull()); 1688 1689 auto GetFooValue = [FooDecl](const Environment &Env) { 1690 return Env.getValue(*FooDecl); 1691 }; 1692 1693 Value *FooVal1 = GetFooValue(Env1); 1694 ASSERT_THAT(FooVal1, NotNull()); 1695 EXPECT_TRUE(isa<TopBoolValue>(FooVal1)) 1696 << debugString(FooVal1->getKind()); 1697 1698 Value *FooVal2 = GetFooValue(Env2); 1699 ASSERT_THAT(FooVal2, NotNull()); 1700 EXPECT_TRUE(isa<TopBoolValue>(FooVal2)) 1701 << debugString(FooVal2->getKind()); 1702 }); 1703 } 1704 1705 TEST_F(TopTest, TopUsedBeforeBranchJoinsToSameAtomicBool) { 1706 std::string Code = R"( 1707 bool makeTop(); 1708 1709 void target(bool Cond, bool F) { 1710 bool Foo = makeTop(); 1711 /*[[p0]]*/ 1712 1713 // Use `Top`. 1714 bool Bar = Foo; 1715 // Force a new CFG block. 1716 if (F) return; 1717 (void)0; 1718 /*[[p1]]*/ 1719 1720 bool Zab1; 1721 bool Zab2; 1722 if (Cond) { 1723 Zab1 = true; 1724 } else { 1725 Zab2 = true; 1726 } 1727 (void)0; 1728 /*[[p2]]*/ 1729 } 1730 )"; 1731 runDataflow( 1732 Code, 1733 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1734 const AnalysisOutputs &AO) { 1735 ASSERT_THAT(Results.keys(), 1736 UnorderedElementsAre("p0", "p1", "p2")); 1737 const Environment &Env0 = getEnvironmentAtAnnotation(Results, "p0"); 1738 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1739 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1740 1741 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1742 ASSERT_THAT(FooDecl, NotNull()); 1743 1744 auto GetFooValue = [FooDecl](const Environment &Env) { 1745 return Env.getValue(*FooDecl); 1746 }; 1747 1748 Value *FooVal0 = GetFooValue(Env0); 1749 ASSERT_THAT(FooVal0, NotNull()); 1750 EXPECT_TRUE(isa<TopBoolValue>(FooVal0)) 1751 << debugString(FooVal0->getKind()); 1752 1753 Value *FooVal1 = GetFooValue(Env1); 1754 ASSERT_THAT(FooVal1, NotNull()); 1755 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal1)) 1756 << debugString(FooVal1->getKind()); 1757 1758 Value *FooVal2 = GetFooValue(Env2); 1759 ASSERT_THAT(FooVal2, NotNull()); 1760 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2)) 1761 << debugString(FooVal2->getKind()); 1762 1763 EXPECT_EQ(FooVal2, FooVal1); 1764 }); 1765 } 1766 1767 TEST_F(TopTest, TopUsedInBothBranchesJoinsToAtomic) { 1768 std::string Code = R"( 1769 bool makeTop(); 1770 1771 void target(bool Cond, bool F) { 1772 bool Foo = makeTop(); 1773 // Force a new CFG block. 1774 if (F) return; 1775 (void)0; 1776 /*[[p1]]*/ 1777 1778 bool Zab1; 1779 bool Zab2; 1780 if (Cond) { 1781 Zab1 = Foo; 1782 } else { 1783 Zab2 = Foo; 1784 } 1785 (void)0; 1786 /*[[p2]]*/ 1787 } 1788 )"; 1789 runDataflow( 1790 Code, 1791 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1792 const AnalysisOutputs &AO) { 1793 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1794 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1795 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1796 1797 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1798 ASSERT_THAT(FooDecl, NotNull()); 1799 1800 auto GetFooValue = [FooDecl](const Environment &Env) { 1801 return Env.getValue(*FooDecl); 1802 }; 1803 1804 Value *FooVal1 = GetFooValue(Env1); 1805 ASSERT_THAT(FooVal1, NotNull()); 1806 EXPECT_TRUE(isa<TopBoolValue>(FooVal1)) 1807 << debugString(FooVal1->getKind()); 1808 1809 Value *FooVal2 = GetFooValue(Env2); 1810 ASSERT_THAT(FooVal2, NotNull()); 1811 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2)) 1812 << debugString(FooVal2->getKind()); 1813 }); 1814 } 1815 1816 TEST_F(TopTest, TopUsedInBothBranchesWithoutPrecisionLoss) { 1817 std::string Code = R"( 1818 bool makeTop(); 1819 1820 void target(bool Cond, bool F) { 1821 bool Foo = makeTop(); 1822 // Force a new CFG block. 1823 if (F) return; 1824 (void)0; 1825 1826 bool Bar; 1827 if (Cond) { 1828 Bar = Foo; 1829 } else { 1830 Bar = Foo; 1831 } 1832 (void)0; 1833 /*[[p]]*/ 1834 } 1835 )"; 1836 runDataflow( 1837 Code, 1838 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1839 const AnalysisOutputs &AO) { 1840 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1841 const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 1842 1843 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1844 ASSERT_THAT(FooDecl, NotNull()); 1845 1846 const ValueDecl *BarDecl = findValueDecl(AO.ASTCtx, "Bar"); 1847 ASSERT_THAT(BarDecl, NotNull()); 1848 1849 auto *FooVal = dyn_cast_or_null<BoolValue>(Env.getValue(*FooDecl)); 1850 ASSERT_THAT(FooVal, NotNull()); 1851 1852 auto *BarVal = dyn_cast_or_null<BoolValue>(Env.getValue(*BarDecl)); 1853 ASSERT_THAT(BarVal, NotNull()); 1854 1855 EXPECT_TRUE(Env.proves( 1856 Env.arena().makeEquals(FooVal->formula(), BarVal->formula()))); 1857 }); 1858 } 1859 1860 TEST_F(TopTest, TopUnusedBeforeLoopHeadJoinsToTop) { 1861 std::string Code = R"( 1862 bool makeTop(); 1863 1864 void target(bool Cond, bool F) { 1865 bool Foo = makeTop(); 1866 // Force a new CFG block. 1867 if (F) return; 1868 (void)0; 1869 /*[[p1]]*/ 1870 1871 while (Cond) { 1872 // Use `Foo`. 1873 bool Zab = Foo; 1874 Zab = false; 1875 Foo = makeTop(); 1876 } 1877 (void)0; 1878 /*[[p2]]*/ 1879 } 1880 )"; 1881 runDataflow( 1882 Code, 1883 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 1884 const AnalysisOutputs &AO) { 1885 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2")); 1886 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1"); 1887 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2"); 1888 1889 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo"); 1890 ASSERT_THAT(FooDecl, NotNull()); 1891 1892 auto GetFooValue = [FooDecl](const Environment &Env) { 1893 return Env.getValue(*FooDecl); 1894 }; 1895 1896 Value *FooVal1 = GetFooValue(Env1); 1897 ASSERT_THAT(FooVal1, NotNull()); 1898 EXPECT_TRUE(isa<TopBoolValue>(FooVal1)) 1899 << debugString(FooVal1->getKind()); 1900 1901 Value *FooVal2 = GetFooValue(Env2); 1902 ASSERT_THAT(FooVal2, NotNull()); 1903 EXPECT_TRUE(isa<TopBoolValue>(FooVal2)) 1904 << debugString(FooVal2->getKind()); 1905 1906 }); 1907 } 1908 1909 TEST_F(TopTest, ForRangeStmtConverges) { 1910 std::string Code = R"( 1911 void target(bool Foo) { 1912 int Ints[10]; 1913 bool B = false; 1914 for (int I : Ints) 1915 B = true; 1916 } 1917 )"; 1918 runDataflow(Code, 1919 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, 1920 const AnalysisOutputs &) { 1921 // No additional expectations. We're only checking that the 1922 // analysis converged. 1923 }); 1924 } 1925 } // namespace 1926