/// Warns on methods overridden in DerivedDecl with respect to BaseDecl. /// FIXME: this warns on all overrides outside of the sliced path in case of /// multiple inheritance. void SlicingCheck::DiagnoseSlicedOverriddenMethods( const Expr &Call, const CXXRecordDecl &DerivedDecl, const CXXRecordDecl &BaseDecl) { if (DerivedDecl.getCanonicalDecl() == BaseDecl.getCanonicalDecl()) return; for (const auto &Method : DerivedDecl.methods()) { // Virtual destructors are OK. We're ignoring constructors since they are // tagged as overrides. if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) continue; if (Method->size_overridden_methods() > 0) { diag(Call.getExprLoc(), "slicing object from type %0 to %1 discards override %2") << &DerivedDecl << &BaseDecl << Method; } } // Recursively process bases. for (const auto &Base : DerivedDecl.bases()) { if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) { if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>( BaseRecordType->getDecl()->getDefinition())) DiagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl); } } }
static bool isParentOf(const CXXRecordDecl &Parent, const CXXRecordDecl &ThisClass) { if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl()) return true; const CXXRecordDecl *ParentCanonicalDecl = Parent.getCanonicalDecl(); return ThisClass.bases_end() != llvm::find_if(ThisClass.bases(), [=](const CXXBaseSpecifier &Base) { auto *BaseDecl = Base.getType()->getAsCXXRecordDecl(); assert(BaseDecl); return ParentCanonicalDecl == BaseDecl->getCanonicalDecl(); }); }
/// Determines whether the accessed entity is accessible. Public members /// have been weeded out by this point. static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC, AccessTarget &Entity) { // Determine the actual naming class. CXXRecordDecl *NamingClass = Entity.getNamingClass(); while (NamingClass->isAnonymousStructOrUnion()) NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); NamingClass = NamingClass->getCanonicalDecl(); AccessSpecifier UnprivilegedAccess = Entity.getAccess(); assert(UnprivilegedAccess != AS_public && "public access not weeded out"); // Before we try to recalculate access paths, try to white-list // accesses which just trade in on the final step, i.e. accesses // which don't require [M4] or [B4]. These are by far the most // common forms of privileged access. if (UnprivilegedAccess != AS_none) { switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) { case AR_dependent: // This is actually an interesting policy decision. We don't // *have* to delay immediately here: we can do the full access // calculation in the hope that friendship on some intermediate // class will make the declaration accessible non-dependently. // But that's not cheap, and odds are very good (note: assertion // made without data) that the friend declaration will determine // access. return AR_dependent; case AR_accessible: return AR_accessible; case AR_inaccessible: break; } } AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext(); // We lower member accesses to base accesses by pretending that the // member is a base class of its declaring class. AccessSpecifier FinalAccess; if (Entity.isMemberAccess()) { // Determine if the declaration is accessible from EC when named // in its declaring class. NamedDecl *Target = Entity.getTargetDecl(); const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); FinalAccess = Target->getAccess(); switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) { case AR_accessible: FinalAccess = AS_public; break; case AR_inaccessible: break; case AR_dependent: return AR_dependent; // see above } if (DeclaringClass == NamingClass) return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); Entity.suppressInstanceContext(); } else { FinalAccess = AS_public; } assert(Entity.getDeclaringClass() != NamingClass); // Append the declaration's access if applicable. CXXBasePaths Paths; CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); if (!Path) return AR_dependent; assert(Path->Access <= UnprivilegedAccess && "access along best path worse than direct?"); if (Path->Access == AS_public) return AR_accessible; return AR_inaccessible; }