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/OperationKinds.h"
13 #include "clang/AST/Type.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/Analysis/CFG.h"
17 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
18 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
19 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
20 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
21 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
22 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
23 #include "clang/Analysis/FlowSensitive/Value.h"
24 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
25 #include "clang/Tooling/Tooling.h"
26 #include "llvm/ADT/STLExtras.h"
27 #include "llvm/ADT/SmallSet.h"
28 #include "llvm/ADT/StringMap.h"
29 #include "llvm/ADT/StringRef.h"
30 #include "llvm/Support/Error.h"
31 #include "llvm/Testing/ADT/StringMapEntry.h"
32 #include "llvm/Testing/Support/Error.h"
33 #include "gmock/gmock.h"
34 #include "gtest/gtest.h"
45 using namespace clang
;
46 using namespace dataflow
;
48 using namespace ast_matchers
;
49 using llvm::IsStringMapEntry
;
50 using ::testing::DescribeMatcher
;
51 using ::testing::IsEmpty
;
52 using ::testing::NotNull
;
53 using ::testing::Test
;
54 using ::testing::UnorderedElementsAre
;
56 class DataflowAnalysisTest
: public Test
{
58 template <typename AnalysisT
>
59 llvm::Expected
<std::vector
<
60 std::optional
<DataflowAnalysisState
<typename
AnalysisT::Lattice
>>>>
61 runAnalysis(llvm::StringRef Code
, AnalysisT (*MakeAnalysis
)(ASTContext
&)) {
62 AST
= tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
64 auto *Func
= selectFirst
<FunctionDecl
>(
66 match(functionDecl(ast_matchers::hasName("target")).bind("func"),
67 AST
->getASTContext()));
68 assert(Func
!= nullptr);
71 std::make_unique
<AdornedCFG
>(llvm::cantFail(AdornedCFG::build(*Func
)));
73 AnalysisT Analysis
= MakeAnalysis(AST
->getASTContext());
74 DACtx
= std::make_unique
<DataflowAnalysisContext
>(
75 std::make_unique
<WatchedLiteralsSolver
>());
76 Environment
Env(*DACtx
, *Func
);
78 return runDataflowAnalysis(*ACFG
, Analysis
, Env
);
81 /// Returns the `CFGBlock` containing `S` (and asserts that it exists).
82 const CFGBlock
*blockForStmt(const Stmt
&S
) {
83 const CFGBlock
*Block
= ACFG
->blockForStmt(S
);
84 assert(Block
!= nullptr);
88 template <typename StateT
>
90 blockStateForStmt(const std::vector
<std::optional
<StateT
>> &BlockStates
,
92 const std::optional
<StateT
> &MaybeState
=
93 BlockStates
[blockForStmt(S
)->getBlockID()];
94 assert(MaybeState
.has_value());
98 /// Returns the first node that matches `Matcher` (and asserts that the match
99 /// was successful, i.e. the returned node is not null).
100 template <typename NodeT
, typename MatcherT
>
101 const NodeT
&matchNode(MatcherT Matcher
) {
102 const auto *Node
= selectFirst
<NodeT
>(
103 "node", match(Matcher
.bind("node"), AST
->getASTContext()));
104 assert(Node
!= nullptr);
108 std::unique_ptr
<ASTUnit
> AST
;
109 std::unique_ptr
<AdornedCFG
> ACFG
;
110 std::unique_ptr
<DataflowAnalysisContext
> DACtx
;
113 TEST_F(DataflowAnalysisTest
, NoopAnalysis
) {
114 auto BlockStates
= llvm::cantFail(
115 runAnalysis
<NoopAnalysis
>("void target() {}", [](ASTContext
&C
) {
116 return NoopAnalysis(C
,
117 // Don't use builtin transfer function.
118 DataflowAnalysisOptions
{std::nullopt
});
120 EXPECT_EQ(BlockStates
.size(), 2u);
121 EXPECT_TRUE(BlockStates
[0].has_value());
122 EXPECT_TRUE(BlockStates
[1].has_value());
125 // Basic test that `diagnoseFunction` calls the Diagnoser function for the
126 // number of elements expected.
127 TEST_F(DataflowAnalysisTest
, DiagnoseFunctionDiagnoserCalledOnEachElement
) {
128 std::string Code
= R
"(void target() { int x = 0; ++x; })";
129 std::unique_ptr
<ASTUnit
> AST
=
130 tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
133 cast
<FunctionDecl
>(findValueDecl(AST
->getASTContext(), "target"));
134 auto Diagnoser
= [](const CFGElement
&Elt
, ASTContext
&,
135 const TransferStateForDiagnostics
<NoopLattice
> &) {
136 llvm::SmallVector
<std::string
> Diagnostics(1);
137 llvm::raw_string_ostream
OS(Diagnostics
.front());
138 Elt
.dumpToStream(OS
);
141 auto Result
= diagnoseFunction
<NoopAnalysis
, std::string
>(
142 *Func
, AST
->getASTContext(), Diagnoser
);
143 // `diagnoseFunction` provides no guarantees about the order in which elements
144 // are visited, so we use `UnorderedElementsAre`.
145 EXPECT_THAT_EXPECTED(Result
, llvm::HasValue(UnorderedElementsAre(
146 "0\n", "int x = 0;\n", "x\n", "++x\n",
147 " (Lifetime ends)\n")));
150 TEST_F(DataflowAnalysisTest
, CanAnalyzeStmt
) {
151 std::string Code
= R
"cc(
152 struct S { bool b; };
157 AST
= tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++11"});
158 const auto &DeclStatement
=
159 matchNode
<DeclStmt
>(declStmt(hasSingleDecl(varDecl(hasName("AnS")))));
160 const auto &Func
= matchNode
<FunctionDecl
>(functionDecl(hasName("foo")));
162 ACFG
= std::make_unique
<AdornedCFG
>(llvm::cantFail(AdornedCFG::build(
163 Func
, const_cast<DeclStmt
&>(DeclStatement
), AST
->getASTContext())));
165 NoopAnalysis Analysis
= NoopAnalysis(AST
->getASTContext());
166 DACtx
= std::make_unique
<DataflowAnalysisContext
>(
167 std::make_unique
<WatchedLiteralsSolver
>());
168 Environment
Env(*DACtx
, const_cast<DeclStmt
&>(DeclStatement
));
170 llvm::Expected
<std::vector
<std::optional
<DataflowAnalysisState
<NoopLattice
>>>>
171 Results
= runDataflowAnalysis(*ACFG
, Analysis
, Env
);
173 ASSERT_THAT_ERROR(Results
.takeError(), llvm::Succeeded());
174 const Environment
&ExitBlockEnv
= Results
->front()->Env
;
175 BoolValue
*BoolFieldValue
= cast
<BoolValue
>(
176 getFieldValue(ExitBlockEnv
.get
<RecordStorageLocation
>(
177 *cast
<VarDecl
>((*DeclStatement
.decl_begin()))),
178 "b", AST
->getASTContext(), ExitBlockEnv
));
179 EXPECT_TRUE(Env
.proves(BoolFieldValue
->formula()));
182 // Tests for the statement-to-block map.
183 using StmtToBlockTest
= DataflowAnalysisTest
;
185 TEST_F(StmtToBlockTest
, ConditionalOperator
) {
186 std::string Code
= R
"(
187 void target(bool b) {
191 ASSERT_THAT_ERROR(runAnalysis
<NoopAnalysis
>(
192 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); })
196 const auto &IDecl
= matchNode
<DeclStmt
>(declStmt(has(varDecl(hasName("i")))));
197 const auto &ConditionalOp
=
198 matchNode
<ConditionalOperator
>(conditionalOperator());
200 // The conditional operator should be associated with the same block as the
201 // `DeclStmt` for `i`. (Specifically, the conditional operator should not be
202 // associated with the block for which it is the terminator.)
203 EXPECT_EQ(blockForStmt(IDecl
), blockForStmt(ConditionalOp
));
206 TEST_F(StmtToBlockTest
, LogicalAnd
) {
207 std::string Code
= R
"(
208 void target(bool b1, bool b2) {
212 ASSERT_THAT_ERROR(runAnalysis
<NoopAnalysis
>(
213 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); })
217 const auto &BDecl
= matchNode
<DeclStmt
>(declStmt(has(varDecl(hasName("b")))));
219 matchNode
<BinaryOperator
>(binaryOperator(hasOperatorName("&&")));
221 // The `&&` operator should be associated with the same block as the
222 // `DeclStmt` for `b`. (Specifically, the `&&` operator should not be
223 // associated with the block for which it is the terminator.)
224 EXPECT_EQ(blockForStmt(BDecl
), blockForStmt(AndOp
));
227 TEST_F(StmtToBlockTest
, IfStatementWithLogicalAnd
) {
228 std::string Code
= R
"(
229 void target(bool b1, bool b2) {
234 ASSERT_THAT_ERROR(runAnalysis
<NoopAnalysis
>(
235 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); })
239 const auto &If
= matchNode
<IfStmt
>(ifStmt());
241 matchNode
<DeclRefExpr
>(declRefExpr(to(varDecl(hasName("b2")))));
243 matchNode
<BinaryOperator
>(binaryOperator(hasOperatorName("&&")));
245 // The if statement is the terminator for the block that contains both `b2`
246 // and the `&&` operator (which appears only as a terminator condition, not
247 // as a regular `CFGElement`).
248 const CFGBlock
*IfBlock
= blockForStmt(If
);
249 const CFGBlock
*B2Block
= blockForStmt(B2
);
250 const CFGBlock
*AndOpBlock
= blockForStmt(AndOp
);
251 EXPECT_EQ(IfBlock
, B2Block
);
252 EXPECT_EQ(IfBlock
, AndOpBlock
);
255 // Tests that check we discard state for expressions correctly.
256 using DiscardExprStateTest
= DataflowAnalysisTest
;
258 TEST_F(DiscardExprStateTest
, WhileStatement
) {
259 std::string Code
= R
"(
261 void target(int *p) {
266 auto BlockStates
= llvm::cantFail(runAnalysis
<NoopAnalysis
>(
267 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); }));
269 const auto &NotEqOp
=
270 matchNode
<BinaryOperator
>(binaryOperator(hasOperatorName("!=")));
271 const auto &CallFoo
=
272 matchNode
<CallExpr
>(callExpr(callee(functionDecl(hasName("foo")))));
274 // In the block that evaluates the expression `p != nullptr`, this expression
275 // is associated with a value.
276 const auto &NotEqOpState
= blockStateForStmt(BlockStates
, NotEqOp
);
277 EXPECT_NE(NotEqOpState
.Env
.getValue(NotEqOp
), nullptr);
279 // In the block that calls `foo(p)`, the value for `p != nullptr` is discarded
280 // because it is not consumed outside the block it is in.
281 const auto &CallFooState
= blockStateForStmt(BlockStates
, CallFoo
);
282 EXPECT_EQ(CallFooState
.Env
.getValue(NotEqOp
), nullptr);
285 TEST_F(DiscardExprStateTest
, BooleanOperator
) {
286 std::string Code
= R
"(
288 void target(bool b1, bool b2) {
293 auto BlockStates
= llvm::cantFail(runAnalysis
<NoopAnalysis
>(
294 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); }));
297 matchNode
<BinaryOperator
>(binaryOperator(hasOperatorName("&&")));
299 matchNode
<CallExpr
>(callExpr(callee(functionDecl(hasName("f")))));
301 // In the block that evaluates the LHS of the `&&` operator, the LHS is
302 // associated with a value, while the right-hand side is not (unsurprisingly,
303 // as it hasn't been evaluated yet).
304 const auto &LHSState
= blockStateForStmt(BlockStates
, *AndOp
.getLHS());
305 auto *LHSValue
= cast
<BoolValue
>(LHSState
.Env
.getValue(*AndOp
.getLHS()));
306 EXPECT_NE(LHSValue
, nullptr);
307 EXPECT_EQ(LHSState
.Env
.getValue(*AndOp
.getRHS()), nullptr);
309 // In the block that evaluates the RHS, both the LHS and RHS are associated
310 // with values, as they are both subexpressions of the `&&` operator, which
311 // is evaluated in a later block.
312 const auto &RHSState
= blockStateForStmt(BlockStates
, *AndOp
.getRHS());
313 EXPECT_EQ(RHSState
.Env
.getValue(*AndOp
.getLHS()), LHSValue
);
314 auto *RHSValue
= RHSState
.Env
.get
<BoolValue
>(*AndOp
.getRHS());
315 EXPECT_NE(RHSValue
, nullptr);
317 // In the block that evaluates `b1 && b2`, the `&&` as well as its operands
318 // are associated with values.
319 const auto &AndOpState
= blockStateForStmt(BlockStates
, AndOp
);
320 EXPECT_EQ(AndOpState
.Env
.getValue(*AndOp
.getLHS()), LHSValue
);
321 EXPECT_EQ(AndOpState
.Env
.getValue(*AndOp
.getRHS()), RHSValue
);
322 EXPECT_EQ(AndOpState
.Env
.getValue(AndOp
),
323 &AndOpState
.Env
.makeAnd(*LHSValue
, *RHSValue
));
325 // In the block that calls `f()`, none of `b1`, `b2`, or `b1 && b2` should be
326 // associated with values.
327 const auto &CallFState
= blockStateForStmt(BlockStates
, CallF
);
328 EXPECT_EQ(CallFState
.Env
.getValue(*AndOp
.getLHS()), nullptr);
329 EXPECT_EQ(CallFState
.Env
.getValue(*AndOp
.getRHS()), nullptr);
330 EXPECT_EQ(CallFState
.Env
.getValue(AndOp
), nullptr);
333 TEST_F(DiscardExprStateTest
, ConditionalOperator
) {
334 std::string Code
= R
"(
342 f(&i, cond() ? 1 : 0);
346 auto BlockStates
= llvm::cantFail(runAnalysis
<NoopAnalysis
>(
347 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); }));
349 const auto &AddrOfI
=
350 matchNode
<UnaryOperator
>(unaryOperator(hasOperatorName("&")));
352 matchNode
<CallExpr
>(callExpr(callee(functionDecl(hasName("f")))));
354 matchNode
<CallExpr
>(callExpr(callee(functionDecl(hasName("g")))));
356 // In the block that evaluates `&i`, it should obviously have a value.
357 const auto &AddrOfIState
= blockStateForStmt(BlockStates
, AddrOfI
);
358 auto *AddrOfIVal
= AddrOfIState
.Env
.get
<PointerValue
>(AddrOfI
);
359 EXPECT_NE(AddrOfIVal
, nullptr);
361 // Because of the conditional operator, the `f(...)` call is evaluated in a
362 // different block than `&i`, but `&i` still needs to have a value here
363 // because it's a subexpression of the call.
364 const auto &CallFState
= blockStateForStmt(BlockStates
, CallF
);
365 EXPECT_NE(&CallFState
, &AddrOfIState
);
366 EXPECT_EQ(CallFState
.Env
.get
<PointerValue
>(AddrOfI
), AddrOfIVal
);
368 // In the block that calls `g()`, `&i` should no longer be associated with a
370 const auto &CallGState
= blockStateForStmt(BlockStates
, CallG
);
371 EXPECT_EQ(CallGState
.Env
.get
<PointerValue
>(AddrOfI
), nullptr);
374 TEST_F(DiscardExprStateTest
, CallWithParenExprTreatedCorrectly
) {
375 // This is a regression test.
376 // In the CFG for `target()` below, the expression that evaluates the function
377 // pointer for `expect` and the actual call are separated into different
378 // baseic blocks (because of the control flow introduced by the `||`
380 // The value for the `expect` function pointer was erroneously discarded
381 // from the environment between these two blocks because the code that
382 // determines whether the expression values for a block need to be preserved
383 // did not ignore the `ParenExpr` around `(i == 1)` (which is not represented
385 std::string Code
= R
"(
386 bool expect(bool, bool);
388 expect(false || (i == 1), false);
391 auto BlockStates
= llvm::cantFail(runAnalysis
<NoopAnalysis
>(
392 Code
, [](ASTContext
&C
) { return NoopAnalysis(C
); }));
394 const auto &FnToPtrDecay
= matchNode
<ImplicitCastExpr
>(
395 implicitCastExpr(hasCastKind(CK_FunctionToPointerDecay
)));
396 const auto &CallExpect
=
397 matchNode
<CallExpr
>(callExpr(callee(functionDecl(hasName("expect")))));
399 // In the block that evaluates the implicit cast of `expect` to a pointer,
400 // this expression is associated with a value.
401 const auto &FnToPtrDecayState
= blockStateForStmt(BlockStates
, FnToPtrDecay
);
402 EXPECT_NE(FnToPtrDecayState
.Env
.getValue(FnToPtrDecay
), nullptr);
404 // In the block that calls `expect()`, the implicit cast of `expect` to a
405 // pointer is still associated with a value.
406 const auto &CallExpectState
= blockStateForStmt(BlockStates
, CallExpect
);
407 EXPECT_NE(CallExpectState
.Env
.getValue(FnToPtrDecay
), nullptr);
410 struct NonConvergingLattice
{
413 bool operator==(const NonConvergingLattice
&Other
) const {
414 return State
== Other
.State
;
417 LatticeJoinEffect
join(const NonConvergingLattice
&Other
) {
418 if (Other
.State
== 0)
419 return LatticeJoinEffect::Unchanged
;
420 State
+= Other
.State
;
421 return LatticeJoinEffect::Changed
;
425 class NonConvergingAnalysis
426 : public DataflowAnalysis
<NonConvergingAnalysis
, NonConvergingLattice
> {
428 explicit NonConvergingAnalysis(ASTContext
&Context
)
429 : DataflowAnalysis
<NonConvergingAnalysis
, NonConvergingLattice
>(
431 // Don't apply builtin transfer function.
432 DataflowAnalysisOptions
{std::nullopt
}) {}
434 static NonConvergingLattice
initialElement() { return {0}; }
436 void transfer(const CFGElement
&, NonConvergingLattice
&E
, Environment
&) {
441 TEST_F(DataflowAnalysisTest
, NonConvergingAnalysis
) {
442 std::string Code
= R
"(
447 auto Res
= runAnalysis
<NonConvergingAnalysis
>(
448 Code
, [](ASTContext
&C
) { return NonConvergingAnalysis(C
); });
449 EXPECT_EQ(llvm::toString(Res
.takeError()),
450 "maximum number of blocks processed");
453 // Regression test for joins of bool-typed lvalue expressions. The first loop
454 // results in two passes through the code that follows. Each pass results in a
455 // different `StorageLocation` for the pointee of `v`. Then, the second loop
456 // causes a join at the loop head where the two environments map expresssion
457 // `*v` to different `StorageLocation`s.
459 // An earlier version crashed for this condition (for boolean-typed lvalues), so
460 // this test only verifies that the analysis runs successfully, without
461 // examining any details of the results.
462 TEST_F(DataflowAnalysisTest
, JoinBoolLValues
) {
463 std::string Code
= R
"(
465 for (int x = 1; x; x = 0)
469 for (int x = 1; x; x = 0)
474 runAnalysis
<NoopAnalysis
>(Code
,
476 auto EnableBuiltIns
= DataflowAnalysisOptions
{
477 DataflowAnalysisContext::Options
{}};
478 return NoopAnalysis(C
, EnableBuiltIns
);
484 struct FunctionCallLattice
{
485 using FunctionSet
= llvm::SmallSet
<std::string
, 8>;
486 FunctionSet CalledFunctions
;
488 bool operator==(const FunctionCallLattice
&Other
) const {
489 return CalledFunctions
== Other
.CalledFunctions
;
492 LatticeJoinEffect
join(const FunctionCallLattice
&Other
) {
493 if (Other
.CalledFunctions
.empty())
494 return LatticeJoinEffect::Unchanged
;
495 const size_t size_before
= CalledFunctions
.size();
496 CalledFunctions
.insert(Other
.CalledFunctions
.begin(),
497 Other
.CalledFunctions
.end());
498 return CalledFunctions
.size() == size_before
? LatticeJoinEffect::Unchanged
499 : LatticeJoinEffect::Changed
;
503 std::ostream
&operator<<(std::ostream
&OS
, const FunctionCallLattice
&L
) {
505 llvm::raw_string_ostream
ROS(S
);
506 llvm::interleaveComma(L
.CalledFunctions
, ROS
);
507 return OS
<< "{" << S
<< "}";
510 class FunctionCallAnalysis
511 : public DataflowAnalysis
<FunctionCallAnalysis
, FunctionCallLattice
> {
513 explicit FunctionCallAnalysis(ASTContext
&Context
)
514 : DataflowAnalysis
<FunctionCallAnalysis
, FunctionCallLattice
>(Context
) {}
516 static FunctionCallLattice
initialElement() { return {}; }
518 void transfer(const CFGElement
&Elt
, FunctionCallLattice
&E
, Environment
&) {
519 auto CS
= Elt
.getAs
<CFGStmt
>();
522 const auto *S
= CS
->getStmt();
523 if (auto *C
= dyn_cast
<CallExpr
>(S
)) {
524 if (auto *F
= dyn_cast
<FunctionDecl
>(C
->getCalleeDecl())) {
525 E
.CalledFunctions
.insert(F
->getNameInfo().getAsString());
531 class NoreturnDestructorTest
: public Test
{
533 template <typename Matcher
>
534 void runDataflow(llvm::StringRef Code
, Matcher Expectations
) {
535 tooling::FileContentMappings FilesContents
;
536 FilesContents
.push_back(std::make_pair
<std::string
, std::string
>(
537 "noreturn_destructor_test_defs.h", R
"(
542 ~Fatal() __attribute__((noreturn));
555 test::checkDataflow
<FunctionCallAnalysis
>(
556 AnalysisInputs
<FunctionCallAnalysis
>(
557 Code
, ast_matchers::hasName("target"),
558 [](ASTContext
&C
, Environment
&) {
559 return FunctionCallAnalysis(C
);
561 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
562 .withASTBuildVirtualMappedFiles(std::move(FilesContents
)),
565 const llvm::StringMap
<
566 DataflowAnalysisState
<FunctionCallLattice
>> &Results
,
567 const AnalysisOutputs
&) {
568 EXPECT_THAT(Results
, Expectations
);
574 MATCHER_P(HoldsFunctionCallLattice
, m
,
575 ((negation
? "doesn't hold" : "holds") +
576 llvm::StringRef(" a lattice element that ") +
577 DescribeMatcher
<FunctionCallLattice
>(m
))
579 return ExplainMatchResult(m
, arg
.Lattice
, result_listener
);
582 MATCHER_P(HasCalledFunctions
, m
,
583 ((negation
? "doesn't hold" : "holds") +
584 llvm::StringRef(" a set of called functions that ") +
585 DescribeMatcher
<FunctionCallLattice::FunctionSet
>(m
))
587 return ExplainMatchResult(m
, arg
.CalledFunctions
, result_listener
);
590 TEST_F(NoreturnDestructorTest
, ConditionalOperatorBothBranchesReturn
) {
591 std::string Code
= R
"(
592 #include "noreturn_destructor_test_defs
.h
"
594 void target(bool b) {
595 int value = b ? foo() : NonFatal().bar();
600 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
601 "p", HoldsFunctionCallLattice(HasCalledFunctions(
602 UnorderedElementsAre("foo", "bar"))))));
605 TEST_F(NoreturnDestructorTest
, ConditionalOperatorLeftBranchReturns
) {
606 std::string Code
= R
"(
607 #include "noreturn_destructor_test_defs
.h
"
609 void target(bool b) {
610 int value = b ? foo() : Fatal().bar();
615 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
616 "p", HoldsFunctionCallLattice(HasCalledFunctions(
617 UnorderedElementsAre("foo"))))));
620 TEST_F(NoreturnDestructorTest
,
621 ConditionalOperatorConstantCondition_LeftBranchReturns
) {
622 std::string Code
= R
"(
623 #include "noreturn_destructor_test_defs
.h
"
626 int value = true ? foo() : Fatal().bar();
631 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
632 "p", HoldsFunctionCallLattice(HasCalledFunctions(
633 UnorderedElementsAre("foo"))))));
636 TEST_F(NoreturnDestructorTest
, ConditionalOperatorRightBranchReturns
) {
637 std::string Code
= R
"(
638 #include "noreturn_destructor_test_defs
.h
"
640 void target(bool b) {
641 int value = b ? Fatal().bar() : foo();
646 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
647 "p", HoldsFunctionCallLattice(HasCalledFunctions(
648 UnorderedElementsAre("foo"))))));
651 TEST_F(NoreturnDestructorTest
,
652 ConditionalOperatorConstantCondition_RightBranchReturns
) {
653 std::string Code
= R
"(
654 #include "noreturn_destructor_test_defs
.h
"
657 int value = false ? Fatal().bar() : foo();
662 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
663 "p", HoldsFunctionCallLattice(HasCalledFunctions(
664 UnorderedElementsAre("foo"))))));
667 TEST_F(NoreturnDestructorTest
, ConditionalOperatorNestedBranchesDoNotReturn
) {
668 std::string Code
= R
"(
669 #include "noreturn_destructor_test_defs
.h
"
671 void target(bool b1, bool b2) {
672 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz());
677 runDataflow(Code
, IsEmpty());
678 // FIXME: Called functions at point `p` should contain "foo".
681 TEST_F(NoreturnDestructorTest
, ConditionalOperatorNestedBranchReturns
) {
682 std::string Code
= R
"(
683 #include "noreturn_destructor_test_defs
.h
"
685 void target(bool b1, bool b2) {
686 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo());
691 runDataflow(Code
, UnorderedElementsAre(IsStringMapEntry(
692 "p", HoldsFunctionCallLattice(HasCalledFunctions(
693 UnorderedElementsAre("baz", "foo"))))));
694 // FIXME: Called functions at point `p` should contain only "foo".
697 // Models an analysis that uses flow conditions.
698 class SpecialBoolAnalysis final
699 : public DataflowAnalysis
<SpecialBoolAnalysis
, NoopLattice
> {
701 explicit SpecialBoolAnalysis(ASTContext
&Context
, Environment
&Env
)
702 : DataflowAnalysis
<SpecialBoolAnalysis
, NoopLattice
>(Context
) {
703 Env
.getDataflowAnalysisContext().setSyntheticFieldCallback(
704 [](QualType Ty
) -> llvm::StringMap
<QualType
> {
705 RecordDecl
*RD
= Ty
->getAsRecordDecl();
706 if (RD
== nullptr || RD
->getIdentifier() == nullptr ||
707 RD
->getName() != "SpecialBool")
709 return {{"is_set", RD
->getASTContext().BoolTy
}};
713 static NoopLattice
initialElement() { return {}; }
715 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
716 auto CS
= Elt
.getAs
<CFGStmt
>();
719 const auto *S
= CS
->getStmt();
720 auto SpecialBoolRecordDecl
= recordDecl(hasName("SpecialBool"));
721 auto HasSpecialBoolType
= hasType(SpecialBoolRecordDecl
);
723 if (const auto *E
= selectFirst
<CXXConstructExpr
>(
724 "call", match(cxxConstructExpr(HasSpecialBoolType
).bind("call"), *S
,
726 Env
.setValue(Env
.getResultObjectLocation(*E
).getSyntheticField("is_set"),
727 Env
.getBoolLiteralValue(false));
728 } else if (const auto *E
= selectFirst
<CXXMemberCallExpr
>(
729 "call", match(cxxMemberCallExpr(callee(cxxMethodDecl(ofClass(
730 SpecialBoolRecordDecl
))))
732 *S
, getASTContext()))) {
733 if (RecordStorageLocation
*ObjectLoc
= getImplicitObjectLocation(*E
, Env
))
734 Env
.setValue(ObjectLoc
->getSyntheticField("is_set"),
735 Env
.getBoolLiteralValue(true));
740 class JoinFlowConditionsTest
: public Test
{
742 template <typename Matcher
>
743 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
745 test::checkDataflow
<SpecialBoolAnalysis
>(
746 AnalysisInputs
<SpecialBoolAnalysis
>(
747 Code
, ast_matchers::hasName("target"),
748 [](ASTContext
&Context
, Environment
&Env
) {
749 return SpecialBoolAnalysis(Context
, Env
);
751 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
752 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
753 DataflowAnalysisState
<NoopLattice
>>
755 const AnalysisOutputs
756 &AO
) { Match(Results
, AO
.ASTCtx
); }),
761 TEST_F(JoinFlowConditionsTest
, JoinDistinctButProvablyEquivalentValues
) {
762 std::string Code
= R
"(
764 SpecialBool() = default;
768 void target(bool Cond) {
784 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
785 ASTContext
&ASTCtx
) {
786 ASSERT_THAT(Results
.keys(),
787 UnorderedElementsAre("p1", "p2", "p3", "p4"));
788 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
789 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
790 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
791 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
793 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
794 ASSERT_THAT(FooDecl
, NotNull());
796 auto GetFoo
= [FooDecl
](const Environment
&Env
) -> const Formula
& {
798 cast
<RecordStorageLocation
>(Env
.getStorageLocation(*FooDecl
));
799 return cast
<BoolValue
>(Env
.getValue(Loc
->getSyntheticField("is_set")))
803 EXPECT_FALSE(Env1
.proves(GetFoo(Env1
)));
804 EXPECT_TRUE(Env2
.proves(GetFoo(Env2
)));
805 EXPECT_TRUE(Env3
.proves(GetFoo(Env3
)));
806 EXPECT_TRUE(Env4
.proves(GetFoo(Env4
)));
810 class NullPointerAnalysis final
811 : public DataflowAnalysis
<NullPointerAnalysis
, NoopLattice
> {
813 explicit NullPointerAnalysis(ASTContext
&Context
)
814 : DataflowAnalysis
<NullPointerAnalysis
, NoopLattice
>(Context
) {}
816 static NoopLattice
initialElement() { return {}; }
818 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
819 auto CS
= Elt
.getAs
<CFGStmt
>();
822 const Stmt
*S
= CS
->getStmt();
823 const Expr
*E
= dyn_cast
<Expr
>(S
);
827 if (!E
->getType()->isPointerType())
830 // Make sure we have a `PointerValue` for `E`.
831 auto *PtrVal
= cast_or_null
<PointerValue
>(Env
.getValue(*E
));
832 if (PtrVal
== nullptr) {
833 PtrVal
= cast
<PointerValue
>(Env
.createValue(E
->getType()));
834 Env
.setValue(*E
, *PtrVal
);
837 if (auto *Cast
= dyn_cast
<ImplicitCastExpr
>(E
);
838 Cast
&& Cast
->getCastKind() == CK_NullToPointer
)
839 PtrVal
->setProperty("is_null", Env
.getBoolLiteralValue(true));
840 else if (auto *Op
= dyn_cast
<UnaryOperator
>(E
);
841 Op
&& Op
->getOpcode() == UO_AddrOf
)
842 PtrVal
->setProperty("is_null", Env
.getBoolLiteralValue(false));
845 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
846 const Environment
&Env1
, const Value
&Val2
,
847 const Environment
&Env2
) override
{
848 // Nothing to say about a value that is not a pointer.
849 if (!Type
->isPointerType())
850 return ComparisonResult::Unknown
;
852 auto *Prop1
= Val1
.getProperty("is_null");
853 auto *Prop2
= Val2
.getProperty("is_null");
854 assert(Prop1
!= nullptr && Prop2
!= nullptr);
855 return areEquivalentValues(*Prop1
, *Prop2
) ? ComparisonResult::Same
856 : ComparisonResult::Different
;
859 void join(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
860 const Value
&Val2
, const Environment
&Env2
, Value
&JoinedVal
,
861 Environment
&JoinedEnv
) override
{
862 // Nothing to say about a value that is not a pointer...
863 if (!Type
->isPointerType())
866 // ... or, a pointer without the `is_null` property.
867 auto *IsNull1
= cast_or_null
<BoolValue
>(Val1
.getProperty("is_null"));
868 auto *IsNull2
= cast_or_null
<BoolValue
>(Val2
.getProperty("is_null"));
869 if (IsNull1
== nullptr || IsNull2
== nullptr)
872 if (IsNull1
== IsNull2
)
873 JoinedVal
.setProperty("is_null", *IsNull1
);
875 JoinedVal
.setProperty("is_null", JoinedEnv
.makeTopBoolValue());
878 std::optional
<WidenResult
> widen(QualType Type
, Value
&Prev
,
879 const Environment
&PrevEnv
, Value
&Current
,
880 Environment
&CurrentEnv
) override
{
881 switch (compare(Type
, Prev
, PrevEnv
, Current
, CurrentEnv
)) {
882 case ComparisonResult::Same
:
883 return WidenResult
{&Current
, LatticeJoinEffect::Unchanged
};
884 case ComparisonResult::Different
: {
885 auto &CurPtr
= cast
<PointerValue
>(Current
);
887 CurrentEnv
.create
<PointerValue
>(CurPtr
.getPointeeLoc());
888 WidenedPtr
.setProperty("is_null", CurrentEnv
.makeTopBoolValue());
889 return WidenResult
{&WidenedPtr
, LatticeJoinEffect::Changed
};
891 case ComparisonResult::Unknown
:
894 llvm_unreachable("all cases in switch covered");
898 class WideningTest
: public Test
{
900 template <typename Matcher
>
901 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
903 checkDataflow
<NullPointerAnalysis
>(
904 AnalysisInputs
<NullPointerAnalysis
>(
905 Code
, ast_matchers::hasName("target"),
906 [](ASTContext
&Context
, Environment
&Env
) {
907 return NullPointerAnalysis(Context
);
909 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
910 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
911 DataflowAnalysisState
<NoopLattice
>>
913 const AnalysisOutputs
914 &AO
) { Match(Results
, AO
.ASTCtx
); }),
919 TEST_F(WideningTest
, JoinDistinctValuesWithDistinctProperties
) {
920 std::string Code
= R
"(
921 void target(bool Cond) {
935 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
936 ASTContext
&ASTCtx
) {
937 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
938 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
939 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
941 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
942 ASSERT_THAT(FooDecl
, NotNull());
944 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
945 return Env
.getValue(*FooDecl
);
948 EXPECT_EQ(GetFooValue(Env1
)->getProperty("is_null"),
949 &Env1
.getBoolLiteralValue(true));
950 EXPECT_EQ(GetFooValue(Env2
)->getProperty("is_null"),
951 &Env2
.getBoolLiteralValue(false));
953 isa
<TopBoolValue
>(GetFooValue(Env3
)->getProperty("is_null")));
957 TEST_F(WideningTest
, JoinDistinctValuesWithSameProperties
) {
958 std::string Code
= R
"(
959 void target(bool Cond) {
977 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
978 ASTContext
&ASTCtx
) {
979 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
980 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
981 const Environment
&Env3
= getEnvironmentAtAnnotation(Results
, "p3");
982 const Environment
&Env4
= getEnvironmentAtAnnotation(Results
, "p4");
984 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
985 ASSERT_THAT(FooDecl
, NotNull());
987 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
988 return Env
.getValue(*FooDecl
);
991 EXPECT_EQ(GetFooValue(Env1
)->getProperty("is_null"),
992 &Env1
.getBoolLiteralValue(true));
993 EXPECT_EQ(GetFooValue(Env2
)->getProperty("is_null"),
994 &Env2
.getBoolLiteralValue(false));
995 EXPECT_EQ(GetFooValue(Env3
)->getProperty("is_null"),
996 &Env3
.getBoolLiteralValue(false));
997 EXPECT_EQ(GetFooValue(Env4
)->getProperty("is_null"),
998 &Env4
.getBoolLiteralValue(false));
1002 TEST_F(WideningTest
, DistinctPointersToTheSameLocationAreEquivalent
) {
1003 std::string Code
= R
"(
1004 void target(int Foo, bool Cond) {
1015 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1016 ASTContext
&ASTCtx
) {
1017 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1018 const auto &FooLoc
=
1019 getLocForDecl
<ScalarStorageLocation
>(ASTCtx
, Env
, "Foo");
1020 const auto &BarVal
= getValueForDecl
<PointerValue
>(ASTCtx
, Env
, "Bar");
1021 EXPECT_EQ(&BarVal
.getPointeeLoc(), &FooLoc
);
1025 TEST_F(WideningTest
, DistinctValuesWithSamePropertiesAreEquivalent
) {
1026 std::string Code
= R
"(
1027 void target(bool Cond) {
1041 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1042 ASTContext
&ASTCtx
) {
1043 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1044 const auto &FooVal
= getValueForDecl
<Value
>(ASTCtx
, Env
, "Foo");
1045 EXPECT_EQ(FooVal
.getProperty("is_null"),
1046 &Env
.getBoolLiteralValue(false));
1050 TEST_F(WideningTest
, DistinctValuesWithDifferentPropertiesWidenedToTop
) {
1051 std::string Code
= R
"(
1052 void target(bool Cond) {
1065 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1066 ASTContext
&ASTCtx
) {
1067 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1068 const auto &FooVal
= getValueForDecl
<Value
>(ASTCtx
, Env
, "Foo");
1069 ASSERT_THAT(FooVal
.getProperty("is_null"), NotNull());
1070 EXPECT_TRUE(areEquivalentValues(*FooVal
.getProperty("is_null"),
1071 Env
.makeTopBoolValue()));
1075 class FlowConditionTest
: public Test
{
1077 template <typename Matcher
>
1078 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
1080 checkDataflow
<NoopAnalysis
>(
1081 AnalysisInputs
<NoopAnalysis
>(
1082 Code
, ast_matchers::hasName("target"),
1083 [](ASTContext
&Context
, Environment
&Env
) {
1084 return NoopAnalysis(Context
);
1086 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1087 /*VerifyResults=*/[&Match
](const llvm::StringMap
<
1088 DataflowAnalysisState
<NoopLattice
>>
1090 const AnalysisOutputs
1091 &AO
) { Match(Results
, AO
.ASTCtx
); }),
1096 TEST_F(FlowConditionTest
, IfStmtSingleVar
) {
1097 std::string Code
= R
"(
1098 void target(bool Foo) {
1110 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1111 ASTContext
&ASTCtx
) {
1112 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1113 ASSERT_THAT(FooDecl
, NotNull());
1115 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1117 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1118 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1119 EXPECT_TRUE(Env1
.proves(FooVal1
));
1121 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1122 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1123 EXPECT_FALSE(Env2
.proves(FooVal2
));
1127 TEST_F(FlowConditionTest
, IfStmtSingleNegatedVar
) {
1128 std::string Code
= R
"(
1129 void target(bool Foo) {
1141 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1142 ASTContext
&ASTCtx
) {
1143 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1144 ASSERT_THAT(FooDecl
, NotNull());
1146 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1148 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1149 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1150 EXPECT_FALSE(Env1
.proves(FooVal1
));
1152 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1153 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1154 EXPECT_TRUE(Env2
.proves(FooVal2
));
1158 TEST_F(FlowConditionTest
, WhileStmt
) {
1159 std::string Code
= R
"(
1160 void target(bool Foo) {
1169 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1170 ASTContext
&ASTCtx
) {
1171 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1172 ASSERT_THAT(FooDecl
, NotNull());
1174 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1175 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1177 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
1178 EXPECT_TRUE(Env
.proves(FooVal
));
1182 TEST_F(FlowConditionTest
, WhileStmtWithAssignmentInCondition
) {
1183 std::string Code
= R
"(
1184 void target(bool Foo) {
1185 // This test checks whether the analysis preserves the connection between
1186 // the value of `Foo` and the assignment expression, despite widening.
1187 // The equality operator generates a fresh boolean variable on each
1188 // interpretation, which forces use of widening.
1189 while ((Foo = (3 == 4))) {
1197 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1198 ASTContext
&ASTCtx
) {
1199 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1200 auto &FooVal
= getValueForDecl
<BoolValue
>(ASTCtx
, Env
, "Foo").formula();
1201 EXPECT_TRUE(Env
.proves(FooVal
));
1205 TEST_F(FlowConditionTest
, Conjunction
) {
1206 std::string Code
= R
"(
1207 void target(bool Foo, bool Bar) {
1217 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1219 ASTContext
&ASTCtx
) {
1220 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1221 ASSERT_THAT(FooDecl
, NotNull());
1223 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1224 ASSERT_THAT(BarDecl
, NotNull());
1226 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1228 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1229 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1230 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1231 EXPECT_TRUE(Env1
.proves(FooVal1
));
1232 EXPECT_TRUE(Env1
.proves(BarVal1
));
1234 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1235 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1236 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1237 EXPECT_FALSE(Env2
.proves(FooVal2
));
1238 EXPECT_FALSE(Env2
.proves(BarVal2
));
1242 TEST_F(FlowConditionTest
, Disjunction
) {
1243 std::string Code
= R
"(
1244 void target(bool Foo, bool Bar) {
1254 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1256 ASTContext
&ASTCtx
) {
1257 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1258 ASSERT_THAT(FooDecl
, NotNull());
1260 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1261 ASSERT_THAT(BarDecl
, NotNull());
1263 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1265 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1266 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1267 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1268 EXPECT_FALSE(Env1
.proves(FooVal1
));
1269 EXPECT_FALSE(Env1
.proves(BarVal1
));
1271 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1272 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1273 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1274 EXPECT_FALSE(Env2
.proves(FooVal2
));
1275 EXPECT_FALSE(Env2
.proves(BarVal2
));
1279 TEST_F(FlowConditionTest
, NegatedConjunction
) {
1280 std::string Code
= R
"(
1281 void target(bool Foo, bool Bar) {
1282 if (!(Foo && Bar)) {
1291 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1293 ASTContext
&ASTCtx
) {
1294 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1295 ASSERT_THAT(FooDecl
, NotNull());
1297 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1298 ASSERT_THAT(BarDecl
, NotNull());
1300 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1302 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1303 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1304 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1305 EXPECT_FALSE(Env1
.proves(FooVal1
));
1306 EXPECT_FALSE(Env1
.proves(BarVal1
));
1308 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1309 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1310 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1311 EXPECT_TRUE(Env2
.proves(FooVal2
));
1312 EXPECT_TRUE(Env2
.proves(BarVal2
));
1316 TEST_F(FlowConditionTest
, DeMorgan
) {
1317 std::string Code
= R
"(
1318 void target(bool Foo, bool Bar) {
1319 if (!(!Foo || !Bar)) {
1328 runDataflow(Code
, [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
1330 ASTContext
&ASTCtx
) {
1331 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1332 ASSERT_THAT(FooDecl
, NotNull());
1334 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1335 ASSERT_THAT(BarDecl
, NotNull());
1337 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1339 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1340 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1341 auto &BarVal1
= cast
<BoolValue
>(Env1
.getValue(*BarDecl
))->formula();
1342 EXPECT_TRUE(Env1
.proves(FooVal1
));
1343 EXPECT_TRUE(Env1
.proves(BarVal1
));
1345 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1346 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1347 auto &BarVal2
= cast
<BoolValue
>(Env2
.getValue(*BarDecl
))->formula();
1348 EXPECT_FALSE(Env2
.proves(FooVal2
));
1349 EXPECT_FALSE(Env2
.proves(BarVal2
));
1353 TEST_F(FlowConditionTest
, Join
) {
1354 std::string Code
= R
"(
1355 void target(bool Foo, bool Bar) {
1369 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1370 ASTContext
&ASTCtx
) {
1371 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1373 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1374 ASSERT_THAT(FooDecl
, NotNull());
1376 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1377 auto &FooVal
= cast
<BoolValue
>(Env
.getValue(*FooDecl
))->formula();
1378 EXPECT_TRUE(Env
.proves(FooVal
));
1382 // Verifies that flow conditions are properly constructed even when the
1383 // condition is not meaningfully interpreted.
1385 // Note: currently, arbitrary function calls are uninterpreted, so the test
1386 // exercises this case. If and when we change that, this test will not add to
1387 // coverage (although it may still test a valuable case).
1388 TEST_F(FlowConditionTest
, OpaqueFlowConditionJoinsToOpaqueBool
) {
1389 std::string Code
= R
"(
1402 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1403 ASTContext
&ASTCtx
) {
1404 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1405 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1407 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1408 ASSERT_THAT(BarDecl
, NotNull());
1410 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1412 EXPECT_FALSE(Env
.proves(BarVal
));
1416 // Verifies that flow conditions are properly constructed even when the
1417 // condition is not meaningfully interpreted.
1419 // Note: currently, fields with recursive type calls are uninterpreted (beneath
1420 // the first instance), so the test exercises this case. If and when we change
1421 // that, this test will not add to coverage (although it may still test a
1423 TEST_F(FlowConditionTest
, OpaqueFieldFlowConditionJoinsToOpaqueBool
) {
1424 std::string Code
= R
"(
1433 void target(Foo F) {
1443 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1444 ASTContext
&ASTCtx
) {
1445 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1446 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1448 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1449 ASSERT_THAT(BarDecl
, NotNull());
1451 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1453 EXPECT_FALSE(Env
.proves(BarVal
));
1457 // Verifies that flow conditions are properly constructed even when the
1458 // condition is not meaningfully interpreted. Adds to above by nesting the
1459 // interestnig case inside a normal branch. This protects against degenerate
1460 // solutions which only test for empty flow conditions, for example.
1461 TEST_F(FlowConditionTest
, OpaqueFlowConditionInsideBranchJoinsToOpaqueBool
) {
1462 std::string Code
= R
"(
1465 void target(bool Cond) {
1477 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1478 ASTContext
&ASTCtx
) {
1479 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1480 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1482 const ValueDecl
*BarDecl
= findValueDecl(ASTCtx
, "Bar");
1483 ASSERT_THAT(BarDecl
, NotNull());
1485 auto &BarVal
= cast
<BoolValue
>(Env
.getValue(*BarDecl
))->formula();
1487 EXPECT_FALSE(Env
.proves(BarVal
));
1491 TEST_F(FlowConditionTest
, PointerToBoolImplicitCast
) {
1492 std::string Code
= R
"(
1493 void target(int *Ptr) {
1506 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1507 ASTContext
&ASTCtx
) {
1508 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1510 const ValueDecl
*FooDecl
= findValueDecl(ASTCtx
, "Foo");
1511 ASSERT_THAT(FooDecl
, NotNull());
1513 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1514 auto &FooVal1
= cast
<BoolValue
>(Env1
.getValue(*FooDecl
))->formula();
1515 EXPECT_TRUE(Env1
.proves(FooVal1
));
1517 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1518 auto &FooVal2
= cast
<BoolValue
>(Env2
.getValue(*FooDecl
))->formula();
1519 EXPECT_FALSE(Env2
.proves(FooVal2
));
1523 class TopAnalysis final
: public DataflowAnalysis
<TopAnalysis
, NoopLattice
> {
1525 explicit TopAnalysis(ASTContext
&Context
)
1526 : DataflowAnalysis
<TopAnalysis
, NoopLattice
>(Context
) {}
1528 static NoopLattice
initialElement() { return {}; }
1530 void transfer(const CFGElement
&Elt
, NoopLattice
&, Environment
&Env
) {
1531 auto CS
= Elt
.getAs
<CFGStmt
>();
1534 const Stmt
*S
= CS
->getStmt();
1535 SmallVector
<BoundNodes
, 1> Matches
=
1536 match(callExpr(callee(functionDecl(hasName("makeTop")))).bind("top"),
1537 *S
, getASTContext());
1538 if (const auto *E
= selectFirst
<CallExpr
>("top", Matches
)) {
1539 Env
.setValue(*E
, Env
.makeTopBoolValue());
1543 ComparisonResult
compare(QualType Type
, const Value
&Val1
,
1544 const Environment
&Env1
, const Value
&Val2
,
1545 const Environment
&Env2
) override
{
1546 // Changes to a sound approximation, which allows us to test whether we can
1547 // (soundly) converge for some loops.
1548 return ComparisonResult::Unknown
;
1552 class TopTest
: public Test
{
1554 template <typename Matcher
>
1555 void runDataflow(llvm::StringRef Code
, Matcher VerifyResults
) {
1557 checkDataflow
<TopAnalysis
>(
1558 AnalysisInputs
<TopAnalysis
>(
1559 Code
, ast_matchers::hasName("target"),
1560 [](ASTContext
&Context
, Environment
&Env
) {
1561 return TopAnalysis(Context
);
1563 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1569 // Tests that when Top is unused it remains Top.
1570 TEST_F(TopTest
, UnusedTopInitializer
) {
1571 std::string Code
= R
"(
1575 bool Foo = makeTop();
1583 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1584 const AnalysisOutputs
&AO
) {
1585 ASSERT_THAT(Results
.keys(),
1586 UnorderedElementsAre("p1", "p2"));
1587 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1588 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1590 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1591 ASSERT_THAT(FooDecl
, NotNull());
1593 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1594 return Env
.getValue(*FooDecl
);
1597 Value
*FooVal1
= GetFooValue(Env1
);
1598 ASSERT_THAT(FooVal1
, NotNull());
1599 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1600 << debugString(FooVal1
->getKind());
1602 Value
*FooVal2
= GetFooValue(Env2
);
1603 ASSERT_THAT(FooVal2
, NotNull());
1604 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1605 << debugString(FooVal2
->getKind());
1607 EXPECT_EQ(FooVal1
, FooVal2
);
1611 // Tests that when Top is unused it remains Top. Like above, but uses the
1612 // assignment form rather than initialization, which uses Top as an lvalue that
1613 // is *not* in an rvalue position.
1614 TEST_F(TopTest
, UnusedTopAssignment
) {
1615 std::string Code
= R
"(
1628 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1629 const AnalysisOutputs
&AO
) {
1630 ASSERT_THAT(Results
.keys(),
1631 UnorderedElementsAre("p1", "p2"));
1632 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1633 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1635 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1636 ASSERT_THAT(FooDecl
, NotNull());
1638 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1639 return Env
.getValue(*FooDecl
);
1642 Value
*FooVal1
= GetFooValue(Env1
);
1643 ASSERT_THAT(FooVal1
, NotNull());
1644 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1645 << debugString(FooVal1
->getKind());
1647 Value
*FooVal2
= GetFooValue(Env2
);
1648 ASSERT_THAT(FooVal2
, NotNull());
1649 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1650 << debugString(FooVal2
->getKind());
1652 EXPECT_EQ(FooVal1
, FooVal2
);
1656 TEST_F(TopTest
, UnusedTopJoinsToTop
) {
1657 std::string Code
= R
"(
1660 void target(bool Cond, bool F) {
1661 bool Foo = makeTop();
1662 // Force a new CFG block.
1680 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1681 const AnalysisOutputs
&AO
) {
1682 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1683 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1684 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1686 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1687 ASSERT_THAT(FooDecl
, NotNull());
1689 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1690 return Env
.getValue(*FooDecl
);
1693 Value
*FooVal1
= GetFooValue(Env1
);
1694 ASSERT_THAT(FooVal1
, NotNull());
1695 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1696 << debugString(FooVal1
->getKind());
1698 Value
*FooVal2
= GetFooValue(Env2
);
1699 ASSERT_THAT(FooVal2
, NotNull());
1700 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1701 << debugString(FooVal2
->getKind());
1705 TEST_F(TopTest
, TopUsedBeforeBranchJoinsToSameAtomicBool
) {
1706 std::string Code
= R
"(
1709 void target(bool Cond, bool F) {
1710 bool Foo = makeTop();
1715 // Force a new CFG block.
1733 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1734 const AnalysisOutputs
&AO
) {
1735 ASSERT_THAT(Results
.keys(),
1736 UnorderedElementsAre("p0", "p1", "p2"));
1737 const Environment
&Env0
= getEnvironmentAtAnnotation(Results
, "p0");
1738 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1739 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1741 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1742 ASSERT_THAT(FooDecl
, NotNull());
1744 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1745 return Env
.getValue(*FooDecl
);
1748 Value
*FooVal0
= GetFooValue(Env0
);
1749 ASSERT_THAT(FooVal0
, NotNull());
1750 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal0
))
1751 << debugString(FooVal0
->getKind());
1753 Value
*FooVal1
= GetFooValue(Env1
);
1754 ASSERT_THAT(FooVal1
, NotNull());
1755 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal1
))
1756 << debugString(FooVal1
->getKind());
1758 Value
*FooVal2
= GetFooValue(Env2
);
1759 ASSERT_THAT(FooVal2
, NotNull());
1760 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1761 << debugString(FooVal2
->getKind());
1763 EXPECT_EQ(FooVal2
, FooVal1
);
1767 TEST_F(TopTest
, TopUsedInBothBranchesJoinsToAtomic
) {
1768 std::string Code
= R
"(
1771 void target(bool Cond, bool F) {
1772 bool Foo = makeTop();
1773 // Force a new CFG block.
1791 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1792 const AnalysisOutputs
&AO
) {
1793 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1794 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1795 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1797 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1798 ASSERT_THAT(FooDecl
, NotNull());
1800 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1801 return Env
.getValue(*FooDecl
);
1804 Value
*FooVal1
= GetFooValue(Env1
);
1805 ASSERT_THAT(FooVal1
, NotNull());
1806 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1807 << debugString(FooVal1
->getKind());
1809 Value
*FooVal2
= GetFooValue(Env2
);
1810 ASSERT_THAT(FooVal2
, NotNull());
1811 EXPECT_TRUE(isa
<AtomicBoolValue
>(FooVal2
))
1812 << debugString(FooVal2
->getKind());
1816 TEST_F(TopTest
, TopUsedInBothBranchesWithoutPrecisionLoss
) {
1817 std::string Code
= R
"(
1820 void target(bool Cond, bool F) {
1821 bool Foo = makeTop();
1822 // Force a new CFG block.
1838 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1839 const AnalysisOutputs
&AO
) {
1840 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
1841 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
1843 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1844 ASSERT_THAT(FooDecl
, NotNull());
1846 const ValueDecl
*BarDecl
= findValueDecl(AO
.ASTCtx
, "Bar");
1847 ASSERT_THAT(BarDecl
, NotNull());
1849 auto *FooVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*FooDecl
));
1850 ASSERT_THAT(FooVal
, NotNull());
1852 auto *BarVal
= dyn_cast_or_null
<BoolValue
>(Env
.getValue(*BarDecl
));
1853 ASSERT_THAT(BarVal
, NotNull());
1855 EXPECT_TRUE(Env
.proves(
1856 Env
.arena().makeEquals(FooVal
->formula(), BarVal
->formula())));
1860 TEST_F(TopTest
, TopUnusedBeforeLoopHeadJoinsToTop
) {
1861 std::string Code
= R
"(
1864 void target(bool Cond, bool F) {
1865 bool Foo = makeTop();
1866 // Force a new CFG block.
1883 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1884 const AnalysisOutputs
&AO
) {
1885 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p1", "p2"));
1886 const Environment
&Env1
= getEnvironmentAtAnnotation(Results
, "p1");
1887 const Environment
&Env2
= getEnvironmentAtAnnotation(Results
, "p2");
1889 const ValueDecl
*FooDecl
= findValueDecl(AO
.ASTCtx
, "Foo");
1890 ASSERT_THAT(FooDecl
, NotNull());
1892 auto GetFooValue
= [FooDecl
](const Environment
&Env
) {
1893 return Env
.getValue(*FooDecl
);
1896 Value
*FooVal1
= GetFooValue(Env1
);
1897 ASSERT_THAT(FooVal1
, NotNull());
1898 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal1
))
1899 << debugString(FooVal1
->getKind());
1901 Value
*FooVal2
= GetFooValue(Env2
);
1902 ASSERT_THAT(FooVal2
, NotNull());
1903 EXPECT_TRUE(isa
<TopBoolValue
>(FooVal2
))
1904 << debugString(FooVal2
->getKind());
1909 TEST_F(TopTest
, ForRangeStmtConverges
) {
1910 std::string Code
= R
"(
1911 void target(bool Foo) {
1919 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
1920 const AnalysisOutputs
&) {
1921 // No additional expectations. We're only checking that the
1922 // analysis converged.