| //===- unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp ---===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" |
| #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include <memory> |
| |
| namespace { |
| |
| using namespace clang; |
| using namespace dataflow; |
| |
| class DataflowAnalysisContextTest : public ::testing::Test { |
| protected: |
| DataflowAnalysisContextTest() |
| : Context(std::make_unique<WatchedLiteralsSolver>()), A(Context.arena()) { |
| } |
| |
| DataflowAnalysisContext Context; |
| Arena &A; |
| }; |
| |
| TEST_F(DataflowAnalysisContextTest, DistinctTopsNotEquivalent) { |
| auto &X = A.makeTopValue(); |
| auto &Y = A.makeTopValue(); |
| EXPECT_FALSE(Context.equivalentFormulas(X.formula(), Y.formula())); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, TautologicalFlowConditionImplies) { |
| Atom FC = A.makeFlowConditionToken(); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, A.makeLiteral(true))); |
| EXPECT_FALSE(Context.flowConditionImplies(FC, A.makeLiteral(false))); |
| EXPECT_FALSE(Context.flowConditionImplies(FC, A.makeAtomRef(A.makeAtom()))); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, TautologicalFlowConditionAllows) { |
| Atom FC = A.makeFlowConditionToken(); |
| EXPECT_TRUE(Context.flowConditionAllows(FC, A.makeLiteral(true))); |
| EXPECT_FALSE(Context.flowConditionAllows(FC, A.makeLiteral(false))); |
| EXPECT_TRUE(Context.flowConditionAllows(FC, A.makeAtomRef(A.makeAtom()))); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, ContradictoryFlowConditionImpliesAnything) { |
| Atom FC = A.makeFlowConditionToken(); |
| Context.addFlowConditionConstraint(FC, A.makeLiteral(false)); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, A.makeLiteral(true))); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, A.makeLiteral(false))); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, A.makeAtomRef(A.makeAtom()))); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, ContradictoryFlowConditionAllowsNothing) { |
| Atom FC = A.makeFlowConditionToken(); |
| Context.addFlowConditionConstraint(FC, A.makeLiteral(false)); |
| EXPECT_FALSE(Context.flowConditionAllows(FC, A.makeLiteral(true))); |
| EXPECT_FALSE(Context.flowConditionAllows(FC, A.makeLiteral(false))); |
| EXPECT_FALSE(Context.flowConditionAllows(FC, A.makeAtomRef(A.makeAtom()))); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, AddFlowConditionConstraint) { |
| Atom FC = A.makeFlowConditionToken(); |
| auto &C = A.makeAtomRef(A.makeAtom()); |
| Context.addFlowConditionConstraint(FC, C); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, C)); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, AddInvariant) { |
| Atom FC = A.makeFlowConditionToken(); |
| auto &C = A.makeAtomRef(A.makeAtom()); |
| Context.addInvariant(C); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, C)); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, InvariantAndFCConstraintInteract) { |
| Atom FC = A.makeFlowConditionToken(); |
| auto &C = A.makeAtomRef(A.makeAtom()); |
| auto &D = A.makeAtomRef(A.makeAtom()); |
| Context.addInvariant(A.makeImplies(C, D)); |
| Context.addFlowConditionConstraint(FC, C); |
| EXPECT_TRUE(Context.flowConditionImplies(FC, D)); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, ForkFlowCondition) { |
| Atom FC1 = A.makeFlowConditionToken(); |
| auto &C1 = A.makeAtomRef(A.makeAtom()); |
| Context.addFlowConditionConstraint(FC1, C1); |
| |
| // Forked flow condition inherits the constraints of its parent flow |
| // condition. |
| Atom FC2 = Context.forkFlowCondition(FC1); |
| EXPECT_TRUE(Context.flowConditionImplies(FC2, C1)); |
| |
| // Adding a new constraint to the forked flow condition does not affect its |
| // parent flow condition. |
| auto &C2 = A.makeAtomRef(A.makeAtom()); |
| Context.addFlowConditionConstraint(FC2, C2); |
| EXPECT_TRUE(Context.flowConditionImplies(FC2, C2)); |
| EXPECT_FALSE(Context.flowConditionImplies(FC1, C2)); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, JoinFlowConditions) { |
| auto &C1 = A.makeAtomRef(A.makeAtom()); |
| auto &C2 = A.makeAtomRef(A.makeAtom()); |
| auto &C3 = A.makeAtomRef(A.makeAtom()); |
| |
| Atom FC1 = A.makeFlowConditionToken(); |
| Context.addFlowConditionConstraint(FC1, C1); |
| Context.addFlowConditionConstraint(FC1, C3); |
| |
| Atom FC2 = A.makeFlowConditionToken(); |
| Context.addFlowConditionConstraint(FC2, C2); |
| Context.addFlowConditionConstraint(FC2, C3); |
| |
| Atom FC3 = Context.joinFlowConditions(FC1, FC2); |
| EXPECT_FALSE(Context.flowConditionImplies(FC3, C1)); |
| EXPECT_FALSE(Context.flowConditionImplies(FC3, C2)); |
| EXPECT_TRUE(Context.flowConditionImplies(FC3, C3)); |
| } |
| |
| TEST_F(DataflowAnalysisContextTest, EquivBoolVals) { |
| auto &X = A.makeAtomRef(A.makeAtom()); |
| auto &Y = A.makeAtomRef(A.makeAtom()); |
| auto &Z = A.makeAtomRef(A.makeAtom()); |
| auto &True = A.makeLiteral(true); |
| auto &False = A.makeLiteral(false); |
| |
| // X == X |
| EXPECT_TRUE(Context.equivalentFormulas(X, X)); |
| // X != Y |
| EXPECT_FALSE(Context.equivalentFormulas(X, Y)); |
| |
| // !X != X |
| EXPECT_FALSE(Context.equivalentFormulas(A.makeNot(X), X)); |
| // !(!X) = X |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeNot(A.makeNot(X)), X)); |
| |
| // (X || X) == X |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeOr(X, X), X)); |
| // (X || Y) != X |
| EXPECT_FALSE(Context.equivalentFormulas(A.makeOr(X, Y), X)); |
| // (X || True) == True |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeOr(X, True), True)); |
| // (X || False) == X |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeOr(X, False), X)); |
| |
| // (X && X) == X |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeAnd(X, X), X)); |
| // (X && Y) != X |
| EXPECT_FALSE(Context.equivalentFormulas(A.makeAnd(X, Y), X)); |
| // (X && True) == X |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeAnd(X, True), X)); |
| // (X && False) == False |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeAnd(X, False), False)); |
| |
| // (X || Y) == (Y || X) |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeOr(X, Y), A.makeOr(Y, X))); |
| // (X && Y) == (Y && X) |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeAnd(X, Y), A.makeAnd(Y, X))); |
| |
| // ((X || Y) || Z) == (X || (Y || Z)) |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeOr(A.makeOr(X, Y), Z), |
| A.makeOr(X, A.makeOr(Y, Z)))); |
| // ((X && Y) && Z) == (X && (Y && Z)) |
| EXPECT_TRUE(Context.equivalentFormulas(A.makeAnd(A.makeAnd(X, Y), Z), |
| A.makeAnd(X, A.makeAnd(Y, Z)))); |
| } |
| |
| } // namespace |