void CopyablePolymorphic::VisitDecl(clang::Decl *decl) { CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(decl); if (!record || !record->hasDefinition() || record->getDefinition() != record || !record->isPolymorphic()) return; CXXConstructorDecl *copyCtor = Utils::copyCtor(record); CXXMethodDecl *copyAssign = Utils::copyAssign(record); const bool hasCallableCopyCtor = copyCtor && !copyCtor->isDeleted() && copyCtor->getAccess() != clang::AS_private; const bool hasCallableCopyAssign = copyAssign && !copyAssign->isDeleted() && copyAssign->getAccess() != clang::AS_private; if (!hasCallableCopyCtor && !hasCallableCopyAssign) return; emitWarning(record->getLocStart(), "Polymorphic class is copyable. Potential slicing."); }
bool CXXBasePaths::lookupInBases(ASTContext &Context, const CXXRecordDecl *Record, CXXRecordDecl::BaseMatchesCallback BaseMatches, bool LookupInDependent) { bool FoundPath = false; // The access of the path down to this record. AccessSpecifier AccessToHere = ScratchPath.Access; bool IsFirstStep = ScratchPath.empty(); for (const auto &BaseSpec : Record->bases()) { // Find the record of the base class subobjects for this type. QualType BaseType = Context.getCanonicalType(BaseSpec.getType()).getUnqualifiedType(); // C++ [temp.dep]p3: // In the definition of a class template or a member of a class template, // if a base class of the class template depends on a template-parameter, // the base class scope is not examined during unqualified name lookup // either at the point of definition of the class template or member or // during an instantiation of the class tem- plate or member. if (!LookupInDependent && BaseType->isDependentType()) continue; // Determine whether we need to visit this base class at all, // updating the count of subobjects appropriately. std::pair<bool, unsigned>& Subobjects = ClassSubobjects[BaseType]; bool VisitBase = true; bool SetVirtual = false; if (BaseSpec.isVirtual()) { VisitBase = !Subobjects.first; Subobjects.first = true; if (isDetectingVirtual() && DetectedVirtual == nullptr) { // If this is the first virtual we find, remember it. If it turns out // there is no base path here, we'll reset it later. DetectedVirtual = BaseType->getAs<RecordType>(); SetVirtual = true; } } else ++Subobjects.second; if (isRecordingPaths()) { // Add this base specifier to the current path. CXXBasePathElement Element; Element.Base = &BaseSpec; Element.Class = Record; if (BaseSpec.isVirtual()) Element.SubobjectNumber = 0; else Element.SubobjectNumber = Subobjects.second; ScratchPath.push_back(Element); // Calculate the "top-down" access to this base class. // The spec actually describes this bottom-up, but top-down is // equivalent because the definition works out as follows: // 1. Write down the access along each step in the inheritance // chain, followed by the access of the decl itself. // For example, in // class A { public: int foo; }; // class B : protected A {}; // class C : public B {}; // class D : private C {}; // we would write: // private public protected public // 2. If 'private' appears anywhere except far-left, access is denied. // 3. Otherwise, overall access is determined by the most restrictive // access in the sequence. if (IsFirstStep) ScratchPath.Access = BaseSpec.getAccessSpecifier(); else ScratchPath.Access = CXXRecordDecl::MergeAccess(AccessToHere, BaseSpec.getAccessSpecifier()); } // Track whether there's a path involving this specific base. bool FoundPathThroughBase = false; if (BaseMatches(&BaseSpec, ScratchPath)) { // We've found a path that terminates at this base. FoundPath = FoundPathThroughBase = true; if (isRecordingPaths()) { // We have a path. Make a copy of it before moving on. Paths.push_back(ScratchPath); } else if (!isFindingAmbiguities()) { // We found a path and we don't care about ambiguities; // return immediately. return FoundPath; } } else if (VisitBase) { CXXRecordDecl *BaseRecord; if (LookupInDependent) { BaseRecord = nullptr; const TemplateSpecializationType *TST = BaseSpec.getType()->getAs<TemplateSpecializationType>(); if (!TST) { if (auto *RT = BaseSpec.getType()->getAs<RecordType>()) BaseRecord = cast<CXXRecordDecl>(RT->getDecl()); } else { TemplateName TN = TST->getTemplateName(); if (auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) BaseRecord = TD->getTemplatedDecl(); } if (BaseRecord) { if (!BaseRecord->hasDefinition() || VisitedDependentRecords.count(BaseRecord)) { BaseRecord = nullptr; } else { VisitedDependentRecords.insert(BaseRecord); } } } else { BaseRecord = cast<CXXRecordDecl>( BaseSpec.getType()->castAs<RecordType>()->getDecl()); } if (BaseRecord && lookupInBases(Context, BaseRecord, BaseMatches, LookupInDependent)) { // C++ [class.member.lookup]p2: // A member name f in one sub-object B hides a member name f in // a sub-object A if A is a base class sub-object of B. Any // declarations that are so hidden are eliminated from // consideration. // There is a path to a base class that meets the criteria. If we're // not collecting paths or finding ambiguities, we're done. FoundPath = FoundPathThroughBase = true; if (!isFindingAmbiguities()) return FoundPath; } } // Pop this base specifier off the current path (if we're // collecting paths). if (isRecordingPaths()) { ScratchPath.pop_back(); } // If we set a virtual earlier, and this isn't a path, forget it again. if (SetVirtual && !FoundPathThroughBase) { DetectedVirtual = nullptr; } } // Reset the scratch path access. ScratchPath.Access = AccessToHere; return FoundPath; }