diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index c29492298bfcfa5a468e187734a4ac20469c4db4..86cf7fc5aff95152e92a842384e0b5647c32febb 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -1136,7 +1136,8 @@ public: // Lookup a method. First, we search locally. If a method isn't // found, we search referenced protocols and class categories. ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance, - bool shallowCategoryLookup= false) const; + bool shallowCategoryLookup= false, + bool CategoryLookup= true) const; ObjCMethodDecl *lookupInstanceMethod(Selector Sel, bool shallowCategoryLookup = false) const { return lookupMethod(Sel, true/*isInstance*/, shallowCategoryLookup); @@ -1155,6 +1156,15 @@ public: return lookupPrivateMethod(Sel, false); } + /// \brief Lookup a setter or getter in the class hierarchy. + /// In this lookup, only class hierarchy and not its categories + /// are looked up + ObjCMethodDecl *lookupPropertyAccessor(const Selector Sel) const { + return lookupMethod(Sel, true/*isInstance*/, + false /*shallowCategoryLookup*/, + false /*CategoryLookup*/); + } + SourceLocation getEndOfDefinitionLoc() const { if (!hasDefinition()) return getLocation(); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 352784e7ef10b2757982ea9fb3121e5d42a4c717..629d775a1bdccda281d9cad8620c7f36792b59e2 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2475,8 +2475,7 @@ public: /// DiagnoseUnimplementedProperties - This routine warns on those properties /// which must be implemented by this implementation. void DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, - ObjCContainerDecl *CDecl, - const SelectorSet &InsMap); + ObjCContainerDecl *CDecl); /// DefaultSynthesizeProperties - This routine default synthesizes all /// properties which must be synthesized in the class's \@implementation. diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 08c59b567d1982cddab3d7ce601be8551a919ce4..43a128137be04fab0d2d258a6308774f6eb4dafa 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -445,7 +445,8 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::lookupInheritedClass( /// the class, its categories, and its super classes (using a linear search). ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, bool isInstance, - bool shallowCategoryLookup) const { + bool shallowCategoryLookup, + bool CategoryLookup) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; @@ -468,23 +469,24 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, return MethodDecl; // Didn't find one yet - now look through categories. - for (ObjCInterfaceDecl::visible_categories_iterator + if (CategoryLookup) + for (ObjCInterfaceDecl::visible_categories_iterator Cat = ClassDecl->visible_categories_begin(), CatEnd = ClassDecl->visible_categories_end(); - Cat != CatEnd; ++Cat) { - if ((MethodDecl = Cat->getMethod(Sel, isInstance))) - return MethodDecl; - - if (!shallowCategoryLookup) { - // Didn't find one yet - look through protocols. - const ObjCList<ObjCProtocolDecl> &Protocols = - Cat->getReferencedProtocols(); - for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), - E = Protocols.end(); I != E; ++I) - if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) - return MethodDecl; + Cat != CatEnd; ++Cat) { + if ((MethodDecl = Cat->getMethod(Sel, isInstance))) + return MethodDecl; + + if (!shallowCategoryLookup) { + // Didn't find one yet - look through protocols. + const ObjCList<ObjCProtocolDecl> &Protocols = + Cat->getReferencedProtocols(); + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), + E = Protocols.end(); I != E; ++I) + if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) + return MethodDecl; + } } - } ClassDecl = ClassDecl->getSuperClass(); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 0e0672c2c30a3511f8eb9ae63f397243683bb208..271c7fcf36e08abf9132b5841c4aa5e7be386e44 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1836,7 +1836,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, if (!(LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile()) || IDecl->isObjCRequiresPropertyDefs()) - DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap); + DiagnoseUnimplementedProperties(S, IMPDecl, CDecl); SelectorSet ClsMap; for (ObjCImplementationDecl::classmeth_iterator @@ -1883,17 +1883,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, E = C->protocol_end(); PI != E; ++PI) CheckProtocolMethodDefs(IMPDecl->getLocation(), *PI, IncompleteImpl, InsMap, ClsMap, CDecl); - // Report unimplemented properties in the category as well. - // When reporting on missing setter/getters, do not report when - // setter/getter is implemented in category's primary class - // implementation. - if (ObjCInterfaceDecl *ID = C->getClassInterface()) - if (ObjCImplDecl *IMP = ID->getImplementation()) { - for (ObjCImplementationDecl::instmeth_iterator - I = IMP->instmeth_begin(), E = IMP->instmeth_end(); I!=E; ++I) - InsMap.insert((*I)->getSelector()); - } - DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap); + DiagnoseUnimplementedProperties(S, IMPDecl, CDecl); } } else llvm_unreachable("invalid ObjCContainerDecl type."); diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index c348a9cb76874d9aa70f6c95ca23172a9de15bc6..2efd43ee3104144dbeedd16212303a8699b06e82 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1660,8 +1660,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { } void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, - ObjCContainerDecl *CDecl, - const SelectorSet &InsMap) { + ObjCContainerDecl *CDecl) { ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; ObjCInterfaceDecl *IDecl; // Gather properties which need not be implemented in this class @@ -1690,6 +1689,26 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, EI = IMPDecl->propimpl_end(); I != EI; ++I) PropImplMap.insert(I->getPropertyDecl()); + SelectorSet InsMap; + // Collect property accessors implemented in current implementation. + for (ObjCImplementationDecl::instmeth_iterator + I = IMPDecl->instmeth_begin(), E = IMPDecl->instmeth_end(); I!=E; ++I) + InsMap.insert((*I)->getSelector()); + + ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl); + ObjCInterfaceDecl *PrimaryClass = 0; + if (C && !C->IsClassExtension()) + if ((PrimaryClass = C->getClassInterface())) + // Report unimplemented properties in the category as well. + if (ObjCImplDecl *IMP = PrimaryClass->getImplementation()) { + // When reporting on missing setter/getters, do not report when + // setter/getter is implemented in category's primary class + // implementation. + for (ObjCImplementationDecl::instmeth_iterator + I = IMP->instmeth_begin(), E = IMP->instmeth_end(); I!=E; ++I) + InsMap.insert((*I)->getSelector()); + } + for (ObjCContainerDecl::PropertyMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; @@ -1699,7 +1718,13 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, PropImplMap.count(Prop) || Prop->getAvailability() == AR_Unavailable) continue; - if (!InsMap.count(Prop->getGetterName())) { + // When reporting on missing property getter implementation in + // categories, do not report when they are declared in primary class, + // class's protocol, or one of it super classes. This is because, + // the class is going to implement them. + if (!InsMap.count(Prop->getGetterName()) && + (PrimaryClass == 0 || + !PrimaryClass->lookupPropertyAccessor(Prop->getGetterName()))) { Diag(IMPDecl->getLocation(), isa<ObjCCategoryDecl>(CDecl) ? diag::warn_setter_getter_impl_required_in_category : @@ -1713,8 +1738,13 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, Diag(RID->getLocation(), diag::note_suppressed_class_declare); } - - if (!Prop->isReadOnly() && !InsMap.count(Prop->getSetterName())) { + // When reporting on missing property setter implementation in + // categories, do not report when they are declared in primary class, + // class's protocol, or one of it super classes. This is because, + // the class is going to implement them. + if (!Prop->isReadOnly() && !InsMap.count(Prop->getSetterName()) && + (PrimaryClass == 0 || + !PrimaryClass->lookupPropertyAccessor(Prop->getSetterName()))) { Diag(IMPDecl->getLocation(), isa<ObjCCategoryDecl>(CDecl) ? diag::warn_setter_getter_impl_required_in_category : diff --git a/test/SemaObjC/property-category-4.m b/test/SemaObjC/property-category-4.m index e7939b32c114126e7c803617b8daf0e7bd0c1aef..f99bd52998c2015fa874aca453b1a78904ea323a 100644 --- a/test/SemaObjC/property-category-4.m +++ b/test/SemaObjC/property-category-4.m @@ -16,3 +16,72 @@ @dynamic d_selectedObjects; // expected-error {{property declared in category 'CAT' cannot be implemented in class implementation}} @end + +// rdar://13713098 +// Test1 +@interface NSArray +- (int)count; +@end + +@protocol MyCountable +@property (readonly) int count; +@end + + +@interface NSArray(Additions) <MyCountable> +@end + +@implementation NSArray(Additions) +@end + +// Test2 +@protocol NSProtocol +- (int)count; +@end + +@interface NSArray1 <NSProtocol> +@end + +@interface NSArray1(Additions) <MyCountable> +@end + +@implementation NSArray1(Additions) +@end + +// Test3 +@interface Super <NSProtocol> +@end + +@interface NSArray2 : Super @end + +@interface NSArray2(Additions) <MyCountable> +@end + +@implementation NSArray2(Additions) +@end + +// Test3 +@interface Super1 <NSProtocol> +@property (readonly) int count; +@end + +@protocol MyCountable1 +@end + +@interface NSArray3 : Super1 <MyCountable1> +@end + +@implementation NSArray3 +@end + +// Test4 +@interface I +@property int d1; +@end + +@interface I(CAT) +@property int d1; +@end + +@implementation I(CAT) +@end diff --git a/test/SemaObjC/property.m b/test/SemaObjC/property.m index 76fdf5b242a225d543b3cf23a093ad9d7a2043f5..748544717386b0c2cd4b7991b2188f9a494d1b17 100644 --- a/test/SemaObjC/property.m +++ b/test/SemaObjC/property.m @@ -11,7 +11,7 @@ @end @interface I(CAT) -@property int d1; // expected-note 2 {{property declared here}} +@property int d1; @end @implementation I @@ -22,8 +22,7 @@ @synthesize name; // OK! property with same name as an accessible ivar of same name @end -@implementation I(CAT) // expected-warning {{property 'd1' requires method 'd1' to be defined }} \ - // expected-warning {{property 'd1' requires method 'setD1:' to be defined }} +@implementation I(CAT) @synthesize d1; // expected-error {{@synthesize not allowed in a category's implementation}} @dynamic bad; // expected-error {{property implementation must have its declaration in the category 'CAT'}} @end