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 @@ -148,39 +148,26 @@ } } +// FIXME: Does not precisely handle non-virtual diamond inheritance. A single +// field decl will be modeled for all instances of the inherited field. static void -getFieldsFromClassHierarchy(QualType Type, bool IgnorePrivateFields, +getFieldsFromClassHierarchy(QualType Type, llvm::DenseSet &Fields) { if (Type->isIncompleteType() || Type->isDependentType() || !Type->isRecordType()) return; - for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { - if (IgnorePrivateFields && - (Field->getAccess() == AS_private || - (Field->getAccess() == AS_none && Type->getAsRecordDecl()->isClass()))) - continue; + for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) Fields.insert(Field); - } - if (auto *CXXRecord = Type->getAsCXXRecordDecl()) { - for (const CXXBaseSpecifier &Base : CXXRecord->bases()) { - // Ignore private fields (including default access in C++ classes) in - // base classes, because they are not visible in derived classes. - getFieldsFromClassHierarchy(Base.getType(), /*IgnorePrivateFields=*/true, - Fields); - } - } + if (auto *CXXRecord = Type->getAsCXXRecordDecl()) + for (const CXXBaseSpecifier &Base : CXXRecord->bases()) + getFieldsFromClassHierarchy(Base.getType(), Fields); } -/// Gets the set of all fields accesible from the type. -/// -/// FIXME: Does not precisely handle non-virtual diamond inheritance. A single -/// field decl will be modeled for all instances of the inherited field. -static llvm::DenseSet -getAccessibleObjectFields(QualType Type) { +/// Gets the set of all fields in the type. +static llvm::DenseSet getObjectFields(QualType Type) { llvm::DenseSet Fields; - // Don't ignore private fields for the class itself, only its super classes. - getFieldsFromClassHierarchy(Type, /*IgnorePrivateFields=*/false, Fields); + getFieldsFromClassHierarchy(Type, Fields); return Fields; } @@ -324,9 +311,8 @@ // 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 : getAccessibleObjectFields(Type)) { + for (const FieldDecl *Field : getObjectFields(Type)) FieldLocs.insert({Field, &createStorageLocation(Field->getType())}); - } return takeOwnership( std::make_unique(Type, std::move(FieldLocs))); } @@ -392,7 +378,7 @@ const QualType Type = AggregateLoc.getType(); assert(Type->isStructureOrClassType()); - for (const FieldDecl *Field : getAccessibleObjectFields(Type)) { + for (const FieldDecl *Field : getObjectFields(Type)) { assert(Field != nullptr); StorageLocation &FieldLoc = AggregateLoc.getChild(*Field); MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field); @@ -508,7 +494,7 @@ // FIXME: Initialize only fields that are accessed in the context that is // being analyzed. llvm::DenseMap FieldValues; - for (const FieldDecl *Field : getAccessibleObjectFields(Type)) { + for (const FieldDecl *Field : getObjectFields(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 @@ -1154,8 +1154,8 @@ // two. // Base-class fields. - EXPECT_THAT(FooVal.getChild(*ADefaultDecl), IsNull()); - EXPECT_THAT(FooVal.getChild(*APrivateDecl), IsNull()); + 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)), @@ -1177,6 +1177,40 @@ }); } +static void derivedBaseMemberExpectations( + llvm::ArrayRef>> + 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( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto &FooVal = *cast(Env.getValue(FooLoc)); + EXPECT_THAT(FooVal.getChild(*BarDecl), NotNull()); + EXPECT_EQ(Env.getValue(FooLoc.getChild(*BarDecl)), FooVal.getChild(*BarDecl)); +} + TEST_F(TransferTest, DerivedBaseMemberStructDefault) { std::string Code = R"( struct A { @@ -1190,41 +1224,28 @@ // [[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()->isRecordType()); - const FieldDecl *BarDecl = nullptr; - for (const clang::CXXBaseSpecifier &Base : - FooDecl->getType()->getAsCXXRecordDecl()->bases()) { - QualType BaseType = Base.getType(); - ASSERT_TRUE(BaseType->isStructureType()); + runDataflow(Code, derivedBaseMemberExpectations); +} - for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) { - if (Field->getNameAsString() == "Bar") { - BarDecl = Field; - } else { - FAIL() << "Unexpected field: " << Field->getNameAsString(); - } - } - } - ASSERT_THAT(BarDecl, NotNull()); +TEST_F(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 { + }; - const auto &FooLoc = *cast( - Env.getStorageLocation(*FooDecl, SkipPast::None)); - const auto &FooVal = *cast(Env.getValue(FooLoc)); - EXPECT_THAT(FooVal.getChild(*BarDecl), NotNull()); - EXPECT_EQ(Env.getValue(FooLoc.getChild(*BarDecl)), - FooVal.getChild(*BarDecl)); - }); + void target() { + B Foo; + (void)Foo.Bar; + // [[p]] + } + )"; + runDataflow(Code, derivedBaseMemberExpectations); } TEST_F(TransferTest, ClassMember) {