egorzhdan created this revision. egorzhdan requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
If a class declares an instance property, and an inheritor class declares a class property with the same name, Clang Sema currently treats the latter as an overridden property, and compares the attributes of the two properties to check for a mismatch. The resulting diagnostics might be misleading, since neither of the properties actually overrides the another one. rdar://86018435 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D116412 Files: clang/lib/Sema/SemaObjCProperty.cpp clang/test/SemaObjC/class-property-inheritance.m
Index: clang/test/SemaObjC/class-property-inheritance.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/class-property-inheritance.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +@class MyObject; + +@interface TopClassWithInstanceProperty +@property(nullable, readonly, strong) MyObject *foo; +@end + +@interface SubClassWithClassProperty : TopClassWithInstanceProperty +@property(nonnull, readonly, copy, class) MyObject *foo; +@end + +@interface TopClassWithClassProperty +@property(nullable, readonly, class) MyObject *foo; +@end + +@interface SubClassWithInstanceProperty : TopClassWithClassProperty +@property(nonnull, readonly, copy) MyObject *foo; +@end Index: clang/lib/Sema/SemaObjCProperty.cpp =================================================================== --- clang/lib/Sema/SemaObjCProperty.cpp +++ clang/lib/Sema/SemaObjCProperty.cpp @@ -225,43 +225,56 @@ if (Res->getType().getObjCLifetime()) checkPropertyDeclWithOwnership(*this, Res); - llvm::SmallPtrSet<ObjCProtocolDecl *, 16> KnownProtos; - if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(ClassDecl)) { - // For a class, compare the property against a property in our superclass. - bool FoundInSuper = false; - ObjCInterfaceDecl *CurrentInterfaceDecl = IFace; - while (ObjCInterfaceDecl *Super = CurrentInterfaceDecl->getSuperClass()) { - if (ObjCPropertyDecl *SuperProp = - Super->lookup(Res->getDeclName()).find_first<ObjCPropertyDecl>()) { - DiagnosePropertyMismatch(Res, SuperProp, Super->getIdentifier(), false); - FoundInSuper = true; - break; + // For an instance property, check consistency with the properties of + // superclasses. + if (Res->isInstanceProperty()) { + llvm::SmallPtrSet<ObjCProtocolDecl *, 16> KnownProtos; + if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(ClassDecl)) { + // For a class, compare the property against a property in our superclass. + bool FoundInSuper = false; + ObjCInterfaceDecl *CurrentInterfaceDecl = IFace; + while (ObjCInterfaceDecl *Super = CurrentInterfaceDecl->getSuperClass()) { + ObjCPropertyDecl *SuperProp = nullptr; + for (auto *LookupResult : Super->lookup(Res->getDeclName())) { + if (auto *Prop = dyn_cast<ObjCPropertyDecl>(LookupResult)) { + if (Prop->isInstanceProperty()) { + SuperProp = Prop; + break; + } + } + } + if (SuperProp) { + DiagnosePropertyMismatch(Res, SuperProp, Super->getIdentifier(), + false); + FoundInSuper = true; + break; + } + CurrentInterfaceDecl = Super; } - CurrentInterfaceDecl = Super; - } - if (FoundInSuper) { - // Also compare the property against a property in our protocols. - for (auto *P : CurrentInterfaceDecl->protocols()) { - CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); + if (FoundInSuper) { + // Also compare the property against a property in our protocols. + for (auto *P : CurrentInterfaceDecl->protocols()) { + CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); + } + } else { + // Slower path: look in all protocols we referenced. + for (auto *P : IFace->all_referenced_protocols()) { + CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); + } } + } else if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(ClassDecl)) { + // We don't check if class extension. Because properties in class + // extension are meant to override some of the attributes and checking has + // already done when property in class extension is constructed. + if (!Cat->IsClassExtension()) + for (auto *P : Cat->protocols()) + CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); } else { - // Slower path: look in all protocols we referenced. - for (auto *P : IFace->all_referenced_protocols()) { + ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(ClassDecl); + for (auto *P : Proto->protocols()) CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); - } } - } else if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(ClassDecl)) { - // We don't check if class extension. Because properties in class extension - // are meant to override some of the attributes and checking has already done - // when property in class extension is constructed. - if (!Cat->IsClassExtension()) - for (auto *P : Cat->protocols()) - CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); - } else { - ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(ClassDecl); - for (auto *P : Proto->protocols()) - CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); } ActOnDocumentableDecl(Res);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits