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/Type.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Analysis/CFG.h"
16 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
17 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
18 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
19 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
20 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
21 #include "clang/Analysis/FlowSensitive/Value.h"
22 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
23 #include "clang/Tooling/Tooling.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/SmallSet.h"
27 #include "llvm/ADT/StringRef.h"
28 #include "llvm/Support/Error.h"
29 #include "llvm/Testing/Support/Error.h"
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32 #include <cassert>
33 #include <memory>
34 #include <ostream>
35 #include <string>
36 #include <utility>
37 #include <vector>
38
39 namespace {
40
41 using namespace clang;
42 using namespace dataflow;
43 using namespace test;
44 using namespace ast_matchers;
45 using ::testing::_;
46 using ::testing::ElementsAre;
47 using ::testing::IsEmpty;
48 using ::testing::IsNull;
49 using ::testing::NotNull;
50 using ::testing::Pair;
51 using ::testing::Test;
52 using ::testing::UnorderedElementsAre;
53
54 template <typename AnalysisT>
55 llvm::Expected<std::vector<
56 llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runAnalysis(llvm::StringRef Code,AnalysisT (* MakeAnalysis)(ASTContext &))57 runAnalysis(llvm::StringRef Code, AnalysisT (*MakeAnalysis)(ASTContext &)) {
58 std::unique_ptr<ASTUnit> AST =
59 tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"});
60
61 auto *Func = selectFirst<FunctionDecl>(
62 "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"),
63 AST->getASTContext()));
64 assert(Func != nullptr);
65
66 Stmt *Body = Func->getBody();
67 assert(Body != nullptr);
68
69 auto CFCtx = llvm::cantFail(
70 ControlFlowContext::build(nullptr, Body, &AST->getASTContext()));
71
72 AnalysisT Analysis = MakeAnalysis(AST->getASTContext());
73 DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>());
74 Environment Env(DACtx);
75
76 return runDataflowAnalysis(CFCtx, Analysis, Env);
77 }
78
TEST(DataflowAnalysisTest,NoopAnalysis)79 TEST(DataflowAnalysisTest, NoopAnalysis) {
80 auto BlockStates = llvm::cantFail(
81 runAnalysis<NoopAnalysis>("void target() {}", [](ASTContext &C) {
82 return NoopAnalysis(C, false);
83 }));
84 EXPECT_EQ(BlockStates.size(), 2u);
85 EXPECT_TRUE(BlockStates[0].has_value());
86 EXPECT_TRUE(BlockStates[1].has_value());
87 }
88
89 struct NonConvergingLattice {
90 int State;
91
operator ==__anon5856d3660111::NonConvergingLattice92 bool operator==(const NonConvergingLattice &Other) const {
93 return State == Other.State;
94 }
95
join__anon5856d3660111::NonConvergingLattice96 LatticeJoinEffect join(const NonConvergingLattice &Other) {
97 if (Other.State == 0)
98 return LatticeJoinEffect::Unchanged;
99 State += Other.State;
100 return LatticeJoinEffect::Changed;
101 }
102 };
103
104 class NonConvergingAnalysis
105 : public DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice> {
106 public:
NonConvergingAnalysis(ASTContext & Context)107 explicit NonConvergingAnalysis(ASTContext &Context)
108 : DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice>(
109 Context,
110 /*ApplyBuiltinTransfer=*/false) {}
111
initialElement()112 static NonConvergingLattice initialElement() { return {0}; }
113
transfer(const Stmt * S,NonConvergingLattice & E,Environment & Env)114 void transfer(const Stmt *S, NonConvergingLattice &E, Environment &Env) {
115 ++E.State;
116 }
117 };
118
TEST(DataflowAnalysisTest,NonConvergingAnalysis)119 TEST(DataflowAnalysisTest, NonConvergingAnalysis) {
120 std::string Code = R"(
121 void target() {
122 while(true) {}
123 }
124 )";
125 auto Res = runAnalysis<NonConvergingAnalysis>(
126 Code, [](ASTContext &C) { return NonConvergingAnalysis(C); });
127 EXPECT_EQ(llvm::toString(Res.takeError()),
128 "maximum number of iterations reached");
129 }
130
131 struct FunctionCallLattice {
132 llvm::SmallSet<std::string, 8> CalledFunctions;
133
operator ==__anon5856d3660111::FunctionCallLattice134 bool operator==(const FunctionCallLattice &Other) const {
135 return CalledFunctions == Other.CalledFunctions;
136 }
137
join__anon5856d3660111::FunctionCallLattice138 LatticeJoinEffect join(const FunctionCallLattice &Other) {
139 if (Other.CalledFunctions.empty())
140 return LatticeJoinEffect::Unchanged;
141 const size_t size_before = CalledFunctions.size();
142 CalledFunctions.insert(Other.CalledFunctions.begin(),
143 Other.CalledFunctions.end());
144 return CalledFunctions.size() == size_before ? LatticeJoinEffect::Unchanged
145 : LatticeJoinEffect::Changed;
146 }
147 };
148
operator <<(std::ostream & OS,const FunctionCallLattice & L)149 std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) {
150 std::string S;
151 llvm::raw_string_ostream ROS(S);
152 llvm::interleaveComma(L.CalledFunctions, ROS);
153 return OS << "{" << S << "}";
154 }
155
156 class FunctionCallAnalysis
157 : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> {
158 public:
FunctionCallAnalysis(ASTContext & Context)159 explicit FunctionCallAnalysis(ASTContext &Context)
160 : DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice>(Context) {}
161
initialElement()162 static FunctionCallLattice initialElement() { return {}; }
163
transfer(const Stmt * S,FunctionCallLattice & E,Environment & Env)164 void transfer(const Stmt *S, FunctionCallLattice &E, Environment &Env) {
165 if (auto *C = dyn_cast<CallExpr>(S)) {
166 if (auto *F = dyn_cast<FunctionDecl>(C->getCalleeDecl())) {
167 E.CalledFunctions.insert(F->getNameInfo().getAsString());
168 }
169 }
170 }
171 };
172
173 class NoreturnDestructorTest : public Test {
174 protected:
175 template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Expectations)176 void runDataflow(llvm::StringRef Code, Matcher Expectations) {
177 tooling::FileContentMappings FilesContents;
178 FilesContents.push_back(std::make_pair<std::string, std::string>(
179 "noreturn_destructor_test_defs.h", R"(
180 int foo();
181
182 class Fatal {
183 public:
184 ~Fatal() __attribute__((noreturn));
185 int bar();
186 int baz();
187 };
188
189 class NonFatal {
190 public:
191 ~NonFatal();
192 int bar();
193 };
194 )"));
195
196 ASSERT_THAT_ERROR(
197 test::checkDataflow<FunctionCallAnalysis>(
198 Code, "target",
199 [](ASTContext &C, Environment &) {
200 return FunctionCallAnalysis(C);
201 },
202 [&Expectations](
203 llvm::ArrayRef<std::pair<
204 std::string, DataflowAnalysisState<FunctionCallLattice>>>
205 Results,
206 ASTContext &) { EXPECT_THAT(Results, Expectations); },
207 {"-fsyntax-only", "-std=c++17"}, FilesContents),
208 llvm::Succeeded());
209 }
210 };
211
212 MATCHER_P(HoldsFunctionCallLattice, m,
213 ((negation ? "doesn't hold" : "holds") +
214 llvm::StringRef(" a lattice element that ") +
215 ::testing::DescribeMatcher<FunctionCallLattice>(m, negation))
216 .str()) {
217 return ExplainMatchResult(m, arg.Lattice, result_listener);
218 }
219
220 MATCHER_P(HasCalledFunctions, m, "") {
221 return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
222 }
223
TEST_F(NoreturnDestructorTest,ConditionalOperatorBothBranchesReturn)224 TEST_F(NoreturnDestructorTest, ConditionalOperatorBothBranchesReturn) {
225 std::string Code = R"(
226 #include "noreturn_destructor_test_defs.h"
227
228 void target(bool b) {
229 int value = b ? foo() : NonFatal().bar();
230 (void)0;
231 // [[p]]
232 }
233 )";
234 runDataflow(Code, UnorderedElementsAre(
235 Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
236 UnorderedElementsAre("foo", "bar"))))));
237 }
238
TEST_F(NoreturnDestructorTest,ConditionalOperatorLeftBranchReturns)239 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
240 std::string Code = R"(
241 #include "noreturn_destructor_test_defs.h"
242
243 void target(bool b) {
244 int value = b ? foo() : Fatal().bar();
245 (void)0;
246 // [[p]]
247 }
248 )";
249 runDataflow(Code, UnorderedElementsAre(
250 Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
251 UnorderedElementsAre("foo"))))));
252 }
253
TEST_F(NoreturnDestructorTest,ConditionalOperatorRightBranchReturns)254 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {
255 std::string Code = R"(
256 #include "noreturn_destructor_test_defs.h"
257
258 void target(bool b) {
259 int value = b ? Fatal().bar() : foo();
260 (void)0;
261 // [[p]]
262 }
263 )";
264 runDataflow(Code, UnorderedElementsAre(
265 Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
266 UnorderedElementsAre("foo"))))));
267 }
268
TEST_F(NoreturnDestructorTest,ConditionalOperatorNestedBranchesDoNotReturn)269 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchesDoNotReturn) {
270 std::string Code = R"(
271 #include "noreturn_destructor_test_defs.h"
272
273 void target(bool b1, bool b2) {
274 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz());
275 (void)0;
276 // [[p]]
277 }
278 )";
279 runDataflow(Code, IsEmpty());
280 // FIXME: Called functions at point `p` should contain "foo".
281 }
282
TEST_F(NoreturnDestructorTest,ConditionalOperatorNestedBranchReturns)283 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {
284 std::string Code = R"(
285 #include "noreturn_destructor_test_defs.h"
286
287 void target(bool b1, bool b2) {
288 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo());
289 (void)0;
290 // [[p]]
291 }
292 )";
293 runDataflow(Code, UnorderedElementsAre(
294 Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
295 UnorderedElementsAre("baz", "foo"))))));
296 // FIXME: Called functions at point `p` should contain only "foo".
297 }
298
299 // Models an analysis that uses flow conditions.
300 class SpecialBoolAnalysis
301 : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> {
302 public:
SpecialBoolAnalysis(ASTContext & Context)303 explicit SpecialBoolAnalysis(ASTContext &Context)
304 : DataflowAnalysis<SpecialBoolAnalysis, NoopLattice>(Context) {}
305
initialElement()306 static NoopLattice initialElement() { return {}; }
307
transfer(const Stmt * S,NoopLattice &,Environment & Env)308 void transfer(const Stmt *S, NoopLattice &, Environment &Env) {
309 auto SpecialBoolRecordDecl = recordDecl(hasName("SpecialBool"));
310 auto HasSpecialBoolType = hasType(SpecialBoolRecordDecl);
311
312 if (const auto *E = selectFirst<CXXConstructExpr>(
313 "call", match(cxxConstructExpr(HasSpecialBoolType).bind("call"), *S,
314 getASTContext()))) {
315 auto &ConstructorVal = *Env.createValue(E->getType());
316 ConstructorVal.setProperty("is_set", Env.getBoolLiteralValue(false));
317 Env.setValue(*Env.getStorageLocation(*E, SkipPast::None), ConstructorVal);
318 } else if (const auto *E = selectFirst<CXXMemberCallExpr>(
319 "call", match(cxxMemberCallExpr(callee(cxxMethodDecl(ofClass(
320 SpecialBoolRecordDecl))))
321 .bind("call"),
322 *S, getASTContext()))) {
323 auto *Object = E->getImplicitObjectArgument();
324 assert(Object != nullptr);
325
326 auto *ObjectLoc =
327 Env.getStorageLocation(*Object, SkipPast::ReferenceThenPointer);
328 assert(ObjectLoc != nullptr);
329
330 auto &ConstructorVal = *Env.createValue(Object->getType());
331 ConstructorVal.setProperty("is_set", Env.getBoolLiteralValue(true));
332 Env.setValue(*ObjectLoc, ConstructorVal);
333 }
334 }
335
compareEquivalent(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2)336 bool compareEquivalent(QualType Type, const Value &Val1,
337 const Environment &Env1, const Value &Val2,
338 const Environment &Env2) final {
339 const auto *Decl = Type->getAsCXXRecordDecl();
340 if (Decl == nullptr || Decl->getIdentifier() == nullptr ||
341 Decl->getName() != "SpecialBool")
342 return false;
343
344 auto *IsSet1 = cast_or_null<BoolValue>(Val1.getProperty("is_set"));
345 if (IsSet1 == nullptr)
346 return true;
347
348 auto *IsSet2 = cast_or_null<BoolValue>(Val2.getProperty("is_set"));
349 if (IsSet2 == nullptr)
350 return false;
351
352 return Env1.flowConditionImplies(*IsSet1) ==
353 Env2.flowConditionImplies(*IsSet2);
354 }
355
356 // Always returns `true` to accept the `MergedVal`.
merge(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & MergedVal,Environment & MergedEnv)357 bool merge(QualType Type, const Value &Val1, const Environment &Env1,
358 const Value &Val2, const Environment &Env2, Value &MergedVal,
359 Environment &MergedEnv) final {
360 const auto *Decl = Type->getAsCXXRecordDecl();
361 if (Decl == nullptr || Decl->getIdentifier() == nullptr ||
362 Decl->getName() != "SpecialBool")
363 return true;
364
365 auto *IsSet1 = cast_or_null<BoolValue>(Val1.getProperty("is_set"));
366 if (IsSet1 == nullptr)
367 return true;
368
369 auto *IsSet2 = cast_or_null<BoolValue>(Val2.getProperty("is_set"));
370 if (IsSet2 == nullptr)
371 return true;
372
373 auto &IsSet = MergedEnv.makeAtomicBoolValue();
374 MergedVal.setProperty("is_set", IsSet);
375 if (Env1.flowConditionImplies(*IsSet1) &&
376 Env2.flowConditionImplies(*IsSet2))
377 MergedEnv.addToFlowCondition(IsSet);
378
379 return true;
380 }
381 };
382
383 class JoinFlowConditionsTest : public Test {
384 protected:
385 template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Match)386 void runDataflow(llvm::StringRef Code, Matcher Match) {
387 ASSERT_THAT_ERROR(
388 test::checkDataflow<SpecialBoolAnalysis>(
389 Code, "target",
390 [](ASTContext &Context, Environment &Env) {
391 return SpecialBoolAnalysis(Context);
392 },
393 [&Match](
394 llvm::ArrayRef<
395 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
396 Results,
397 ASTContext &ASTCtx) { Match(Results, ASTCtx); },
398 {"-fsyntax-only", "-std=c++17"}),
399 llvm::Succeeded());
400 }
401 };
402
TEST_F(JoinFlowConditionsTest,JoinDistinctButProvablyEquivalentValues)403 TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) {
404 std::string Code = R"(
405 struct SpecialBool {
406 SpecialBool() = default;
407 void set();
408 };
409
410 void target(bool Cond) {
411 SpecialBool Foo;
412 /*[[p1]]*/
413 if (Cond) {
414 Foo.set();
415 /*[[p2]]*/
416 } else {
417 Foo.set();
418 /*[[p3]]*/
419 }
420 (void)0;
421 /*[[p4]]*/
422 }
423 )";
424 runDataflow(
425 Code, [](llvm::ArrayRef<
426 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
427 Results,
428 ASTContext &ASTCtx) {
429 ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
430 Pair("p2", _), Pair("p1", _)));
431 const Environment &Env1 = Results[3].second.Env;
432 const Environment &Env2 = Results[2].second.Env;
433 const Environment &Env3 = Results[1].second.Env;
434 const Environment &Env4 = Results[0].second.Env;
435
436 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
437 ASSERT_THAT(FooDecl, NotNull());
438
439 auto GetFooValue = [FooDecl](const Environment &Env) {
440 return cast<BoolValue>(
441 Env.getValue(*FooDecl, SkipPast::None)->getProperty("is_set"));
442 };
443
444 EXPECT_FALSE(Env1.flowConditionImplies(*GetFooValue(Env1)));
445 EXPECT_TRUE(Env2.flowConditionImplies(*GetFooValue(Env2)));
446 EXPECT_TRUE(Env3.flowConditionImplies(*GetFooValue(Env3)));
447 EXPECT_TRUE(Env4.flowConditionImplies(*GetFooValue(Env3)));
448 });
449 }
450
451 class OptionalIntAnalysis
452 : public DataflowAnalysis<OptionalIntAnalysis, NoopLattice> {
453 public:
OptionalIntAnalysis(ASTContext & Context,BoolValue & HasValueTop)454 explicit OptionalIntAnalysis(ASTContext &Context, BoolValue &HasValueTop)
455 : DataflowAnalysis<OptionalIntAnalysis, NoopLattice>(Context),
456 HasValueTop(HasValueTop) {}
457
initialElement()458 static NoopLattice initialElement() { return {}; }
459
transfer(const Stmt * S,NoopLattice &,Environment & Env)460 void transfer(const Stmt *S, NoopLattice &, Environment &Env) {
461 auto OptionalIntRecordDecl = recordDecl(hasName("OptionalInt"));
462 auto HasOptionalIntType = hasType(OptionalIntRecordDecl);
463
464 if (const auto *E = selectFirst<CXXConstructExpr>(
465 "call", match(cxxConstructExpr(HasOptionalIntType).bind("call"), *S,
466 getASTContext()))) {
467 auto &ConstructorVal = *Env.createValue(E->getType());
468 ConstructorVal.setProperty("has_value", Env.getBoolLiteralValue(false));
469 Env.setValue(*Env.getStorageLocation(*E, SkipPast::None), ConstructorVal);
470 } else if (const auto *E = selectFirst<CXXOperatorCallExpr>(
471 "call",
472 match(cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(
473 OptionalIntRecordDecl))))
474 .bind("call"),
475 *S, getASTContext()))) {
476 assert(E->getNumArgs() > 0);
477 auto *Object = E->getArg(0);
478 assert(Object != nullptr);
479
480 auto *ObjectLoc =
481 Env.getStorageLocation(*Object, SkipPast::ReferenceThenPointer);
482 assert(ObjectLoc != nullptr);
483
484 auto &ConstructorVal = *Env.createValue(Object->getType());
485 ConstructorVal.setProperty("has_value", Env.getBoolLiteralValue(true));
486 Env.setValue(*ObjectLoc, ConstructorVal);
487 }
488 }
489
compareEquivalent(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2)490 bool compareEquivalent(QualType Type, const Value &Val1,
491 const Environment &Env1, const Value &Val2,
492 const Environment &Env2) final {
493 // Nothing to say about a value that does not model an `OptionalInt`.
494 if (!Type->isRecordType() ||
495 Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
496 return false;
497
498 return Val1.getProperty("has_value") == Val2.getProperty("has_value");
499 }
500
merge(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & MergedVal,Environment & MergedEnv)501 bool merge(QualType Type, const Value &Val1, const Environment &Env1,
502 const Value &Val2, const Environment &Env2, Value &MergedVal,
503 Environment &MergedEnv) final {
504 // Nothing to say about a value that does not model an `OptionalInt`.
505 if (!Type->isRecordType() ||
506 Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
507 return false;
508
509 auto *HasValue1 = cast_or_null<BoolValue>(Val1.getProperty("has_value"));
510 if (HasValue1 == nullptr)
511 return false;
512
513 auto *HasValue2 = cast_or_null<BoolValue>(Val2.getProperty("has_value"));
514 if (HasValue2 == nullptr)
515 return false;
516
517 if (HasValue1 == HasValue2)
518 MergedVal.setProperty("has_value", *HasValue1);
519 else
520 MergedVal.setProperty("has_value", HasValueTop);
521 return true;
522 }
523
524 BoolValue &HasValueTop;
525 };
526
527 class WideningTest : public Test {
528 protected:
529 template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Match)530 void runDataflow(llvm::StringRef Code, Matcher Match) {
531 tooling::FileContentMappings FilesContents;
532 FilesContents.push_back(
533 std::make_pair<std::string, std::string>("widening_test_defs.h", R"(
534 struct OptionalInt {
535 OptionalInt() = default;
536 OptionalInt& operator=(int);
537 };
538 )"));
539 ASSERT_THAT_ERROR(
540 test::checkDataflow<OptionalIntAnalysis>(
541 Code, "target",
542 [this](ASTContext &Context, Environment &Env) {
543 assert(HasValueTop == nullptr);
544 HasValueTop =
545 &Env.takeOwnership(std::make_unique<AtomicBoolValue>());
546 return OptionalIntAnalysis(Context, *HasValueTop);
547 },
548 [&Match](
549 llvm::ArrayRef<
550 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
551 Results,
552 ASTContext &ASTCtx) { Match(Results, ASTCtx); },
553 {"-fsyntax-only", "-std=c++17"}, FilesContents),
554 llvm::Succeeded());
555 }
556
557 BoolValue *HasValueTop = nullptr;
558 };
559
TEST_F(WideningTest,JoinDistinctValuesWithDistinctProperties)560 TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
561 std::string Code = R"(
562 #include "widening_test_defs.h"
563
564 void target(bool Cond) {
565 OptionalInt Foo;
566 /*[[p1]]*/
567 if (Cond) {
568 Foo = 1;
569 /*[[p2]]*/
570 }
571 (void)0;
572 /*[[p3]]*/
573 }
574 )";
575 runDataflow(
576 Code,
577 [this](llvm::ArrayRef<
578 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
579 Results,
580 ASTContext &ASTCtx) {
581 ASSERT_THAT(Results,
582 ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
583 const Environment &Env1 = Results[2].second.Env;
584 const Environment &Env2 = Results[1].second.Env;
585 const Environment &Env3 = Results[0].second.Env;
586
587 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
588 ASSERT_THAT(FooDecl, NotNull());
589
590 auto GetFooValue = [FooDecl](const Environment &Env) {
591 return Env.getValue(*FooDecl, SkipPast::None);
592 };
593
594 EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
595 &Env1.getBoolLiteralValue(false));
596 EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
597 &Env2.getBoolLiteralValue(true));
598 EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value"), HasValueTop);
599 });
600 }
601
TEST_F(WideningTest,JoinDistinctValuesWithSameProperties)602 TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {
603 std::string Code = R"(
604 #include "widening_test_defs.h"
605
606 void target(bool Cond) {
607 OptionalInt Foo;
608 /*[[p1]]*/
609 if (Cond) {
610 Foo = 1;
611 /*[[p2]]*/
612 } else {
613 Foo = 2;
614 /*[[p3]]*/
615 }
616 (void)0;
617 /*[[p4]]*/
618 }
619 )";
620 runDataflow(Code,
621 [](llvm::ArrayRef<
622 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
623 Results,
624 ASTContext &ASTCtx) {
625 ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
626 Pair("p2", _), Pair("p1", _)));
627 const Environment &Env1 = Results[3].second.Env;
628 const Environment &Env2 = Results[2].second.Env;
629 const Environment &Env3 = Results[1].second.Env;
630 const Environment &Env4 = Results[0].second.Env;
631
632 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
633 ASSERT_THAT(FooDecl, NotNull());
634
635 auto GetFooValue = [FooDecl](const Environment &Env) {
636 return Env.getValue(*FooDecl, SkipPast::None);
637 };
638
639 EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
640 &Env1.getBoolLiteralValue(false));
641 EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
642 &Env2.getBoolLiteralValue(true));
643 EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value"),
644 &Env3.getBoolLiteralValue(true));
645 EXPECT_EQ(GetFooValue(Env4)->getProperty("has_value"),
646 &Env4.getBoolLiteralValue(true));
647 });
648 }
649
TEST_F(WideningTest,DistinctPointersToTheSameLocationAreEquivalent)650 TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) {
651 std::string Code = R"(
652 void target(int Foo, bool Cond) {
653 int *Bar = &Foo;
654 while (Cond) {
655 Bar = &Foo;
656 }
657 (void)0;
658 // [[p]]
659 }
660 )";
661 runDataflow(Code,
662 [](llvm::ArrayRef<
663 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
664 Results,
665 ASTContext &ASTCtx) {
666 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
667 const Environment &Env = Results[0].second.Env;
668
669 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
670 ASSERT_THAT(FooDecl, NotNull());
671
672 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
673 ASSERT_THAT(BarDecl, NotNull());
674
675 const auto *FooLoc = cast<ScalarStorageLocation>(
676 Env.getStorageLocation(*FooDecl, SkipPast::None));
677 const auto *BarVal =
678 cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
679 EXPECT_EQ(&BarVal->getPointeeLoc(), FooLoc);
680 });
681 }
682
TEST_F(WideningTest,DistinctValuesWithSamePropertiesAreEquivalent)683 TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {
684 std::string Code = R"(
685 #include "widening_test_defs.h"
686
687 void target(bool Cond) {
688 OptionalInt Foo;
689 Foo = 1;
690 while (Cond) {
691 Foo = 2;
692 }
693 (void)0;
694 /*[[p]]*/
695 }
696 )";
697 runDataflow(Code,
698 [](llvm::ArrayRef<
699 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
700 Results,
701 ASTContext &ASTCtx) {
702 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
703 const Environment &Env = Results[0].second.Env;
704
705 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
706 ASSERT_THAT(FooDecl, NotNull());
707
708 const auto *FooVal = Env.getValue(*FooDecl, SkipPast::None);
709 EXPECT_EQ(FooVal->getProperty("has_value"),
710 &Env.getBoolLiteralValue(true));
711 });
712 }
713
714 class FlowConditionTest : public Test {
715 protected:
716 template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Match)717 void runDataflow(llvm::StringRef Code, Matcher Match) {
718 ASSERT_THAT_ERROR(
719 test::checkDataflow<NoopAnalysis>(
720 Code, "target",
721 [](ASTContext &Context, Environment &Env) {
722 return NoopAnalysis(Context, true);
723 },
724 [&Match](
725 llvm::ArrayRef<
726 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
727 Results,
728 ASTContext &ASTCtx) { Match(Results, ASTCtx); },
729 {"-fsyntax-only", "-std=c++17"}),
730 llvm::Succeeded());
731 }
732 };
733
TEST_F(FlowConditionTest,IfStmtSingleVar)734 TEST_F(FlowConditionTest, IfStmtSingleVar) {
735 std::string Code = R"(
736 void target(bool Foo) {
737 if (Foo) {
738 (void)0;
739 /*[[p1]]*/
740 } else {
741 (void)1;
742 /*[[p2]]*/
743 }
744 }
745 )";
746 runDataflow(Code,
747 [](llvm::ArrayRef<
748 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
749 Results,
750 ASTContext &ASTCtx) {
751 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
752 ASSERT_THAT(FooDecl, NotNull());
753
754 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
755
756 const Environment &Env1 = Results[1].second.Env;
757 auto *FooVal1 =
758 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
759 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
760
761 const Environment &Env2 = Results[0].second.Env;
762 auto *FooVal2 =
763 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
764 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
765 });
766 }
767
TEST_F(FlowConditionTest,IfStmtSingleNegatedVar)768 TEST_F(FlowConditionTest, IfStmtSingleNegatedVar) {
769 std::string Code = R"(
770 void target(bool Foo) {
771 if (!Foo) {
772 (void)0;
773 /*[[p1]]*/
774 } else {
775 (void)1;
776 /*[[p2]]*/
777 }
778 }
779 )";
780 runDataflow(Code,
781 [](llvm::ArrayRef<
782 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
783 Results,
784 ASTContext &ASTCtx) {
785 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
786 ASSERT_THAT(FooDecl, NotNull());
787
788 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
789
790 const Environment &Env1 = Results[1].second.Env;
791 auto *FooVal1 =
792 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
793 EXPECT_FALSE(Env1.flowConditionImplies(*FooVal1));
794
795 const Environment &Env2 = Results[0].second.Env;
796 auto *FooVal2 =
797 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
798 EXPECT_TRUE(Env2.flowConditionImplies(*FooVal2));
799 });
800 }
801
TEST_F(FlowConditionTest,WhileStmt)802 TEST_F(FlowConditionTest, WhileStmt) {
803 std::string Code = R"(
804 void target(bool Foo) {
805 while (Foo) {
806 (void)0;
807 /*[[p]]*/
808 }
809 }
810 )";
811 runDataflow(
812 Code, [](llvm::ArrayRef<
813 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
814 Results,
815 ASTContext &ASTCtx) {
816 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
817 ASSERT_THAT(FooDecl, NotNull());
818
819 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
820 const Environment &Env = Results[0].second.Env;
821
822 auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
823 EXPECT_TRUE(Env.flowConditionImplies(*FooVal));
824 });
825 }
826
TEST_F(FlowConditionTest,Conjunction)827 TEST_F(FlowConditionTest, Conjunction) {
828 std::string Code = R"(
829 void target(bool Foo, bool Bar) {
830 if (Foo && Bar) {
831 (void)0;
832 /*[[p1]]*/
833 } else {
834 (void)1;
835 /*[[p2]]*/
836 }
837 }
838 )";
839 runDataflow(Code,
840 [](llvm::ArrayRef<
841 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
842 Results,
843 ASTContext &ASTCtx) {
844 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
845 ASSERT_THAT(FooDecl, NotNull());
846
847 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
848 ASSERT_THAT(BarDecl, NotNull());
849
850 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
851
852 const Environment &Env1 = Results[1].second.Env;
853 auto *FooVal1 =
854 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
855 auto *BarVal1 =
856 cast<BoolValue>(Env1.getValue(*BarDecl, SkipPast::None));
857 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
858 EXPECT_TRUE(Env1.flowConditionImplies(*BarVal1));
859
860 const Environment &Env2 = Results[0].second.Env;
861 auto *FooVal2 =
862 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
863 auto *BarVal2 =
864 cast<BoolValue>(Env2.getValue(*BarDecl, SkipPast::None));
865 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
866 EXPECT_FALSE(Env2.flowConditionImplies(*BarVal2));
867 });
868 }
869
TEST_F(FlowConditionTest,Disjunction)870 TEST_F(FlowConditionTest, Disjunction) {
871 std::string Code = R"(
872 void target(bool Foo, bool Bar) {
873 if (Foo || Bar) {
874 (void)0;
875 /*[[p1]]*/
876 } else {
877 (void)1;
878 /*[[p2]]*/
879 }
880 }
881 )";
882 runDataflow(Code,
883 [](llvm::ArrayRef<
884 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
885 Results,
886 ASTContext &ASTCtx) {
887 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
888 ASSERT_THAT(FooDecl, NotNull());
889
890 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
891 ASSERT_THAT(BarDecl, NotNull());
892
893 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
894
895 const Environment &Env1 = Results[1].second.Env;
896 auto *FooVal1 =
897 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
898 auto *BarVal1 =
899 cast<BoolValue>(Env1.getValue(*BarDecl, SkipPast::None));
900 EXPECT_FALSE(Env1.flowConditionImplies(*FooVal1));
901 EXPECT_FALSE(Env1.flowConditionImplies(*BarVal1));
902
903 const Environment &Env2 = Results[0].second.Env;
904 auto *FooVal2 =
905 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
906 auto *BarVal2 =
907 cast<BoolValue>(Env2.getValue(*BarDecl, SkipPast::None));
908 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
909 EXPECT_FALSE(Env2.flowConditionImplies(*BarVal2));
910 });
911 }
912
TEST_F(FlowConditionTest,NegatedConjunction)913 TEST_F(FlowConditionTest, NegatedConjunction) {
914 std::string Code = R"(
915 void target(bool Foo, bool Bar) {
916 if (!(Foo && Bar)) {
917 (void)0;
918 /*[[p1]]*/
919 } else {
920 (void)1;
921 /*[[p2]]*/
922 }
923 }
924 )";
925 runDataflow(Code,
926 [](llvm::ArrayRef<
927 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
928 Results,
929 ASTContext &ASTCtx) {
930 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
931 ASSERT_THAT(FooDecl, NotNull());
932
933 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
934 ASSERT_THAT(BarDecl, NotNull());
935
936 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
937
938 const Environment &Env1 = Results[1].second.Env;
939 auto *FooVal1 =
940 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
941 auto *BarVal1 =
942 cast<BoolValue>(Env1.getValue(*BarDecl, SkipPast::None));
943 EXPECT_FALSE(Env1.flowConditionImplies(*FooVal1));
944 EXPECT_FALSE(Env1.flowConditionImplies(*BarVal1));
945
946 const Environment &Env2 = Results[0].second.Env;
947 auto *FooVal2 =
948 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
949 auto *BarVal2 =
950 cast<BoolValue>(Env2.getValue(*BarDecl, SkipPast::None));
951 EXPECT_TRUE(Env2.flowConditionImplies(*FooVal2));
952 EXPECT_TRUE(Env2.flowConditionImplies(*BarVal2));
953 });
954 }
955
TEST_F(FlowConditionTest,DeMorgan)956 TEST_F(FlowConditionTest, DeMorgan) {
957 std::string Code = R"(
958 void target(bool Foo, bool Bar) {
959 if (!(!Foo || !Bar)) {
960 (void)0;
961 /*[[p1]]*/
962 } else {
963 (void)1;
964 /*[[p2]]*/
965 }
966 }
967 )";
968 runDataflow(Code,
969 [](llvm::ArrayRef<
970 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
971 Results,
972 ASTContext &ASTCtx) {
973 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
974 ASSERT_THAT(FooDecl, NotNull());
975
976 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
977 ASSERT_THAT(BarDecl, NotNull());
978
979 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
980
981 const Environment &Env1 = Results[1].second.Env;
982 auto *FooVal1 =
983 cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::None));
984 auto *BarVal1 =
985 cast<BoolValue>(Env1.getValue(*BarDecl, SkipPast::None));
986 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
987 EXPECT_TRUE(Env1.flowConditionImplies(*BarVal1));
988
989 const Environment &Env2 = Results[0].second.Env;
990 auto *FooVal2 =
991 cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::None));
992 auto *BarVal2 =
993 cast<BoolValue>(Env2.getValue(*BarDecl, SkipPast::None));
994 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
995 EXPECT_FALSE(Env2.flowConditionImplies(*BarVal2));
996 });
997 }
998
TEST_F(FlowConditionTest,Join)999 TEST_F(FlowConditionTest, Join) {
1000 std::string Code = R"(
1001 void target(bool Foo, bool Bar) {
1002 if (Bar) {
1003 if (!Foo)
1004 return;
1005 } else {
1006 if (!Foo)
1007 return;
1008 }
1009 (void)0;
1010 /*[[p]]*/
1011 }
1012 )";
1013 runDataflow(
1014 Code, [](llvm::ArrayRef<
1015 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1016 Results,
1017 ASTContext &ASTCtx) {
1018 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1019
1020 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1021 ASSERT_THAT(FooDecl, NotNull());
1022
1023 const Environment &Env = Results[0].second.Env;
1024 auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
1025 EXPECT_TRUE(Env.flowConditionImplies(*FooVal));
1026 });
1027 }
1028
1029 // Verifies that flow conditions are properly constructed even when the
1030 // condition is not meaningfully interpreted.
1031 //
1032 // Note: currently, arbitrary function calls are uninterpreted, so the test
1033 // exercises this case. If and when we change that, this test will not add to
1034 // coverage (although it may still test a valuable case).
TEST_F(FlowConditionTest,OpaqueFlowConditionMergesToOpaqueBool)1035 TEST_F(FlowConditionTest, OpaqueFlowConditionMergesToOpaqueBool) {
1036 std::string Code = R"(
1037 bool foo();
1038
1039 void target() {
1040 bool Bar = true;
1041 if (foo())
1042 Bar = false;
1043 (void)0;
1044 /*[[p]]*/
1045 }
1046 )";
1047 runDataflow(
1048 Code, [](llvm::ArrayRef<
1049 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1050 Results,
1051 ASTContext &ASTCtx) {
1052 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1053 const Environment &Env = Results[0].second.Env;
1054
1055 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1056 ASSERT_THAT(BarDecl, NotNull());
1057
1058 auto &BarVal =
1059 *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
1060
1061 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1062 });
1063 }
1064
1065 // Verifies that flow conditions are properly constructed even when the
1066 // condition is not meaningfully interpreted.
1067 //
1068 // Note: currently, fields with recursive type calls are uninterpreted (beneath
1069 // the first instance), so the test exercises this case. If and when we change
1070 // that, this test will not add to coverage (although it may still test a
1071 // valuable case).
TEST_F(FlowConditionTest,OpaqueFieldFlowConditionMergesToOpaqueBool)1072 TEST_F(FlowConditionTest, OpaqueFieldFlowConditionMergesToOpaqueBool) {
1073 std::string Code = R"(
1074 struct Rec {
1075 Rec* Next;
1076 };
1077
1078 struct Foo {
1079 Rec* X;
1080 };
1081
1082 void target(Foo F) {
1083 bool Bar = true;
1084 if (F.X->Next)
1085 Bar = false;
1086 (void)0;
1087 /*[[p]]*/
1088 }
1089 )";
1090 runDataflow(
1091 Code, [](llvm::ArrayRef<
1092 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1093 Results,
1094 ASTContext &ASTCtx) {
1095 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1096 const Environment &Env = Results[0].second.Env;
1097
1098 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1099 ASSERT_THAT(BarDecl, NotNull());
1100
1101 auto &BarVal =
1102 *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
1103
1104 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1105 });
1106 }
1107
1108 // Verifies that flow conditions are properly constructed even when the
1109 // condition is not meaningfully interpreted. Adds to above by nesting the
1110 // interestnig case inside a normal branch. This protects against degenerate
1111 // solutions which only test for empty flow conditions, for example.
TEST_F(FlowConditionTest,OpaqueFlowConditionInsideBranchMergesToOpaqueBool)1112 TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchMergesToOpaqueBool) {
1113 std::string Code = R"(
1114 bool foo();
1115
1116 void target(bool Cond) {
1117 bool Bar = true;
1118 if (Cond) {
1119 if (foo())
1120 Bar = false;
1121 (void)0;
1122 /*[[p]]*/
1123 }
1124 }
1125 )";
1126 runDataflow(
1127 Code, [](llvm::ArrayRef<
1128 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1129 Results,
1130 ASTContext &ASTCtx) {
1131 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1132 const Environment &Env = Results[0].second.Env;
1133
1134 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1135 ASSERT_THAT(BarDecl, NotNull());
1136
1137 auto &BarVal =
1138 *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
1139
1140 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1141 });
1142 }
1143
TEST_F(FlowConditionTest,PointerToBoolImplicitCast)1144 TEST_F(FlowConditionTest, PointerToBoolImplicitCast) {
1145 std::string Code = R"(
1146 void target(int *Ptr) {
1147 bool Foo = false;
1148 if (Ptr) {
1149 Foo = true;
1150 /*[[p1]]*/
1151 }
1152
1153 (void)0;
1154 /*[[p2]]*/
1155 }
1156 )";
1157 runDataflow(
1158 Code, [](llvm::ArrayRef<
1159 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1160 Results,
1161 ASTContext &ASTCtx) {
1162 ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
1163 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1164 ASSERT_THAT(FooDecl, NotNull());
1165
1166 const Environment &Env1 = Results[1].second.Env;
1167 auto &FooVal1 =
1168 *cast<BoolValue>(Env1.getValue(*FooDecl, SkipPast::Reference));
1169 EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
1170
1171 const Environment &Env2 = Results[0].second.Env;
1172 auto &FooVal2 =
1173 *cast<BoolValue>(Env2.getValue(*FooDecl, SkipPast::Reference));
1174 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
1175 });
1176 }
1177
1178 } // namespace
1179