diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -88,6 +88,22 @@ return It == ExprToLoc.end() ? nullptr : It->second; } + /// Assigns `Loc` as the storage location of the `this` pointee. + /// + /// Requirements: + /// + /// The `this` pointee must not be assigned a storage location. + void setThisPointeeStorageLocation(StorageLocation &Loc) { + assert(ThisPointeeLoc == nullptr); + ThisPointeeLoc = &Loc; + } + + /// Returns the storage location assigned to the `this` pointee or null if the + /// `this` pointee has no assigned storage location. + StorageLocation *getThisPointeeStorageLocation() const { + return ThisPointeeLoc; + } + private: // Storage for the state of a program. std::vector> Locs; @@ -101,7 +117,7 @@ llvm::DenseMap DeclToLoc; llvm::DenseMap ExprToLoc; - // FIXME: Add `StorageLocation` for `this`. + StorageLocation *ThisPointeeLoc = nullptr; // FIXME: Add support for boolean expressions. }; diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" @@ -39,12 +40,27 @@ None, /// An optional reference should be skipped past. Reference, + /// An optional reference should be skipped past, then an optional pointer + /// should be skipped past. + ReferenceThenPointer, }; /// Holds the state of the program (store and heap) at a given program point. class Environment { public: - Environment(DataflowAnalysisContext &DACtx) : DACtx(&DACtx) {} + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program. + explicit Environment(DataflowAnalysisContext &DACtx) : DACtx(&DACtx) {} + + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program. + /// + /// If `DeclCtx` is a function, initializes the environment with symbolic + /// representations of the function parameters. + /// + /// If `DeclCtx` is a non-static member function, initializes the environment + /// with a symbolic representation of the `this` pointee. + Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); bool operator==(const Environment &) const; @@ -92,6 +108,11 @@ /// assigned a storage location in the environment. StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const; + /// Returns the storage location assigned to the `this` pointee in the + /// environment or null if the `this` pointee has no assigned storage location + /// in the environment. + StorageLocation *getThisPointeeStorageLocation() const; + /// Creates a value appropriate for `Type`, assigns it to `Loc`, and returns /// it, if `Type` is supported, otherwise return null. If `Type` is a pointer /// or reference type, creates all the necessary storage locations and values diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -14,6 +14,7 @@ #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" @@ -40,6 +41,31 @@ return Result; } +Environment::Environment(DataflowAnalysisContext &DACtx, + const DeclContext &DeclCtx) + : Environment(DACtx) { + if (const auto *FuncDecl = dyn_cast(&DeclCtx)) { + for (const auto *ParamDecl : FuncDecl->parameters()) { + assert(ParamDecl != nullptr); + auto &ParamLoc = createStorageLocation(*ParamDecl); + setStorageLocation(*ParamDecl, ParamLoc); + initValueInStorageLocation(ParamLoc, ParamDecl->getType()); + } + } + + if (const auto *MethodDecl = dyn_cast(&DeclCtx)) { + if (!MethodDecl->isStatic()) { + QualType ThisPointeeType = MethodDecl->getThisObjectType(); + // FIXME: Add support for union types. + if (!ThisPointeeType->isUnionType()) { + auto &ThisPointeeLoc = createStorageLocation(ThisPointeeType); + DACtx.setThisPointeeStorageLocation(ThisPointeeLoc); + initValueInStorageLocation(ThisPointeeLoc, ThisPointeeType); + } + } + } +} + bool Environment::operator==(const Environment &Other) const { assert(DACtx == Other.DACtx); return DeclToLoc == Other.DeclToLoc && LocToVal == Other.LocToVal; @@ -124,6 +150,10 @@ return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP); } +StorageLocation *Environment::getThisPointeeStorageLocation() const { + return DACtx->getThisPointeeStorageLocation(); +} + void Environment::setValue(const StorageLocation &Loc, Value &Value) { LocToVal[&Loc] = &Value; } @@ -242,6 +272,11 @@ if (auto *Val = dyn_cast_or_null(getValue(Loc))) return Val->getPointeeLoc(); return Loc; + case SkipPast::ReferenceThenPointer: + StorageLocation &LocPastRef = skip(Loc, SkipPast::Reference); + if (auto *Val = dyn_cast_or_null(getValue(LocPastRef))) + return Val->getPointeeLoc(); + return LocPastRef; } llvm_unreachable("bad SkipPast kind"); } diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -123,6 +123,43 @@ // FIXME: Add support for UO_AddrOf, UO_LNot. } + void VisitCXXThisExpr(const CXXThisExpr *S) { + auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation(); + assert(ThisPointeeLoc != nullptr); + + auto &Loc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, Loc); + Env.setValue(Loc, Env.takeOwnership( + std::make_unique(*ThisPointeeLoc))); + } + + void VisitMemberExpr(const MemberExpr *S) { + ValueDecl *Member = S->getMemberDecl(); + assert(Member != nullptr); + + if (Member->isFunctionOrFunctionTemplate()) + return; + + auto *BaseLoc = cast_or_null( + Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer)); + if (BaseLoc == nullptr) + return; + + // FIXME: Add support for union types. + if (BaseLoc->getType()->isUnionType()) + return; + + auto &MemberLoc = BaseLoc->getChild(*Member); + if (MemberLoc.getType()->isReferenceType()) { + Env.setStorageLocation(*S, MemberLoc); + } else { + auto &Loc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, Loc); + Env.setValue( + Loc, Env.takeOwnership(std::make_unique(MemberLoc))); + } + } + // FIXME: Add support for: // - CallExpr // - CXXBindTemporaryExpr @@ -131,9 +168,7 @@ // - CXXFunctionalCastExpr // - CXXOperatorCallExpr // - CXXStaticCastExpr - // - CXXThisExpr // - MaterializeTemporaryExpr - // - MemberExpr private: void visitVarDecl(const VarDecl &D) { diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -99,7 +99,7 @@ ASSERT_TRUE((bool)CFCtx) << "Could not build ControlFlowContext."; DataflowAnalysisContext DACtx; - Environment Env(DACtx); + Environment Env(DACtx, *F); auto Analysis = MakeAnalysis(Context, Env); llvm::Expected> diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -67,7 +67,7 @@ TEST_F(TransferTest, IntVarDecl) { std::string Code = R"( void target() { - int foo; + int Foo; // [[p]] } )"; @@ -79,7 +79,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const StorageLocation *FooLoc = @@ -93,12 +93,12 @@ TEST_F(TransferTest, StructVarDecl) { std::string Code = R"( - struct Foo { + struct A { int Bar; }; void target() { - Foo foo; + A Foo; // [[p]] } )"; @@ -110,7 +110,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); ASSERT_TRUE(FooDecl->getType()->isStructureType()); @@ -139,12 +139,12 @@ TEST_F(TransferTest, ClassVarDecl) { std::string Code = R"( - class Foo { + class A { int Bar; }; void target() { - Foo foo; + A Foo; // [[p]] } )"; @@ -156,7 +156,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); ASSERT_TRUE(FooDecl->getType()->isClassType()); @@ -185,12 +185,12 @@ TEST_F(TransferTest, ReferenceVarDecl) { std::string Code = R"( - struct Foo {}; + struct A {}; - Foo& getFoo(); + A &getA(); void target() { - Foo& foo = getFoo(); + A &Foo = getA(); // [[p]] } )"; @@ -202,7 +202,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const StorageLocation *FooLoc = @@ -221,25 +221,25 @@ TEST_F(TransferTest, SelfReferentialReferenceVarDecl) { std::string Code = R"( - struct Foo; + struct A; - struct Baz {}; + struct B {}; - struct Bar { - Foo& FooRef; - Foo* FooPtr; - Baz& BazRef; - Baz* BazPtr; + struct C { + A &FooRef; + A *FooPtr; + B &BazRef; + B *BazPtr; }; - struct Foo { - Bar& Bar; + struct A { + C &Bar; }; - Foo& getFoo(); + A &getA(); void target() { - Foo& foo = getFoo(); + A &Foo = getA(); // [[p]] } )"; @@ -250,7 +250,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); ASSERT_TRUE(FooDecl->getType()->isReferenceType()); @@ -330,12 +330,12 @@ TEST_F(TransferTest, PointerVarDecl) { std::string Code = R"( - struct Foo {}; + struct A {}; - Foo* getFoo(); + A *getA(); void target() { - Foo* foo = getFoo(); + A *Foo = getA(); // [[p]] } )"; @@ -347,7 +347,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const StorageLocation *FooLoc = @@ -365,25 +365,25 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) { std::string Code = R"( - struct Foo; + struct A; - struct Baz {}; + struct B {}; - struct Bar { - Foo& FooRef; - Foo* FooPtr; - Baz& BazRef; - Baz* BazPtr; + struct C { + A &FooRef; + A *FooPtr; + B &BazRef; + B *BazPtr; }; - struct Foo { - Bar* Bar; + struct A { + C *Bar; }; - Foo* getFoo(); + A *getA(); void target() { - Foo* foo = getFoo(); + A *Foo = getA(); // [[p]] } )"; @@ -395,7 +395,7 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); ASSERT_TRUE(FooDecl->getType()->isPointerType()); @@ -487,14 +487,14 @@ TEST_F(TransferTest, JoinVarDecl) { std::string Code = R"( - void target(bool b) { - int foo; + void target(bool B) { + int Foo; // [[p1]] - if (b) { - int bar; + if (B) { + int Bar; // [[p2]] } else { - int baz; + int Baz; // [[p3]] } (void)0; @@ -507,13 +507,13 @@ ASTContext &ASTCtx) { ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _), Pair("p2", _), Pair("p1", _))); - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); ASSERT_THAT(BazDecl, NotNull()); const Environment &Env1 = Results[3].second.Env; @@ -543,9 +543,9 @@ TEST_F(TransferTest, BinaryOperatorAssign) { std::string Code = R"( void target() { - int foo; - int bar; - (bar) = (foo); + int Foo; + int Bar; + (Bar) = (Foo); // [[p]] } )"; @@ -557,13 +557,13 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal); @@ -573,8 +573,8 @@ TEST_F(TransferTest, VarDeclInitAssign) { std::string Code = R"( void target() { - int foo; - int bar = foo; + int Foo; + int Bar = Foo; // [[p]] } )"; @@ -586,13 +586,13 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal); @@ -602,9 +602,9 @@ TEST_F(TransferTest, VarDeclInitAssignChained) { std::string Code = R"( void target() { - int foo; - int bar; - int baz = (bar = foo); + int Foo; + int Bar; + int Baz = (Bar = Foo); // [[p]] } )"; @@ -616,16 +616,16 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); ASSERT_THAT(BazDecl, NotNull()); EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal); @@ -636,10 +636,10 @@ TEST_F(TransferTest, VarDeclInitAssignPtrDeref) { std::string Code = R"( void target() { - int foo; - int *bar; - *(bar) = foo; - int baz = *(bar); + int Foo; + int *Bar; + *(Bar) = Foo; + int Baz = *(Bar); // [[p]] } )"; @@ -651,20 +651,20 @@ ASSERT_THAT(Results, ElementsAre(Pair("p", _))); const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); const auto *BarVal = cast(Env.getValue(*BarDecl, SkipPast::None)); EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); ASSERT_THAT(BazDecl, NotNull()); EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal); @@ -674,13 +674,13 @@ TEST_F(TransferTest, AssignToAndFromReference) { std::string Code = R"( void target() { - int foo; - int bar; - int& baz = foo; + int Foo; + int Bar; + int &Baz = Foo; // [[p1]] - baz = bar; - int qux = baz; - int& quux = baz; + Baz = Bar; + int Qux = Baz; + int &Quux = Baz; // [[p2]] } )"; @@ -693,19 +693,19 @@ const Environment &Env1 = Results[0].second.Env; const Environment &Env2 = Results[1].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); ASSERT_THAT(FooDecl, NotNull()); const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(BarVal)); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); ASSERT_THAT(BazDecl, NotNull()); EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal); @@ -713,14 +713,386 @@ EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal); EXPECT_EQ(Env2.getValue(*FooDecl, SkipPast::None), BarVal); - const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "qux"); + const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux"); ASSERT_THAT(QuxDecl, NotNull()); EXPECT_EQ(Env2.getValue(*QuxDecl, SkipPast::None), BarVal); - const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "quux"); + const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux"); ASSERT_THAT(QuuxDecl, NotNull()); EXPECT_EQ(Env2.getValue(*QuuxDecl, SkipPast::Reference), BarVal); }); } +TEST_F(TransferTest, MultipleParamDecls) { + std::string Code = R"( + void target(int Foo, int Bar) { + (void)0; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + 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(FooLoc)); + + const Value *FooVal = Env.getValue(*FooLoc); + ASSERT_TRUE(isa_and_nonnull(FooVal)); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const StorageLocation *BarLoc = + Env.getStorageLocation(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarLoc)); + + const Value *BarVal = Env.getValue(*BarLoc); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + }); +} + +TEST_F(TransferTest, StructParamDecl) { + std::string Code = R"( + struct A { + int Bar; + }; + + void target(A Foo) { + (void)0; + // [[p]] + } + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + 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( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *BarLoc = + cast(&FooLoc->getChild(*BarDecl)); + + const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); + ASSERT_EQ(Env.getValue(*BarLoc), BarVal); + }); +} + +TEST_F(TransferTest, ReferenceParamDecl) { + std::string Code = R"( + struct A {}; + + void target(A &Foo) { + (void)0; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + 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(FooLoc)); + + const ReferenceValue *FooVal = + cast(Env.getValue(*FooLoc)); + const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); + ASSERT_TRUE(isa(&FooPointeeLoc)); + + const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); + ASSERT_TRUE(isa_and_nonnull(FooPointeeVal)); + }); +} + +TEST_F(TransferTest, PointerParamDecl) { + std::string Code = R"( + struct A {}; + + void target(A *Foo) { + (void)0; + // [[p]] + } + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + 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(FooLoc)); + + const PointerValue *FooVal = cast(Env.getValue(*FooLoc)); + const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); + ASSERT_TRUE(isa(&FooPointeeLoc)); + + const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); + ASSERT_TRUE(isa_and_nonnull(FooPointeeVal)); + }); +} + +TEST_F(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>> + 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( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal); + }); +} + +TEST_F(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>> + 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( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal); + }); +} + +TEST_F(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>> + 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( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); + const auto *BarPointeeVal = + cast(Env.getValue(BarVal->getPointeeLoc())); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarPointeeVal); + }); +} + +TEST_F(TransferTest, StructThisMember) { + std::string Code = R"( + struct A { + int Bar; + + void target() { + int Foo = Bar; + // [[p]] + } + }; + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const auto *ThisLoc = + cast(Env.getThisPointeeStorageLocation()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const auto *BarLoc = + cast(&ThisLoc->getChild(*BarDecl)); + ASSERT_TRUE(isa_and_nonnull(BarLoc)); + + const Value *BarVal = Env.getValue(*BarLoc); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal); + }); +} + +TEST_F(TransferTest, ClassThisMember) { + std::string Code = R"( + class A { + int Bar; + + void target() { + int Foo = Bar; + // [[p]] + } + }; + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const auto *ThisLoc = + cast(Env.getThisPointeeStorageLocation()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const auto *BarLoc = + cast(&ThisLoc->getChild(*BarDecl)); + ASSERT_TRUE(isa_and_nonnull(BarLoc)); + + const Value *BarVal = Env.getValue(*BarLoc); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal); + }); +} + } // namespace