//===- ArenaTest.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/Arena.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang::dataflow { namespace { using llvm::HasValue; using testing::Ref; class ArenaTest : public ::testing::Test { protected: Arena A; }; TEST_F(ArenaTest, CreateAtomicBoolValueReturnsDistinctValues) { auto &X = A.makeAtomValue(); auto &Y = A.makeAtomValue(); EXPECT_NE(&X, &Y); } TEST_F(ArenaTest, CreateTopBoolValueReturnsDistinctValues) { auto &X = A.makeTopValue(); auto &Y = A.makeTopValue(); EXPECT_NE(&X, &Y); } TEST_F(ArenaTest, GetOrCreateConjunctionReturnsSameExprOnSubsequentCalls) { auto &X = A.makeAtomRef(A.makeAtom()); auto &Y = A.makeAtomRef(A.makeAtom()); auto &XAndY1 = A.makeAnd(X, Y); auto &XAndY2 = A.makeAnd(X, Y); EXPECT_EQ(&XAndY1, &XAndY2); auto &YAndX = A.makeAnd(Y, X); EXPECT_EQ(&XAndY1, &YAndX); auto &Z = A.makeAtomRef(A.makeAtom()); auto &XAndZ = A.makeAnd(X, Z); EXPECT_NE(&XAndY1, &XAndZ); } TEST_F(ArenaTest, GetOrCreateDisjunctionReturnsSameExprOnSubsequentCalls) { auto &X = A.makeAtomRef(A.makeAtom()); auto &Y = A.makeAtomRef(A.makeAtom()); auto &XOrY1 = A.makeOr(X, Y); auto &XOrY2 = A.makeOr(X, Y); EXPECT_EQ(&XOrY1, &XOrY2); auto &YOrX = A.makeOr(Y, X); EXPECT_EQ(&XOrY1, &YOrX); auto &Z = A.makeAtomRef(A.makeAtom()); auto &XOrZ = A.makeOr(X, Z); EXPECT_NE(&XOrY1, &XOrZ); } TEST_F(ArenaTest, GetOrCreateNegationReturnsSameExprOnSubsequentCalls) { auto &X = A.makeAtomRef(A.makeAtom()); auto &NotX1 = A.makeNot(X); auto &NotX2 = A.makeNot(X); EXPECT_EQ(&NotX1, &NotX2); auto &Y = A.makeAtomRef(A.makeAtom()); auto &NotY = A.makeNot(Y); EXPECT_NE(&NotX1, &NotY); } TEST_F(ArenaTest, GetOrCreateImplicationReturnsSameExprOnSubsequentCalls) { auto &X = A.makeAtomRef(A.makeAtom()); auto &Y = A.makeAtomRef(A.makeAtom()); auto &XImpliesY1 = A.makeImplies(X, Y); auto &XImpliesY2 = A.makeImplies(X, Y); EXPECT_EQ(&XImpliesY1, &XImpliesY2); auto &YImpliesX = A.makeImplies(Y, X); EXPECT_NE(&XImpliesY1, &YImpliesX); auto &Z = A.makeAtomRef(A.makeAtom()); auto &XImpliesZ = A.makeImplies(X, Z); EXPECT_NE(&XImpliesY1, &XImpliesZ); } TEST_F(ArenaTest, GetOrCreateIffReturnsSameExprOnSubsequentCalls) { auto &X = A.makeAtomRef(A.makeAtom()); auto &Y = A.makeAtomRef(A.makeAtom()); auto &XIffY1 = A.makeEquals(X, Y); auto &XIffY2 = A.makeEquals(X, Y); EXPECT_EQ(&XIffY1, &XIffY2); auto &YIffX = A.makeEquals(Y, X); EXPECT_EQ(&XIffY1, &YIffX); auto &Z = A.makeAtomRef(A.makeAtom()); auto &XIffZ = A.makeEquals(X, Z); EXPECT_NE(&XIffY1, &XIffZ); } TEST_F(ArenaTest, Interning) { Atom X = A.makeAtom(); Atom Y = A.makeAtom(); const Formula &F1 = A.makeAnd(A.makeAtomRef(X), A.makeAtomRef(Y)); const Formula &F2 = A.makeAnd(A.makeAtomRef(Y), A.makeAtomRef(X)); EXPECT_EQ(&F1, &F2); BoolValue &B1 = A.makeBoolValue(F1); BoolValue &B2 = A.makeBoolValue(F2); EXPECT_EQ(&B1, &B2); EXPECT_EQ(&B1.formula(), &F1); } TEST_F(ArenaTest, ParseFormula) { Atom V5{5}; Atom V6{6}; EXPECT_THAT_EXPECTED(A.parseFormula("V5"), HasValue(Ref(A.makeAtomRef(V5)))); EXPECT_THAT_EXPECTED(A.parseFormula("true"), HasValue(Ref(A.makeLiteral(true)))); EXPECT_THAT_EXPECTED(A.parseFormula("!V5"), HasValue(Ref(A.makeNot(A.makeAtomRef(V5))))); EXPECT_THAT_EXPECTED( A.parseFormula("(V5 = V6)"), HasValue(Ref(A.makeEquals(A.makeAtomRef(V5), A.makeAtomRef(V6))))); EXPECT_THAT_EXPECTED( A.parseFormula("(V5 => V6)"), HasValue(Ref(A.makeImplies(A.makeAtomRef(V5), A.makeAtomRef(V6))))); EXPECT_THAT_EXPECTED( A.parseFormula("(V5 & V6)"), HasValue(Ref(A.makeAnd(A.makeAtomRef(V5), A.makeAtomRef(V6))))); EXPECT_THAT_EXPECTED( A.parseFormula("(V5 | V6)"), HasValue(Ref(A.makeOr(A.makeAtomRef(V5), A.makeAtomRef(V6))))); EXPECT_THAT_EXPECTED( A.parseFormula("((V5 & (V6 & !false)) => ((V5 | V6) | false))"), HasValue(Ref( A.makeImplies(A.makeAnd(A.makeAtomRef(V5), A.makeAnd(A.makeAtomRef(V6), A.makeNot(A.makeLiteral(false)))), A.makeOr(A.makeOr(A.makeAtomRef(V5), A.makeAtomRef(V6)), A.makeLiteral(false)))))); EXPECT_THAT_EXPECTED( A.parseFormula("(V0 => error)"), llvm::FailedWithMessage(R"(bad formula at offset 7 (V0 => error) ^)")); EXPECT_THAT_EXPECTED( A.parseFormula("V1 V2"), llvm::FailedWithMessage(R"(bad formula at offset 3 V1 V2 ^)")); } TEST_F(ArenaTest, IdentitySimplification) { auto &X = A.makeAtomRef(A.makeAtom()); EXPECT_EQ(&X, &A.makeAnd(X, X)); EXPECT_EQ(&X, &A.makeOr(X, X)); EXPECT_EQ(&A.makeLiteral(true), &A.makeImplies(X, X)); EXPECT_EQ(&A.makeLiteral(true), &A.makeEquals(X, X)); EXPECT_EQ(&X, &A.makeNot(A.makeNot(X))); } TEST_F(ArenaTest, LiteralSimplification) { auto &X = A.makeAtomRef(A.makeAtom()); EXPECT_EQ(&X, &A.makeAnd(X, A.makeLiteral(true))); EXPECT_EQ(&A.makeLiteral(false), &A.makeAnd(X, A.makeLiteral(false))); EXPECT_EQ(&A.makeLiteral(true), &A.makeOr(X, A.makeLiteral(true))); EXPECT_EQ(&X, &A.makeOr(X, A.makeLiteral(false))); EXPECT_EQ(&A.makeLiteral(true), &A.makeImplies(X, A.makeLiteral(true))); EXPECT_EQ(&A.makeNot(X), &A.makeImplies(X, A.makeLiteral(false))); EXPECT_EQ(&X, &A.makeImplies(A.makeLiteral(true), X)); EXPECT_EQ(&A.makeLiteral(true), &A.makeImplies(A.makeLiteral(false), X)); EXPECT_EQ(&X, &A.makeEquals(X, A.makeLiteral(true))); EXPECT_EQ(&A.makeNot(X), &A.makeEquals(X, A.makeLiteral(false))); EXPECT_EQ(&A.makeLiteral(false), &A.makeNot(A.makeLiteral(true))); EXPECT_EQ(&A.makeLiteral(true), &A.makeNot(A.makeLiteral(false))); } } // namespace } // namespace clang::dataflow