diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3fdef9a0f80e..822124f0dd9b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5649,6 +5649,16 @@ def note_parameter_here : Note< def note_method_return_type_change : Note< "compiler has implicitly changed method %0 return type">; +def warn_impl_required_for_class_property : Warning< + "class property %0 requires method %1 to be defined - " + "use @dynamic or provide a method implementation " + "in this class implementation">, + InGroup; +def warn_impl_required_in_category_for_class_property : Warning< + "class property %0 requires method %1 to be defined - " + "use @dynamic or provide a method implementation in this category">, + InGroup; + // C++ casts // These messages adhere to the TryCast pattern: %0 is an int specifying the // cast type, %1 is the source type, %2 is the destination type. diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 322469499f19..1bf88f4e9393 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -1530,37 +1530,50 @@ bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, /// CollectImmediateProperties - This routine collects all properties in /// the class and its conforming protocols; but not those in its super class. -static void CollectImmediateProperties(ObjCContainerDecl *CDecl, - ObjCContainerDecl::PropertyMap &PropMap, - ObjCContainerDecl::PropertyMap &SuperPropMap, - bool IncludeProtocols = true) { +static void +CollectImmediateProperties(ObjCContainerDecl *CDecl, + ObjCContainerDecl::PropertyMap &PropMap, + ObjCContainerDecl::PropertyMap &SuperPropMap, + bool CollectClassPropsOnly = false, + bool IncludeProtocols = true) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { - for (auto *Prop : IDecl->properties()) + for (auto *Prop : IDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; + } // Collect the properties from visible extensions. for (auto *Ext : IDecl->visible_extensions()) - CollectImmediateProperties(Ext, PropMap, SuperPropMap, IncludeProtocols); + CollectImmediateProperties(Ext, PropMap, SuperPropMap, + CollectClassPropsOnly, IncludeProtocols); if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : IDecl->all_referenced_protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } if (ObjCCategoryDecl *CATDecl = dyn_cast(CDecl)) { - for (auto *Prop : CATDecl->properties()) + for (auto *Prop : CATDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; + } if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : CATDecl->protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { for (auto *Prop : PDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())]; @@ -1575,9 +1588,10 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, PropEntry = Prop; } } - // scan through protocol's protocols. + // Scan through protocol's protocols. for (auto *PI : PDecl->protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } @@ -1765,19 +1779,22 @@ static void DiagnoseUnimplementedAccessor(Sema &S, (PrimaryClass == nullptr || !PrimaryClass->lookupPropertyAccessor(Method, C, Prop->isClassProperty()))) { - S.Diag(IMPDecl->getLocation(), - isa(CDecl) ? - diag::warn_setter_getter_impl_required_in_category : - diag::warn_setter_getter_impl_required) - << Prop->getDeclName() << Method; - S.Diag(Prop->getLocation(), - diag::note_property_declare); - if (S.LangOpts.ObjCDefaultSynthProperties && - S.LangOpts.ObjCRuntime.isNonFragile()) - if (ObjCInterfaceDecl *ID = dyn_cast(CDecl)) - if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) - S.Diag(RID->getLocation(), diag::note_suppressed_class_declare); - } + unsigned diag = + isa(CDecl) + ? (Prop->isClassProperty() + ? diag::warn_impl_required_in_category_for_class_property + : diag::warn_setter_getter_impl_required_in_category) + : (Prop->isClassProperty() + ? diag::warn_impl_required_for_class_property + : diag::warn_setter_getter_impl_required); + S.Diag(IMPDecl->getLocation(), diag) << Prop->getDeclName() << Method; + S.Diag(Prop->getLocation(), diag::note_property_declare); + if (S.LangOpts.ObjCDefaultSynthProperties && + S.LangOpts.ObjCRuntime.isNonFragile()) + if (ObjCInterfaceDecl *ID = dyn_cast(CDecl)) + if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) + S.Diag(RID->getLocation(), diag::note_suppressed_class_declare); + } } void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, @@ -1786,25 +1803,27 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl::PropertyMap PropMap; ObjCInterfaceDecl *IDecl = dyn_cast(CDecl); - if (!SynthesizeProperties) { - ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; - // Gather properties which need not be implemented in this class - // or category. - if (!IDecl) - if (ObjCCategoryDecl *C = dyn_cast(CDecl)) { - // For categories, no need to implement properties declared in - // its primary class (and its super classes) if property is - // declared in one of those containers. - if ((IDecl = C->getClassInterface())) { - ObjCInterfaceDecl::PropertyDeclOrder PO; - IDecl->collectPropertiesToImplement(NoNeedToImplPropMap, PO); - } + // Since we don't synthesize class properties, we should emit diagnose even + // if SynthesizeProperties is true. + ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; + // Gather properties which need not be implemented in this class + // or category. + if (!IDecl) + if (ObjCCategoryDecl *C = dyn_cast(CDecl)) { + // For categories, no need to implement properties declared in + // its primary class (and its super classes) if property is + // declared in one of those containers. + if ((IDecl = C->getClassInterface())) { + ObjCInterfaceDecl::PropertyDeclOrder PO; + IDecl->collectPropertiesToImplement(NoNeedToImplPropMap, PO); } - if (IDecl) - CollectSuperClassPropertyImplementations(IDecl, NoNeedToImplPropMap); + } + if (IDecl) + CollectSuperClassPropertyImplementations(IDecl, NoNeedToImplPropMap); - CollectImmediateProperties(CDecl, PropMap, NoNeedToImplPropMap); - } + // When SynthesizeProperties is true, we only check class properties. + CollectImmediateProperties(CDecl, PropMap, NoNeedToImplPropMap, + SynthesizeProperties/*CollectClassPropsOnly*/); // Scan the @interface to see if any of the protocols it adopts // require an explicit implementation, via attribute @@ -1826,6 +1845,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; LazyMap.reset(new ObjCContainerDecl::PropertyMap()); CollectImmediateProperties(CDecl, *LazyMap, NoNeedToImplPropMap, + /* CollectClassPropsOnly */ false, /* IncludeProtocols */ false); } // Add the properties of 'PDecl' to the list of properties that @@ -1868,7 +1888,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, for (ObjCContainerDecl::PropertyMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; - // Is there a matching propery synthesize/dynamic? + // Is there a matching property synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || PropImplMap.count(Prop) || diff --git a/clang/test/Parser/objc-class-property.m b/clang/test/Parser/objc-class-property.m index 202352c33b7f..e4c3b0766b4a 100644 --- a/clang/test/Parser/objc-class-property.m +++ b/clang/test/Parser/objc-class-property.m @@ -15,7 +15,6 @@ @property int z; @property(readonly) int ro, ro2; @property (class) int c; -@property (class) int c2; @end @implementation A diff --git a/clang/test/SemaObjC/objc-class-property.m b/clang/test/SemaObjC/objc-class-property.m index 37a8178ceb6a..5e1b866cbc89 100644 --- a/clang/test/SemaObjC/objc-class-property.m +++ b/clang/test/SemaObjC/objc-class-property.m @@ -18,11 +18,13 @@ @property int z; @property(readonly) int ro, ro2; @property (class) int c; -@property (class) int c2; +@property (class) int c2; // expected-note {{property declared here}} \ + // expected-note {{property declared here}} @property (class) int x; @end -@implementation A +@implementation A // expected-warning {{class property 'c2' requires method 'c2' to be defined}} \ + // expected-warning {{class property 'c2' requires method 'setC2:' to be defined}} @dynamic x; // refers to the instance property @dynamic (class) x; // refers to the class property @synthesize z, c2; // expected-error {{@synthesize not allowed on a class property 'c2'}}