1 //===- unittests/Analysis/FlowSensitive/SignAnalysisTest.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 // This file defines a simplistic version of Sign Analysis as a demo of a
10 // forward, monotonic dataflow analysis. The implementation uses 3 boolean
11 // values to represent the sign lattice (negative, zero, positive). In
12 // practice, 2 booleans would be enough, however, this approach has the
13 // advantage of clarity over the optimized solution.
15 //===----------------------------------------------------------------------===//
17 #include "TestingSupport.h"
18 #include "clang/ASTMatchers/ASTMatchFinder.h"
19 #include "clang/ASTMatchers/ASTMatchers.h"
20 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
21 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
22 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Testing/Annotations/Annotations.h"
25 #include "llvm/Testing/Support/Error.h"
26 #include "gtest/gtest.h"
31 using namespace clang
;
32 using namespace dataflow
;
33 using namespace ast_matchers
;
35 using ::testing::UnorderedElementsAre
;
37 enum class Sign
: int { Negative
, Zero
, Positive
};
39 Sign
getSign(int64_t V
) {
40 return V
== 0 ? Sign::Zero
: (V
< 0 ? Sign::Negative
: Sign::Positive
);
43 using LatticeTransferState
= TransferState
<NoopLattice
>;
45 constexpr char kVar
[] = "var";
47 void initNegative(Value
&Val
, Environment
&Env
) {
48 Val
.setProperty("neg", Env
.getBoolLiteralValue(true));
49 Val
.setProperty("zero", Env
.getBoolLiteralValue(false));
50 Val
.setProperty("pos", Env
.getBoolLiteralValue(false));
52 void initPositive(Value
&Val
, Environment
&Env
) {
53 Val
.setProperty("neg", Env
.getBoolLiteralValue(false));
54 Val
.setProperty("zero", Env
.getBoolLiteralValue(false));
55 Val
.setProperty("pos", Env
.getBoolLiteralValue(true));
57 void initZero(Value
&Val
, Environment
&Env
) {
58 Val
.setProperty("neg", Env
.getBoolLiteralValue(false));
59 Val
.setProperty("zero", Env
.getBoolLiteralValue(true));
60 Val
.setProperty("pos", Env
.getBoolLiteralValue(false));
63 // The boolean properties that are associated to a Value. If a property is not
64 // set then these are null pointers, otherwise, the pointed BoolValues are
65 // owned by the Environment.
66 struct SignProperties
{
67 BoolValue
*Neg
, *Zero
, *Pos
;
69 void setSignProperties(Value
&Val
, const SignProperties
&Ps
) {
70 Val
.setProperty("neg", *Ps
.Neg
);
71 Val
.setProperty("zero", *Ps
.Zero
);
72 Val
.setProperty("pos", *Ps
.Pos
);
74 SignProperties
initUnknown(Value
&Val
, Environment
&Env
) {
75 SignProperties Ps
{&Env
.makeAtomicBoolValue(), &Env
.makeAtomicBoolValue(),
76 &Env
.makeAtomicBoolValue()};
77 setSignProperties(Val
, Ps
);
80 SignProperties
getSignProperties(const Value
&Val
, const Environment
&Env
) {
81 return {dyn_cast_or_null
<BoolValue
>(Val
.getProperty("neg")),
82 dyn_cast_or_null
<BoolValue
>(Val
.getProperty("zero")),
83 dyn_cast_or_null
<BoolValue
>(Val
.getProperty("pos"))};
86 void transferUninitializedInt(const DeclStmt
*D
,
87 const MatchFinder::MatchResult
&M
,
88 LatticeTransferState
&State
) {
89 const auto *Var
= M
.Nodes
.getNodeAs
<clang::VarDecl
>(kVar
);
90 assert(Var
!= nullptr);
91 const StorageLocation
*Loc
= State
.Env
.getStorageLocation(*Var
);
92 Value
*Val
= State
.Env
.getValue(*Loc
);
93 initUnknown(*Val
, State
.Env
);
96 // Get the Value (1), the properties for the operand (2), and the properties
97 // for the unary operator (3). The return value is a tuple of (1,2,3).
99 // The returned Value (1) is a nullptr, if there is no Value associated to the
100 // operand of the unary operator, or if the properties are not set for that
102 // Other than that, new sign properties are created for the Value of the
103 // unary operator and a new Value is created for the unary operator itself if
104 // it hadn't have any previously.
105 std::tuple
<Value
*, SignProperties
, SignProperties
>
106 getValueAndSignProperties(const UnaryOperator
*UO
,
107 const MatchFinder::MatchResult
&M
,
108 LatticeTransferState
&State
) {
109 // The DeclRefExpr refers to this variable in the operand.
110 const auto *OperandVar
= M
.Nodes
.getNodeAs
<clang::VarDecl
>(kVar
);
111 assert(OperandVar
!= nullptr);
112 const auto *OperandValue
= State
.Env
.getValue(*OperandVar
);
114 return {nullptr, {}, {}};
116 // Value of the unary op.
117 auto *UnaryOpValue
= State
.Env
.getValue(*UO
);
119 UnaryOpValue
= &State
.Env
.makeAtomicBoolValue();
120 State
.Env
.setValue(*UO
, *UnaryOpValue
);
123 // Properties for the operand (sub expression).
124 SignProperties OperandProps
= getSignProperties(*OperandValue
, State
.Env
);
125 if (OperandProps
.Neg
== nullptr)
126 return {nullptr, {}, {}};
127 // Properties for the operator expr itself.
128 SignProperties UnaryOpProps
= initUnknown(*UnaryOpValue
, State
.Env
);
129 return {UnaryOpValue
, UnaryOpProps
, OperandProps
};
132 void transferBinary(const BinaryOperator
*BO
, const MatchFinder::MatchResult
&M
,
133 LatticeTransferState
&State
) {
134 auto &A
= State
.Env
.arena();
136 if (BoolValue
*V
= State
.Env
.get
<BoolValue
>(*BO
)) {
137 Comp
= &V
->formula();
139 Comp
= &A
.makeAtomRef(A
.makeAtom());
140 State
.Env
.setValue(*BO
, A
.makeBoolValue(*Comp
));
143 // FIXME Use this as well:
144 // auto *NegatedComp = &State.Env.makeNot(*Comp);
146 auto *LHS
= State
.Env
.getValue(*BO
->getLHS());
147 auto *RHS
= State
.Env
.getValue(*BO
->getRHS());
152 SignProperties LHSProps
= getSignProperties(*LHS
, State
.Env
);
153 SignProperties RHSProps
= getSignProperties(*RHS
, State
.Env
);
154 if (LHSProps
.Neg
== nullptr || RHSProps
.Neg
== nullptr)
157 switch (BO
->getOpcode()) {
161 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Pos
->formula(),
162 LHSProps
.Pos
->formula())));
165 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Zero
->formula(),
166 LHSProps
.Pos
->formula())));
171 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Neg
->formula(),
172 LHSProps
.Neg
->formula())));
175 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Zero
->formula(),
176 LHSProps
.Neg
->formula())));
181 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Pos
->formula(),
182 LHSProps
.Pos
->formula())));
187 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Neg
->formula(),
188 LHSProps
.Neg
->formula())));
192 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Neg
->formula(),
193 LHSProps
.Neg
->formula())));
195 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Zero
->formula(),
196 LHSProps
.Zero
->formula())));
198 A
.makeImplies(*Comp
, A
.makeImplies(RHSProps
.Pos
->formula(),
199 LHSProps
.Pos
->formula())));
204 llvm_unreachable("not implemented");
208 void transferUnaryMinus(const UnaryOperator
*UO
,
209 const MatchFinder::MatchResult
&M
,
210 LatticeTransferState
&State
) {
211 auto &A
= State
.Env
.arena();
212 auto [UnaryOpValue
, UnaryOpProps
, OperandProps
] =
213 getValueAndSignProperties(UO
, M
, State
);
217 // a is pos ==> -a is neg
219 A
.makeImplies(OperandProps
.Pos
->formula(), UnaryOpProps
.Neg
->formula()));
220 // a is neg ==> -a is pos
222 A
.makeImplies(OperandProps
.Neg
->formula(), UnaryOpProps
.Pos
->formula()));
223 // a is zero ==> -a is zero
224 State
.Env
.assume(A
.makeImplies(OperandProps
.Zero
->formula(),
225 UnaryOpProps
.Zero
->formula()));
228 void transferUnaryNot(const UnaryOperator
*UO
,
229 const MatchFinder::MatchResult
&M
,
230 LatticeTransferState
&State
) {
231 auto &A
= State
.Env
.arena();
232 auto [UnaryOpValue
, UnaryOpProps
, OperandProps
] =
233 getValueAndSignProperties(UO
, M
, State
);
237 // a is neg or pos ==> !a is zero
238 State
.Env
.assume(A
.makeImplies(
239 A
.makeOr(OperandProps
.Pos
->formula(), OperandProps
.Neg
->formula()),
240 UnaryOpProps
.Zero
->formula()));
242 // FIXME Handle this logic universally, not just for unary not. But Where to
243 // put the generic handler, transferExpr maybe?
244 if (auto *UOBoolVal
= dyn_cast
<BoolValue
>(UnaryOpValue
)) {
247 A
.makeEquals(UOBoolVal
->formula(), OperandProps
.Zero
->formula()));
248 // !a <==> !a is not zero
249 State
.Env
.assume(A
.makeEquals(UOBoolVal
->formula(),
250 A
.makeNot(UnaryOpProps
.Zero
->formula())));
254 // Returns the `Value` associated with `E` (which may be either a prvalue or
255 // glvalue). Creates a `Value` or `StorageLocation` as needed if `E` does not
256 // have either of these associated with it yet.
258 // If this functionality turns out to be needed in more cases, this function
259 // should be moved to a more central location.
260 Value
*getOrCreateValue(const Expr
*E
, Environment
&Env
) {
261 Value
*Val
= nullptr;
262 if (E
->isGLValue()) {
263 StorageLocation
*Loc
= Env
.getStorageLocation(*E
);
265 Loc
= &Env
.createStorageLocation(*E
);
266 Env
.setStorageLocation(*E
, *Loc
);
268 Val
= Env
.getValue(*Loc
);
270 Val
= Env
.createValue(E
->getType());
271 Env
.setValue(*Loc
, *Val
);
274 Val
= Env
.getValue(*E
);
276 Val
= Env
.createValue(E
->getType());
277 Env
.setValue(*E
, *Val
);
280 assert(Val
!= nullptr);
285 void transferExpr(const Expr
*E
, const MatchFinder::MatchResult
&M
,
286 LatticeTransferState
&State
) {
287 const ASTContext
&Context
= *M
.Context
;
289 Value
*Val
= getOrCreateValue(E
, State
.Env
);
291 // The sign symbolic values have been initialized already.
292 if (Val
->getProperty("neg"))
296 // An integer expression which we cannot evaluate.
297 if (!(E
->EvaluateAsInt(R
, Context
) && R
.Val
.isInt())) {
298 initUnknown(*Val
, State
.Env
);
302 const Sign S
= getSign(R
.Val
.getInt().getExtValue());
305 initNegative(*Val
, State
.Env
);
308 initZero(*Val
, State
.Env
);
311 initPositive(*Val
, State
.Env
);
316 auto refToVar() { return declRefExpr(to(varDecl().bind(kVar
))); }
318 auto buildTransferMatchSwitch() {
319 // Note, the order of the cases is important, the most generic should be
321 // FIXME Discover what happens if there are multiple matching ASTMatchers for
322 // one Stmt? All matching case's handler should be called and in what order?
323 return CFGMatchSwitchBuilder
<LatticeTransferState
>()
324 // a op b (comparison)
325 .CaseOfCFGStmt
<BinaryOperator
>(binaryOperator(isComparisonOperator()),
328 // FIXME handle binop +,-,*,/
331 .CaseOfCFGStmt
<UnaryOperator
>(
332 unaryOperator(hasOperatorName("-"),
333 hasUnaryOperand(hasDescendant(refToVar()))),
337 .CaseOfCFGStmt
<UnaryOperator
>(
338 unaryOperator(hasOperatorName("!"),
339 hasUnaryOperand(hasDescendant(refToVar()))),
343 .CaseOfCFGStmt
<DeclStmt
>(declStmt(hasSingleDecl(varDecl(
344 decl().bind(kVar
), hasType(isInteger()),
345 unless(hasInitializer(expr()))))),
346 transferUninitializedInt
)
349 .CaseOfCFGStmt
<Expr
>(expr(hasType(isInteger())), transferExpr
)
354 class SignPropagationAnalysis
355 : public DataflowAnalysis
<SignPropagationAnalysis
, NoopLattice
> {
357 SignPropagationAnalysis(ASTContext
&Context
)
358 : DataflowAnalysis
<SignPropagationAnalysis
, NoopLattice
>(Context
),
359 TransferMatchSwitch(buildTransferMatchSwitch()) {}
361 static NoopLattice
initialElement() { return {}; }
363 void transfer(const CFGElement
&Elt
, NoopLattice
&L
, Environment
&Env
) {
364 LatticeTransferState
State(L
, Env
);
365 TransferMatchSwitch(Elt
, getASTContext(), State
);
367 void join(QualType Type
, const Value
&Val1
, const Environment
&Env1
,
368 const Value
&Val2
, const Environment
&Env2
, Value
&MergedVal
,
369 Environment
&MergedEnv
) override
;
372 CFGMatchSwitch
<TransferState
<NoopLattice
>> TransferMatchSwitch
;
375 BoolValue
&joinBoolValues(BoolValue
&Bool1
, const Environment
&Env1
,
376 BoolValue
&Bool2
, const Environment
&Env2
,
377 Environment
&JoinedEnv
) {
378 if (&Bool1
== &Bool2
) {
382 auto &B1
= Bool1
.formula();
383 auto &B2
= Bool2
.formula();
385 auto &A
= JoinedEnv
.arena();
386 auto &JoinedBool
= JoinedEnv
.makeAtomicBoolValue();
388 // If `Bool1` and `Bool2` is constrained to the same true / false value,
389 // `JoinedBool` can be constrained similarly without needing to consider the
390 // path taken - this simplifies the flow condition tracked in `JoinedEnv`.
391 // Otherwise, information about which path was taken is used to associate
392 // `JoinedBool` with `Bool1` and `Bool2`.
393 if (Env1
.proves(B1
) && Env2
.proves(B2
)) {
394 JoinedEnv
.assume(JoinedBool
.formula());
395 } else if (Env1
.proves(A
.makeNot(B1
)) && Env2
.proves(A
.makeNot(B2
))) {
396 JoinedEnv
.assume(A
.makeNot(JoinedBool
.formula()));
401 void SignPropagationAnalysis::join(QualType Type
, const Value
&Val1
,
402 const Environment
&Env1
, const Value
&Val2
,
403 const Environment
&Env2
, Value
&JoinedVal
,
404 Environment
&JoinedEnv
) {
405 if (!Type
->isIntegerType())
407 SignProperties Ps1
= getSignProperties(Val1
, Env1
);
408 SignProperties Ps2
= getSignProperties(Val2
, Env2
);
409 if (!Ps1
.Neg
|| !Ps2
.Neg
)
411 BoolValue
&JoinedNeg
=
412 joinBoolValues(*Ps1
.Neg
, Env1
, *Ps2
.Neg
, Env2
, JoinedEnv
);
413 BoolValue
&JoinedZero
=
414 joinBoolValues(*Ps1
.Zero
, Env1
, *Ps2
.Zero
, Env2
, JoinedEnv
);
415 BoolValue
&JoinedPos
=
416 joinBoolValues(*Ps1
.Pos
, Env1
, *Ps2
.Pos
, Env2
, JoinedEnv
);
417 setSignProperties(JoinedVal
,
418 SignProperties
{&JoinedNeg
, &JoinedZero
, &JoinedPos
});
421 template <typename Matcher
>
422 void runDataflow(llvm::StringRef Code
, Matcher Match
,
423 LangStandard::Kind Std
= LangStandard::lang_cxx17
,
424 llvm::StringRef TargetFun
= "fun") {
425 using ast_matchers::hasName
;
427 checkDataflow
<SignPropagationAnalysis
>(
428 AnalysisInputs
<SignPropagationAnalysis
>(
429 Code
, hasName(TargetFun
),
430 [](ASTContext
&C
, Environment
&) {
431 return SignPropagationAnalysis(C
);
434 {"-fsyntax-only", "-fno-delayed-template-parsing",
436 std::string(LangStandard::getLangStandardForKind(Std
)
439 [&Match
](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
441 const AnalysisOutputs
&AO
) { Match(Results
, AO
.ASTCtx
); }),
445 // FIXME add this to testing support.
446 template <typename NodeType
, typename MatcherType
>
447 const NodeType
*findFirst(ASTContext
&ASTCtx
, const MatcherType
&M
) {
448 auto TargetNodes
= match(M
.bind("v"), ASTCtx
);
449 assert(TargetNodes
.size() == 1 && "Match must be unique");
450 auto *const Result
= selectFirst
<NodeType
>("v", TargetNodes
);
451 assert(Result
!= nullptr);
455 template <typename Node
>
456 std::pair
<testing::AssertionResult
, Value
*>
457 getProperty(const Environment
&Env
, ASTContext
&ASTCtx
, const Node
*N
,
458 StringRef Property
) {
460 return {testing::AssertionFailure() << "No node", nullptr};
461 const StorageLocation
*Loc
= Env
.getStorageLocation(*N
);
462 if (!isa_and_nonnull
<ScalarStorageLocation
>(Loc
))
463 return {testing::AssertionFailure() << "No location", nullptr};
464 const Value
*Val
= Env
.getValue(*Loc
);
466 return {testing::AssertionFailure() << "No value", nullptr};
467 auto *Prop
= Val
->getProperty(Property
);
468 if (!isa_and_nonnull
<BoolValue
>(Prop
))
469 return {testing::AssertionFailure() << "No property for " << Property
,
471 return {testing::AssertionSuccess(), Prop
};
474 // Test if the given property of the given node is implied by the flow
475 // condition. If 'Implies' is false then check if it is not implied.
476 template <typename Node
>
477 testing::AssertionResult
isPropertyImplied(const Environment
&Env
,
478 ASTContext
&ASTCtx
, const Node
*N
,
479 StringRef Property
, bool Implies
) {
480 auto [Result
, Prop
] = getProperty(Env
, ASTCtx
, N
, Property
);
483 auto *BVProp
= cast
<BoolValue
>(Prop
);
484 if (Env
.proves(BVProp
->formula()) != Implies
)
485 return testing::AssertionFailure()
486 << Property
<< " is " << (Implies
? "not" : "") << " implied"
487 << ", but should " << (Implies
? "" : "not ") << "be";
488 return testing::AssertionSuccess();
491 template <typename Node
>
492 testing::AssertionResult
isNegative(const Node
*N
, ASTContext
&ASTCtx
,
493 const Environment
&Env
) {
494 testing::AssertionResult R
= isPropertyImplied(Env
, ASTCtx
, N
, "neg", true);
497 R
= isPropertyImplied(Env
, ASTCtx
, N
, "zero", false);
500 return isPropertyImplied(Env
, ASTCtx
, N
, "pos", false);
502 template <typename Node
>
503 testing::AssertionResult
isPositive(const Node
*N
, ASTContext
&ASTCtx
,
504 const Environment
&Env
) {
505 testing::AssertionResult R
= isPropertyImplied(Env
, ASTCtx
, N
, "pos", true);
508 R
= isPropertyImplied(Env
, ASTCtx
, N
, "zero", false);
511 return isPropertyImplied(Env
, ASTCtx
, N
, "neg", false);
513 template <typename Node
>
514 testing::AssertionResult
isZero(const Node
*N
, ASTContext
&ASTCtx
,
515 const Environment
&Env
) {
516 testing::AssertionResult R
= isPropertyImplied(Env
, ASTCtx
, N
, "zero", true);
519 R
= isPropertyImplied(Env
, ASTCtx
, N
, "pos", false);
522 return isPropertyImplied(Env
, ASTCtx
, N
, "neg", false);
524 template <typename Node
>
525 testing::AssertionResult
isTop(const Node
*N
, ASTContext
&ASTCtx
,
526 const Environment
&Env
) {
527 testing::AssertionResult R
= isPropertyImplied(Env
, ASTCtx
, N
, "zero", false);
530 R
= isPropertyImplied(Env
, ASTCtx
, N
, "pos", false);
533 return isPropertyImplied(Env
, ASTCtx
, N
, "neg", false);
536 TEST(SignAnalysisTest
, Init
) {
537 std::string Code
= R
"(
551 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
552 ASTContext
&ASTCtx
) {
553 // ASTCtx.getTranslationUnitDecl()->dump();
554 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
555 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
557 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
558 const ValueDecl
*B
= findValueDecl(ASTCtx
, "b");
559 const ValueDecl
*C
= findValueDecl(ASTCtx
, "c");
560 const ValueDecl
*D
= findValueDecl(ASTCtx
, "d");
561 const ValueDecl
*E
= findValueDecl(ASTCtx
, "e");
562 const ValueDecl
*F
= findValueDecl(ASTCtx
, "f");
564 EXPECT_TRUE(isNegative(A
, ASTCtx
, Env
));
565 EXPECT_TRUE(isZero(B
, ASTCtx
, Env
));
566 EXPECT_TRUE(isPositive(C
, ASTCtx
, Env
));
567 EXPECT_TRUE(isTop(D
, ASTCtx
, Env
));
568 EXPECT_TRUE(isTop(E
, ASTCtx
, Env
));
569 EXPECT_TRUE(isPositive(F
, ASTCtx
, Env
));
571 LangStandard::lang_cxx17
);
574 TEST(SignAnalysisTest
, UnaryMinus
) {
575 std::string Code
= R
"(
584 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
585 ASTContext
&ASTCtx
) {
586 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
587 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
589 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
590 const ValueDecl
*B
= findValueDecl(ASTCtx
, "b");
591 EXPECT_TRUE(isPositive(A
, ASTCtx
, Env
));
592 EXPECT_TRUE(isNegative(B
, ASTCtx
, Env
));
594 LangStandard::lang_cxx17
);
597 TEST(SignAnalysisTest
, UnaryNot
) {
598 std::string Code
= R
"(
607 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
608 ASTContext
&ASTCtx
) {
609 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p"));
610 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
612 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
613 const ValueDecl
*B
= findValueDecl(ASTCtx
, "b");
614 EXPECT_TRUE(isPositive(A
, ASTCtx
, Env
));
615 EXPECT_TRUE(isZero(B
, ASTCtx
, Env
));
617 LangStandard::lang_cxx17
);
620 TEST(SignAnalysisTest
, UnaryNotInIf
) {
621 std::string Code
= R
"(
639 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
640 ASTContext
&ASTCtx
) {
641 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q"));
642 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
643 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
645 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
646 const ValueDecl
*PA
= findValueDecl(ASTCtx
, "p_a");
647 const ValueDecl
*PNA
= findValueDecl(ASTCtx
, "p_not_a");
648 const ValueDecl
*QA
= findValueDecl(ASTCtx
, "q_a");
649 const ValueDecl
*QNA
= findValueDecl(ASTCtx
, "q_not_a");
652 EXPECT_TRUE(isZero(A
, ASTCtx
, EnvP
));
653 EXPECT_TRUE(isZero(PA
, ASTCtx
, EnvP
));
654 EXPECT_TRUE(isTop(PNA
, ASTCtx
, EnvP
));
657 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvQ
));
658 EXPECT_TRUE(isTop(QA
, ASTCtx
, EnvQ
));
659 EXPECT_TRUE(isZero(QNA
, ASTCtx
, EnvQ
));
661 LangStandard::lang_cxx17
);
664 TEST(SignAnalysisTest
, BinaryGT
) {
665 std::string Code
= R
"(
690 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
691 ASTContext
&ASTCtx
) {
692 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r", "s"));
693 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
694 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
695 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
696 const Environment
&EnvS
= getEnvironmentAtAnnotation(Results
, "s");
698 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
701 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvP
));
703 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvQ
));
705 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
707 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvS
));
709 LangStandard::lang_cxx17
);
712 TEST(SignAnalysisTest
, BinaryLT
) {
713 std::string Code
= R
"(
738 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
739 ASTContext
&ASTCtx
) {
740 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r", "s"));
741 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
742 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
743 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
744 const Environment
&EnvS
= getEnvironmentAtAnnotation(Results
, "s");
746 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
749 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvP
));
751 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvQ
));
753 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
755 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvS
));
757 LangStandard::lang_cxx17
);
760 TEST(SignAnalysisTest
, BinaryGE
) {
761 std::string Code
= R
"(
786 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
787 ASTContext
&ASTCtx
) {
788 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r", "s"));
789 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
790 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
791 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
792 const Environment
&EnvS
= getEnvironmentAtAnnotation(Results
, "s");
794 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
797 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvP
));
799 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvQ
));
801 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
803 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvS
));
805 LangStandard::lang_cxx17
);
808 TEST(SignAnalysisTest
, BinaryLE
) {
809 std::string Code
= R
"(
834 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
835 ASTContext
&ASTCtx
) {
836 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r", "s"));
837 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
838 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
839 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
840 const Environment
&EnvS
= getEnvironmentAtAnnotation(Results
, "s");
842 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
845 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvP
));
847 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvQ
));
849 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
851 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvS
));
853 LangStandard::lang_cxx17
);
856 TEST(SignAnalysisTest
, BinaryEQ
) {
857 std::string Code
= R
"(
877 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
878 ASTContext
&ASTCtx
) {
879 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("n", "z", "p"));
880 const Environment
&EnvN
= getEnvironmentAtAnnotation(Results
, "n");
881 const Environment
&EnvZ
= getEnvironmentAtAnnotation(Results
, "z");
882 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
884 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
887 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvN
));
889 EXPECT_TRUE(isZero(A
, ASTCtx
, EnvZ
));
891 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvP
));
893 LangStandard::lang_cxx17
);
896 TEST(SignAnalysisTest
, ComplexLoopCondition
) {
897 std::string Code
= R
"(
901 while ((a = foo()) > 0 && (b = foo()) > 0) {
910 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
911 ASTContext
&ASTCtx
) {
912 const Environment
&Env
= getEnvironmentAtAnnotation(Results
, "p");
914 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
915 const ValueDecl
*B
= findValueDecl(ASTCtx
, "b");
917 EXPECT_TRUE(isPositive(A
, ASTCtx
, Env
));
918 EXPECT_TRUE(isPositive(B
, ASTCtx
, Env
));
920 LangStandard::lang_cxx17
);
923 TEST(SignAnalysisTest
, JoinToTop
) {
924 std::string Code
= R
"(
943 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
944 ASTContext
&ASTCtx
) {
945 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r"));
946 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
947 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
948 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
950 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
953 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvP
));
955 EXPECT_TRUE(isPositive(A
, ASTCtx
, EnvQ
));
957 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
959 LangStandard::lang_cxx17
);
962 TEST(SignAnalysisTest
, JoinToNeg
) {
963 std::string Code
= R
"(
982 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
983 ASTContext
&ASTCtx
) {
984 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r"));
985 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
986 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
987 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
989 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
992 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvP
));
994 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvQ
));
996 EXPECT_TRUE(isNegative(A
, ASTCtx
, EnvR
));
998 LangStandard::lang_cxx17
);
1001 TEST(SignAnalysisTest
, NestedIfs
) {
1002 std::string Code
= R
"(
1020 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
1021 ASTContext
&ASTCtx
) {
1022 ASSERT_THAT(Results
.keys(), UnorderedElementsAre("p", "q", "r"));
1023 const Environment
&EnvP
= getEnvironmentAtAnnotation(Results
, "p");
1024 const Environment
&EnvQ
= getEnvironmentAtAnnotation(Results
, "q");
1025 const Environment
&EnvR
= getEnvironmentAtAnnotation(Results
, "r");
1027 const ValueDecl
*A
= findValueDecl(ASTCtx
, "a");
1030 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvP
));
1032 EXPECT_TRUE(isZero(A
, ASTCtx
, EnvQ
));
1034 EXPECT_TRUE(isTop(A
, ASTCtx
, EnvR
));
1036 LangStandard::lang_cxx17
);