blob: 1f5fce1f2dd4677b3ab71f235c9a07faf25867ba [file] [log] [blame]
//===- unittests/Analysis/FlowSensitive/RecordOpsTest.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/RecordOps.h"
#include "TestingSupport.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
namespace clang {
namespace dataflow {
namespace test {
namespace {
void runDataflow(
llvm::StringRef Code,
std::function<
void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
ASTContext &)>
VerifyResults,
LangStandard::Kind Std = LangStandard::lang_cxx17,
llvm::StringRef TargetFun = "target") {
ASSERT_THAT_ERROR(
checkDataflowWithNoopAnalysis(Code, VerifyResults,
DataflowAnalysisOptions{BuiltinOptions{}},
Std, TargetFun),
llvm::Succeeded());
}
TEST(RecordOpsTest, CopyRecord) {
std::string Code = R"(
struct S {
int outer_int;
int &ref;
struct {
int inner_int;
} inner;
};
void target(S s1, S s2) {
(void)s1.outer_int;
(void)s1.ref;
(void)s1.inner.inner_int;
// [[p]]
}
)";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
auto &S1 = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "s1");
auto &S2 = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "s2");
auto &Inner1 = cast<AggregateStorageLocation>(S1.getChild(*InnerDecl));
auto &Inner2 = cast<AggregateStorageLocation>(S2.getChild(*InnerDecl));
EXPECT_NE(Env.getValue(S1.getChild(*OuterIntDecl)),
Env.getValue(S2.getChild(*OuterIntDecl)));
EXPECT_NE(Env.getValue(S1.getChild(*RefDecl)),
Env.getValue(S2.getChild(*RefDecl)));
EXPECT_NE(Env.getValue(Inner1.getChild(*InnerIntDecl)),
Env.getValue(Inner2.getChild(*InnerIntDecl)));
auto *S1Val = cast<StructValue>(Env.getValue(S1));
auto *S2Val = cast<StructValue>(Env.getValue(S2));
EXPECT_NE(S1Val, S2Val);
S1Val->setProperty("prop", Env.getBoolLiteralValue(true));
copyRecord(S1, S2, Env);
EXPECT_EQ(Env.getValue(S1.getChild(*OuterIntDecl)),
Env.getValue(S2.getChild(*OuterIntDecl)));
EXPECT_EQ(Env.getValue(S1.getChild(*RefDecl)),
Env.getValue(S2.getChild(*RefDecl)));
EXPECT_EQ(Env.getValue(Inner1.getChild(*InnerIntDecl)),
Env.getValue(Inner2.getChild(*InnerIntDecl)));
S1Val = cast<StructValue>(Env.getValue(S1));
S2Val = cast<StructValue>(Env.getValue(S2));
EXPECT_NE(S1Val, S2Val);
EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true));
});
}
TEST(RecordOpsTest, RecordsEqual) {
std::string Code = R"(
struct S {
int outer_int;
int &ref;
struct {
int inner_int;
} inner;
};
void target(S s1, S s2) {
(void)s1.outer_int;
(void)s1.ref;
(void)s1.inner.inner_int;
// [[p]]
}
)";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");
const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");
const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");
const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");
auto &S1 = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "s1");
auto &S2 = getLocForDecl<AggregateStorageLocation>(ASTCtx, Env, "s2");
auto &Inner2 = cast<AggregateStorageLocation>(S2.getChild(*InnerDecl));
cast<StructValue>(Env.getValue(S1))
->setProperty("prop", Env.getBoolLiteralValue(true));
// Strategy: Create two equal records, then verify each of the various
// ways in which records can differ causes recordsEqual to return false.
// changes we can make to the record.
// This test reuses the same objects for multiple checks, which isn't
// great, but seems better than duplicating the setup code for every
// check.
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 has a different outer_int.
Env.setValue(S2.getChild(*OuterIntDecl), Env.create<IntegerValue>());
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 doesn't have outer_int at all.
Env.clearValue(S2.getChild(*OuterIntDecl));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 has a different ref.
Env.setValue(S2.getChild(*RefDecl),
Env.create<ReferenceValue>(Env.createStorageLocation(
RefDecl->getType().getNonReferenceType())));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 doesn't have ref at all.
Env.clearValue(S2.getChild(*RefDecl));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 as a different inner_int.
Env.setValue(Inner2.getChild(*InnerIntDecl),
Env.create<IntegerValue>());
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S1 and S2 have the same property with different values.
cast<StructValue>(Env.getValue(S2))
->setProperty("prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S1 has a property that S2 doesn't have.
cast<StructValue>(Env.getValue(S1))
->setProperty("other_prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
// We modified S1 this time, so need to copy back the other way.
copyRecord(S2, S1, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S2 has a property that S1 doesn't have.
cast<StructValue>(Env.getValue(S2))
->setProperty("other_prop", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
copyRecord(S1, S2, Env);
EXPECT_TRUE(recordsEqual(S1, S2, Env));
// S1 and S2 have the same number of properties, but with different
// names.
cast<StructValue>(Env.getValue(S1))
->setProperty("prop1", Env.getBoolLiteralValue(false));
cast<StructValue>(Env.getValue(S2))
->setProperty("prop2", Env.getBoolLiteralValue(false));
EXPECT_FALSE(recordsEqual(S1, S2, Env));
});
}
} // namespace
} // namespace test
} // namespace dataflow
} // namespace clang