Objective-C: Tighten the rules when warning
is issused for on overriding 'readwrite'
property which is not auto-synthesized.
Buttom line is that if hueristics determine
that there will be a user implemented setter,
no warning will be issued. // rdar://13388503
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177662 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h
index 43f255f..b6ece31 100644
--- a/include/clang/AST/DeclObjC.h
+++ b/include/clang/AST/DeclObjC.h
@@ -544,6 +544,7 @@
ObjCMethodDecl *getClassMethod(Selector Sel) const {
return getMethod(Sel, false/*isInstance*/);
}
+ bool HasUserDeclaredSetterMethod(const ObjCPropertyDecl *P) const;
ObjCIvarDecl *getIvarDecl(IdentifierInfo *Id) const;
ObjCPropertyDecl *FindPropertyDeclaration(IdentifierInfo *PropertyId) const;
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 97d0870..50bf121 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -92,6 +92,72 @@
return 0;
}
+/// HasUserDeclaredSetterMethod - This routine returns 'true' if a user declared setter
+/// method was found in the class, its protocols, its super classes or categories.
+/// It also returns 'true' if one of its categories has declared a 'readwrite' property.
+/// This is because, user must provide a setter method for the category's 'readwrite'
+/// property.
+bool
+ObjCContainerDecl::HasUserDeclaredSetterMethod(const ObjCPropertyDecl *Property) const {
+ Selector Sel = Property->getSetterName();
+ lookup_const_result R = lookup(Sel);
+ for (lookup_const_iterator Meth = R.begin(), MethEnd = R.end();
+ Meth != MethEnd; ++Meth) {
+ ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*Meth);
+ if (MD && MD->isInstanceMethod() && !MD->isImplicit())
+ return true;
+ }
+
+ if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(this)) {
+ // Also look into categories, including class extensions, looking
+ // for a user declared instance method.
+ for (ObjCInterfaceDecl::visible_categories_iterator
+ Cat = ID->visible_categories_begin(),
+ CatEnd = ID->visible_categories_end();
+ Cat != CatEnd;
+ ++Cat) {
+ if (ObjCMethodDecl *MD = Cat->getInstanceMethod(Sel))
+ if (!MD->isImplicit())
+ return true;
+ if (Cat->IsClassExtension())
+ continue;
+ // Also search through the categories looking for a 'readwrite' declaration
+ // of this property. If one found, presumably a setter will be provided
+ // (properties declared in categories will not get auto-synthesized).
+ for (ObjCContainerDecl::prop_iterator P = Cat->prop_begin(),
+ E = Cat->prop_end(); P != E; ++P)
+ if (P->getIdentifier() == Property->getIdentifier()) {
+ if (P->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readwrite)
+ return true;
+ break;
+ }
+ }
+
+ // Also look into protocols, for a user declared instance method.
+ for (ObjCInterfaceDecl::all_protocol_iterator P =
+ ID->all_referenced_protocol_begin(),
+ PE = ID->all_referenced_protocol_end(); P != PE; ++P) {
+ ObjCProtocolDecl *Proto = (*P);
+ if (Proto->HasUserDeclaredSetterMethod(Property))
+ return true;
+ }
+ // And in its super class.
+ ObjCInterfaceDecl *OSC = ID->getSuperClass();
+ while (OSC) {
+ if (OSC->HasUserDeclaredSetterMethod(Property))
+ return true;
+ OSC = OSC->getSuperClass();
+ }
+ }
+ if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(this))
+ for (ObjCProtocolDecl::protocol_iterator PI = PD->protocol_begin(),
+ E = PD->protocol_end(); PI != E; ++PI) {
+ if ((*PI)->HasUserDeclaredSetterMethod(Property))
+ return true;
+ }
+ return false;
+}
+
ObjCPropertyDecl *
ObjCPropertyDecl::findPropertyDecl(const DeclContext *DC,
IdentifierInfo *propertyID) {
diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp
index aae8ae6..121ed8c 100644
--- a/lib/Sema/SemaObjCProperty.cpp
+++ b/lib/Sema/SemaObjCProperty.cpp
@@ -1590,7 +1590,8 @@
if ((Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readwrite) &&
(PropInSuperClass->getPropertyAttributes() &
ObjCPropertyDecl::OBJC_PR_readonly) &&
- !IMPDecl->getInstanceMethod(Prop->getSetterName())) {
+ !IMPDecl->getInstanceMethod(Prop->getSetterName()) &&
+ !IDecl->HasUserDeclaredSetterMethod(Prop)) {
Diag(Prop->getLocation(), diag::warn_no_autosynthesis_property)
<< Prop->getIdentifier()->getName();
Diag(PropInSuperClass->getLocation(), diag::note_property_declare);
diff --git a/test/SemaObjC/default-synthesize-3.m b/test/SemaObjC/default-synthesize-3.m
index 09de765..82f968d 100644
--- a/test/SemaObjC/default-synthesize-3.m
+++ b/test/SemaObjC/default-synthesize-3.m
@@ -80,3 +80,34 @@
}
@end
+
+// More test where such warnings should not be issued.
+@protocol MyProtocol
+-(void)setProp1:(id)x;
+@end
+
+@protocol P1 <MyProtocol>
+@end
+
+@interface B
+@property (readonly) id prop;
+@property (readonly) id prop1;
+@property (readonly) id prop2;
+@end
+
+@interface B()
+-(void)setProp:(id)x;
+@end
+
+@interface B(cat)
+@property (readwrite) id prop2;
+@end
+
+@interface S : B<P1>
+@property (assign,readwrite) id prop;
+@property (assign,readwrite) id prop1;
+@property (assign,readwrite) id prop2;
+@end
+
+@implementation S
+@end