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 @@ -260,6 +260,12 @@ llvm::DenseMap LocToVal; + // Maps locations of struct members to symbolic values of the structs that own + // them and the decls of the struct members. + llvm::DenseMap> + MemberLocToStruct; + // FIXME: Add flow condition constraints. }; 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 @@ -93,6 +93,9 @@ if (ExprToLoc != Other.ExprToLoc) return false; + if (MemberLocToStruct != Other.MemberLocToStruct) + return false; + if (LocToVal.size() != Other.LocToVal.size()) return false; @@ -131,6 +134,12 @@ if (ExprToLocSizeBefore != ExprToLoc.size()) Effect = LatticeJoinEffect::Changed; + const unsigned MemberLocToStructSizeBefore = MemberLocToStruct.size(); + MemberLocToStruct = + intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct); + if (MemberLocToStructSizeBefore != MemberLocToStruct.size()) + Effect = LatticeJoinEffect::Changed; + // Move `LocToVal` so that `Environment::ValueModel::merge` can safely assign // values to storage locations while this code iterates over the current // assignments. @@ -240,9 +249,25 @@ for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { assert(Field != nullptr); - setValue(AggregateLoc.getChild(*Field), StructVal->getChild(*Field)); + StorageLocation &FieldLoc = AggregateLoc.getChild(*Field); + MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field); + setValue(FieldLoc, StructVal->getChild(*Field)); } } + + auto IT = MemberLocToStruct.find(&Loc); + if (IT != MemberLocToStruct.end()) { + // `Loc` is the location of a struct member so we need to also update the + // value of the member in the corresponding `StructValue`. + + assert(IT->second.first != nullptr); + StructValue &StructVal = *IT->second.first; + + assert(IT->second.second != nullptr); + const ValueDecl &Member = *IT->second.second; + + StructVal.setChild(Member, Val); + } } Value *Environment::getValue(const StorageLocation &Loc) const { 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 @@ -2153,4 +2153,48 @@ }); } +TEST_F(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>> + 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(Env.getValue(*BarDecl, SkipPast::None)); + + const auto *A2Val = + cast(Env.getValue(*A2Decl, SkipPast::None)); + EXPECT_EQ(&A2Val->getChild(*FooDecl), BarVal); + }); +} + } // namespace