4618 lines
146 KiB
C++
4618 lines
146 KiB
C++
//===- unittests/Analysis/FlowSensitive/TransferTest.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 "TestingSupport.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
|
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
|
|
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
|
|
#include "clang/Analysis/FlowSensitive/Value.h"
|
|
#include "clang/Basic/LangStandard.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
using namespace clang;
|
|
using namespace dataflow;
|
|
using namespace test;
|
|
using ::testing::IsNull;
|
|
using ::testing::NotNull;
|
|
using ::testing::UnorderedElementsAre;
|
|
|
|
template <typename Matcher>
|
|
void runDataflow(llvm::StringRef Code, Matcher Match,
|
|
DataflowAnalysisOptions Options,
|
|
LangStandard::Kind Std = LangStandard::lang_cxx17,
|
|
llvm::StringRef TargetFun = "target") {
|
|
using ast_matchers::hasName;
|
|
ASSERT_THAT_ERROR(
|
|
checkDataflow<NoopAnalysis>(
|
|
AnalysisInputs<NoopAnalysis>(Code, hasName(TargetFun),
|
|
[Options](ASTContext &C, Environment &) {
|
|
return NoopAnalysis(C, Options);
|
|
})
|
|
.withASTBuildArgs(
|
|
{"-fsyntax-only", "-fno-delayed-template-parsing",
|
|
"-std=" +
|
|
std::string(LangStandard::getLangStandardForKind(Std)
|
|
.getName())}),
|
|
/*VerifyResults=*/
|
|
[&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
|
|
&Results,
|
|
const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }),
|
|
llvm::Succeeded());
|
|
}
|
|
|
|
template <typename Matcher>
|
|
void runDataflow(llvm::StringRef Code, Matcher Match,
|
|
LangStandard::Kind Std = LangStandard::lang_cxx17,
|
|
bool ApplyBuiltinTransfer = true,
|
|
llvm::StringRef TargetFun = "target") {
|
|
runDataflow(Code, std::move(Match),
|
|
{ApplyBuiltinTransfer ? TransferOptions{}
|
|
: llvm::Optional<TransferOptions>()},
|
|
Std, TargetFun);
|
|
}
|
|
|
|
TEST(TransferTest, IntVarDeclNotTrackedWhenTransferDisabled) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getStorageLocation(*FooDecl, SkipPast::None), nullptr);
|
|
},
|
|
LangStandard::lang_cxx17,
|
|
/*ApplyBuiltinTransfer=*/false);
|
|
}
|
|
|
|
TEST(TransferTest, BoolVarDecl) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
bool Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<BoolValue>(FooVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, IntVarDecl) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructVarDecl) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isStructureType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructVarDeclWithInit) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
A Gen();
|
|
|
|
void target() {
|
|
A Foo = Gen();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isStructureType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ClassVarDecl) {
|
|
std::string Code = R"(
|
|
class A {
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isClassType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ReferenceVarDecl) {
|
|
std::string Code = R"(
|
|
struct A {};
|
|
|
|
A &getA();
|
|
|
|
void target() {
|
|
A &Foo = getA();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const ReferenceValue *FooVal =
|
|
cast<ReferenceValue>(Env.getValue(*FooLoc));
|
|
const StorageLocation &FooReferentLoc = FooVal->getReferentLoc();
|
|
EXPECT_TRUE(isa<AggregateStorageLocation>(&FooReferentLoc));
|
|
|
|
const Value *FooReferentVal = Env.getValue(FooReferentLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<StructValue>(FooReferentVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, SelfReferentialReferenceVarDecl) {
|
|
std::string Code = R"(
|
|
struct A;
|
|
|
|
struct B {};
|
|
|
|
struct C {
|
|
A &FooRef;
|
|
A *FooPtr;
|
|
B &BazRef;
|
|
B *BazPtr;
|
|
};
|
|
|
|
struct A {
|
|
C &Bar;
|
|
};
|
|
|
|
A &getA();
|
|
|
|
void target() {
|
|
A &Foo = getA();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
|
|
&Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isReferenceType());
|
|
ASSERT_TRUE(FooDecl->getType().getNonReferenceType()->isStructureType());
|
|
const auto FooFields =
|
|
FooDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
ASSERT_TRUE(BarDecl->getType()->isReferenceType());
|
|
ASSERT_TRUE(BarDecl->getType().getNonReferenceType()->isStructureType());
|
|
const auto BarFields =
|
|
BarDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *FooRefDecl = nullptr;
|
|
FieldDecl *FooPtrDecl = nullptr;
|
|
FieldDecl *BazRefDecl = nullptr;
|
|
FieldDecl *BazPtrDecl = nullptr;
|
|
for (FieldDecl *Field : BarFields) {
|
|
if (Field->getNameAsString() == "FooRef") {
|
|
FooRefDecl = Field;
|
|
} else if (Field->getNameAsString() == "FooPtr") {
|
|
FooPtrDecl = Field;
|
|
} else if (Field->getNameAsString() == "BazRef") {
|
|
BazRefDecl = Field;
|
|
} else if (Field->getNameAsString() == "BazPtr") {
|
|
BazPtrDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(FooRefDecl, NotNull());
|
|
ASSERT_THAT(FooPtrDecl, NotNull());
|
|
ASSERT_THAT(BazRefDecl, NotNull());
|
|
ASSERT_THAT(BazPtrDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<ScalarStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc));
|
|
const auto *FooReferentVal =
|
|
cast<StructValue>(Env.getValue(FooVal->getReferentLoc()));
|
|
|
|
const auto *BarVal =
|
|
cast<ReferenceValue>(FooReferentVal->getChild(*BarDecl));
|
|
const auto *BarReferentVal =
|
|
cast<StructValue>(Env.getValue(BarVal->getReferentLoc()));
|
|
|
|
const auto *FooRefVal =
|
|
cast<ReferenceValue>(BarReferentVal->getChild(*FooRefDecl));
|
|
const StorageLocation &FooReferentLoc = FooRefVal->getReferentLoc();
|
|
EXPECT_THAT(Env.getValue(FooReferentLoc), IsNull());
|
|
|
|
const auto *FooPtrVal =
|
|
cast<PointerValue>(BarReferentVal->getChild(*FooPtrDecl));
|
|
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
|
|
EXPECT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
|
|
|
|
const auto *BazRefVal =
|
|
cast<ReferenceValue>(BarReferentVal->getChild(*BazRefDecl));
|
|
const StorageLocation &BazReferentLoc = BazRefVal->getReferentLoc();
|
|
EXPECT_THAT(Env.getValue(BazReferentLoc), NotNull());
|
|
|
|
const auto *BazPtrVal =
|
|
cast<PointerValue>(BarReferentVal->getChild(*BazPtrDecl));
|
|
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
|
|
EXPECT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, PointerVarDecl) {
|
|
std::string Code = R"(
|
|
struct A {};
|
|
|
|
A *getA();
|
|
|
|
void target() {
|
|
A *Foo = getA();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
|
|
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
|
|
|
|
const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, SelfReferentialPointerVarDecl) {
|
|
std::string Code = R"(
|
|
struct A;
|
|
|
|
struct B {};
|
|
|
|
struct C {
|
|
A &FooRef;
|
|
A *FooPtr;
|
|
B &BazRef;
|
|
B *BazPtr;
|
|
};
|
|
|
|
struct A {
|
|
C *Bar;
|
|
};
|
|
|
|
A *getA();
|
|
|
|
void target() {
|
|
A *Foo = getA();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isPointerType());
|
|
ASSERT_TRUE(FooDecl->getType()
|
|
->getAs<PointerType>()
|
|
->getPointeeType()
|
|
->isStructureType());
|
|
const auto FooFields = FooDecl->getType()
|
|
->getAs<PointerType>()
|
|
->getPointeeType()
|
|
->getAsRecordDecl()
|
|
->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
ASSERT_TRUE(BarDecl->getType()->isPointerType());
|
|
ASSERT_TRUE(BarDecl->getType()
|
|
->getAs<PointerType>()
|
|
->getPointeeType()
|
|
->isStructureType());
|
|
const auto BarFields = BarDecl->getType()
|
|
->getAs<PointerType>()
|
|
->getPointeeType()
|
|
->getAsRecordDecl()
|
|
->fields();
|
|
|
|
FieldDecl *FooRefDecl = nullptr;
|
|
FieldDecl *FooPtrDecl = nullptr;
|
|
FieldDecl *BazRefDecl = nullptr;
|
|
FieldDecl *BazPtrDecl = nullptr;
|
|
for (FieldDecl *Field : BarFields) {
|
|
if (Field->getNameAsString() == "FooRef") {
|
|
FooRefDecl = Field;
|
|
} else if (Field->getNameAsString() == "FooPtr") {
|
|
FooPtrDecl = Field;
|
|
} else if (Field->getNameAsString() == "BazRef") {
|
|
BazRefDecl = Field;
|
|
} else if (Field->getNameAsString() == "BazPtr") {
|
|
BazPtrDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(FooRefDecl, NotNull());
|
|
ASSERT_THAT(FooPtrDecl, NotNull());
|
|
ASSERT_THAT(BazRefDecl, NotNull());
|
|
ASSERT_THAT(BazPtrDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<ScalarStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
|
|
const auto *FooPointeeVal =
|
|
cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
|
|
|
|
const auto *BarVal =
|
|
cast<PointerValue>(FooPointeeVal->getChild(*BarDecl));
|
|
const auto *BarPointeeVal =
|
|
cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
|
|
|
|
const auto *FooRefVal =
|
|
cast<ReferenceValue>(BarPointeeVal->getChild(*FooRefDecl));
|
|
const StorageLocation &FooReferentLoc = FooRefVal->getReferentLoc();
|
|
EXPECT_THAT(Env.getValue(FooReferentLoc), IsNull());
|
|
|
|
const auto *FooPtrVal =
|
|
cast<PointerValue>(BarPointeeVal->getChild(*FooPtrDecl));
|
|
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
|
|
EXPECT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
|
|
|
|
const auto *BazRefVal =
|
|
cast<ReferenceValue>(BarPointeeVal->getChild(*BazRefDecl));
|
|
const StorageLocation &BazReferentLoc = BazRefVal->getReferentLoc();
|
|
EXPECT_THAT(Env.getValue(BazReferentLoc), NotNull());
|
|
|
|
const auto *BazPtrVal =
|
|
cast<PointerValue>(BarPointeeVal->getChild(*BazPtrDecl));
|
|
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
|
|
EXPECT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, MultipleVarsDecl) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo, Bar;
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const StorageLocation *BarLoc =
|
|
Env.getStorageLocation(*BarDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, JoinVarDecl) {
|
|
std::string Code = R"(
|
|
void target(bool B) {
|
|
int Foo;
|
|
// [[p1]]
|
|
if (B) {
|
|
int Bar;
|
|
// [[p2]]
|
|
} else {
|
|
int Baz;
|
|
// [[p3]]
|
|
}
|
|
(void)0;
|
|
// [[p4]]
|
|
}
|
|
)";
|
|
runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
|
|
&Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2", "p3", "p4"));
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env1.getStorageLocation(*FooDecl, SkipPast::None);
|
|
EXPECT_THAT(FooLoc, NotNull());
|
|
EXPECT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
|
|
EXPECT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
|
|
|
|
const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
|
|
EXPECT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
|
|
EXPECT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull());
|
|
EXPECT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
|
|
|
|
const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3");
|
|
EXPECT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
|
|
EXPECT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
|
|
EXPECT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull());
|
|
|
|
const Environment &Env4 = getEnvironmentAtAnnotation(Results, "p4");
|
|
EXPECT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
|
|
EXPECT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
|
|
EXPECT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BinaryOperatorAssign) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int Bar;
|
|
(Bar) = (Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, VarDeclInitAssign) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int Bar = Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, VarDeclInitAssignChained) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int Bar;
|
|
int Baz = (Bar = Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
|
|
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, VarDeclInitAssignPtrDeref) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int *Bar;
|
|
*(Bar) = Foo;
|
|
int Baz = *(Bar);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarVal =
|
|
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal);
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AssignToAndFromReference) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int Bar;
|
|
int &Baz = Foo;
|
|
// [[p1]]
|
|
Baz = Bar;
|
|
int Qux = Baz;
|
|
int &Quux = Baz;
|
|
// [[p2]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
|
|
const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
|
|
const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal);
|
|
|
|
EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal);
|
|
EXPECT_EQ(Env2.getValue(*FooDecl, SkipPast::None), BarVal);
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
EXPECT_EQ(Env2.getValue(*QuxDecl, SkipPast::None), BarVal);
|
|
|
|
const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
|
|
ASSERT_THAT(QuuxDecl, NotNull());
|
|
EXPECT_EQ(Env2.getValue(*QuuxDecl, SkipPast::Reference), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, MultipleParamDecls) {
|
|
std::string Code = R"(
|
|
void target(int Foo, int Bar) {
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const StorageLocation *BarLoc =
|
|
Env.getStorageLocation(*BarDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructParamDecl) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
void target(A Foo) {
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isStructureType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ReferenceParamDecl) {
|
|
std::string Code = R"(
|
|
struct A {};
|
|
|
|
void target(A &Foo) {
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const ReferenceValue *FooVal =
|
|
dyn_cast<ReferenceValue>(Env.getValue(*FooLoc));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const StorageLocation &FooReferentLoc = FooVal->getReferentLoc();
|
|
EXPECT_TRUE(isa<AggregateStorageLocation>(&FooReferentLoc));
|
|
|
|
const Value *FooReferentVal = Env.getValue(FooReferentLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<StructValue>(FooReferentVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, PointerParamDecl) {
|
|
std::string Code = R"(
|
|
struct A {};
|
|
|
|
void target(A *Foo) {
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
|
|
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
|
|
|
|
const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructMember) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
void target(A Foo) {
|
|
int Baz = Foo.Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isStructureType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DerivedBaseMemberClass) {
|
|
std::string Code = R"(
|
|
class A {
|
|
int ADefault;
|
|
protected:
|
|
int AProtected;
|
|
private:
|
|
int APrivate;
|
|
public:
|
|
int APublic;
|
|
};
|
|
|
|
class B : public A {
|
|
int BDefault;
|
|
protected:
|
|
int BProtected;
|
|
private:
|
|
int BPrivate;
|
|
};
|
|
|
|
void target() {
|
|
B Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
ASSERT_TRUE(FooDecl->getType()->isRecordType());
|
|
|
|
// Derived-class fields.
|
|
const FieldDecl *BDefaultDecl = nullptr;
|
|
const FieldDecl *BProtectedDecl = nullptr;
|
|
const FieldDecl *BPrivateDecl = nullptr;
|
|
for (const FieldDecl *Field :
|
|
FooDecl->getType()->getAsRecordDecl()->fields()) {
|
|
if (Field->getNameAsString() == "BDefault") {
|
|
BDefaultDecl = Field;
|
|
} else if (Field->getNameAsString() == "BProtected") {
|
|
BProtectedDecl = Field;
|
|
} else if (Field->getNameAsString() == "BPrivate") {
|
|
BPrivateDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BDefaultDecl, NotNull());
|
|
ASSERT_THAT(BProtectedDecl, NotNull());
|
|
ASSERT_THAT(BPrivateDecl, NotNull());
|
|
|
|
// Base-class fields.
|
|
const FieldDecl *ADefaultDecl = nullptr;
|
|
const FieldDecl *APrivateDecl = nullptr;
|
|
const FieldDecl *AProtectedDecl = nullptr;
|
|
const FieldDecl *APublicDecl = nullptr;
|
|
for (const clang::CXXBaseSpecifier &Base :
|
|
FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
|
|
QualType BaseType = Base.getType();
|
|
ASSERT_TRUE(BaseType->isRecordType());
|
|
for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
|
|
if (Field->getNameAsString() == "ADefault") {
|
|
ADefaultDecl = Field;
|
|
} else if (Field->getNameAsString() == "AProtected") {
|
|
AProtectedDecl = Field;
|
|
} else if (Field->getNameAsString() == "APrivate") {
|
|
APrivateDecl = Field;
|
|
} else if (Field->getNameAsString() == "APublic") {
|
|
APublicDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
}
|
|
ASSERT_THAT(ADefaultDecl, NotNull());
|
|
ASSERT_THAT(AProtectedDecl, NotNull());
|
|
ASSERT_THAT(APrivateDecl, NotNull());
|
|
ASSERT_THAT(APublicDecl, NotNull());
|
|
|
|
const auto &FooLoc = *cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto &FooVal = *cast<StructValue>(Env.getValue(FooLoc));
|
|
|
|
// Note: we can't test presence of children in `FooLoc`, because
|
|
// `getChild` requires its argument be present (or fails an assert). So,
|
|
// we limit to testing presence in `FooVal` and coherence between the
|
|
// two.
|
|
|
|
// Base-class fields.
|
|
EXPECT_THAT(FooVal.getChild(*ADefaultDecl), NotNull());
|
|
EXPECT_THAT(FooVal.getChild(*APrivateDecl), NotNull());
|
|
|
|
EXPECT_THAT(FooVal.getChild(*AProtectedDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*APublicDecl)),
|
|
FooVal.getChild(*APublicDecl));
|
|
EXPECT_THAT(FooVal.getChild(*APublicDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*AProtectedDecl)),
|
|
FooVal.getChild(*AProtectedDecl));
|
|
|
|
// Derived-class fields.
|
|
EXPECT_THAT(FooVal.getChild(*BDefaultDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BDefaultDecl)),
|
|
FooVal.getChild(*BDefaultDecl));
|
|
EXPECT_THAT(FooVal.getChild(*BProtectedDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BProtectedDecl)),
|
|
FooVal.getChild(*BProtectedDecl));
|
|
EXPECT_THAT(FooVal.getChild(*BPrivateDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BPrivateDecl)),
|
|
FooVal.getChild(*BPrivateDecl));
|
|
});
|
|
}
|
|
|
|
static void derivedBaseMemberExpectations(
|
|
const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isRecordType());
|
|
const FieldDecl *BarDecl = nullptr;
|
|
for (const clang::CXXBaseSpecifier &Base :
|
|
FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
|
|
QualType BaseType = Base.getType();
|
|
ASSERT_TRUE(BaseType->isStructureType());
|
|
|
|
for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto &FooLoc = *cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto &FooVal = *cast<StructValue>(Env.getValue(FooLoc));
|
|
EXPECT_THAT(FooVal.getChild(*BarDecl), NotNull());
|
|
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BarDecl)), FooVal.getChild(*BarDecl));
|
|
}
|
|
|
|
TEST(TransferTest, DerivedBaseMemberStructDefault) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
struct B : public A {
|
|
};
|
|
|
|
void target() {
|
|
B Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(Code, derivedBaseMemberExpectations);
|
|
}
|
|
|
|
TEST(TransferTest, DerivedBaseMemberPrivateFriend) {
|
|
// Include an access to `Foo.Bar` to verify the analysis doesn't crash on that
|
|
// access.
|
|
std::string Code = R"(
|
|
struct A {
|
|
private:
|
|
friend void target();
|
|
int Bar;
|
|
};
|
|
struct B : public A {
|
|
};
|
|
|
|
void target() {
|
|
B Foo;
|
|
(void)Foo.Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(Code, derivedBaseMemberExpectations);
|
|
}
|
|
|
|
TEST(TransferTest, ClassMember) {
|
|
std::string Code = R"(
|
|
class A {
|
|
public:
|
|
int Bar;
|
|
};
|
|
|
|
void target(A Foo) {
|
|
int Baz = Foo.Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isClassType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BaseClassInitializer) {
|
|
using ast_matchers::cxxConstructorDecl;
|
|
using ast_matchers::hasName;
|
|
using ast_matchers::ofClass;
|
|
|
|
std::string Code = R"(
|
|
class A {
|
|
public:
|
|
A(int I) : Bar(I) {}
|
|
int Bar;
|
|
};
|
|
|
|
class B : public A {
|
|
public:
|
|
B(int I) : A(I) {
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
ASSERT_THAT_ERROR(
|
|
checkDataflow<NoopAnalysis>(
|
|
AnalysisInputs<NoopAnalysis>(
|
|
Code, cxxConstructorDecl(ofClass(hasName("B"))),
|
|
[](ASTContext &C, Environment &) {
|
|
return NoopAnalysis(C, /*ApplyBuiltinTransfer=*/true);
|
|
})
|
|
.withASTBuildArgs(
|
|
{"-fsyntax-only", "-fno-delayed-template-parsing",
|
|
"-std=" + std::string(LangStandard::getLangStandardForKind(
|
|
LangStandard::lang_cxx17)
|
|
.getName())}),
|
|
/*VerifyResults=*/
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
const AnalysisOutputs &) {
|
|
// Regression test to verify that base-class initializers do not
|
|
// trigger an assertion. If we add support for such initializers in
|
|
// the future, we can expand this test to check more specific
|
|
// properties.
|
|
EXPECT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
}),
|
|
llvm::Succeeded());
|
|
}
|
|
|
|
TEST(TransferTest, ReferenceMember) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int &Bar;
|
|
};
|
|
|
|
void target(A Foo) {
|
|
int Baz = Foo.Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
ASSERT_TRUE(FooDecl->getType()->isStructureType());
|
|
auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BarDecl = nullptr;
|
|
for (FieldDecl *Field : FooFields) {
|
|
if (Field->getNameAsString() == "Bar") {
|
|
BarDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<ReferenceValue>(FooVal->getChild(*BarDecl));
|
|
const auto *BarReferentVal =
|
|
cast<IntegerValue>(Env.getValue(BarVal->getReferentLoc()));
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarReferentVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructThisMember) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
|
|
struct B {
|
|
int Baz;
|
|
};
|
|
|
|
B Qux;
|
|
|
|
void target() {
|
|
int Foo = Bar;
|
|
int Quux = Qux.Baz;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
ASSERT_TRUE(QuxDecl->getType()->isStructureType());
|
|
auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BazDecl = nullptr;
|
|
for (FieldDecl *Field : QuxFields) {
|
|
if (Field->getNameAsString() == "Baz") {
|
|
BazDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *QuxLoc =
|
|
cast<AggregateStorageLocation>(&ThisLoc->getChild(*QuxDecl));
|
|
const auto *QuxVal = dyn_cast<StructValue>(Env.getValue(*QuxLoc));
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const auto *BazLoc =
|
|
cast<ScalarStorageLocation>(&QuxLoc->getChild(*BazDecl));
|
|
const auto *BazVal = cast<IntegerValue>(QuxVal->getChild(*BazDecl));
|
|
EXPECT_EQ(Env.getValue(*BazLoc), BazVal);
|
|
|
|
const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
|
|
ASSERT_THAT(QuuxDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*QuuxDecl, SkipPast::None), BazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ClassThisMember) {
|
|
std::string Code = R"(
|
|
class A {
|
|
int Bar;
|
|
|
|
class B {
|
|
public:
|
|
int Baz;
|
|
};
|
|
|
|
B Qux;
|
|
|
|
void target() {
|
|
int Foo = Bar;
|
|
int Quux = Qux.Baz;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const auto *ThisLoc =
|
|
cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
ASSERT_TRUE(QuxDecl->getType()->isClassType());
|
|
auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
|
|
|
|
FieldDecl *BazDecl = nullptr;
|
|
for (FieldDecl *Field : QuxFields) {
|
|
if (Field->getNameAsString() == "Baz") {
|
|
BazDecl = Field;
|
|
} else {
|
|
FAIL() << "Unexpected field: " << Field->getNameAsString();
|
|
}
|
|
}
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *QuxLoc =
|
|
cast<AggregateStorageLocation>(&ThisLoc->getChild(*QuxDecl));
|
|
const auto *QuxVal = dyn_cast<StructValue>(Env.getValue(*QuxLoc));
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const auto *BazLoc =
|
|
cast<ScalarStorageLocation>(&QuxLoc->getChild(*BazDecl));
|
|
const auto *BazVal = cast<IntegerValue>(QuxVal->getChild(*BazDecl));
|
|
EXPECT_EQ(Env.getValue(*BazLoc), BazVal);
|
|
|
|
const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
|
|
ASSERT_THAT(QuuxDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*QuuxDecl, SkipPast::None), BazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructThisInLambda) {
|
|
std::string ThisCaptureCode = R"(
|
|
struct A {
|
|
void frob() {
|
|
[this]() {
|
|
int Foo = Bar;
|
|
// [[p1]]
|
|
}();
|
|
}
|
|
|
|
int Bar;
|
|
};
|
|
)";
|
|
runDataflow(
|
|
ThisCaptureCode,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p1");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
|
|
},
|
|
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
|
|
|
|
std::string RefCaptureDefaultCode = R"(
|
|
struct A {
|
|
void frob() {
|
|
[&]() {
|
|
int Foo = Bar;
|
|
// [[p2]]
|
|
}();
|
|
}
|
|
|
|
int Bar;
|
|
};
|
|
)";
|
|
runDataflow(
|
|
RefCaptureDefaultCode,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p2"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p2");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
|
|
},
|
|
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
|
|
|
|
std::string FreeFunctionLambdaCode = R"(
|
|
void foo() {
|
|
int Bar;
|
|
[&]() {
|
|
int Foo = Bar;
|
|
// [[p3]]
|
|
}();
|
|
}
|
|
)";
|
|
runDataflow(
|
|
FreeFunctionLambdaCode,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p3"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p3");
|
|
|
|
EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
|
|
},
|
|
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
|
|
}
|
|
|
|
TEST(TransferTest, ConstructorInitializer) {
|
|
std::string Code = R"(
|
|
struct target {
|
|
int Bar;
|
|
|
|
target(int Foo) : Bar(Foo) {
|
|
int Qux = Bar;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DefaultInitializer) {
|
|
std::string Code = R"(
|
|
struct target {
|
|
int Bar;
|
|
int Baz = Bar;
|
|
|
|
target(int Foo) : Bar(Foo) {
|
|
int Qux = Baz;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DefaultInitializerReference) {
|
|
std::string Code = R"(
|
|
struct target {
|
|
int &Bar;
|
|
int &Baz = Bar;
|
|
|
|
target(int &Foo) : Bar(Foo) {
|
|
int &Qux = Baz;
|
|
// [[p]]
|
|
}
|
|
};
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
|
|
Env.getThisPointeeStorageLocation());
|
|
ASSERT_THAT(ThisLoc, NotNull());
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<ReferenceValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const auto *QuxVal =
|
|
cast<ReferenceValue>(Env.getValue(*QuxDecl, SkipPast::None));
|
|
EXPECT_EQ(&QuxVal->getReferentLoc(), &FooVal->getReferentLoc());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, TemporaryObject) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
A Foo = A();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ElidableConstructor) {
|
|
// This test is effectively the same as TransferTest.TemporaryObject, but
|
|
// the code is compiled as C++ 14.
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
A Foo = A();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc =
|
|
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<IntegerValue>(FooVal->getChild(*BarDecl));
|
|
EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
|
|
},
|
|
LangStandard::lang_cxx14);
|
|
}
|
|
|
|
TEST(TransferTest, AssignmentOperator) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Baz;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
A Bar;
|
|
// [[p1]]
|
|
Foo = Bar;
|
|
// [[p2]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
|
|
const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
|
|
const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *FooLoc1 = cast<AggregateStorageLocation>(
|
|
Env1.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc1 = cast<AggregateStorageLocation>(
|
|
Env1.getStorageLocation(*BarDecl, SkipPast::None));
|
|
|
|
const auto *FooVal1 = cast<StructValue>(Env1.getValue(*FooLoc1));
|
|
const auto *BarVal1 = cast<StructValue>(Env1.getValue(*BarLoc1));
|
|
EXPECT_NE(FooVal1, BarVal1);
|
|
|
|
const auto *FooBazVal1 =
|
|
cast<IntegerValue>(Env1.getValue(FooLoc1->getChild(*BazDecl)));
|
|
const auto *BarBazVal1 =
|
|
cast<IntegerValue>(Env1.getValue(BarLoc1->getChild(*BazDecl)));
|
|
EXPECT_NE(FooBazVal1, BarBazVal1);
|
|
|
|
const auto *FooLoc2 = cast<AggregateStorageLocation>(
|
|
Env2.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc2 = cast<AggregateStorageLocation>(
|
|
Env2.getStorageLocation(*BarDecl, SkipPast::None));
|
|
|
|
const auto *FooVal2 = cast<StructValue>(Env2.getValue(*FooLoc2));
|
|
const auto *BarVal2 = cast<StructValue>(Env2.getValue(*BarLoc2));
|
|
EXPECT_EQ(FooVal2, BarVal2);
|
|
|
|
const auto *FooBazVal2 =
|
|
cast<IntegerValue>(Env2.getValue(FooLoc1->getChild(*BazDecl)));
|
|
const auto *BarBazVal2 =
|
|
cast<IntegerValue>(Env2.getValue(BarLoc1->getChild(*BazDecl)));
|
|
EXPECT_EQ(FooBazVal2, BarBazVal2);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, CopyConstructor) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Baz;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
A Bar = Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*BarDecl, SkipPast::None));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<StructValue>(Env.getValue(*BarLoc));
|
|
EXPECT_EQ(FooVal, BarVal);
|
|
|
|
const auto *FooBazVal =
|
|
cast<IntegerValue>(Env.getValue(FooLoc->getChild(*BazDecl)));
|
|
const auto *BarBazVal =
|
|
cast<IntegerValue>(Env.getValue(BarLoc->getChild(*BazDecl)));
|
|
EXPECT_EQ(FooBazVal, BarBazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, CopyConstructorWithParens) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Baz;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
A Bar((A(Foo)));
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc = cast<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*BarDecl, SkipPast::None));
|
|
|
|
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
|
|
const auto *BarVal = cast<StructValue>(Env.getValue(*BarLoc));
|
|
EXPECT_EQ(FooVal, BarVal);
|
|
|
|
const auto *FooBazVal =
|
|
cast<IntegerValue>(Env.getValue(FooLoc->getChild(*BazDecl)));
|
|
const auto *BarBazVal =
|
|
cast<IntegerValue>(Env.getValue(BarLoc->getChild(*BazDecl)));
|
|
EXPECT_EQ(FooBazVal, BarBazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, MoveConstructor) {
|
|
std::string Code = R"(
|
|
namespace std {
|
|
|
|
template <typename T> struct remove_reference { using type = T; };
|
|
template <typename T> struct remove_reference<T&> { using type = T; };
|
|
template <typename T> struct remove_reference<T&&> { using type = T; };
|
|
|
|
template <typename T>
|
|
using remove_reference_t = typename remove_reference<T>::type;
|
|
|
|
template <typename T>
|
|
std::remove_reference_t<T>&& move(T&& x);
|
|
|
|
} // namespace std
|
|
|
|
struct A {
|
|
int Baz;
|
|
};
|
|
|
|
void target() {
|
|
A Foo;
|
|
A Bar;
|
|
// [[p1]]
|
|
Foo = std::move(Bar);
|
|
// [[p2]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
|
|
const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
|
|
const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *FooLoc1 = cast<AggregateStorageLocation>(
|
|
Env1.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarLoc1 = cast<AggregateStorageLocation>(
|
|
Env1.getStorageLocation(*BarDecl, SkipPast::None));
|
|
|
|
const auto *FooVal1 = cast<StructValue>(Env1.getValue(*FooLoc1));
|
|
const auto *BarVal1 = cast<StructValue>(Env1.getValue(*BarLoc1));
|
|
EXPECT_NE(FooVal1, BarVal1);
|
|
|
|
const auto *FooBazVal1 =
|
|
cast<IntegerValue>(Env1.getValue(FooLoc1->getChild(*BazDecl)));
|
|
const auto *BarBazVal1 =
|
|
cast<IntegerValue>(Env1.getValue(BarLoc1->getChild(*BazDecl)));
|
|
EXPECT_NE(FooBazVal1, BarBazVal1);
|
|
|
|
const auto *FooLoc2 = cast<AggregateStorageLocation>(
|
|
Env2.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *FooVal2 = cast<StructValue>(Env2.getValue(*FooLoc2));
|
|
EXPECT_EQ(FooVal2, BarVal1);
|
|
|
|
const auto *FooBazVal2 =
|
|
cast<IntegerValue>(Env2.getValue(FooLoc1->getChild(*BazDecl)));
|
|
EXPECT_EQ(FooBazVal2, BarBazVal1);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BindTemporary) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
virtual ~A() = default;
|
|
|
|
int Baz;
|
|
};
|
|
|
|
void target(A Foo) {
|
|
int Bar = A(Foo).Baz;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto &FooVal =
|
|
*cast<StructValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_EQ(BarVal, FooVal.getChild(*BazDecl));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StaticCast) {
|
|
std::string Code = R"(
|
|
void target(int Foo) {
|
|
int Bar = static_cast<int>(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
const auto *BarVal = Env.getValue(*BarDecl, SkipPast::None);
|
|
EXPECT_TRUE(isa<IntegerValue>(FooVal));
|
|
EXPECT_TRUE(isa<IntegerValue>(BarVal));
|
|
EXPECT_EQ(FooVal, BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, IntegralCast) {
|
|
std::string Code = R"(
|
|
void target(int Foo) {
|
|
long Bar = Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
const auto *BarVal = Env.getValue(*BarDecl, SkipPast::None);
|
|
EXPECT_TRUE(isa<IntegerValue>(FooVal));
|
|
EXPECT_TRUE(isa<IntegerValue>(BarVal));
|
|
EXPECT_EQ(FooVal, BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, IntegraltoBooleanCast) {
|
|
std::string Code = R"(
|
|
void target(int Foo) {
|
|
bool Bar = Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
const auto *BarVal = Env.getValue(*BarDecl, SkipPast::None);
|
|
EXPECT_TRUE(isa<IntegerValue>(FooVal));
|
|
EXPECT_TRUE(isa<BoolValue>(BarVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, IntegralToBooleanCastFromBool) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
int Zab = Foo;
|
|
bool Bar = Zab;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal = Env.getValue(*FooDecl, SkipPast::None);
|
|
const auto *BarVal = Env.getValue(*BarDecl, SkipPast::None);
|
|
EXPECT_TRUE(isa<BoolValue>(FooVal));
|
|
EXPECT_TRUE(isa<BoolValue>(BarVal));
|
|
EXPECT_EQ(FooVal, BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, NullToPointerCast) {
|
|
std::string Code = R"(
|
|
using my_nullptr_t = decltype(nullptr);
|
|
struct Baz {};
|
|
void target() {
|
|
int *FooX = nullptr;
|
|
int *FooY = nullptr;
|
|
bool **Bar = nullptr;
|
|
Baz *Baz = nullptr;
|
|
my_nullptr_t Null = 0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooXDecl = findValueDecl(ASTCtx, "FooX");
|
|
ASSERT_THAT(FooXDecl, NotNull());
|
|
|
|
const ValueDecl *FooYDecl = findValueDecl(ASTCtx, "FooY");
|
|
ASSERT_THAT(FooYDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null");
|
|
ASSERT_THAT(NullDecl, NotNull());
|
|
|
|
const auto *FooXVal =
|
|
cast<PointerValue>(Env.getValue(*FooXDecl, SkipPast::None));
|
|
const auto *FooYVal =
|
|
cast<PointerValue>(Env.getValue(*FooYDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
const auto *BazVal =
|
|
cast<PointerValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
const auto *NullVal =
|
|
cast<PointerValue>(Env.getValue(*NullDecl, SkipPast::None));
|
|
|
|
EXPECT_EQ(FooXVal, FooYVal);
|
|
EXPECT_NE(FooXVal, BarVal);
|
|
EXPECT_NE(FooXVal, BazVal);
|
|
EXPECT_NE(BarVal, BazVal);
|
|
|
|
const StorageLocation &FooPointeeLoc = FooXVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<ScalarStorageLocation>(FooPointeeLoc));
|
|
EXPECT_THAT(Env.getValue(FooPointeeLoc), IsNull());
|
|
|
|
const StorageLocation &BarPointeeLoc = BarVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<ScalarStorageLocation>(BarPointeeLoc));
|
|
EXPECT_THAT(Env.getValue(BarPointeeLoc), IsNull());
|
|
|
|
const StorageLocation &BazPointeeLoc = BazVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<AggregateStorageLocation>(BazPointeeLoc));
|
|
EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
|
|
|
|
const StorageLocation &NullPointeeLoc = NullVal->getPointeeLoc();
|
|
EXPECT_TRUE(isa<ScalarStorageLocation>(NullPointeeLoc));
|
|
EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, NullToMemberPointerCast) {
|
|
std::string Code = R"(
|
|
struct Foo {};
|
|
void target(Foo *Foo) {
|
|
int Foo::*MemberPointer = nullptr;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *MemberPointerDecl =
|
|
findValueDecl(ASTCtx, "MemberPointer");
|
|
ASSERT_THAT(MemberPointerDecl, NotNull());
|
|
|
|
const auto *MemberPointerVal = cast<PointerValue>(
|
|
Env.getValue(*MemberPointerDecl, SkipPast::None));
|
|
|
|
const StorageLocation &MemberLoc = MemberPointerVal->getPointeeLoc();
|
|
EXPECT_THAT(Env.getValue(MemberLoc), IsNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AddrOfValue) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
int Foo;
|
|
int *Bar = &Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooLoc = cast<ScalarStorageLocation>(
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_EQ(&BarVal->getPointeeLoc(), FooLoc);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AddrOfReference) {
|
|
std::string Code = R"(
|
|
void target(int *Foo) {
|
|
int *Bar = &(*Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<PointerValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_EQ(&BarVal->getPointeeLoc(), &FooVal->getPointeeLoc());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DerefDependentPtr) {
|
|
std::string Code = R"(
|
|
template <typename T>
|
|
void target(T *Foo) {
|
|
T &Bar = *Foo;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<PointerValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<ReferenceValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_EQ(&BarVal->getReferentLoc(), &FooVal->getPointeeLoc());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, VarDeclInitAssignConditionalOperator) {
|
|
std::string Code = R"(
|
|
struct A {};
|
|
|
|
void target(A Foo, A Bar, bool Cond) {
|
|
A Baz = Cond ? Foo : Bar;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<StructValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
const auto *BarVal =
|
|
cast<StructValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
|
|
const auto *BazVal =
|
|
dyn_cast<StructValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
ASSERT_THAT(BazVal, NotNull());
|
|
|
|
EXPECT_NE(BazVal, FooVal);
|
|
EXPECT_NE(BazVal, BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, VarDeclInDoWhile) {
|
|
std::string Code = R"(
|
|
void target(int *Foo) {
|
|
do {
|
|
int Bar = *Foo;
|
|
} while (true);
|
|
(void)0;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *FooVal =
|
|
cast<PointerValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
const auto *FooPointeeVal =
|
|
cast<IntegerValue>(Env.getValue(FooVal->getPointeeLoc()));
|
|
|
|
const auto *BarVal = dyn_cast_or_null<IntegerValue>(
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
ASSERT_THAT(BarVal, NotNull());
|
|
|
|
EXPECT_EQ(BarVal, FooPointeeVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AggregateInitialization) {
|
|
std::string BracesCode = R"(
|
|
struct A {
|
|
int Foo;
|
|
};
|
|
|
|
struct B {
|
|
int Bar;
|
|
A Baz;
|
|
int Qux;
|
|
};
|
|
|
|
void target(int BarArg, int FooArg, int QuxArg) {
|
|
B Quux{BarArg, {FooArg}, QuxArg};
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
std::string BraceEllisionCode = R"(
|
|
struct A {
|
|
int Foo;
|
|
};
|
|
|
|
struct B {
|
|
int Bar;
|
|
A Baz;
|
|
int Qux;
|
|
};
|
|
|
|
void target(int BarArg, int FooArg, int QuxArg) {
|
|
B Quux = {BarArg, FooArg, QuxArg};
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
for (const std::string &Code : {BracesCode, BraceEllisionCode}) {
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const ValueDecl *FooArgDecl = findValueDecl(ASTCtx, "FooArg");
|
|
ASSERT_THAT(FooArgDecl, NotNull());
|
|
|
|
const ValueDecl *BarArgDecl = findValueDecl(ASTCtx, "BarArg");
|
|
ASSERT_THAT(BarArgDecl, NotNull());
|
|
|
|
const ValueDecl *QuxArgDecl = findValueDecl(ASTCtx, "QuxArg");
|
|
ASSERT_THAT(QuxArgDecl, NotNull());
|
|
|
|
const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
|
|
ASSERT_THAT(QuuxDecl, NotNull());
|
|
|
|
const auto *FooArgVal =
|
|
cast<IntegerValue>(Env.getValue(*FooArgDecl, SkipPast::None));
|
|
const auto *BarArgVal =
|
|
cast<IntegerValue>(Env.getValue(*BarArgDecl, SkipPast::None));
|
|
const auto *QuxArgVal =
|
|
cast<IntegerValue>(Env.getValue(*QuxArgDecl, SkipPast::None));
|
|
|
|
const auto *QuuxVal =
|
|
cast<StructValue>(Env.getValue(*QuuxDecl, SkipPast::None));
|
|
ASSERT_THAT(QuuxVal, NotNull());
|
|
|
|
const auto *BazVal = cast<StructValue>(QuuxVal->getChild(*BazDecl));
|
|
ASSERT_THAT(BazVal, NotNull());
|
|
|
|
EXPECT_EQ(QuuxVal->getChild(*BarDecl), BarArgVal);
|
|
EXPECT_EQ(BazVal->getChild(*FooDecl), FooArgVal);
|
|
EXPECT_EQ(QuuxVal->getChild(*QuxDecl), QuxArgVal);
|
|
});
|
|
}
|
|
}
|
|
|
|
TEST(TransferTest, AssignToUnionMember) {
|
|
std::string Code = R"(
|
|
union A {
|
|
int Foo;
|
|
};
|
|
|
|
void target(int Bar) {
|
|
A Baz;
|
|
Baz.Foo = Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
ASSERT_TRUE(BazDecl->getType()->isUnionType());
|
|
|
|
const auto *BazLoc = dyn_cast_or_null<AggregateStorageLocation>(
|
|
Env.getStorageLocation(*BazDecl, SkipPast::None));
|
|
ASSERT_THAT(BazLoc, NotNull());
|
|
|
|
// FIXME: Add support for union types.
|
|
EXPECT_THAT(Env.getValue(*BazLoc), IsNull());
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AssignFromBoolLiteral) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
bool Foo = true;
|
|
bool Bar = false;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal = dyn_cast_or_null<AtomicBoolValue>(
|
|
Env.getValue(*FooDecl, SkipPast::None));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarVal = dyn_cast_or_null<AtomicBoolValue>(
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
ASSERT_THAT(BarVal, NotNull());
|
|
|
|
EXPECT_EQ(FooVal, &Env.getBoolLiteralValue(true));
|
|
EXPECT_EQ(BarVal, &Env.getBoolLiteralValue(false));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AssignFromCompositeBoolExpression) {
|
|
{
|
|
std::string Code = R"(
|
|
void target(bool Foo, bool Bar, bool Qux) {
|
|
bool Baz = (Foo) && (Bar || Qux);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*FooDecl, SkipPast::None));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
ASSERT_THAT(BarVal, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const auto *QuxVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*QuxDecl, SkipPast::None));
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *BazVal = dyn_cast_or_null<ConjunctionValue>(
|
|
Env.getValue(*BazDecl, SkipPast::None));
|
|
ASSERT_THAT(BazVal, NotNull());
|
|
EXPECT_EQ(&BazVal->getLeftSubValue(), FooVal);
|
|
|
|
const auto *BazRightSubValVal =
|
|
cast<DisjunctionValue>(&BazVal->getRightSubValue());
|
|
EXPECT_EQ(&BazRightSubValVal->getLeftSubValue(), BarVal);
|
|
EXPECT_EQ(&BazRightSubValVal->getRightSubValue(), QuxVal);
|
|
});
|
|
}
|
|
|
|
{
|
|
std::string Code = R"(
|
|
void target(bool Foo, bool Bar, bool Qux) {
|
|
bool Baz = (Foo && Qux) || (Bar);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*FooDecl, SkipPast::None));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
ASSERT_THAT(BarVal, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const auto *QuxVal = dyn_cast_or_null<BoolValue>(
|
|
Env.getValue(*QuxDecl, SkipPast::None));
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const auto *BazVal = dyn_cast_or_null<DisjunctionValue>(
|
|
Env.getValue(*BazDecl, SkipPast::None));
|
|
ASSERT_THAT(BazVal, NotNull());
|
|
|
|
const auto *BazLeftSubValVal =
|
|
cast<ConjunctionValue>(&BazVal->getLeftSubValue());
|
|
EXPECT_EQ(&BazLeftSubValVal->getLeftSubValue(), FooVal);
|
|
EXPECT_EQ(&BazLeftSubValVal->getRightSubValue(), QuxVal);
|
|
|
|
EXPECT_EQ(&BazVal->getRightSubValue(), BarVal);
|
|
});
|
|
}
|
|
|
|
{
|
|
std::string Code = R"(
|
|
void target(bool A, bool B, bool C, bool D) {
|
|
bool Foo = ((A && B) && C) && D;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *ADecl = findValueDecl(ASTCtx, "A");
|
|
ASSERT_THAT(ADecl, NotNull());
|
|
|
|
const auto *AVal =
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(*ADecl, SkipPast::None));
|
|
ASSERT_THAT(AVal, NotNull());
|
|
|
|
const ValueDecl *BDecl = findValueDecl(ASTCtx, "B");
|
|
ASSERT_THAT(BDecl, NotNull());
|
|
|
|
const auto *BVal =
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(*BDecl, SkipPast::None));
|
|
ASSERT_THAT(BVal, NotNull());
|
|
|
|
const ValueDecl *CDecl = findValueDecl(ASTCtx, "C");
|
|
ASSERT_THAT(CDecl, NotNull());
|
|
|
|
const auto *CVal =
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(*CDecl, SkipPast::None));
|
|
ASSERT_THAT(CVal, NotNull());
|
|
|
|
const ValueDecl *DDecl = findValueDecl(ASTCtx, "D");
|
|
ASSERT_THAT(DDecl, NotNull());
|
|
|
|
const auto *DVal =
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(*DDecl, SkipPast::None));
|
|
ASSERT_THAT(DVal, NotNull());
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal = dyn_cast_or_null<ConjunctionValue>(
|
|
Env.getValue(*FooDecl, SkipPast::None));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const auto &FooLeftSubVal =
|
|
cast<ConjunctionValue>(FooVal->getLeftSubValue());
|
|
const auto &FooLeftLeftSubVal =
|
|
cast<ConjunctionValue>(FooLeftSubVal.getLeftSubValue());
|
|
EXPECT_EQ(&FooLeftLeftSubVal.getLeftSubValue(), AVal);
|
|
EXPECT_EQ(&FooLeftLeftSubVal.getRightSubValue(), BVal);
|
|
EXPECT_EQ(&FooLeftSubVal.getRightSubValue(), CVal);
|
|
EXPECT_EQ(&FooVal->getRightSubValue(), DVal);
|
|
});
|
|
}
|
|
}
|
|
|
|
TEST(TransferTest, AssignFromBoolNegation) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
bool Foo = true;
|
|
bool Bar = !(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const auto *FooVal = dyn_cast_or_null<AtomicBoolValue>(
|
|
Env.getValue(*FooDecl, SkipPast::None));
|
|
ASSERT_THAT(FooVal, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const auto *BarVal = dyn_cast_or_null<NegationValue>(
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
ASSERT_THAT(BarVal, NotNull());
|
|
|
|
EXPECT_EQ(&BarVal->getSubVal(), FooVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BuiltinExpect) {
|
|
std::string Code = R"(
|
|
void target(long Foo) {
|
|
long Bar = __builtin_expect(Foo, true);
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
});
|
|
}
|
|
|
|
// `__builtin_expect` takes and returns a `long` argument, so other types
|
|
// involve casts. This verifies that we identify the input and output in that
|
|
// case.
|
|
TEST(TransferTest, BuiltinExpectBoolArg) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
bool Bar = __builtin_expect(Foo, true);
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BuiltinUnreachable) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
bool Bar = false;
|
|
if (Foo)
|
|
Bar = Foo;
|
|
else
|
|
__builtin_unreachable();
|
|
(void)0;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
// `__builtin_unreachable` promises that the code is
|
|
// unreachable, so the compiler treats the "then" branch as the
|
|
// only possible predecessor of this statement.
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BuiltinTrap) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
bool Bar = false;
|
|
if (Foo)
|
|
Bar = Foo;
|
|
else
|
|
__builtin_trap();
|
|
(void)0;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
// `__builtin_trap` ensures program termination, so only the
|
|
// "then" branch is a predecessor of this statement.
|
|
EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None),
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BuiltinDebugTrap) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
bool Bar = false;
|
|
if (Foo)
|
|
Bar = Foo;
|
|
else
|
|
__builtin_debugtrap();
|
|
(void)0;
|
|
/*[[p]]*/
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
// `__builtin_debugtrap` doesn't ensure program termination.
|
|
EXPECT_NE(Env.getValue(*FooDecl, SkipPast::None),
|
|
Env.getValue(*BarDecl, SkipPast::None));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StaticIntSingleVarDecl) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
static int Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StaticIntGroupVarDecl) {
|
|
std::string Code = R"(
|
|
void target() {
|
|
static int Foo, Bar;
|
|
(void)0;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const StorageLocation *FooLoc =
|
|
Env.getStorageLocation(*FooDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
|
|
|
|
const StorageLocation *BarLoc =
|
|
Env.getStorageLocation(*BarDecl, SkipPast::None);
|
|
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
|
|
|
|
const Value *FooVal = Env.getValue(*FooLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
|
|
|
|
const Value *BarVal = Env.getValue(*BarLoc);
|
|
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
|
|
|
|
EXPECT_NE(FooVal, BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, GlobalIntVarDecl) {
|
|
std::string Code = R"(
|
|
static int Foo;
|
|
|
|
void target() {
|
|
int Bar = Foo;
|
|
int Baz = Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const Value *BarVal =
|
|
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
const Value *BazVal =
|
|
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
EXPECT_EQ(BarVal, BazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StaticMemberIntVarDecl) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
static int Foo;
|
|
};
|
|
|
|
void target(A a) {
|
|
int Bar = a.Foo;
|
|
int Baz = a.Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const Value *BarVal =
|
|
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
const Value *BazVal =
|
|
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
EXPECT_EQ(BarVal, BazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StaticMemberRefVarDecl) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
static int &Foo;
|
|
};
|
|
|
|
void target(A a) {
|
|
int Bar = a.Foo;
|
|
int Baz = a.Foo;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const Value *BarVal =
|
|
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
const Value *BazVal =
|
|
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
EXPECT_EQ(BarVal, BazVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, AssignMemberBeforeCopy) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Foo;
|
|
};
|
|
|
|
void target() {
|
|
A A1;
|
|
A A2;
|
|
int Bar;
|
|
A1.Foo = Bar;
|
|
A2 = A1;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *A1Decl = findValueDecl(ASTCtx, "A1");
|
|
ASSERT_THAT(A1Decl, NotNull());
|
|
|
|
const ValueDecl *A2Decl = findValueDecl(ASTCtx, "A2");
|
|
ASSERT_THAT(A2Decl, NotNull());
|
|
|
|
const auto *BarVal =
|
|
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
|
|
const auto *A2Val =
|
|
cast<StructValue>(Env.getValue(*A2Decl, SkipPast::None));
|
|
EXPECT_EQ(A2Val->getChild(*FooDecl), BarVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BooleanEquality) {
|
|
std::string Code = R"(
|
|
void target(bool Bar) {
|
|
bool Foo = true;
|
|
if (Bar == Foo) {
|
|
(void)0;
|
|
/*[[p-then]]*/
|
|
} else {
|
|
(void)0;
|
|
/*[[p-else]]*/
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-then", "p-else"));
|
|
const Environment &EnvThen =
|
|
getEnvironmentAtAnnotation(Results, "p-then");
|
|
const Environment &EnvElse =
|
|
getEnvironmentAtAnnotation(Results, "p-else");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &BarValThen =
|
|
*cast<BoolValue>(EnvThen.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_TRUE(EnvThen.flowConditionImplies(BarValThen));
|
|
|
|
auto &BarValElse =
|
|
*cast<BoolValue>(EnvElse.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_FALSE(EnvElse.flowConditionImplies(BarValElse));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BooleanInequality) {
|
|
std::string Code = R"(
|
|
void target(bool Bar) {
|
|
bool Foo = true;
|
|
if (Bar != Foo) {
|
|
(void)0;
|
|
/*[[p-then]]*/
|
|
} else {
|
|
(void)0;
|
|
/*[[p-else]]*/
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-then", "p-else"));
|
|
const Environment &EnvThen =
|
|
getEnvironmentAtAnnotation(Results, "p-then");
|
|
const Environment &EnvElse =
|
|
getEnvironmentAtAnnotation(Results, "p-else");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &BarValThen =
|
|
*cast<BoolValue>(EnvThen.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_FALSE(EnvThen.flowConditionImplies(BarValThen));
|
|
|
|
auto &BarValElse =
|
|
*cast<BoolValue>(EnvElse.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_TRUE(EnvElse.flowConditionImplies(BarValElse));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, CorrelatedBranches) {
|
|
std::string Code = R"(
|
|
void target(bool B, bool C) {
|
|
if (B) {
|
|
return;
|
|
}
|
|
(void)0;
|
|
/*[[p0]]*/
|
|
if (C) {
|
|
B = true;
|
|
/*[[p1]]*/
|
|
}
|
|
if (B) {
|
|
(void)0;
|
|
/*[[p2]]*/
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p0", "p1", "p2"));
|
|
|
|
const ValueDecl *CDecl = findValueDecl(ASTCtx, "C");
|
|
ASSERT_THAT(CDecl, NotNull());
|
|
|
|
{
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p0");
|
|
const ValueDecl *BDecl = findValueDecl(ASTCtx, "B");
|
|
ASSERT_THAT(BDecl, NotNull());
|
|
auto &BVal = *cast<BoolValue>(Env.getValue(*BDecl, SkipPast::None));
|
|
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BVal)));
|
|
}
|
|
|
|
{
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p1");
|
|
auto &CVal = *cast<BoolValue>(Env.getValue(*CDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(CVal));
|
|
}
|
|
|
|
{
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p2");
|
|
auto &CVal = *cast<BoolValue>(Env.getValue(*CDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(CVal));
|
|
}
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, LoopWithAssignmentConverges) {
|
|
std::string Code = R"(
|
|
bool foo();
|
|
|
|
void target() {
|
|
do {
|
|
bool Bar = foo();
|
|
if (Bar) break;
|
|
(void)Bar;
|
|
/*[[p]]*/
|
|
} while (true);
|
|
}
|
|
)";
|
|
// The key property that we are verifying is implicit in `runDataflow` --
|
|
// namely, that the analysis succeeds, rather than hitting the maximum number
|
|
// of iterations.
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, LoopWithStagedAssignments) {
|
|
std::string Code = R"(
|
|
bool foo();
|
|
|
|
void target() {
|
|
bool Bar = false;
|
|
bool Err = false;
|
|
while (foo()) {
|
|
if (Bar)
|
|
Err = true;
|
|
Bar = true;
|
|
/*[[p]]*/
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
const ValueDecl *ErrDecl = findValueDecl(ASTCtx, "Err");
|
|
ASSERT_THAT(ErrDecl, NotNull());
|
|
|
|
auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
auto &ErrVal = *cast<BoolValue>(Env.getValue(*ErrDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(BarVal));
|
|
// An unsound analysis, for example only evaluating the loop once, can
|
|
// conclude that `Err` is false. So, we test that this conclusion is not
|
|
// reached.
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(ErrVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, LoopWithReferenceAssignmentConverges) {
|
|
std::string Code = R"(
|
|
bool &foo();
|
|
|
|
void target() {
|
|
do {
|
|
bool& Bar = foo();
|
|
if (Bar) break;
|
|
(void)Bar;
|
|
/*[[p]]*/
|
|
} while (true);
|
|
}
|
|
)";
|
|
// The key property that we are verifying is that the analysis succeeds,
|
|
// rather than hitting the maximum number of iterations.
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &BarVal =
|
|
*cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, LoopWithStructReferenceAssignmentConverges) {
|
|
std::string Code = R"(
|
|
struct Lookup {
|
|
int x;
|
|
};
|
|
|
|
void target(Lookup val, bool b) {
|
|
const Lookup* l = nullptr;
|
|
while (b) {
|
|
l = &val;
|
|
/*[[p-inner]]*/
|
|
}
|
|
(void)0;
|
|
/*[[p-outer]]*/
|
|
}
|
|
)";
|
|
// The key property that we are verifying is implicit in `runDataflow` --
|
|
// namely, that the analysis succeeds, rather than hitting the maximum number
|
|
// of iterations.
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-inner", "p-outer"));
|
|
const Environment &InnerEnv =
|
|
getEnvironmentAtAnnotation(Results, "p-inner");
|
|
const Environment &OuterEnv =
|
|
getEnvironmentAtAnnotation(Results, "p-outer");
|
|
|
|
const ValueDecl *ValDecl = findValueDecl(ASTCtx, "val");
|
|
ASSERT_THAT(ValDecl, NotNull());
|
|
|
|
const ValueDecl *LDecl = findValueDecl(ASTCtx, "l");
|
|
ASSERT_THAT(LDecl, NotNull());
|
|
|
|
// Inner.
|
|
auto *LVal =
|
|
dyn_cast<PointerValue>(InnerEnv.getValue(*LDecl, SkipPast::None));
|
|
ASSERT_THAT(LVal, NotNull());
|
|
|
|
EXPECT_EQ(&LVal->getPointeeLoc(),
|
|
InnerEnv.getStorageLocation(*ValDecl, SkipPast::Reference));
|
|
|
|
// Outer.
|
|
LVal =
|
|
dyn_cast<PointerValue>(OuterEnv.getValue(*LDecl, SkipPast::None));
|
|
ASSERT_THAT(LVal, NotNull());
|
|
|
|
// The loop body may not have been executed, so we should not conclude
|
|
// that `l` points to `val`.
|
|
EXPECT_NE(&LVal->getPointeeLoc(),
|
|
OuterEnv.getStorageLocation(*ValDecl, SkipPast::Reference));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
|
|
std::string Code = R"(
|
|
union Union {
|
|
int A;
|
|
float B;
|
|
};
|
|
|
|
void foo() {
|
|
Union A;
|
|
Union B;
|
|
A = B;
|
|
}
|
|
)";
|
|
// This is a crash regression test when calling the transfer function on a
|
|
// `CXXThisExpr` that refers to a union.
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
|
|
ASTContext &) {},
|
|
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
|
|
}
|
|
|
|
TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Foo;
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
int Qux;
|
|
A Baz;
|
|
Baz.Foo = Qux;
|
|
auto &FooRef = Baz.Foo;
|
|
auto &BarRef = Baz.Bar;
|
|
auto &[BoundFooRef, BoundBarRef] = Baz;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
|
|
ASSERT_THAT(FooRefDecl, NotNull());
|
|
|
|
const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
|
|
ASSERT_THAT(BarRefDecl, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, "BoundFooRef");
|
|
ASSERT_THAT(BoundFooRefDecl, NotNull());
|
|
|
|
const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, "BoundBarRef");
|
|
ASSERT_THAT(BoundBarRefDecl, NotNull());
|
|
|
|
const StorageLocation *FooRefLoc =
|
|
Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(FooRefLoc, NotNull());
|
|
|
|
const StorageLocation *BarRefLoc =
|
|
Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(BarRefLoc, NotNull());
|
|
|
|
const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const StorageLocation *BoundFooRefLoc =
|
|
Env.getStorageLocation(*BoundFooRefDecl, SkipPast::Reference);
|
|
EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
|
|
|
|
const StorageLocation *BoundBarRefLoc =
|
|
Env.getStorageLocation(*BoundBarRefDecl, SkipPast::Reference);
|
|
EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
|
|
|
|
EXPECT_EQ(Env.getValue(*BoundFooRefDecl, SkipPast::Reference), QuxVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructuredBindingAssignFromStructRefMembersToRefs) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int &Foo;
|
|
int &Bar;
|
|
};
|
|
|
|
void target(A Baz) {
|
|
int Qux;
|
|
Baz.Foo = Qux;
|
|
auto &FooRef = Baz.Foo;
|
|
auto &BarRef = Baz.Bar;
|
|
auto &[BoundFooRef, BoundBarRef] = Baz;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
|
|
ASSERT_THAT(FooRefDecl, NotNull());
|
|
|
|
const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
|
|
ASSERT_THAT(BarRefDecl, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, "BoundFooRef");
|
|
ASSERT_THAT(BoundFooRefDecl, NotNull());
|
|
|
|
const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, "BoundBarRef");
|
|
ASSERT_THAT(BoundBarRefDecl, NotNull());
|
|
|
|
const StorageLocation *FooRefLoc =
|
|
Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(FooRefLoc, NotNull());
|
|
|
|
const StorageLocation *BarRefLoc =
|
|
Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(BarRefLoc, NotNull());
|
|
|
|
const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const StorageLocation *BoundFooRefLoc =
|
|
Env.getStorageLocation(*BoundFooRefDecl, SkipPast::Reference);
|
|
EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
|
|
|
|
const StorageLocation *BoundBarRefLoc =
|
|
Env.getStorageLocation(*BoundBarRefDecl, SkipPast::Reference);
|
|
EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
|
|
|
|
EXPECT_EQ(Env.getValue(*BoundFooRefDecl, SkipPast::Reference), QuxVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToInts) {
|
|
std::string Code = R"(
|
|
struct A {
|
|
int Foo;
|
|
int Bar;
|
|
};
|
|
|
|
void target() {
|
|
int Qux;
|
|
A Baz;
|
|
Baz.Foo = Qux;
|
|
auto &FooRef = Baz.Foo;
|
|
auto &BarRef = Baz.Bar;
|
|
auto [BoundFoo, BoundBar] = Baz;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
|
|
ASSERT_THAT(FooRefDecl, NotNull());
|
|
|
|
const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
|
|
ASSERT_THAT(BarRefDecl, NotNull());
|
|
|
|
const ValueDecl *BoundFooDecl = findValueDecl(ASTCtx, "BoundFoo");
|
|
ASSERT_THAT(BoundFooDecl, NotNull());
|
|
|
|
const ValueDecl *BoundBarDecl = findValueDecl(ASTCtx, "BoundBar");
|
|
ASSERT_THAT(BoundBarDecl, NotNull());
|
|
|
|
const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
|
|
ASSERT_THAT(QuxDecl, NotNull());
|
|
|
|
const StorageLocation *FooRefLoc =
|
|
Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(FooRefLoc, NotNull());
|
|
|
|
const StorageLocation *BarRefLoc =
|
|
Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
|
|
ASSERT_THAT(BarRefLoc, NotNull());
|
|
|
|
const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
|
|
ASSERT_THAT(QuxVal, NotNull());
|
|
|
|
const StorageLocation *BoundFooLoc =
|
|
Env.getStorageLocation(*BoundFooDecl, SkipPast::Reference);
|
|
EXPECT_NE(BoundFooLoc, FooRefLoc);
|
|
|
|
const StorageLocation *BoundBarLoc =
|
|
Env.getStorageLocation(*BoundBarDecl, SkipPast::Reference);
|
|
EXPECT_NE(BoundBarLoc, BarRefLoc);
|
|
|
|
EXPECT_EQ(Env.getValue(*BoundFooDecl, SkipPast::Reference), QuxVal);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, BinaryOperatorComma) {
|
|
std::string Code = R"(
|
|
void target(int Foo, int Bar) {
|
|
int &Baz = (Foo, Bar);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
const StorageLocation *BarLoc =
|
|
Env.getStorageLocation(*BarDecl, SkipPast::Reference);
|
|
ASSERT_THAT(BarLoc, NotNull());
|
|
|
|
const StorageLocation *BazLoc =
|
|
Env.getStorageLocation(*BazDecl, SkipPast::Reference);
|
|
EXPECT_EQ(BazLoc, BarLoc);
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, IfStmtBranchExtendsFlowCondition) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
if (Foo) {
|
|
(void)0;
|
|
// [[if_then]]
|
|
} else {
|
|
(void)0;
|
|
// [[if_else]]
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("if_then", "if_else"));
|
|
const Environment &ThenEnv =
|
|
getEnvironmentAtAnnotation(Results, "if_then");
|
|
const Environment &ElseEnv =
|
|
getEnvironmentAtAnnotation(Results, "if_else");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
BoolValue &ThenFooVal =
|
|
*cast<BoolValue>(ThenEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(ThenEnv.flowConditionImplies(ThenFooVal));
|
|
|
|
BoolValue &ElseFooVal =
|
|
*cast<BoolValue>(ElseEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(ElseEnv.flowConditionImplies(ElseEnv.makeNot(ElseFooVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, WhileStmtBranchExtendsFlowCondition) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
while (Foo) {
|
|
(void)0;
|
|
// [[loop_body]]
|
|
}
|
|
(void)0;
|
|
// [[after_loop]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(),
|
|
UnorderedElementsAre("loop_body", "after_loop"));
|
|
const Environment &LoopBodyEnv =
|
|
getEnvironmentAtAnnotation(Results, "loop_body");
|
|
const Environment &AfterLoopEnv =
|
|
getEnvironmentAtAnnotation(Results, "after_loop");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
BoolValue &LoopBodyFooVal =
|
|
*cast<BoolValue>(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal));
|
|
|
|
BoolValue &AfterLoopFooVal =
|
|
*cast<BoolValue>(AfterLoopEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
|
|
AfterLoopEnv.makeNot(AfterLoopFooVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, DoWhileStmtBranchExtendsFlowCondition) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
bool Bar = true;
|
|
do {
|
|
(void)0;
|
|
// [[loop_body]]
|
|
Bar = false;
|
|
} while (Foo);
|
|
(void)0;
|
|
// [[after_loop]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(),
|
|
UnorderedElementsAre("loop_body", "after_loop"));
|
|
const Environment &LoopBodyEnv =
|
|
getEnvironmentAtAnnotation(Results, "loop_body");
|
|
const Environment &AfterLoopEnv =
|
|
getEnvironmentAtAnnotation(Results, "after_loop");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
BoolValue &LoopBodyFooVal =
|
|
*cast<BoolValue>(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
|
|
BoolValue &LoopBodyBarVal =
|
|
*cast<BoolValue>(LoopBodyEnv.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_TRUE(LoopBodyEnv.flowConditionImplies(
|
|
LoopBodyEnv.makeOr(LoopBodyBarVal, LoopBodyFooVal)));
|
|
|
|
BoolValue &AfterLoopFooVal =
|
|
*cast<BoolValue>(AfterLoopEnv.getValue(*FooDecl, SkipPast::None));
|
|
BoolValue &AfterLoopBarVal =
|
|
*cast<BoolValue>(AfterLoopEnv.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
|
|
AfterLoopEnv.makeNot(AfterLoopFooVal)));
|
|
EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
|
|
AfterLoopEnv.makeNot(AfterLoopBarVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ForStmtBranchExtendsFlowCondition) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
for (; Foo;) {
|
|
(void)0;
|
|
// [[loop_body]]
|
|
}
|
|
(void)0;
|
|
// [[after_loop]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(),
|
|
UnorderedElementsAre("loop_body", "after_loop"));
|
|
const Environment &LoopBodyEnv =
|
|
getEnvironmentAtAnnotation(Results, "loop_body");
|
|
const Environment &AfterLoopEnv =
|
|
getEnvironmentAtAnnotation(Results, "after_loop");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
BoolValue &LoopBodyFooVal =
|
|
*cast<BoolValue>(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal));
|
|
|
|
BoolValue &AfterLoopFooVal =
|
|
*cast<BoolValue>(AfterLoopEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(AfterLoopEnv.flowConditionImplies(
|
|
AfterLoopEnv.makeNot(AfterLoopFooVal)));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ForStmtBranchWithoutConditionDoesNotExtendFlowCondition) {
|
|
std::string Code = R"(
|
|
void target(bool Foo) {
|
|
for (;;) {
|
|
(void)0;
|
|
// [[loop_body]]
|
|
}
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("loop_body"));
|
|
const Environment &LoopBodyEnv =
|
|
getEnvironmentAtAnnotation(Results, "loop_body");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
BoolValue &LoopBodyFooVal =
|
|
*cast<BoolValue>(LoopBodyEnv.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_FALSE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal));
|
|
});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveOptionDisabled) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool(bool &Var) { Var = true; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{/*.ContextSensitiveOpts=*/std::nullopt}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveDepthZero) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool(bool &Var) { Var = true; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/0}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetTrue) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool(bool &Var) { Var = true; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetFalse) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool(bool &Var) { Var = false; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetBothTrueAndFalse) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool(bool &Var, bool Val) { Var = Val; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
bool Bar = GiveBool();
|
|
SetBool(Foo, true);
|
|
SetBool(Bar, false);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
|
|
auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(BarVal));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetTwoLayersDepthOne) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool1(bool &Var) { Var = true; }
|
|
void SetBool2(bool &Var) { SetBool1(Var); }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool2(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/1}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetTwoLayersDepthTwo) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool1(bool &Var) { Var = true; }
|
|
void SetBool2(bool &Var) { SetBool1(Var); }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool2(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/2}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetThreeLayersDepthTwo) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool1(bool &Var) { Var = true; }
|
|
void SetBool2(bool &Var) { SetBool1(Var); }
|
|
void SetBool3(bool &Var) { SetBool2(Var); }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool3(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/2}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetThreeLayersDepthThree) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
void SetBool1(bool &Var) { Var = true; }
|
|
void SetBool2(bool &Var) { SetBool1(Var); }
|
|
void SetBool3(bool &Var) { SetBool2(Var); }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
SetBool3(Foo);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/3}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMutualRecursion) {
|
|
std::string Code = R"(
|
|
bool Pong(bool X, bool Y);
|
|
|
|
bool Ping(bool X, bool Y) {
|
|
if (X) {
|
|
return Y;
|
|
} else {
|
|
return Pong(!X, Y);
|
|
}
|
|
}
|
|
|
|
bool Pong(bool X, bool Y) {
|
|
if (Y) {
|
|
return X;
|
|
} else {
|
|
return Ping(X, !Y);
|
|
}
|
|
}
|
|
|
|
void target() {
|
|
bool Foo = Ping(false, false);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
// The analysis doesn't crash...
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
// ... but it also can't prove anything here.
|
|
EXPECT_FALSE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{/*.Depth=*/4}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetMultipleLines) {
|
|
std::string Code = R"(
|
|
void SetBools(bool &Var1, bool &Var2) {
|
|
Var1 = true;
|
|
Var2 = false;
|
|
}
|
|
|
|
void target() {
|
|
bool Foo = false;
|
|
bool Bar = true;
|
|
SetBools(Foo, Bar);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
|
|
auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(BarVal));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveSetMultipleBlocks) {
|
|
std::string Code = R"(
|
|
void IfCond(bool Cond, bool &Then, bool &Else) {
|
|
if (Cond) {
|
|
Then = true;
|
|
} else {
|
|
Else = true;
|
|
}
|
|
}
|
|
|
|
void target() {
|
|
bool Foo = false;
|
|
bool Bar = false;
|
|
bool Baz = false;
|
|
IfCond(Foo, Bar, Baz);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
|
|
ASSERT_THAT(BarDecl, NotNull());
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
|
|
EXPECT_FALSE(Env.flowConditionImplies(BarVal));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
|
|
|
|
auto &BazVal = *cast<BoolValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(BazVal));
|
|
EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveReturnVoid) {
|
|
std::string Code = R"(
|
|
void Noop() { return; }
|
|
|
|
void target() {
|
|
Noop();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
// This just tests that the analysis doesn't crash.
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveReturnTrue) {
|
|
std::string Code = R"(
|
|
bool GiveBool() { return true; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveReturnFalse) {
|
|
std::string Code = R"(
|
|
bool GiveBool() { return false; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveReturnArg) {
|
|
std::string Code = R"(
|
|
bool GiveBool();
|
|
bool GiveBack(bool Arg) { return Arg; }
|
|
|
|
void target() {
|
|
bool Foo = GiveBool();
|
|
bool Bar = GiveBack(Foo);
|
|
bool Baz = Foo == Bar;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
|
|
ASSERT_THAT(BazDecl, NotNull());
|
|
|
|
auto &BazVal = *cast<BoolValue>(Env.getValue(*BazDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(BazVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveReturnInt) {
|
|
std::string Code = R"(
|
|
int identity(int x) { return x; }
|
|
|
|
void target() {
|
|
int y = identity(42);
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
// This just tests that the analysis doesn't crash.
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodLiteral) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
bool giveBool() { return true; }
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
bool Foo = MyObj.giveBool();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodGetter) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
bool getField() { return Field; }
|
|
|
|
bool Field;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
MyObj.Field = true;
|
|
bool Foo = MyObj.getField();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodSetter) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
void setField(bool Val) { Field = Val; }
|
|
|
|
bool Field;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
MyObj.setField(true);
|
|
bool Foo = MyObj.Field;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodGetterAndSetter) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
bool getField() { return Field; }
|
|
void setField(bool Val) { Field = Val; }
|
|
|
|
private:
|
|
bool Field;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
MyObj.setField(true);
|
|
bool Foo = MyObj.getField();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodTwoLayersVoid) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
void Inner() { MyField = true; }
|
|
void Outer() { Inner(); }
|
|
|
|
bool MyField;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
MyObj.Outer();
|
|
bool Foo = MyObj.MyField;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));;
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveMethodTwoLayersReturn) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
bool Inner() { return MyField; }
|
|
bool Outer() { return Inner(); }
|
|
|
|
bool MyField;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
MyObj.MyField = true;
|
|
bool Foo = MyObj.Outer();
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));;
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveConstructorBody) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
MyClass() { MyField = true; }
|
|
|
|
bool MyField;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
bool Foo = MyObj.MyField;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveConstructorInitializer) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
MyClass() : MyField(true) {}
|
|
|
|
bool MyField;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
bool Foo = MyObj.MyField;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
TEST(TransferTest, ContextSensitiveConstructorDefault) {
|
|
std::string Code = R"(
|
|
class MyClass {
|
|
public:
|
|
MyClass() = default;
|
|
|
|
bool MyField = true;
|
|
};
|
|
|
|
void target() {
|
|
MyClass MyObj;
|
|
bool Foo = MyObj.MyField;
|
|
// [[p]]
|
|
}
|
|
)";
|
|
runDataflow(
|
|
Code,
|
|
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
|
|
ASTContext &ASTCtx) {
|
|
ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
|
|
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
|
|
|
|
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
|
|
ASSERT_THAT(FooDecl, NotNull());
|
|
|
|
auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
|
|
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
|
|
},
|
|
{TransferOptions{ContextSensitiveOptions{}}});
|
|
}
|
|
|
|
} // namespace
|