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 @@ -164,6 +164,36 @@ return JoinedConstraints; } +static llvm::DenseSet +getObjectFieldsWithBases(QualType Type) { + llvm::DenseSet Fields; + std::function Traverse; + Traverse = [&Traverse, &Fields](QualType Type, bool add_private_fields) { + if (Type->isIncompleteType() || Type->isDependentType()) + return; + + for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { + if (!add_private_fields) { + if (Field->getAccess() == clang::AS_private || + (Field->getAccess() == clang::AS_none && + Type->getAsRecordDecl()->isClass())) { + continue; + } + } + Fields.insert(Field); + } + if (auto *CXXRecord = Type->getAsCXXRecordDecl()) { + for (const clang::CXXBaseSpecifier &Base : CXXRecord->bases()) { + // Don't add private fields (including default access in C++ classes) + // in base classes, as they are not visible in derived classes. + Traverse(Base.getType(), /*add_private_fields=*/false); + } + } + }; + Traverse(Type, /*add_private_fields=*/true); + return Fields; +} + Environment::Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx) : Environment(DACtx) { @@ -296,7 +326,7 @@ // FIXME: Explore options to avoid eager initialization of fields as some of // them might not be needed for a particular analysis. llvm::DenseMap FieldLocs; - for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { + for (const FieldDecl *Field : getObjectFieldsWithBases(Type)) { FieldLocs.insert({Field, &createStorageLocation(Field->getType())}); } return takeOwnership( @@ -479,7 +509,7 @@ // FIXME: Initialize only fields that are accessed in the context that is // being analyzed. llvm::DenseMap FieldValues; - for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { + for (const FieldDecl *Field : getObjectFieldsWithBases(Type)) { assert(Field != nullptr); QualType FieldType = Field->getType(); 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 @@ -1009,6 +1009,59 @@ }); } +TEST_F(TransferTest, DerivedBaseMember) { + std::string Code = R"( + struct A { + int Bar; + }; + struct B : public A { + }; + + void target(B 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()); + 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( + 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 {