1 //===- unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp ===//
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
7 //===----------------------------------------------------------------------===//
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/DebugSupport.h"
21 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
22 #include "clang/Analysis/FlowSensitive/Value.h"
23 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
24 #include "clang/Tooling/Tooling.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/SmallSet.h"
27 #include "llvm/ADT/StringMap.h"
28 #include "llvm/ADT/StringRef.h"
29 #include "llvm/Support/Error.h"
30 #include "llvm/Testing/ADT/StringMapEntry.h"
31 #include "llvm/Testing/Support/Error.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
44 using namespace clang
;
45 using namespace dataflow
;
47 using namespace ast_matchers
;
48 using llvm::IsStringMapEntry
;
49 using ::testing::DescribeMatcher
;
50 using ::testing::IsEmpty
;
51 using ::testing::NotNull
;
52 using ::testing::Test
;
53 using ::testing::UnorderedElementsAre
;
55 template <typename AnalysisT
>
56 llvm::Expected
<std::vector
<
57 std::optional
<DataflowAnalysisState
<typename
AnalysisT::Lattice
>>>>
58 runAnalysis(llvm::StringRef Code
, AnalysisT (*MakeAnalysis
)(ASTContext
&)) {
59 std::unique_ptr
<ASTUnit
> AST
=
60 tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
62 auto *Func
= selectFirst
<FunctionDecl
>(
63 "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"),
64 AST
->getASTContext()));
65 assert(Func
!= nullptr);
68 llvm::cantFail(ControlFlowContext::build(*Func
));
70 AnalysisT Analysis
= MakeAnalysis(AST
->getASTContext());
71 DataflowAnalysisContext
DACtx(std::make_unique
<WatchedLiteralsSolver
>());
72 Environment
Env(DACtx
);
74 return runDataflowAnalysis(CFCtx
, Analysis
, Env
);
77 TEST(DataflowAnalysisTest
, NoopAnalysis
) {
78 auto BlockStates
= llvm::cantFail(
79 runAnalysis
<NoopAnalysis
>("void target() {}", [](ASTContext
&C
) {
80 return NoopAnalysis(C
,
81 // Don't use builtin transfer function.
82 DataflowAnalysisOptions
{std::nullopt
});
84 EXPECT_EQ(BlockStates
.size(), 2u);
85 EXPECT_TRUE(BlockStates
[0].has_value());
86 EXPECT_TRUE(BlockStates
[1].has_value());
89 // Basic test that `diagnoseFunction` calls the Diagnoser function for the
90 // number of elements expected.
91 TEST(DataflowAnalysisTest
, DiagnoseFunctionDiagnoserCalledOnEachElement
) {
92 std::string Code
= R
"(void target() { int x = 0; ++x; })";
93 std::unique_ptr
<ASTUnit
> AST
=
94 tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
97 cast
<FunctionDecl
>(findValueDecl(AST
->getASTContext(), "target"));
98 auto Diagnoser
= [](const CFGElement
&Elt
, ASTContext
&,
99 const TransferStateForDiagnostics
<NoopLattice
> &) {
100 llvm::SmallVector
<std::string
> Diagnostics(1);
101 llvm::raw_string_ostream
OS(Diagnostics
.front());
102 Elt
.dumpToStream(OS
);
105 auto Result
= diagnoseFunction
<NoopAnalysis
, std::string
>(
106 *Func
, AST
->getASTContext(), Diagnoser
);
107 // `diagnoseFunction` provides no guarantees about the order in which elements
108 // are visited, so we use `UnorderedElementsAre`.
109 EXPECT_THAT_EXPECTED(Result
, llvm::HasValue(UnorderedElementsAre(
110 "0\n", "int x = 0;\n", "x\n", "++x\n",
111 " (Lifetime ends)\n")));
114 struct NonConvergingLattice
{
117 bool operator==(const NonConvergingLattice
&Other
) const {
118 return State
== Other
.State
;
121 LatticeJoinEffect
join(const NonConvergingLattice
&Other
) {
122 if (Other
.State
== 0)
123 return LatticeJoinEffect::Unchanged
;
124 State
+= Other
.State
;
125 return LatticeJoinEffect::Changed
;
129 class NonConvergingAnalysis
130 : public DataflowAnalysis
<NonConvergingAnalysis
, NonConvergingLattice
> {
132 explicit NonConvergingAnalysis(ASTContext
&Context
)
133 : DataflowAnalysis
<NonConvergingAnalysis
, NonConvergingLattice
>(
135 // Don't apply builtin transfer function.
136 DataflowAnalysisOptions
{std::nullopt
}) {}
138 static NonConvergingLattice
initialElement() { return {0}; }
140 void transfer(const CFGElement
&, NonConvergingLattice
&E
, Environment
&) {
145 TEST(DataflowAnalysisTest
, NonConvergingAnalysis
) {
146 std::string Code
= R
"(
151 auto Res
= runAnalysis
<NonConvergingAnalysis
>(
152 Code
, [](ASTContext
&C
) { return NonConvergingAnalysis(C
); });
153 EXPECT_EQ(llvm::toString(Res
.takeError()),
154 "maximum number of iterations reached");
157 // Regression test for joins of bool-typed lvalue expressions. The first loop
158 // results in two passes through the code that follows. Each pass results in a
159 // different `StorageLocation` for the pointee of `v`. Then, the second loop
160 // causes a join at the loop head where the two environments map expresssion
161 // `*v` to different `StorageLocation`s.
163 // An earlier version crashed for this condition (for boolean-typed lvalues), so
164 // this test only verifies that the analysis runs successfully, without
165 // examining any details of the results.
166 TEST(DataflowAnalysisTest
, JoinBoolLValues
) {
167 std::string Code
= R
"(
169 for (int x = 1; x; x = 0)
173 for (int x = 1; x; x = 0)
178 runAnalysis
<NoopAnalysis
>(Code
,
180 auto EnableBuiltIns
= DataflowAnalysisOptions
{
181 DataflowAnalysisContext::Options
{}};
182 return NoopAnalysis(C
, EnableBuiltIns
);
188 struct FunctionCallLattice
{
189 using FunctionSet
= llvm::SmallSet
<std::string
, 8>;
190 FunctionSet CalledFunctions
;
192 bool operator==(const FunctionCallLattice
&Other
) const {
193 return CalledFunctions
== Other
.CalledFunctions
;
196 LatticeJoinEffect
join(const FunctionCallLattice
&Other
) {
197 if (Other
.CalledFunctions
.empty())
198 return LatticeJoinEffect::Unchanged
;
199 const size_t size_before
= CalledFunctions
.size();
200 CalledFunctions
.insert(Other
.CalledFunctions
.begin(),
201 Other
.CalledFunctions
.end());
202 return CalledFunctions
.size() == size_before
? LatticeJoinEffect::Unchanged
203 : LatticeJoinEffect::Changed
;
207 std::ostream
&operator<<(std::ostream
&OS
, const FunctionCallLattice
&L
) {
209 llvm::raw_string_ostream
ROS(S
);
210 llvm::interleaveComma(L
.CalledFunctions
, ROS
);
211 return OS
<< "{" << S
<< "}";
214 class FunctionCallAnalysis
215 : public DataflowAnalysis
<FunctionCallAnalysis
, FunctionCallLattice
> {
217 explicit FunctionCallAnalysis(ASTContext
&Context
)
218 : DataflowAnalysis
<FunctionCallAnalysis
, FunctionCallLattice
>(Context
) {}
220 static FunctionCallLattice
initialElement() { return {}; }
222 void transfer(const CFGElement
&Elt
, FunctionCallLattice
&E
, Environment
&) {
223 auto CS
= Elt
.getAs
<CFGStmt
>();
226 const auto *S
= CS
->getStmt();
227 if (auto *C
= dyn_cast
<CallExpr
>(S
)) {
228 if (auto *F
= dyn_cast
<FunctionDecl
>(C
->getCalleeDecl())) {
229 E
.CalledFunctions
.insert(F
->getNameInfo().getAsString());
235 class NoreturnDestructorTest
: public Test
{
237 template <typename Matcher
>
238 void runDataflow(llvm::StringRef Code
, Matcher Expectations
) {
239 tooling::FileContentMappings FilesContents
;
240 FilesContents
.push_back(std::make_pair
<std::string
, std::string
>(
241 "noreturn_destructor_test_defs.h", R
"(
246 ~Fatal() __attribute__((noreturn));
259 test::checkDataflow
<FunctionCallAnalysis
>(
260 AnalysisInputs
<FunctionCallAnalysis
>(
261 Code
, ast_matchers::hasName("target"),
262 [](ASTContext
&C
, Environment
&) {
263 return FunctionCallAnalysis(C
);
265 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
266 .withASTBuildVirtualMappedFiles(std::move(FilesContents
)),
269 const llvm::StringMap
<
270 DataflowAnalysisState
<FunctionCallLattice
>> &Results
,
271 const AnalysisOutputs
&) {
272 EXPECT_THAT(Results
, Expectations
);
278 MATCHER_P(HoldsFunctionCallLattice
, m
,
279 ((negation
? "doesn't hold" : "holds") +
280 llvm::StringRef(" a lattice element that ") +
281 DescribeMatcher
<FunctionCallLattice
>(m
))
283 return ExplainMatchResult(m
, arg
.Lattice
, result_listener
);
286 MATCHER_P(HasCalledFunctions
, m
,
287 ((negation
? "doesn't hold" : "holds") +
288 llvm::StringRef(" a set of called functions that ") +
289 DescribeMatcher
<FunctionCallLattice::FunctionSet
>(m
))
291 return ExplainMatchResult(m
, arg
.CalledFunctions
, result_listener
);
294 TEST_F(NoreturnDestructorTest
, ConditionalOperatorBothBranchesReturn
) {
295 std::string Code
= R
"(
296 #include "noreturn_destructor_test_defs
.h
"
298 void target(bool b) {
299 int value = b ? foo() : NonFatal().bar();
304 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
305 "p", HoldsFunctionCallLattice(HasCalledFunctions(
306 UnorderedElementsAre("foo", "bar"))))));
309 TEST_F(NoreturnDestructorTest
, ConditionalOperatorLeftBranchReturns
) {
310 std::string Code
= R
"(
311 #include "noreturn_destructor_test_defs
.h
"
313 void target(bool b) {
314 int value = b ? foo() : Fatal().bar();
319 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
320 "p", HoldsFunctionCallLattice(HasCalledFunctions(
321 UnorderedElementsAre("foo"))))));
324 TEST_F(NoreturnDestructorTest
,
325 ConditionalOperatorConstantCondition_LeftBranchReturns
) {
326 std::string Code
= R
"(
327 #include "noreturn_destructor_test_defs
.h
"
330 int value = true ? foo() : Fatal().bar();
335 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
336 "p", HoldsFunctionCallLattice(HasCalledFunctions(
337 UnorderedElementsAre("foo"))))));
340 TEST_F(NoreturnDestructorTest
, ConditionalOperatorRightBranchReturns
) {
341 std::string Code
= R
"(
342 #include "noreturn_destructor_test_defs
.h
"
344 void target(bool b) {
345 int value = b ? Fatal().bar() : foo();
350 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
351 "p", HoldsFunctionCallLattice(HasCalledFunctions(
352 UnorderedElementsAre("foo"))))));
355 TEST_F(NoreturnDestructorTest
,
356 ConditionalOperatorConstantCondition_RightBranchReturns
) {
357 std::string Code
= R
"(
358 #include "noreturn_destructor_test_defs
.h
"
361 int value = false ? Fatal().bar() : foo();
366 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
367 "p", HoldsFunctionCallLattice(HasCalledFunctions(
368 UnorderedElementsAre("foo"))))));
371 TEST_F(NoreturnDestructorTest
, ConditionalOperatorNestedBranchesDoNotReturn
) {
372 std::string Code
= R
"(
373 #include "noreturn_destructor_test_defs
.h
"
375 void target(bool b1, bool b2) {
376 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz());
381 runDataflow(Code
, IsEmpty());
382 // FIXME: Called functions at point `p` should contain "foo".
385 TEST_F(NoreturnDestructorTest
, ConditionalOperatorNestedBranchReturns
) {
386 std::string Code
= R
"(
387 #include "noreturn_destructor_test_defs
.h
"
389 void target(bool b1, bool b2) {
390 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo());
395 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
396 "p", HoldsFunctionCallLattice(HasCalledFunctions(
397 UnorderedElementsAre("baz", "foo"))))));
398 // FIXME: Called functions at point `p` should contain only "foo".
401 // Models an analysis that uses flow conditions.
402 class SpecialBoolAnalysis final
403 : public DataflowAnalysis
<SpecialBoolAnalysis
, NoopLattice
> {
405 explicit SpecialBoolAnalysis(ASTContext
&Context
)
406 : DataflowAnalysis
<SpecialBoolAnalysis
, NoopLattice
>(Context
) {}
408 static NoopLattice
initialElement() { return {}; }
410 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
411 auto CS
= Elt
.getAs
<CFGStmt
>();
414 const auto *S
= CS
->getStmt();
415 auto SpecialBoolRecordDecl
= recordDecl(hasName("SpecialBool"));
416 auto HasSpecialBoolType
= hasType(SpecialBoolRecordDecl
);
418 if (const auto *E
= selectFirst
<CXXConstructExpr
>(
419 "call", match(cxxConstructExpr(HasSpecialBoolType
).bind("call"), *S
,
421 cast
<RecordValue
>(Env
.getValue(*E
))
422 ->setProperty("is_set", Env
.getBoolLiteralValue(false));
423 } else if (const auto *E
= selectFirst
<CXXMemberCallExpr
>(
424 "call", match(cxxMemberCallExpr(callee(cxxMethodDecl(ofClass(
425 SpecialBoolRecordDecl
))))
427 *S
, getASTContext()))) {
429 *cast
<RecordStorageLocation
>(getImplicitObjectLocation(*E
, Env
));
431 refreshRecordValue(ObjectLoc
, Env
)
432 .setProperty("is_set", Env
.getBoolLiteralValue(true));
436 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
437 const Environment
&Env1
, const Value
&Val2
,
438 const Environment
&Env2
) override
{
439 const auto *Decl
= Type
->getAsCXXRecordDecl();
440 if (Decl
== nullptr || Decl
->getIdentifier() == nullptr ||
441 Decl
->getName() != "SpecialBool")
442 return ComparisonResult::Unknown
;
444 auto *IsSet1
= cast_or_null
<BoolValue
>(Val1
.getProperty("is_set"));
445 auto *IsSet2
= cast_or_null
<BoolValue
>(Val2
.getProperty("is_set"));
446 if (IsSet1
== nullptr)
447 return IsSet2
== nullptr ? ComparisonResult::Same
448 : ComparisonResult::Different
;
450 if (IsSet2
== nullptr)
451 return ComparisonResult::Different
;
453 return Env1
.proves(IsSet1
->formula()) == Env2
.proves(IsSet2
->formula())
454 ? ComparisonResult::Same
455 : ComparisonResult::Different
;
458 // Always returns `true` to accept the `MergedVal`.
459 bool merge(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
460 const Value
&Val2
, const Environment
&Env2
, Value
&MergedVal
,
461 Environment
&MergedEnv
) override
{
462 const auto *Decl
= Type
->getAsCXXRecordDecl();
463 if (Decl
== nullptr || Decl
->getIdentifier() == nullptr ||
464 Decl
->getName() != "SpecialBool")
467 auto *IsSet1
= cast_or_null
<BoolValue
>(Val1
.getProperty("is_set"));
468 if (IsSet1
== nullptr)
471 auto *IsSet2
= cast_or_null
<BoolValue
>(Val2
.getProperty("is_set"));
472 if (IsSet2
== nullptr)
475 auto &IsSet
= MergedEnv
.makeAtomicBoolValue();
476 MergedVal
.setProperty("is_set", IsSet
);
477 if (Env1
.proves(IsSet1
->formula()) && Env2
.proves(IsSet2
->formula()))
478 MergedEnv
.assume(IsSet
.formula());
484 class JoinFlowConditionsTest
: public Test
{
486 template <typename Matcher
>
487 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
489 test::checkDataflow
<SpecialBoolAnalysis
>(
490 AnalysisInputs
<SpecialBoolAnalysis
>(
491 Code
, ast_matchers::hasName("target"),
492 [](ASTContext
&Context
, Environment
&Env
) {
493 return SpecialBoolAnalysis(Context
);
495 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
496 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
497 DataflowAnalysisState
<NoopLattice
>>
499 const AnalysisOutputs
500 &AO
) { Match(Results
, AO
.ASTCtx
); }),
505 TEST_F(JoinFlowConditionsTest
, JoinDistinctButProvablyEquivalentValues
) {
506 std::string Code
= R
"(
508 SpecialBool() = default;
512 void target(bool Cond) {
528 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
529 ASTContext
&ASTCtx
) {
530 ASSERT_THAT(Results
.keys(),
531 UnorderedElementsAre("p1", "p2", "p3", "p4"));
532 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
533 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
534 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
535 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
537 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
538 ASSERT_THAT(FooDecl
, NotNull());
540 auto GetFoo
= [FooDecl
](const Environment
&Env
) -> const Formula
& {
541 return cast
<BoolValue
>(Env
.getValue(*FooDecl
)->getProperty("is_set"))
545 EXPECT_FALSE(Env1
.proves(GetFoo(Env1
)));
546 EXPECT_TRUE(Env2
.proves(GetFoo(Env2
)));
547 EXPECT_TRUE(Env3
.proves(GetFoo(Env3
)));
548 EXPECT_TRUE(Env4
.proves(GetFoo(Env4
)));
552 class OptionalIntAnalysis final
553 : public DataflowAnalysis
<OptionalIntAnalysis
, NoopLattice
> {
555 explicit OptionalIntAnalysis(ASTContext
&Context
)
556 : DataflowAnalysis
<OptionalIntAnalysis
, NoopLattice
>(Context
) {}
558 static NoopLattice
initialElement() { return {}; }
560 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
561 auto CS
= Elt
.getAs
<CFGStmt
>();
564 const Stmt
*S
= CS
->getStmt();
565 auto OptionalIntRecordDecl
= recordDecl(hasName("OptionalInt"));
566 auto HasOptionalIntType
= hasType(OptionalIntRecordDecl
);
568 SmallVector
<BoundNodes
, 1> Matches
= match(
569 stmt(anyOf(cxxConstructExpr(HasOptionalIntType
).bind("construct"),
571 callee(cxxMethodDecl(ofClass(OptionalIntRecordDecl
))))
573 *S
, getASTContext());
574 if (const auto *E
= selectFirst
<CXXConstructExpr
>(
575 "construct", Matches
)) {
576 cast
<RecordValue
>(Env
.getValue(*E
))
577 ->setProperty("has_value", Env
.getBoolLiteralValue(false));
578 } else if (const auto *E
=
579 selectFirst
<CXXOperatorCallExpr
>("operator", Matches
)) {
580 assert(E
->getNumArgs() > 0);
581 auto *Object
= E
->getArg(0);
582 assert(Object
!= nullptr);
584 refreshRecordValue(*Object
, Env
)
585 .setProperty("has_value", Env
.getBoolLiteralValue(true));
589 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
590 const Environment
&Env1
, const Value
&Val2
,
591 const Environment
&Env2
) override
{
592 // Nothing to say about a value that does not model an `OptionalInt`.
593 if (!Type
->isRecordType() ||
594 Type
->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
595 return ComparisonResult::Unknown
;
597 auto *Prop1
= Val1
.getProperty("has_value");
598 auto *Prop2
= Val2
.getProperty("has_value");
599 assert(Prop1
!= nullptr && Prop2
!= nullptr);
600 return areEquivalentValues(*Prop1
, *Prop2
) ? ComparisonResult::Same
601 : ComparisonResult::Different
;
604 bool merge(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
605 const Value
&Val2
, const Environment
&Env2
, Value
&MergedVal
,
606 Environment
&MergedEnv
) override
{
607 // Nothing to say about a value that does not model an `OptionalInt`.
608 if (!Type
->isRecordType() ||
609 Type
->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
612 auto *HasValue1
= cast_or_null
<BoolValue
>(Val1
.getProperty("has_value"));
613 if (HasValue1
== nullptr)
616 auto *HasValue2
= cast_or_null
<BoolValue
>(Val2
.getProperty("has_value"));
617 if (HasValue2
== nullptr)
620 if (HasValue1
== HasValue2
)
621 MergedVal
.setProperty("has_value", *HasValue1
);
623 MergedVal
.setProperty("has_value", MergedEnv
.makeTopBoolValue());
628 class WideningTest
: public Test
{
630 template <typename Matcher
>
631 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
632 tooling::FileContentMappings FilesContents
;
633 FilesContents
.push_back(
634 std::make_pair
<std::string
, std::string
>("widening_test_defs.h", R
"(
636 OptionalInt() = default;
637 OptionalInt& operator=(int);
641 checkDataflow
<OptionalIntAnalysis
>(
642 AnalysisInputs
<OptionalIntAnalysis
>(
643 Code
, ast_matchers::hasName("target"),
644 [](ASTContext
&Context
, Environment
&Env
) {
645 return OptionalIntAnalysis(Context
);
647 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
648 .withASTBuildVirtualMappedFiles(std::move(FilesContents
)),
649 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
650 DataflowAnalysisState
<NoopLattice
>>
652 const AnalysisOutputs
653 &AO
) { Match(Results
, AO
.ASTCtx
); }),
658 TEST_F(WideningTest
, JoinDistinctValuesWithDistinctProperties
) {
659 std::string Code
= R
"(
660 #include "widening_test_defs
.h
"
662 void target(bool Cond) {
675 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
676 ASTContext
&ASTCtx
) {
677 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2", "p3"));
678 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
679 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
680 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
682 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
683 ASSERT_THAT(FooDecl
, NotNull());
685 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
686 return Env
.getValue(*FooDecl
);
689 EXPECT_EQ(GetFooValue(Env1
)->getProperty("has_value"),
690 &Env1
.getBoolLiteralValue(false));
691 EXPECT_EQ(GetFooValue(Env2
)->getProperty("has_value"),
692 &Env2
.getBoolLiteralValue(true));
694 isa
<TopBoolValue
>(GetFooValue(Env3
)->getProperty("has_value")));
698 TEST_F(WideningTest
, JoinDistinctValuesWithSameProperties
) {
699 std::string Code
= R
"(
700 #include "widening_test_defs
.h
"
702 void target(bool Cond) {
718 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
719 ASTContext
&ASTCtx
) {
720 ASSERT_THAT(Results
.keys(),
721 UnorderedElementsAre("p1", "p2", "p3", "p4"));
722 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
723 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
724 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
725 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
727 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
728 ASSERT_THAT(FooDecl
, NotNull());
730 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
731 return Env
.getValue(*FooDecl
);
734 EXPECT_EQ(GetFooValue(Env1
)->getProperty("has_value"),
735 &Env1
.getBoolLiteralValue(false));
736 EXPECT_EQ(GetFooValue(Env2
)->getProperty("has_value"),
737 &Env2
.getBoolLiteralValue(true));
738 EXPECT_EQ(GetFooValue(Env3
)->getProperty("has_value"),
739 &Env3
.getBoolLiteralValue(true));
740 EXPECT_EQ(GetFooValue(Env4
)->getProperty("has_value"),
741 &Env4
.getBoolLiteralValue(true));
745 TEST_F(WideningTest
, DistinctPointersToTheSameLocationAreEquivalent
) {
746 std::string Code
= R
"(
747 void target(int Foo, bool Cond) {
758 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
759 ASTContext
&ASTCtx
) {
760 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
761 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
763 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
764 ASSERT_THAT(FooDecl
, NotNull());
766 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
767 ASSERT_THAT(BarDecl
, NotNull());
770 cast
<ScalarStorageLocation
>(Env
.getStorageLocation(*FooDecl
));
771 const auto *BarVal
= cast
<PointerValue
>(Env
.getValue(*BarDecl
));
772 EXPECT_EQ(&BarVal
->getPointeeLoc(), FooLoc
);
776 TEST_F(WideningTest
, DistinctValuesWithSamePropertiesAreEquivalent
) {
777 std::string Code
= R
"(
778 #include "widening_test_defs
.h
"
780 void target(bool Cond) {
792 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
793 ASTContext
&ASTCtx
) {
794 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
795 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
797 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
798 ASSERT_THAT(FooDecl
, NotNull());
800 const auto *FooVal
= Env
.getValue(*FooDecl
);
801 EXPECT_EQ(FooVal
->getProperty("has_value"),
802 &Env
.getBoolLiteralValue(true));
806 class FlowConditionTest
: public Test
{
808 template <typename Matcher
>
809 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
811 checkDataflow
<NoopAnalysis
>(
812 AnalysisInputs
<NoopAnalysis
>(
813 Code
, ast_matchers::hasName("target"),
814 [](ASTContext
&Context
, Environment
&Env
) {
815 return NoopAnalysis(Context
);
817 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
818 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
819 DataflowAnalysisState
<NoopLattice
>>
821 const AnalysisOutputs
822 &AO
) { Match(Results
, AO
.ASTCtx
); }),
827 TEST_F(FlowConditionTest
, IfStmtSingleVar
) {
828 std::string Code
= R
"(
829 void target(bool Foo) {
841 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
842 ASTContext
&ASTCtx
) {
843 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
844 ASSERT_THAT(FooDecl
, NotNull());
846 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
848 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
849 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
850 EXPECT_TRUE(Env1
.proves(FooVal1
));
852 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
853 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
854 EXPECT_FALSE(Env2
.proves(FooVal2
));
858 TEST_F(FlowConditionTest
, IfStmtSingleNegatedVar
) {
859 std::string Code
= R
"(
860 void target(bool Foo) {
872 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
873 ASTContext
&ASTCtx
) {
874 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
875 ASSERT_THAT(FooDecl
, NotNull());
877 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
879 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
880 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
881 EXPECT_FALSE(Env1
.proves(FooVal1
));
883 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
884 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
885 EXPECT_TRUE(Env2
.proves(FooVal2
));
889 TEST_F(FlowConditionTest
, WhileStmt
) {
890 std::string Code
= R
"(
891 void target(bool Foo) {
900 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
901 ASTContext
&ASTCtx
) {
902 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
903 ASSERT_THAT(FooDecl
, NotNull());
905 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
906 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
908 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
909 EXPECT_TRUE(Env
.proves(FooVal
));
913 TEST_F(FlowConditionTest
, WhileStmtWithAssignmentInCondition
) {
914 std::string Code
= R
"(
915 void target(bool Foo) {
916 // This test checks whether the analysis preserves the connection between
917 // the value of `Foo` and the assignment expression, despite widening.
918 // The equality operator generates a fresh boolean variable on each
919 // interpretation, which forces use of widening.
920 while ((Foo = (3 == 4))) {
928 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
929 ASTContext
&ASTCtx
) {
930 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
931 auto &FooVal
= getValueForDecl
<BoolValue
>(ASTCtx
, Env
, "Foo").formula();
932 EXPECT_TRUE(Env
.proves(FooVal
));
936 TEST_F(FlowConditionTest
, Conjunction
) {
937 std::string Code
= R
"(
938 void target(bool Foo, bool Bar) {
948 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
950 ASTContext
&ASTCtx
) {
951 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
952 ASSERT_THAT(FooDecl
, NotNull());
954 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
955 ASSERT_THAT(BarDecl
, NotNull());
957 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
959 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
960 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
961 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
962 EXPECT_TRUE(Env1
.proves(FooVal1
));
963 EXPECT_TRUE(Env1
.proves(BarVal1
));
965 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
966 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
967 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
968 EXPECT_FALSE(Env2
.proves(FooVal2
));
969 EXPECT_FALSE(Env2
.proves(BarVal2
));
973 TEST_F(FlowConditionTest
, Disjunction
) {
974 std::string Code
= R
"(
975 void target(bool Foo, bool Bar) {
985 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
987 ASTContext
&ASTCtx
) {
988 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
989 ASSERT_THAT(FooDecl
, NotNull());
991 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
992 ASSERT_THAT(BarDecl
, NotNull());
994 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
996 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
997 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
998 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
999 EXPECT_FALSE(Env1
.proves(FooVal1
));
1000 EXPECT_FALSE(Env1
.proves(BarVal1
));
1002 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1003 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1004 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1005 EXPECT_FALSE(Env2
.proves(FooVal2
));
1006 EXPECT_FALSE(Env2
.proves(BarVal2
));
1010 TEST_F(FlowConditionTest
, NegatedConjunction
) {
1011 std::string Code
= R
"(
1012 void target(bool Foo, bool Bar) {
1013 if (!(Foo && Bar)) {
1022 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1024 ASTContext
&ASTCtx
) {
1025 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1026 ASSERT_THAT(FooDecl
, NotNull());
1028 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1029 ASSERT_THAT(BarDecl
, NotNull());
1031 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1033 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1034 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1035 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1036 EXPECT_FALSE(Env1
.proves(FooVal1
));
1037 EXPECT_FALSE(Env1
.proves(BarVal1
));
1039 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1040 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1041 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1042 EXPECT_TRUE(Env2
.proves(FooVal2
));
1043 EXPECT_TRUE(Env2
.proves(BarVal2
));
1047 TEST_F(FlowConditionTest
, DeMorgan
) {
1048 std::string Code
= R
"(
1049 void target(bool Foo, bool Bar) {
1050 if (!(!Foo || !Bar)) {
1059 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1061 ASTContext
&ASTCtx
) {
1062 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1063 ASSERT_THAT(FooDecl
, NotNull());
1065 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1066 ASSERT_THAT(BarDecl
, NotNull());
1068 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1070 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1071 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1072 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1073 EXPECT_TRUE(Env1
.proves(FooVal1
));
1074 EXPECT_TRUE(Env1
.proves(BarVal1
));
1076 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1077 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1078 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1079 EXPECT_FALSE(Env2
.proves(FooVal2
));
1080 EXPECT_FALSE(Env2
.proves(BarVal2
));
1084 TEST_F(FlowConditionTest
, Join
) {
1085 std::string Code
= R
"(
1086 void target(bool Foo, bool Bar) {
1100 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1101 ASTContext
&ASTCtx
) {
1102 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1104 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1105 ASSERT_THAT(FooDecl
, NotNull());
1107 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1108 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
1109 EXPECT_TRUE(Env
.proves(FooVal
));
1113 // Verifies that flow conditions are properly constructed even when the
1114 // condition is not meaningfully interpreted.
1116 // Note: currently, arbitrary function calls are uninterpreted, so the test
1117 // exercises this case. If and when we change that, this test will not add to
1118 // coverage (although it may still test a valuable case).
1119 TEST_F(FlowConditionTest
, OpaqueFlowConditionMergesToOpaqueBool
) {
1120 std::string Code
= R
"(
1133 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1134 ASTContext
&ASTCtx
) {
1135 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1136 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1138 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1139 ASSERT_THAT(BarDecl
, NotNull());
1141 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1143 EXPECT_FALSE(Env
.proves(BarVal
));
1147 // Verifies that flow conditions are properly constructed even when the
1148 // condition is not meaningfully interpreted.
1150 // Note: currently, fields with recursive type calls are uninterpreted (beneath
1151 // the first instance), so the test exercises this case. If and when we change
1152 // that, this test will not add to coverage (although it may still test a
1154 TEST_F(FlowConditionTest
, OpaqueFieldFlowConditionMergesToOpaqueBool
) {
1155 std::string Code
= R
"(
1164 void target(Foo F) {
1174 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1175 ASTContext
&ASTCtx
) {
1176 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1177 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1179 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1180 ASSERT_THAT(BarDecl
, NotNull());
1182 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1184 EXPECT_FALSE(Env
.proves(BarVal
));
1188 // Verifies that flow conditions are properly constructed even when the
1189 // condition is not meaningfully interpreted. Adds to above by nesting the
1190 // interestnig case inside a normal branch. This protects against degenerate
1191 // solutions which only test for empty flow conditions, for example.
1192 TEST_F(FlowConditionTest
, OpaqueFlowConditionInsideBranchMergesToOpaqueBool
) {
1193 std::string Code
= R
"(
1196 void target(bool Cond) {
1208 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1209 ASTContext
&ASTCtx
) {
1210 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1211 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1213 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1214 ASSERT_THAT(BarDecl
, NotNull());
1216 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1218 EXPECT_FALSE(Env
.proves(BarVal
));
1222 TEST_F(FlowConditionTest
, PointerToBoolImplicitCast
) {
1223 std::string Code
= R
"(
1224 void target(int *Ptr) {
1237 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1238 ASTContext
&ASTCtx
) {
1239 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1241 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1242 ASSERT_THAT(FooDecl
, NotNull());
1244 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1245 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1246 EXPECT_TRUE(Env1
.proves(FooVal1
));
1248 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1249 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1250 EXPECT_FALSE(Env2
.proves(FooVal2
));
1254 class TopAnalysis final
: public DataflowAnalysis
<TopAnalysis
, NoopLattice
> {
1256 explicit TopAnalysis(ASTContext
&Context
)
1257 : DataflowAnalysis
<TopAnalysis
, NoopLattice
>(Context
) {}
1259 static NoopLattice
initialElement() { return {}; }
1261 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
1262 auto CS
= Elt
.getAs
<CFGStmt
>();
1265 const Stmt
*S
= CS
->getStmt();
1266 SmallVector
<BoundNodes
, 1> Matches
=
1267 match(callExpr(callee(functionDecl(hasName("makeTop")))).bind("top"),
1268 *S
, getASTContext());
1269 if (const auto *E
= selectFirst
<CallExpr
>("top", Matches
)) {
1270 Env
.setValue(*E
, Env
.makeTopBoolValue());
1274 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
1275 const Environment
&Env1
, const Value
&Val2
,
1276 const Environment
&Env2
) override
{
1277 // Changes to a sound approximation, which allows us to test whether we can
1278 // (soundly) converge for some loops.
1279 return ComparisonResult::Unknown
;
1283 class TopTest
: public Test
{
1285 template <typename Matcher
>
1286 void runDataflow(llvm::StringRef Code
, Matcher VerifyResults
) {
1288 checkDataflow
<TopAnalysis
>(
1289 AnalysisInputs
<TopAnalysis
>(
1290 Code
, ast_matchers::hasName("target"),
1291 [](ASTContext
&Context
, Environment
&Env
) {
1292 return TopAnalysis(Context
);
1294 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1300 // Tests that when Top is unused it remains Top.
1301 TEST_F(TopTest
, UnusedTopInitializer
) {
1302 std::string Code
= R
"(
1306 bool Foo = makeTop();
1314 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1315 const AnalysisOutputs
&AO
) {
1316 ASSERT_THAT(Results
.keys(),
1317 UnorderedElementsAre("p1", "p2"));
1318 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1319 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1321 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1322 ASSERT_THAT(FooDecl
, NotNull());
1324 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1325 return Env
.getValue(*FooDecl
);
1328 Value
*FooVal1
= GetFooValue(Env1
);
1329 ASSERT_THAT(FooVal1
, NotNull());
1330 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1331 << debugString(FooVal1
->getKind());
1333 Value
*FooVal2
= GetFooValue(Env2
);
1334 ASSERT_THAT(FooVal2
, NotNull());
1335 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1336 << debugString(FooVal2
->getKind());
1338 EXPECT_EQ(FooVal1
, FooVal2
);
1342 // Tests that when Top is unused it remains Top. Like above, but uses the
1343 // assignment form rather than initialization, which uses Top as an lvalue that
1344 // is *not* in an rvalue position.
1345 TEST_F(TopTest
, UnusedTopAssignment
) {
1346 std::string Code
= R
"(
1359 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1360 const AnalysisOutputs
&AO
) {
1361 ASSERT_THAT(Results
.keys(),
1362 UnorderedElementsAre("p1", "p2"));
1363 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1364 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1366 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1367 ASSERT_THAT(FooDecl
, NotNull());
1369 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1370 return Env
.getValue(*FooDecl
);
1373 Value
*FooVal1
= GetFooValue(Env1
);
1374 ASSERT_THAT(FooVal1
, NotNull());
1375 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1376 << debugString(FooVal1
->getKind());
1378 Value
*FooVal2
= GetFooValue(Env2
);
1379 ASSERT_THAT(FooVal2
, NotNull());
1380 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1381 << debugString(FooVal2
->getKind());
1383 EXPECT_EQ(FooVal1
, FooVal2
);
1387 TEST_F(TopTest
, UnusedTopJoinsToTop
) {
1388 std::string Code
= R
"(
1391 void target(bool Cond, bool F) {
1392 bool Foo = makeTop();
1393 // Force a new CFG block.
1411 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1412 const AnalysisOutputs
&AO
) {
1413 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1414 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1415 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1417 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1418 ASSERT_THAT(FooDecl
, NotNull());
1420 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1421 return Env
.getValue(*FooDecl
);
1424 Value
*FooVal1
= GetFooValue(Env1
);
1425 ASSERT_THAT(FooVal1
, NotNull());
1426 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1427 << debugString(FooVal1
->getKind());
1429 Value
*FooVal2
= GetFooValue(Env2
);
1430 ASSERT_THAT(FooVal2
, NotNull());
1431 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1432 << debugString(FooVal2
->getKind());
1436 TEST_F(TopTest
, TopUsedBeforeBranchJoinsToSameAtomicBool
) {
1437 std::string Code
= R
"(
1440 void target(bool Cond, bool F) {
1441 bool Foo = makeTop();
1446 // Force a new CFG block.
1464 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1465 const AnalysisOutputs
&AO
) {
1466 ASSERT_THAT(Results
.keys(),
1467 UnorderedElementsAre("p0", "p1", "p2"));
1468 const Environment
&Env0
= getEnvironmentAtAnnotation(Results
, "p0");
1469 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1470 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1472 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1473 ASSERT_THAT(FooDecl
, NotNull());
1475 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1476 return Env
.getValue(*FooDecl
);
1479 Value
*FooVal0
= GetFooValue(Env0
);
1480 ASSERT_THAT(FooVal0
, NotNull());
1481 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal0
))
1482 << debugString(FooVal0
->getKind());
1484 Value
*FooVal1
= GetFooValue(Env1
);
1485 ASSERT_THAT(FooVal1
, NotNull());
1486 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal1
))
1487 << debugString(FooVal1
->getKind());
1489 Value
*FooVal2
= GetFooValue(Env2
);
1490 ASSERT_THAT(FooVal2
, NotNull());
1491 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1492 << debugString(FooVal2
->getKind());
1494 EXPECT_EQ(FooVal2
, FooVal1
);
1498 TEST_F(TopTest
, TopUsedInBothBranchesJoinsToAtomic
) {
1499 std::string Code
= R
"(
1502 void target(bool Cond, bool F) {
1503 bool Foo = makeTop();
1504 // Force a new CFG block.
1522 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1523 const AnalysisOutputs
&AO
) {
1524 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1525 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1526 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1528 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1529 ASSERT_THAT(FooDecl
, NotNull());
1531 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1532 return Env
.getValue(*FooDecl
);
1535 Value
*FooVal1
= GetFooValue(Env1
);
1536 ASSERT_THAT(FooVal1
, NotNull());
1537 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1538 << debugString(FooVal1
->getKind());
1540 Value
*FooVal2
= GetFooValue(Env2
);
1541 ASSERT_THAT(FooVal2
, NotNull());
1542 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1543 << debugString(FooVal2
->getKind());
1547 TEST_F(TopTest
, TopUsedInBothBranchesWithoutPrecisionLoss
) {
1548 std::string Code
= R
"(
1551 void target(bool Cond, bool F) {
1552 bool Foo = makeTop();
1553 // Force a new CFG block.
1569 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1570 const AnalysisOutputs
&AO
) {
1571 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1572 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1574 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1575 ASSERT_THAT(FooDecl
, NotNull());
1577 const ValueDecl
*BarDecl
= findValueDecl(AO
.ASTCtx
, "Bar");
1578 ASSERT_THAT(BarDecl
, NotNull());
1580 auto *FooVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*FooDecl
));
1581 ASSERT_THAT(FooVal
, NotNull());
1583 auto *BarVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*BarDecl
));
1584 ASSERT_THAT(BarVal
, NotNull());
1586 EXPECT_TRUE(Env
.proves(
1587 Env
.arena().makeEquals(FooVal
->formula(), BarVal
->formula())));
1591 TEST_F(TopTest
, TopUnusedBeforeLoopHeadJoinsToTop
) {
1592 std::string Code
= R
"(
1595 void target(bool Cond, bool F) {
1596 bool Foo = makeTop();
1597 // Force a new CFG block.
1614 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1615 const AnalysisOutputs
&AO
) {
1616 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1617 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1618 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1620 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1621 ASSERT_THAT(FooDecl
, NotNull());
1623 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1624 return Env
.getValue(*FooDecl
);
1627 Value
*FooVal1
= GetFooValue(Env1
);
1628 ASSERT_THAT(FooVal1
, NotNull());
1629 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1630 << debugString(FooVal1
->getKind());
1632 Value
*FooVal2
= GetFooValue(Env2
);
1633 ASSERT_THAT(FooVal2
, NotNull());
1634 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1635 << debugString(FooVal2
->getKind());
1640 TEST_F(TopTest
, ForRangeStmtConverges
) {
1641 std::string Code
= R
"(
1642 void target(bool Foo) {
1650 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
1651 const AnalysisOutputs
&) {
1652 // No additional expectations. We're only checking that the
1653 // analysis converged.