From 8dbda516d343706bae904f800c6d64e145d58a8c Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian <fjahanian@apple.com> Date: Mon, 20 May 2013 21:20:24 +0000 Subject: [PATCH] Objective-C [qoi]: When an class conforms to multiple protocols that declare the same property of incompatible types, issue a warning when class implementation synthesizes the property. // rdar://13075400 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@182316 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 6 +++ include/clang/Basic/DiagnosticSemaKinds.td | 5 ++ lib/AST/DeclObjC.cpp | 24 +++++++++ lib/Sema/SemaObjCProperty.cpp | 54 ++++++++++++++++++++ test/SemaObjC/property-ambiguous-synthesis.m | 48 +++++++++++++++++ 5 files changed, 137 insertions(+) create mode 100644 test/SemaObjC/property-ambiguous-synthesis.m diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 0028240349a..e4ca61a5c07 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -553,6 +553,9 @@ public: typedef llvm::DenseMap<IdentifierInfo*, ObjCPropertyDecl*> PropertyMap; + typedef llvm::DenseMap<const ObjCProtocolDecl *, ObjCPropertyDecl*> + ProtocolPropertyMap; + typedef llvm::SmallVector<ObjCPropertyDecl*, 8> PropertyDeclOrder; /// This routine collects list of properties to be implemented in the class. @@ -1513,6 +1516,9 @@ public: virtual void collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const; + +void collectInheritedProtocolProperties(const ObjCPropertyDecl *Property, + ProtocolPropertyMap &PM) const; static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCProtocol; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 55fae9ffedf..f1a179c259c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -488,6 +488,9 @@ def warn_property_attribute : Warning< "'%1' attribute on property %0 does not match the property inherited from %2">; def warn_property_types_are_incompatible : Warning< "property type %0 is incompatible with type %1 inherited from %2">; +def warn_protocol_property_mismatch : Warning< + "property of type %0 was selected for synthesis">, + InGroup<DiagGroup<"protocol-property-synthesis-ambiguity">>; def err_undef_interface : Error<"cannot find interface declaration for %0">; def err_category_forward_interface : Error< "cannot define %select{category|class extension}0 for undefined class %1">; @@ -729,6 +732,8 @@ def error_category_property : Error< "class implementation">; def note_property_declare : Note< "property declared here">; +def note_protocol_property_declare : Note< + "it could also be property of type %0 declared here">; def note_property_synthesize : Note< "property synthesized here">; def error_synthesize_category_decl : Error< diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 4ddbb22199b..0291803fd46 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -1493,6 +1493,30 @@ void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM, } } + +void ObjCProtocolDecl::collectInheritedProtocolProperties( + const ObjCPropertyDecl *Property, + ProtocolPropertyMap &PM) const { + if (const ObjCProtocolDecl *PDecl = getDefinition()) { + bool MatchFound = false; + for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), + E = PDecl->prop_end(); P != E; ++P) { + ObjCPropertyDecl *Prop = *P; + if (Prop == Property) + continue; + if (Prop->getIdentifier() == Property->getIdentifier()) { + PM[PDecl] = Prop; + MatchFound = true; + break; + } + } + // Scan through protocol's protocols which did not have a matching property. + if (!MatchFound) + for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), + E = PDecl->protocol_end(); PI != E; ++PI) + (*PI)->collectInheritedProtocolProperties(Property, PM); + } +} //===----------------------------------------------------------------------===// // ObjCCategoryDecl diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 269c65e6082..e49edc908aa 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -714,6 +714,58 @@ static void setImpliedPropertyAttributeForReadOnlyProperty( return; } +/// DiagnosePropertyMismatchDeclInProtocols - diagnose properties declared +/// in inherited protocols with mismatched types. Since any of them can +/// be candidate for synthesis. +void DiagnosePropertyMismatchDeclInProtocols(Sema &S, SourceLocation AtLoc, + ObjCInterfaceDecl *ClassDecl, + ObjCPropertyDecl *Property) { + ObjCInterfaceDecl::ProtocolPropertyMap PropMap; + for (ObjCInterfaceDecl::all_protocol_iterator + PI = ClassDecl->all_referenced_protocol_begin(), + E = ClassDecl->all_referenced_protocol_end(); PI != E; ++PI) { + if (const ObjCProtocolDecl *PDecl = (*PI)->getDefinition()) + PDecl->collectInheritedProtocolProperties(Property, PropMap); + } + if (ObjCInterfaceDecl *SDecl = ClassDecl->getSuperClass()) + while (SDecl) { + for (ObjCInterfaceDecl::all_protocol_iterator + PI = SDecl->all_referenced_protocol_begin(), + E = SDecl->all_referenced_protocol_end(); PI != E; ++PI) { + if (const ObjCProtocolDecl *PDecl = (*PI)->getDefinition()) + PDecl->collectInheritedProtocolProperties(Property, PropMap); + } + SDecl = SDecl->getSuperClass(); + } + + if (PropMap.empty()) + return; + + QualType RHSType = S.Context.getCanonicalType(Property->getType()); + bool FirsTime = true; + for (ObjCInterfaceDecl::ProtocolPropertyMap::iterator + I = PropMap.begin(), E = PropMap.end(); I != E; I++) { + ObjCPropertyDecl *Prop = I->second; + QualType LHSType = S.Context.getCanonicalType(Prop->getType()); + if (!S.Context.propertyTypesAreCompatible(LHSType, RHSType)) { + bool IncompatibleObjC = false; + QualType ConvertedType; + if (!S.isObjCPointerConversion(RHSType, LHSType, ConvertedType, IncompatibleObjC) + || IncompatibleObjC) { + if (FirsTime) { + S.Diag(Property->getLocation(), diag::warn_protocol_property_mismatch) + << Property->getType(); + FirsTime = false; + } + S.Diag(Prop->getLocation(), diag::note_protocol_property_declare) + << Prop->getType(); + } + } + } + if (!FirsTime && AtLoc.isValid()) + S.Diag(AtLoc, diag::note_property_synthesize); +} + /// DiagnoseClassAndClassExtPropertyMismatch - diagnose inconsistant property /// attribute declared in primary class and attributes overridden in any of its /// class extensions. @@ -879,6 +931,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } } } + if (Synthesize && isa<ObjCProtocolDecl>(property->getDeclContext())) + DiagnosePropertyMismatchDeclInProtocols(*this, AtLoc, IDecl, property); DiagnoseClassAndClassExtPropertyMismatch(*this, IDecl, property); diff --git a/test/SemaObjC/property-ambiguous-synthesis.m b/test/SemaObjC/property-ambiguous-synthesis.m new file mode 100644 index 00000000000..98f59450744 --- /dev/null +++ b/test/SemaObjC/property-ambiguous-synthesis.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// rdar://13075400 + +@protocol FooAsID +@property (copy) id foo; // expected-note 2 {{it could also be property of type 'id' declared here}} \\ + // expected-warning {{property of type 'id' was selected for synthesis}} +@end + +@protocol FooAsDouble +@property double foo; // expected-warning 2 {{property of type 'double' was selected for synthesis}} \ + // expected-note {{it could also be property of type 'double' declared here}} +@end + +@protocol FooAsShort +@property short foo; // expected-note {{it could also be property of type 'short' declared here}} +@end + +@interface NSObject @end + +@interface AnObject : NSObject<FooAsDouble,FooAsID> +@end + +@interface Sub : AnObject +@end + +@implementation Sub +@synthesize foo=_MyFooIvar; // expected-note {{property synthesized here}} +@end + + +@interface AnotherObject : NSObject<FooAsDouble, FooAsID,FooAsDouble, FooAsID, FooAsDouble,FooAsID> +@end + +@implementation AnotherObject +@synthesize foo=_MyFooIvar; // expected-note {{property synthesized here}} +@end + + +@interface YetAnotherObject : NSObject<FooAsID,FooAsShort, FooAsDouble,FooAsID, FooAsShort> +@end + +@implementation YetAnotherObject +@synthesize foo=_MyFooIvar; // expected-note {{property synthesized here}} +@end + +double func(YetAnotherObject *object) { + return [object foo]; // expected-error {{returning 'id' from a function with incompatible result type 'double'}} +} -- GitLab