[ELF] Replace inExpr with lexState. NFC
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / TypeErasedDataflowAnalysisTest.cpp
blob8717d9753d161b147360ba75075554c33d7bbf45
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 //===----------------------------------------------------------------------===//
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>
43 namespace {
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;
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"});
64 auto *Func = selectFirst<FunctionDecl>(
65 "func",
66 match(functionDecl(ast_matchers::hasName("target")).bind("func"),
67 AST->getASTContext()));
68 assert(Func != nullptr);
70 ACFG =
71 std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build(*Func)));
73 AnalysisT Analysis = MakeAnalysis(AST->getASTContext());
74 DACtx = std::make_unique<DataflowAnalysisContext>(
75 std::make_unique<WatchedLiteralsSolver>());
76 Environment Env(*DACtx, *Func);
78 return runDataflowAnalysis(*ACFG, Analysis, Env);
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;
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;
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;
108 std::unique_ptr<ASTUnit> AST;
109 std::unique_ptr<AdornedCFG> ACFG;
110 std::unique_ptr<DataflowAnalysisContext> DACtx;
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());
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"});
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;
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")));
150 TEST_F(DataflowAnalysisTest, CanAnalyzeStmt) {
151 std::string Code = R"cc(
152 struct S { bool b; };
153 void foo() {
154 S AnS = S{true};
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")));
162 ACFG = std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build(
163 Func, const_cast<DeclStmt &>(DeclStatement), AST->getASTContext())));
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));
170 llvm::Expected<std::vector<std::optional<DataflowAnalysisState<NoopLattice>>>>
171 Results = runDataflowAnalysis(*ACFG, Analysis, Env);
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()));
182 // Tests for the statement-to-block map.
183 using StmtToBlockTest = DataflowAnalysisTest;
185 TEST_F(StmtToBlockTest, ConditionalOperator) {
186 std::string Code = R"(
187 void target(bool b) {
188 int i = b ? 1 : 0;
191 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
192 Code, [](ASTContext &C) { return NoopAnalysis(C); })
193 .takeError(),
194 llvm::Succeeded());
196 const auto &IDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("i")))));
197 const auto &ConditionalOp =
198 matchNode<ConditionalOperator>(conditionalOperator());
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));
206 TEST_F(StmtToBlockTest, LogicalAnd) {
207 std::string Code = R"(
208 void target(bool b1, bool b2) {
209 bool b = b1 && b2;
212 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
213 Code, [](ASTContext &C) { return NoopAnalysis(C); })
214 .takeError(),
215 llvm::Succeeded());
217 const auto &BDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("b")))));
218 const auto &AndOp =
219 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&")));
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));
227 TEST_F(StmtToBlockTest, IfStatementWithLogicalAnd) {
228 std::string Code = R"(
229 void target(bool b1, bool b2) {
230 if (b1 && b2)
234 ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
235 Code, [](ASTContext &C) { return NoopAnalysis(C); })
236 .takeError(),
237 llvm::Succeeded());
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("&&")));
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);
255 // Tests that check we discard state for expressions correctly.
256 using DiscardExprStateTest = DataflowAnalysisTest;
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);
266 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
267 Code, [](ASTContext &C) { return NoopAnalysis(C); }));
269 const auto &NotEqOp =
270 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("!=")));
271 const auto &CallFoo =
272 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("foo")))));
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);
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);
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();
293 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
294 Code, [](ASTContext &C) { return NoopAnalysis(C); }));
296 const auto &AndOp =
297 matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&")));
298 const auto &CallF =
299 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("f")))));
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);
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);
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));
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);
333 TEST_F(DiscardExprStateTest, ConditionalOperator) {
334 std::string Code = R"(
335 void f(int*, int);
336 void g();
337 bool cond();
339 void target() {
340 int i = 0;
341 if (cond())
342 f(&i, cond() ? 1 : 0);
343 g();
346 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
347 Code, [](ASTContext &C) { return NoopAnalysis(C); }));
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")))));
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);
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);
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);
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);
391 auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
392 Code, [](ASTContext &C) { return NoopAnalysis(C); }));
394 const auto &FnToPtrDecay = matchNode<ImplicitCastExpr>(
395 implicitCastExpr(hasCastKind(CK_FunctionToPointerDecay)));
396 const auto &CallExpect =
397 matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("expect")))));
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);
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);
410 struct NonConvergingLattice {
411 int State;
413 bool operator==(const NonConvergingLattice &Other) const {
414 return State == Other.State;
417 LatticeJoinEffect join(const NonConvergingLattice &Other) {
418 if (Other.State == 0)
419 return LatticeJoinEffect::Unchanged;
420 State += Other.State;
421 return LatticeJoinEffect::Changed;
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}) {}
434 static NonConvergingLattice initialElement() { return {0}; }
436 void transfer(const CFGElement &, NonConvergingLattice &E, Environment &) {
437 ++E.State;
441 TEST_F(DataflowAnalysisTest, NonConvergingAnalysis) {
442 std::string Code = R"(
443 void target() {
444 while(true) {}
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");
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.
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;
473 ASSERT_THAT_ERROR(
474 runAnalysis<NoopAnalysis>(Code,
475 [](ASTContext &C) {
476 auto EnableBuiltIns = DataflowAnalysisOptions{
477 DataflowAnalysisContext::Options{}};
478 return NoopAnalysis(C, EnableBuiltIns);
480 .takeError(),
481 llvm::Succeeded());
484 struct FunctionCallLattice {
485 using FunctionSet = llvm::SmallSet<std::string, 8>;
486 FunctionSet CalledFunctions;
488 bool operator==(const FunctionCallLattice &Other) const {
489 return CalledFunctions == Other.CalledFunctions;
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;
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 << "}";
510 class FunctionCallAnalysis
511 : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> {
512 public:
513 explicit FunctionCallAnalysis(ASTContext &Context)
514 : DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice>(Context) {}
516 static FunctionCallLattice initialElement() { return {}; }
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());
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();
540 class Fatal {
541 public:
542 ~Fatal() __attribute__((noreturn));
543 int bar();
544 int baz();
547 class NonFatal {
548 public:
549 ~NonFatal();
550 int bar();
552 )"));
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);
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);
570 llvm::Succeeded());
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);
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);
590 TEST_F(NoreturnDestructorTest, ConditionalOperatorBothBranchesReturn) {
591 std::string Code = R"(
592 #include "noreturn_destructor_test_defs.h"
594 void target(bool b) {
595 int value = b ? foo() : NonFatal().bar();
596 (void)0;
597 // [[p]]
600 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
601 "p", HoldsFunctionCallLattice(HasCalledFunctions(
602 UnorderedElementsAre("foo", "bar"))))));
605 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
606 std::string Code = R"(
607 #include "noreturn_destructor_test_defs.h"
609 void target(bool b) {
610 int value = b ? foo() : Fatal().bar();
611 (void)0;
612 // [[p]]
615 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
616 "p", HoldsFunctionCallLattice(HasCalledFunctions(
617 UnorderedElementsAre("foo"))))));
620 TEST_F(NoreturnDestructorTest,
621 ConditionalOperatorConstantCondition_LeftBranchReturns) {
622 std::string Code = R"(
623 #include "noreturn_destructor_test_defs.h"
625 void target() {
626 int value = true ? foo() : Fatal().bar();
627 (void)0;
628 // [[p]]
631 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
632 "p", HoldsFunctionCallLattice(HasCalledFunctions(
633 UnorderedElementsAre("foo"))))));
636 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {
637 std::string Code = R"(
638 #include "noreturn_destructor_test_defs.h"
640 void target(bool b) {
641 int value = b ? Fatal().bar() : foo();
642 (void)0;
643 // [[p]]
646 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
647 "p", HoldsFunctionCallLattice(HasCalledFunctions(
648 UnorderedElementsAre("foo"))))));
651 TEST_F(NoreturnDestructorTest,
652 ConditionalOperatorConstantCondition_RightBranchReturns) {
653 std::string Code = R"(
654 #include "noreturn_destructor_test_defs.h"
656 void target() {
657 int value = false ? Fatal().bar() : foo();
658 (void)0;
659 // [[p]]
662 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
663 "p", HoldsFunctionCallLattice(HasCalledFunctions(
664 UnorderedElementsAre("foo"))))));
667 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchesDoNotReturn) {
668 std::string Code = R"(
669 #include "noreturn_destructor_test_defs.h"
671 void target(bool b1, bool b2) {
672 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz());
673 (void)0;
674 // [[p]]
677 runDataflow(Code, IsEmpty());
678 // FIXME: Called functions at point `p` should contain "foo".
681 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {
682 std::string Code = R"(
683 #include "noreturn_destructor_test_defs.h"
685 void target(bool b1, bool b2) {
686 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo());
687 (void)0;
688 // [[p]]
691 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
692 "p", HoldsFunctionCallLattice(HasCalledFunctions(
693 UnorderedElementsAre("baz", "foo"))))));
694 // FIXME: Called functions at point `p` should contain only "foo".
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}};
713 static NoopLattice initialElement() { return {}; }
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);
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));
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);
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());
761 TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) {
762 std::string Code = R"(
763 struct SpecialBool {
764 SpecialBool() = default;
765 void set();
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]]*/
778 (void)0;
779 /*[[p4]]*/
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");
793 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
794 ASSERT_THAT(FooDecl, NotNull());
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();
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)));
810 class NullPointerAnalysis final
811 : public DataflowAnalysis<NullPointerAnalysis, NoopLattice> {
812 public:
813 explicit NullPointerAnalysis(ASTContext &Context)
814 : DataflowAnalysis<NullPointerAnalysis, NoopLattice>(Context) {}
816 static NoopLattice initialElement() { return {}; }
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;
827 if (!E->getType()->isPointerType())
828 return;
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);
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));
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;
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;
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;
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;
872 if (IsNull1 == IsNull2)
873 JoinedVal.setProperty("is_null", *IsNull1);
874 else
875 JoinedVal.setProperty("is_null", JoinedEnv.makeTopBoolValue());
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};
891 case ComparisonResult::Unknown:
892 return std::nullopt;
894 llvm_unreachable("all cases in switch covered");
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);
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());
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]]*/
929 (void)0;
930 /*[[p3]]*/
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");
941 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
942 ASSERT_THAT(FooDecl, NotNull());
944 auto GetFooValue = [FooDecl](const Environment &Env) {
945 return Env.getValue(*FooDecl);
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")));
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]]*/
971 (void)0;
972 /*[[p4]]*/
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");
984 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
985 ASSERT_THAT(FooDecl, NotNull());
987 auto GetFooValue = [FooDecl](const Environment &Env) {
988 return Env.getValue(*FooDecl);
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));
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;
1009 (void)0;
1010 // [[p]]
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);
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;
1035 (void)0;
1036 /*[[p]]*/
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));
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;
1059 (void)0;
1060 /*[[p]]*/
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()));
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);
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());
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]]*/
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());
1115 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1117 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1118 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1119 EXPECT_TRUE(Env1.proves(FooVal1));
1121 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1122 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1123 EXPECT_FALSE(Env2.proves(FooVal2));
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]]*/
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());
1146 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1148 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1149 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1150 EXPECT_FALSE(Env1.proves(FooVal1));
1152 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1153 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1154 EXPECT_TRUE(Env2.proves(FooVal2));
1158 TEST_F(FlowConditionTest, WhileStmt) {
1159 std::string Code = R"(
1160 void target(bool Foo) {
1161 while (Foo) {
1162 (void)0;
1163 /*[[p]]*/
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());
1174 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1175 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1177 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula();
1178 EXPECT_TRUE(Env.proves(FooVal));
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]]*/
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));
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]]*/
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());
1223 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1224 ASSERT_THAT(BarDecl, NotNull());
1226 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
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));
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));
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]]*/
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());
1260 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1261 ASSERT_THAT(BarDecl, NotNull());
1263 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
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));
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));
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]]*/
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());
1297 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1298 ASSERT_THAT(BarDecl, NotNull());
1300 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
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));
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));
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]]*/
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());
1334 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1335 ASSERT_THAT(BarDecl, NotNull());
1337 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
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));
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));
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;
1363 (void)0;
1364 /*[[p]]*/
1367 runDataflow(
1368 Code,
1369 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1370 ASTContext &ASTCtx) {
1371 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1373 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1374 ASSERT_THAT(FooDecl, NotNull());
1376 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1377 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula();
1378 EXPECT_TRUE(Env.proves(FooVal));
1382 // Verifies that flow conditions are properly constructed even when the
1383 // condition is not meaningfully interpreted.
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();
1392 void target() {
1393 bool Bar = true;
1394 if (foo())
1395 Bar = false;
1396 (void)0;
1397 /*[[p]]*/
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");
1407 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1408 ASSERT_THAT(BarDecl, NotNull());
1410 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1412 EXPECT_FALSE(Env.proves(BarVal));
1416 // Verifies that flow conditions are properly constructed even when the
1417 // condition is not meaningfully interpreted.
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;
1429 struct Foo {
1430 Rec* X;
1433 void target(Foo F) {
1434 bool Bar = true;
1435 if (F.X->Next)
1436 Bar = false;
1437 (void)0;
1438 /*[[p]]*/
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");
1448 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1449 ASSERT_THAT(BarDecl, NotNull());
1451 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1453 EXPECT_FALSE(Env.proves(BarVal));
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();
1465 void target(bool Cond) {
1466 bool Bar = true;
1467 if (Cond) {
1468 if (foo())
1469 Bar = false;
1470 (void)0;
1471 /*[[p]]*/
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");
1482 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1483 ASSERT_THAT(BarDecl, NotNull());
1485 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1487 EXPECT_FALSE(Env.proves(BarVal));
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]]*/
1500 (void)0;
1501 /*[[p2]]*/
1504 runDataflow(
1505 Code,
1506 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1507 ASTContext &ASTCtx) {
1508 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1510 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1511 ASSERT_THAT(FooDecl, NotNull());
1513 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1514 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1515 EXPECT_TRUE(Env1.proves(FooVal1));
1517 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1518 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1519 EXPECT_FALSE(Env2.proves(FooVal2));
1523 class TopAnalysis final : public DataflowAnalysis<TopAnalysis, NoopLattice> {
1524 public:
1525 explicit TopAnalysis(ASTContext &Context)
1526 : DataflowAnalysis<TopAnalysis, NoopLattice>(Context) {}
1528 static NoopLattice initialElement() { return {}; }
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());
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;
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);
1563 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1564 VerifyResults),
1565 llvm::Succeeded());
1569 // Tests that when Top is unused it remains Top.
1570 TEST_F(TopTest, UnusedTopInitializer) {
1571 std::string Code = R"(
1572 bool makeTop();
1574 void target() {
1575 bool Foo = makeTop();
1576 /*[[p1]]*/
1577 (void)0;
1578 /*[[p2]]*/
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");
1590 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1591 ASSERT_THAT(FooDecl, NotNull());
1593 auto GetFooValue = [FooDecl](const Environment &Env) {
1594 return Env.getValue(*FooDecl);
1597 Value *FooVal1 = GetFooValue(Env1);
1598 ASSERT_THAT(FooVal1, NotNull());
1599 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1600 << debugString(FooVal1->getKind());
1602 Value *FooVal2 = GetFooValue(Env2);
1603 ASSERT_THAT(FooVal2, NotNull());
1604 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1605 << debugString(FooVal2->getKind());
1607 EXPECT_EQ(FooVal1, FooVal2);
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();
1618 void target() {
1619 bool Foo;
1620 Foo = makeTop();
1621 /*[[p1]]*/
1622 (void)0;
1623 /*[[p2]]*/
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");
1635 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1636 ASSERT_THAT(FooDecl, NotNull());
1638 auto GetFooValue = [FooDecl](const Environment &Env) {
1639 return Env.getValue(*FooDecl);
1642 Value *FooVal1 = GetFooValue(Env1);
1643 ASSERT_THAT(FooVal1, NotNull());
1644 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1645 << debugString(FooVal1->getKind());
1647 Value *FooVal2 = GetFooValue(Env2);
1648 ASSERT_THAT(FooVal2, NotNull());
1649 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1650 << debugString(FooVal2->getKind());
1652 EXPECT_EQ(FooVal1, FooVal2);
1656 TEST_F(TopTest, UnusedTopJoinsToTop) {
1657 std::string Code = R"(
1658 bool makeTop();
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]]*/
1667 bool Zab1;
1668 bool Zab2;
1669 if (Cond) {
1670 Zab1 = true;
1671 } else {
1672 Zab2 = true;
1674 (void)0;
1675 /*[[p2]]*/
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");
1686 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1687 ASSERT_THAT(FooDecl, NotNull());
1689 auto GetFooValue = [FooDecl](const Environment &Env) {
1690 return Env.getValue(*FooDecl);
1693 Value *FooVal1 = GetFooValue(Env1);
1694 ASSERT_THAT(FooVal1, NotNull());
1695 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1696 << debugString(FooVal1->getKind());
1698 Value *FooVal2 = GetFooValue(Env2);
1699 ASSERT_THAT(FooVal2, NotNull());
1700 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1701 << debugString(FooVal2->getKind());
1705 TEST_F(TopTest, TopUsedBeforeBranchJoinsToSameAtomicBool) {
1706 std::string Code = R"(
1707 bool makeTop();
1709 void target(bool Cond, bool F) {
1710 bool Foo = makeTop();
1711 /*[[p0]]*/
1713 // Use `Top`.
1714 bool Bar = Foo;
1715 // Force a new CFG block.
1716 if (F) return;
1717 (void)0;
1718 /*[[p1]]*/
1720 bool Zab1;
1721 bool Zab2;
1722 if (Cond) {
1723 Zab1 = true;
1724 } else {
1725 Zab2 = true;
1727 (void)0;
1728 /*[[p2]]*/
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");
1741 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1742 ASSERT_THAT(FooDecl, NotNull());
1744 auto GetFooValue = [FooDecl](const Environment &Env) {
1745 return Env.getValue(*FooDecl);
1748 Value *FooVal0 = GetFooValue(Env0);
1749 ASSERT_THAT(FooVal0, NotNull());
1750 EXPECT_TRUE(isa<TopBoolValue>(FooVal0))
1751 << debugString(FooVal0->getKind());
1753 Value *FooVal1 = GetFooValue(Env1);
1754 ASSERT_THAT(FooVal1, NotNull());
1755 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal1))
1756 << debugString(FooVal1->getKind());
1758 Value *FooVal2 = GetFooValue(Env2);
1759 ASSERT_THAT(FooVal2, NotNull());
1760 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2))
1761 << debugString(FooVal2->getKind());
1763 EXPECT_EQ(FooVal2, FooVal1);
1767 TEST_F(TopTest, TopUsedInBothBranchesJoinsToAtomic) {
1768 std::string Code = R"(
1769 bool makeTop();
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]]*/
1778 bool Zab1;
1779 bool Zab2;
1780 if (Cond) {
1781 Zab1 = Foo;
1782 } else {
1783 Zab2 = Foo;
1785 (void)0;
1786 /*[[p2]]*/
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");
1797 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1798 ASSERT_THAT(FooDecl, NotNull());
1800 auto GetFooValue = [FooDecl](const Environment &Env) {
1801 return Env.getValue(*FooDecl);
1804 Value *FooVal1 = GetFooValue(Env1);
1805 ASSERT_THAT(FooVal1, NotNull());
1806 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1807 << debugString(FooVal1->getKind());
1809 Value *FooVal2 = GetFooValue(Env2);
1810 ASSERT_THAT(FooVal2, NotNull());
1811 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2))
1812 << debugString(FooVal2->getKind());
1816 TEST_F(TopTest, TopUsedInBothBranchesWithoutPrecisionLoss) {
1817 std::string Code = R"(
1818 bool makeTop();
1820 void target(bool Cond, bool F) {
1821 bool Foo = makeTop();
1822 // Force a new CFG block.
1823 if (F) return;
1824 (void)0;
1826 bool Bar;
1827 if (Cond) {
1828 Bar = Foo;
1829 } else {
1830 Bar = Foo;
1832 (void)0;
1833 /*[[p]]*/
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");
1843 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1844 ASSERT_THAT(FooDecl, NotNull());
1846 const ValueDecl *BarDecl = findValueDecl(AO.ASTCtx, "Bar");
1847 ASSERT_THAT(BarDecl, NotNull());
1849 auto *FooVal = dyn_cast_or_null<BoolValue>(Env.getValue(*FooDecl));
1850 ASSERT_THAT(FooVal, NotNull());
1852 auto *BarVal = dyn_cast_or_null<BoolValue>(Env.getValue(*BarDecl));
1853 ASSERT_THAT(BarVal, NotNull());
1855 EXPECT_TRUE(Env.proves(
1856 Env.arena().makeEquals(FooVal->formula(), BarVal->formula())));
1860 TEST_F(TopTest, TopUnusedBeforeLoopHeadJoinsToTop) {
1861 std::string Code = R"(
1862 bool makeTop();
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]]*/
1871 while (Cond) {
1872 // Use `Foo`.
1873 bool Zab = Foo;
1874 Zab = false;
1875 Foo = makeTop();
1877 (void)0;
1878 /*[[p2]]*/
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");
1889 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1890 ASSERT_THAT(FooDecl, NotNull());
1892 auto GetFooValue = [FooDecl](const Environment &Env) {
1893 return Env.getValue(*FooDecl);
1896 Value *FooVal1 = GetFooValue(Env1);
1897 ASSERT_THAT(FooVal1, NotNull());
1898 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1899 << debugString(FooVal1->getKind());
1901 Value *FooVal2 = GetFooValue(Env2);
1902 ASSERT_THAT(FooVal2, NotNull());
1903 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1904 << debugString(FooVal2->getKind());
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;
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.
1925 } // namespace