| //===- 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 "NoopAnalysis.h" |
| #include "TestingSupport.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Analysis/FlowSensitive/DataflowEnvironment.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::_; |
| using ::testing::ElementsAre; |
| using ::testing::IsNull; |
| using ::testing::NotNull; |
| using ::testing::Pair; |
| using ::testing::SizeIs; |
| |
| template <typename Matcher> |
| void runDataflow(llvm::StringRef Code, Matcher Match, |
| LangStandard::Kind Std = LangStandard::lang_cxx17, |
| bool ApplyBuiltinTransfer = true, |
| llvm::StringRef TargetFun = "target") { |
| ASSERT_THAT_ERROR( |
| test::checkDataflow<NoopAnalysis>( |
| Code, TargetFun, |
| [ApplyBuiltinTransfer](ASTContext &C, Environment &) { |
| return NoopAnalysis(C, ApplyBuiltinTransfer); |
| }, |
| [&Match]( |
| llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { Match(Results, ASTCtx); }, |
| {"-fsyntax-only", "-fno-delayed-template-parsing", |
| "-std=" + |
| std::string( |
| LangStandard::getLangStandardForKind(Std).getName())}), |
| llvm::Succeeded()); |
| } |
| |
| TEST(TransferTest, IntVarDeclNotTrackedWhenTransferDisabled) { |
| std::string Code = R"( |
| void target() { |
| int Foo; |
| // [[p]] |
| } |
| )"; |
| runDataflow( |
| Code, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef<std::pair< |
| std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef<std::pair< |
| std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _), |
| Pair("p2", _), Pair("p1", _))); |
| 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 = Results[3].second.Env; |
| 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 = Results[2].second.Env; |
| 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 = Results[1].second.Env; |
| 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 = Results[0].second.Env; |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _))); |
| const Environment &Env1 = Results[0].second.Env; |
| const Environment &Env2 = Results[1].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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( |
| llvm::ArrayRef<std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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( |
| test::checkDataflow<NoopAnalysis>( |
| Code, cxxConstructorDecl(ofClass(hasName("B"))), |
| [](ASTContext &C, Environment &) { |
| return NoopAnalysis(C, /*ApplyBuiltinTransfer=*/true); |
| }, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| // 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, ElementsAre(Pair("p", _))); |
| }, |
| {"-fsyntax-only", "-fno-delayed-template-parsing", |
| "-std=" + std::string(LangStandard::getLangStandardForKind( |
| LangStandard::lang_cxx17) |
| .getName())}), |
| llvm::Succeeded()); |
| } |
| |
| TEST(TransferTest, ReferenceMember) { |
| std::string Code = R"( |
| struct A { |
| int &Bar; |
| }; |
| |
| void target(A Foo) { |
| int Baz = Foo.Bar; |
| // [[p]] |
| } |
| )"; |
| runDataflow( |
| Code, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p1", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p2", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p3", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _))); |
| const Environment &Env1 = Results[0].second.Env; |
| const Environment &Env2 = Results[1].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _))); |
| const Environment &Env1 = Results[0].second.Env; |
| const Environment &Env2 = Results[1].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const auto &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const auto &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const auto &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const auto &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const auto &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p-else", _), Pair("p-then", _))); |
| const Environment &EnvElse = Results[0].second.Env; |
| const Environment &EnvThen = Results[1].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p-else", _), Pair("p-then", _))); |
| const Environment &EnvElse = Results[0].second.Env; |
| const Environment &EnvThen = Results[1].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, SizeIs(3)); |
| |
| const ValueDecl *CDecl = findValueDecl(ASTCtx, "C"); |
| ASSERT_THAT(CDecl, NotNull()); |
| |
| { |
| ASSERT_THAT(Results[2], Pair("p0", _)); |
| const Environment &Env = Results[2].second.Env; |
| 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))); |
| } |
| |
| { |
| ASSERT_THAT(Results[1], Pair("p1", _)); |
| const Environment &Env = Results[1].second.Env; |
| auto &CVal = *cast<BoolValue>(Env.getValue(*CDecl, SkipPast::None)); |
| EXPECT_TRUE(Env.flowConditionImplies(CVal)); |
| } |
| |
| { |
| ASSERT_THAT(Results[0], Pair("p2", _)); |
| const Environment &Env = Results[0].second.Env; |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, 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 implicit in `runDataflow` -- |
| // namely, that the analysis succeeds, rather than hitting the maximum number |
| // of iterations. |
| runDataflow( |
| Code, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, |
| ElementsAre(Pair("p-outer", _), Pair("p-inner", _))); |
| const Environment &OuterEnv = Results[0].second.Env; |
| const Environment &InnerEnv = Results[1].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, |
| [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("p", _))); |
| const Environment &Env = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, |
| ElementsAre(Pair("if_else", _), Pair("if_then", _))); |
| const Environment &ThenEnv = Results[1].second.Env; |
| const Environment &ElseEnv = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, |
| ElementsAre(Pair("after_loop", _), Pair("loop_body", _))); |
| const Environment &LoopBodyEnv = Results[1].second.Env; |
| const Environment &AfterLoopEnv = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, |
| ElementsAre(Pair("after_loop", _), Pair("loop_body", _))); |
| const Environment &LoopBodyEnv = Results[1].second.Env; |
| const Environment &AfterLoopEnv = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, |
| ElementsAre(Pair("after_loop", _), Pair("loop_body", _))); |
| const Environment &LoopBodyEnv = Results[1].second.Env; |
| const Environment &AfterLoopEnv = Results[0].second.Env; |
| |
| 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, [](llvm::ArrayRef< |
| std::pair<std::string, DataflowAnalysisState<NoopLattice>>> |
| Results, |
| ASTContext &ASTCtx) { |
| ASSERT_THAT(Results, ElementsAre(Pair("loop_body", _))); |
| const Environment &LoopBodyEnv = Results[0].second.Env; |
| |
| const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); |
| ASSERT_THAT(FooDecl, NotNull()); |
| |
| BoolValue &LoopBodyFooVal = |
| *cast<BoolValue>(LoopBodyEnv.getValue(*FooDecl, SkipPast::None)); |
| EXPECT_FALSE(LoopBodyEnv.flowConditionImplies(LoopBodyFooVal)); |
| }); |
| } |
| |
| } // namespace |