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::ElementsAre
;
51 using ::testing::IsEmpty
;
52 using ::testing::NotNull
;
53 using ::testing::Test
;
54 using ::testing::UnorderedElementsAre
;
56 template <typename AnalysisT
>
57 llvm::Expected
<std::vector
<
58 std::optional
<DataflowAnalysisState
<typename
AnalysisT::Lattice
>>>>
59 runAnalysis(llvm::StringRef Code
, AnalysisT (*MakeAnalysis
)(ASTContext
&)) {
60 std::unique_ptr
<ASTUnit
> AST
=
61 tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
63 auto *Func
= selectFirst
<FunctionDecl
>(
64 "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"),
65 AST
->getASTContext()));
66 assert(Func
!= nullptr);
69 llvm::cantFail(ControlFlowContext::build(*Func
));
71 AnalysisT Analysis
= MakeAnalysis(AST
->getASTContext());
72 DataflowAnalysisContext
DACtx(std::make_unique
<WatchedLiteralsSolver
>());
73 Environment
Env(DACtx
);
75 return runDataflowAnalysis(CFCtx
, Analysis
, Env
);
78 TEST(DataflowAnalysisTest
, NoopAnalysis
) {
79 auto BlockStates
= llvm::cantFail(
80 runAnalysis
<NoopAnalysis
>("void target() {}", [](ASTContext
&C
) {
81 return NoopAnalysis(C
,
82 // Don't use builtin transfer function.
83 DataflowAnalysisOptions
{std::nullopt
});
85 EXPECT_EQ(BlockStates
.size(), 2u);
86 EXPECT_TRUE(BlockStates
[0].has_value());
87 EXPECT_TRUE(BlockStates
[1].has_value());
90 // Basic test that `diagnoseFunction` calls the Diagnoser function for the
91 // number of elements expected.
92 TEST(DataflowAnalysisTest
, DiagnoseFunctionDiagnoserCalledOnEachElement
) {
93 std::string Code
= R
"(void target() { int x = 0; ++x; })";
94 std::unique_ptr
<ASTUnit
> AST
=
95 tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
98 cast
<FunctionDecl
>(findValueDecl(AST
->getASTContext(), "target"));
99 auto Diagnoser
= [](const CFGElement
&Elt
, ASTContext
&,
100 const TransferStateForDiagnostics
<NoopLattice
> &) {
101 llvm::SmallVector
<std::string
> Diagnostics(1);
102 llvm::raw_string_ostream
OS(Diagnostics
.front());
103 Elt
.dumpToStream(OS
);
106 auto Result
= diagnoseFunction
<NoopAnalysis
, std::string
>(
107 *Func
, AST
->getASTContext(), Diagnoser
);
108 // `diagnoseFunction` provides no guarantees about the order in which elements
109 // are visited, so we use `UnorderedElementsAre`.
110 EXPECT_THAT_EXPECTED(Result
, llvm::HasValue(UnorderedElementsAre(
111 "0\n", "int x = 0;\n", "x\n", "++x\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
.flowConditionImplies(IsSet1
->formula()) ==
454 Env2
.flowConditionImplies(IsSet2
->formula())
455 ? ComparisonResult::Same
456 : ComparisonResult::Different
;
459 // Always returns `true` to accept the `MergedVal`.
460 bool merge(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
461 const Value
&Val2
, const Environment
&Env2
, Value
&MergedVal
,
462 Environment
&MergedEnv
) override
{
463 const auto *Decl
= Type
->getAsCXXRecordDecl();
464 if (Decl
== nullptr || Decl
->getIdentifier() == nullptr ||
465 Decl
->getName() != "SpecialBool")
468 auto *IsSet1
= cast_or_null
<BoolValue
>(Val1
.getProperty("is_set"));
469 if (IsSet1
== nullptr)
472 auto *IsSet2
= cast_or_null
<BoolValue
>(Val2
.getProperty("is_set"));
473 if (IsSet2
== nullptr)
476 auto &IsSet
= MergedEnv
.makeAtomicBoolValue();
477 MergedVal
.setProperty("is_set", IsSet
);
478 if (Env1
.flowConditionImplies(IsSet1
->formula()) &&
479 Env2
.flowConditionImplies(IsSet2
->formula()))
480 MergedEnv
.addToFlowCondition(IsSet
.formula());
486 class JoinFlowConditionsTest
: public Test
{
488 template <typename Matcher
>
489 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
491 test::checkDataflow
<SpecialBoolAnalysis
>(
492 AnalysisInputs
<SpecialBoolAnalysis
>(
493 Code
, ast_matchers::hasName("target"),
494 [](ASTContext
&Context
, Environment
&Env
) {
495 return SpecialBoolAnalysis(Context
);
497 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
498 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
499 DataflowAnalysisState
<NoopLattice
>>
501 const AnalysisOutputs
502 &AO
) { Match(Results
, AO
.ASTCtx
); }),
507 TEST_F(JoinFlowConditionsTest
, JoinDistinctButProvablyEquivalentValues
) {
508 std::string Code
= R
"(
510 SpecialBool() = default;
514 void target(bool Cond) {
530 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
531 ASTContext
&ASTCtx
) {
532 ASSERT_THAT(Results
.keys(),
533 UnorderedElementsAre("p1", "p2", "p3", "p4"));
534 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
535 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
536 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
537 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
539 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
540 ASSERT_THAT(FooDecl
, NotNull());
542 auto GetFoo
= [FooDecl
](const Environment
&Env
) -> const Formula
& {
543 return cast
<BoolValue
>(Env
.getValue(*FooDecl
)->getProperty("is_set"))
547 EXPECT_FALSE(Env1
.flowConditionImplies(GetFoo(Env1
)));
548 EXPECT_TRUE(Env2
.flowConditionImplies(GetFoo(Env2
)));
549 EXPECT_TRUE(Env3
.flowConditionImplies(GetFoo(Env3
)));
550 EXPECT_TRUE(Env4
.flowConditionImplies(GetFoo(Env4
)));
554 class OptionalIntAnalysis final
555 : public DataflowAnalysis
<OptionalIntAnalysis
, NoopLattice
> {
557 explicit OptionalIntAnalysis(ASTContext
&Context
)
558 : DataflowAnalysis
<OptionalIntAnalysis
, NoopLattice
>(Context
) {}
560 static NoopLattice
initialElement() { return {}; }
562 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
563 auto CS
= Elt
.getAs
<CFGStmt
>();
566 const Stmt
*S
= CS
->getStmt();
567 auto OptionalIntRecordDecl
= recordDecl(hasName("OptionalInt"));
568 auto HasOptionalIntType
= hasType(OptionalIntRecordDecl
);
570 SmallVector
<BoundNodes
, 1> Matches
= match(
571 stmt(anyOf(cxxConstructExpr(HasOptionalIntType
).bind("construct"),
573 callee(cxxMethodDecl(ofClass(OptionalIntRecordDecl
))))
575 *S
, getASTContext());
576 if (const auto *E
= selectFirst
<CXXConstructExpr
>(
577 "construct", Matches
)) {
578 cast
<RecordValue
>(Env
.getValue(*E
))
579 ->setProperty("has_value", Env
.getBoolLiteralValue(false));
580 } else if (const auto *E
=
581 selectFirst
<CXXOperatorCallExpr
>("operator", Matches
)) {
582 assert(E
->getNumArgs() > 0);
583 auto *Object
= E
->getArg(0);
584 assert(Object
!= nullptr);
586 refreshRecordValue(*Object
, Env
)
587 .setProperty("has_value", Env
.getBoolLiteralValue(true));
591 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
592 const Environment
&Env1
, const Value
&Val2
,
593 const Environment
&Env2
) override
{
594 // Nothing to say about a value that does not model an `OptionalInt`.
595 if (!Type
->isRecordType() ||
596 Type
->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
597 return ComparisonResult::Unknown
;
599 auto *Prop1
= Val1
.getProperty("has_value");
600 auto *Prop2
= Val2
.getProperty("has_value");
601 assert(Prop1
!= nullptr && Prop2
!= nullptr);
602 return areEquivalentValues(*Prop1
, *Prop2
) ? ComparisonResult::Same
603 : ComparisonResult::Different
;
606 bool merge(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
607 const Value
&Val2
, const Environment
&Env2
, Value
&MergedVal
,
608 Environment
&MergedEnv
) override
{
609 // Nothing to say about a value that does not model an `OptionalInt`.
610 if (!Type
->isRecordType() ||
611 Type
->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
614 auto *HasValue1
= cast_or_null
<BoolValue
>(Val1
.getProperty("has_value"));
615 if (HasValue1
== nullptr)
618 auto *HasValue2
= cast_or_null
<BoolValue
>(Val2
.getProperty("has_value"));
619 if (HasValue2
== nullptr)
622 if (HasValue1
== HasValue2
)
623 MergedVal
.setProperty("has_value", *HasValue1
);
625 MergedVal
.setProperty("has_value", MergedEnv
.makeTopBoolValue());
630 class WideningTest
: public Test
{
632 template <typename Matcher
>
633 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
634 tooling::FileContentMappings FilesContents
;
635 FilesContents
.push_back(
636 std::make_pair
<std::string
, std::string
>("widening_test_defs.h", R
"(
638 OptionalInt() = default;
639 OptionalInt& operator=(int);
643 checkDataflow
<OptionalIntAnalysis
>(
644 AnalysisInputs
<OptionalIntAnalysis
>(
645 Code
, ast_matchers::hasName("target"),
646 [](ASTContext
&Context
, Environment
&Env
) {
647 return OptionalIntAnalysis(Context
);
649 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
650 .withASTBuildVirtualMappedFiles(std::move(FilesContents
)),
651 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
652 DataflowAnalysisState
<NoopLattice
>>
654 const AnalysisOutputs
655 &AO
) { Match(Results
, AO
.ASTCtx
); }),
660 TEST_F(WideningTest
, JoinDistinctValuesWithDistinctProperties
) {
661 std::string Code
= R
"(
662 #include "widening_test_defs
.h
"
664 void target(bool Cond) {
677 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
678 ASTContext
&ASTCtx
) {
679 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2", "p3"));
680 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
681 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
682 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
684 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
685 ASSERT_THAT(FooDecl
, NotNull());
687 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
688 return Env
.getValue(*FooDecl
);
691 EXPECT_EQ(GetFooValue(Env1
)->getProperty("has_value"),
692 &Env1
.getBoolLiteralValue(false));
693 EXPECT_EQ(GetFooValue(Env2
)->getProperty("has_value"),
694 &Env2
.getBoolLiteralValue(true));
696 isa
<TopBoolValue
>(GetFooValue(Env3
)->getProperty("has_value")));
700 TEST_F(WideningTest
, JoinDistinctValuesWithSameProperties
) {
701 std::string Code
= R
"(
702 #include "widening_test_defs
.h
"
704 void target(bool Cond) {
720 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
721 ASTContext
&ASTCtx
) {
722 ASSERT_THAT(Results
.keys(),
723 UnorderedElementsAre("p1", "p2", "p3", "p4"));
724 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
725 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
726 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
727 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
729 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
730 ASSERT_THAT(FooDecl
, NotNull());
732 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
733 return Env
.getValue(*FooDecl
);
736 EXPECT_EQ(GetFooValue(Env1
)->getProperty("has_value"),
737 &Env1
.getBoolLiteralValue(false));
738 EXPECT_EQ(GetFooValue(Env2
)->getProperty("has_value"),
739 &Env2
.getBoolLiteralValue(true));
740 EXPECT_EQ(GetFooValue(Env3
)->getProperty("has_value"),
741 &Env3
.getBoolLiteralValue(true));
742 EXPECT_EQ(GetFooValue(Env4
)->getProperty("has_value"),
743 &Env4
.getBoolLiteralValue(true));
747 TEST_F(WideningTest
, DistinctPointersToTheSameLocationAreEquivalent
) {
748 std::string Code
= R
"(
749 void target(int Foo, bool Cond) {
760 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
761 ASTContext
&ASTCtx
) {
762 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
763 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
765 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
766 ASSERT_THAT(FooDecl
, NotNull());
768 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
769 ASSERT_THAT(BarDecl
, NotNull());
772 cast
<ScalarStorageLocation
>(Env
.getStorageLocation(*FooDecl
));
773 const auto *BarVal
= cast
<PointerValue
>(Env
.getValue(*BarDecl
));
774 EXPECT_EQ(&BarVal
->getPointeeLoc(), FooLoc
);
778 TEST_F(WideningTest
, DistinctValuesWithSamePropertiesAreEquivalent
) {
779 std::string Code
= R
"(
780 #include "widening_test_defs
.h
"
782 void target(bool Cond) {
794 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
795 ASTContext
&ASTCtx
) {
796 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
797 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
799 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
800 ASSERT_THAT(FooDecl
, NotNull());
802 const auto *FooVal
= Env
.getValue(*FooDecl
);
803 EXPECT_EQ(FooVal
->getProperty("has_value"),
804 &Env
.getBoolLiteralValue(true));
808 class FlowConditionTest
: public Test
{
810 template <typename Matcher
>
811 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
813 checkDataflow
<NoopAnalysis
>(
814 AnalysisInputs
<NoopAnalysis
>(
815 Code
, ast_matchers::hasName("target"),
816 [](ASTContext
&Context
, Environment
&Env
) {
817 return NoopAnalysis(Context
);
819 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
820 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
821 DataflowAnalysisState
<NoopLattice
>>
823 const AnalysisOutputs
824 &AO
) { Match(Results
, AO
.ASTCtx
); }),
829 TEST_F(FlowConditionTest
, IfStmtSingleVar
) {
830 std::string Code
= R
"(
831 void target(bool Foo) {
843 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
844 ASTContext
&ASTCtx
) {
845 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
846 ASSERT_THAT(FooDecl
, NotNull());
848 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
850 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
851 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
852 EXPECT_TRUE(Env1
.flowConditionImplies(FooVal1
));
854 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
855 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
856 EXPECT_FALSE(Env2
.flowConditionImplies(FooVal2
));
860 TEST_F(FlowConditionTest
, IfStmtSingleNegatedVar
) {
861 std::string Code
= R
"(
862 void target(bool Foo) {
874 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
875 ASTContext
&ASTCtx
) {
876 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
877 ASSERT_THAT(FooDecl
, NotNull());
879 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
881 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
882 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
883 EXPECT_FALSE(Env1
.flowConditionImplies(FooVal1
));
885 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
886 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
887 EXPECT_TRUE(Env2
.flowConditionImplies(FooVal2
));
891 TEST_F(FlowConditionTest
, WhileStmt
) {
892 std::string Code
= R
"(
893 void target(bool Foo) {
902 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
903 ASTContext
&ASTCtx
) {
904 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
905 ASSERT_THAT(FooDecl
, NotNull());
907 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
908 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
910 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
911 EXPECT_TRUE(Env
.flowConditionImplies(FooVal
));
915 TEST_F(FlowConditionTest
, Conjunction
) {
916 std::string Code
= R
"(
917 void target(bool Foo, bool Bar) {
927 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
929 ASTContext
&ASTCtx
) {
930 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
931 ASSERT_THAT(FooDecl
, NotNull());
933 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
934 ASSERT_THAT(BarDecl
, NotNull());
936 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
938 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
939 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
940 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
941 EXPECT_TRUE(Env1
.flowConditionImplies(FooVal1
));
942 EXPECT_TRUE(Env1
.flowConditionImplies(BarVal1
));
944 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
945 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
946 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
947 EXPECT_FALSE(Env2
.flowConditionImplies(FooVal2
));
948 EXPECT_FALSE(Env2
.flowConditionImplies(BarVal2
));
952 TEST_F(FlowConditionTest
, Disjunction
) {
953 std::string Code
= R
"(
954 void target(bool Foo, bool Bar) {
964 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
966 ASTContext
&ASTCtx
) {
967 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
968 ASSERT_THAT(FooDecl
, NotNull());
970 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
971 ASSERT_THAT(BarDecl
, NotNull());
973 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
975 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
976 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
977 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
978 EXPECT_FALSE(Env1
.flowConditionImplies(FooVal1
));
979 EXPECT_FALSE(Env1
.flowConditionImplies(BarVal1
));
981 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
982 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
983 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
984 EXPECT_FALSE(Env2
.flowConditionImplies(FooVal2
));
985 EXPECT_FALSE(Env2
.flowConditionImplies(BarVal2
));
989 TEST_F(FlowConditionTest
, NegatedConjunction
) {
990 std::string Code
= R
"(
991 void target(bool Foo, bool Bar) {
1001 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1003 ASTContext
&ASTCtx
) {
1004 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1005 ASSERT_THAT(FooDecl
, NotNull());
1007 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1008 ASSERT_THAT(BarDecl
, NotNull());
1010 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1012 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1013 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1014 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1015 EXPECT_FALSE(Env1
.flowConditionImplies(FooVal1
));
1016 EXPECT_FALSE(Env1
.flowConditionImplies(BarVal1
));
1018 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1019 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1020 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1021 EXPECT_TRUE(Env2
.flowConditionImplies(FooVal2
));
1022 EXPECT_TRUE(Env2
.flowConditionImplies(BarVal2
));
1026 TEST_F(FlowConditionTest
, DeMorgan
) {
1027 std::string Code
= R
"(
1028 void target(bool Foo, bool Bar) {
1029 if (!(!Foo || !Bar)) {
1038 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1040 ASTContext
&ASTCtx
) {
1041 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1042 ASSERT_THAT(FooDecl
, NotNull());
1044 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1045 ASSERT_THAT(BarDecl
, NotNull());
1047 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1049 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1050 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1051 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1052 EXPECT_TRUE(Env1
.flowConditionImplies(FooVal1
));
1053 EXPECT_TRUE(Env1
.flowConditionImplies(BarVal1
));
1055 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1056 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1057 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1058 EXPECT_FALSE(Env2
.flowConditionImplies(FooVal2
));
1059 EXPECT_FALSE(Env2
.flowConditionImplies(BarVal2
));
1063 TEST_F(FlowConditionTest
, Join
) {
1064 std::string Code
= R
"(
1065 void target(bool Foo, bool Bar) {
1079 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1080 ASTContext
&ASTCtx
) {
1081 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1083 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1084 ASSERT_THAT(FooDecl
, NotNull());
1086 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1087 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
1088 EXPECT_TRUE(Env
.flowConditionImplies(FooVal
));
1092 // Verifies that flow conditions are properly constructed even when the
1093 // condition is not meaningfully interpreted.
1095 // Note: currently, arbitrary function calls are uninterpreted, so the test
1096 // exercises this case. If and when we change that, this test will not add to
1097 // coverage (although it may still test a valuable case).
1098 TEST_F(FlowConditionTest
, OpaqueFlowConditionMergesToOpaqueBool
) {
1099 std::string Code
= R
"(
1112 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1113 ASTContext
&ASTCtx
) {
1114 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1115 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1117 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1118 ASSERT_THAT(BarDecl
, NotNull());
1120 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1122 EXPECT_FALSE(Env
.flowConditionImplies(BarVal
));
1126 // Verifies that flow conditions are properly constructed even when the
1127 // condition is not meaningfully interpreted.
1129 // Note: currently, fields with recursive type calls are uninterpreted (beneath
1130 // the first instance), so the test exercises this case. If and when we change
1131 // that, this test will not add to coverage (although it may still test a
1133 TEST_F(FlowConditionTest
, OpaqueFieldFlowConditionMergesToOpaqueBool
) {
1134 std::string Code
= R
"(
1143 void target(Foo F) {
1153 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1154 ASTContext
&ASTCtx
) {
1155 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1156 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1158 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1159 ASSERT_THAT(BarDecl
, NotNull());
1161 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1163 EXPECT_FALSE(Env
.flowConditionImplies(BarVal
));
1167 // Verifies that flow conditions are properly constructed even when the
1168 // condition is not meaningfully interpreted. Adds to above by nesting the
1169 // interestnig case inside a normal branch. This protects against degenerate
1170 // solutions which only test for empty flow conditions, for example.
1171 TEST_F(FlowConditionTest
, OpaqueFlowConditionInsideBranchMergesToOpaqueBool
) {
1172 std::string Code
= R
"(
1175 void target(bool Cond) {
1187 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1188 ASTContext
&ASTCtx
) {
1189 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1190 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1192 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1193 ASSERT_THAT(BarDecl
, NotNull());
1195 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1197 EXPECT_FALSE(Env
.flowConditionImplies(BarVal
));
1201 TEST_F(FlowConditionTest
, PointerToBoolImplicitCast
) {
1202 std::string Code
= R
"(
1203 void target(int *Ptr) {
1216 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1217 ASTContext
&ASTCtx
) {
1218 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1220 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1221 ASSERT_THAT(FooDecl
, NotNull());
1223 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1224 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1225 EXPECT_TRUE(Env1
.flowConditionImplies(FooVal1
));
1227 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1228 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1229 EXPECT_FALSE(Env2
.flowConditionImplies(FooVal2
));
1233 class TopAnalysis final
: public DataflowAnalysis
<TopAnalysis
, NoopLattice
> {
1235 explicit TopAnalysis(ASTContext
&Context
)
1236 : DataflowAnalysis
<TopAnalysis
, NoopLattice
>(Context
) {}
1238 static NoopLattice
initialElement() { return {}; }
1240 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
1241 auto CS
= Elt
.getAs
<CFGStmt
>();
1244 const Stmt
*S
= CS
->getStmt();
1245 SmallVector
<BoundNodes
, 1> Matches
=
1246 match(callExpr(callee(functionDecl(hasName("makeTop")))).bind("top"),
1247 *S
, getASTContext());
1248 if (const auto *E
= selectFirst
<CallExpr
>("top", Matches
)) {
1249 Env
.setValue(*E
, Env
.makeTopBoolValue());
1253 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
1254 const Environment
&Env1
, const Value
&Val2
,
1255 const Environment
&Env2
) override
{
1256 // Changes to a sound approximation, which allows us to test whether we can
1257 // (soundly) converge for some loops.
1258 return ComparisonResult::Unknown
;
1262 class TopTest
: public Test
{
1264 template <typename Matcher
>
1265 void runDataflow(llvm::StringRef Code
, Matcher VerifyResults
) {
1267 checkDataflow
<TopAnalysis
>(
1268 AnalysisInputs
<TopAnalysis
>(
1269 Code
, ast_matchers::hasName("target"),
1270 [](ASTContext
&Context
, Environment
&Env
) {
1271 return TopAnalysis(Context
);
1273 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1279 // Tests that when Top is unused it remains Top.
1280 TEST_F(TopTest
, UnusedTopInitializer
) {
1281 std::string Code
= R
"(
1285 bool Foo = makeTop();
1293 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1294 const AnalysisOutputs
&AO
) {
1295 ASSERT_THAT(Results
.keys(),
1296 UnorderedElementsAre("p1", "p2"));
1297 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1298 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1300 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1301 ASSERT_THAT(FooDecl
, NotNull());
1303 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1304 return Env
.getValue(*FooDecl
);
1307 Value
*FooVal1
= GetFooValue(Env1
);
1308 ASSERT_THAT(FooVal1
, NotNull());
1309 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1310 << debugString(FooVal1
->getKind());
1312 Value
*FooVal2
= GetFooValue(Env2
);
1313 ASSERT_THAT(FooVal2
, NotNull());
1314 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1315 << debugString(FooVal2
->getKind());
1317 EXPECT_EQ(FooVal1
, FooVal2
);
1321 // Tests that when Top is unused it remains Top. Like above, but uses the
1322 // assignment form rather than initialization, which uses Top as an lvalue that
1323 // is *not* in an rvalue position.
1324 TEST_F(TopTest
, UnusedTopAssignment
) {
1325 std::string Code
= R
"(
1338 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1339 const AnalysisOutputs
&AO
) {
1340 ASSERT_THAT(Results
.keys(),
1341 UnorderedElementsAre("p1", "p2"));
1342 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1343 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1345 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1346 ASSERT_THAT(FooDecl
, NotNull());
1348 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1349 return Env
.getValue(*FooDecl
);
1352 Value
*FooVal1
= GetFooValue(Env1
);
1353 ASSERT_THAT(FooVal1
, NotNull());
1354 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1355 << debugString(FooVal1
->getKind());
1357 Value
*FooVal2
= GetFooValue(Env2
);
1358 ASSERT_THAT(FooVal2
, NotNull());
1359 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1360 << debugString(FooVal2
->getKind());
1362 EXPECT_EQ(FooVal1
, FooVal2
);
1366 TEST_F(TopTest
, UnusedTopJoinsToTop
) {
1367 std::string Code
= R
"(
1370 void target(bool Cond, bool F) {
1371 bool Foo = makeTop();
1372 // Force a new CFG block.
1390 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1391 const AnalysisOutputs
&AO
) {
1392 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1393 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1394 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1396 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1397 ASSERT_THAT(FooDecl
, NotNull());
1399 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1400 return Env
.getValue(*FooDecl
);
1403 Value
*FooVal1
= GetFooValue(Env1
);
1404 ASSERT_THAT(FooVal1
, NotNull());
1405 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1406 << debugString(FooVal1
->getKind());
1408 Value
*FooVal2
= GetFooValue(Env2
);
1409 ASSERT_THAT(FooVal2
, NotNull());
1410 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1411 << debugString(FooVal2
->getKind());
1415 TEST_F(TopTest
, TopUsedBeforeBranchJoinsToSameAtomicBool
) {
1416 std::string Code
= R
"(
1419 void target(bool Cond, bool F) {
1420 bool Foo = makeTop();
1425 // Force a new CFG block.
1443 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1444 const AnalysisOutputs
&AO
) {
1445 ASSERT_THAT(Results
.keys(),
1446 UnorderedElementsAre("p0", "p1", "p2"));
1447 const Environment
&Env0
= getEnvironmentAtAnnotation(Results
, "p0");
1448 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1449 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1451 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1452 ASSERT_THAT(FooDecl
, NotNull());
1454 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1455 return Env
.getValue(*FooDecl
);
1458 Value
*FooVal0
= GetFooValue(Env0
);
1459 ASSERT_THAT(FooVal0
, NotNull());
1460 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal0
))
1461 << debugString(FooVal0
->getKind());
1463 Value
*FooVal1
= GetFooValue(Env1
);
1464 ASSERT_THAT(FooVal1
, NotNull());
1465 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal1
))
1466 << debugString(FooVal1
->getKind());
1468 Value
*FooVal2
= GetFooValue(Env2
);
1469 ASSERT_THAT(FooVal2
, NotNull());
1470 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1471 << debugString(FooVal2
->getKind());
1473 EXPECT_EQ(FooVal2
, FooVal1
);
1477 TEST_F(TopTest
, TopUsedInBothBranchesJoinsToAtomic
) {
1478 std::string Code
= R
"(
1481 void target(bool Cond, bool F) {
1482 bool Foo = makeTop();
1483 // Force a new CFG block.
1501 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1502 const AnalysisOutputs
&AO
) {
1503 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1504 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1505 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1507 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1508 ASSERT_THAT(FooDecl
, NotNull());
1510 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1511 return Env
.getValue(*FooDecl
);
1514 Value
*FooVal1
= GetFooValue(Env1
);
1515 ASSERT_THAT(FooVal1
, NotNull());
1516 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1517 << debugString(FooVal1
->getKind());
1519 Value
*FooVal2
= GetFooValue(Env2
);
1520 ASSERT_THAT(FooVal2
, NotNull());
1521 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1522 << debugString(FooVal2
->getKind());
1526 TEST_F(TopTest
, TopUsedInBothBranchesWithoutPrecisionLoss
) {
1527 std::string Code
= R
"(
1530 void target(bool Cond, bool F) {
1531 bool Foo = makeTop();
1532 // Force a new CFG block.
1548 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1549 const AnalysisOutputs
&AO
) {
1550 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1551 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1553 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1554 ASSERT_THAT(FooDecl
, NotNull());
1556 const ValueDecl
*BarDecl
= findValueDecl(AO
.ASTCtx
, "Bar");
1557 ASSERT_THAT(BarDecl
, NotNull());
1559 auto *FooVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*FooDecl
));
1560 ASSERT_THAT(FooVal
, NotNull());
1562 auto *BarVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*BarDecl
));
1563 ASSERT_THAT(BarVal
, NotNull());
1565 EXPECT_TRUE(Env
.flowConditionImplies(
1566 Env
.arena().makeEquals(FooVal
->formula(), BarVal
->formula())));
1570 TEST_F(TopTest
, TopUnusedBeforeLoopHeadJoinsToTop
) {
1571 std::string Code
= R
"(
1574 void target(bool Cond, bool F) {
1575 bool Foo = makeTop();
1576 // Force a new CFG block.
1593 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1594 const AnalysisOutputs
&AO
) {
1595 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1596 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1597 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1599 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1600 ASSERT_THAT(FooDecl
, NotNull());
1602 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1603 return Env
.getValue(*FooDecl
);
1606 Value
*FooVal1
= GetFooValue(Env1
);
1607 ASSERT_THAT(FooVal1
, NotNull());
1608 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1609 << debugString(FooVal1
->getKind());
1611 Value
*FooVal2
= GetFooValue(Env2
);
1612 ASSERT_THAT(FooVal2
, NotNull());
1613 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1614 << debugString(FooVal2
->getKind());
1619 TEST_F(TopTest
, ForRangeStmtConverges
) {
1620 std::string Code
= R
"(
1621 void target(bool Foo) {
1629 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
1630 const AnalysisOutputs
&) {
1631 // No additional expectations. We're only checking that the
1632 // analysis converged.