diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 7db912903b4e..774de1a4fedb 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -451,6 +451,14 @@ public: return ImplementationControl(DeclImplementation); } + /// Returns true if this specific method declaration is marked with the + /// designated initializer attribute. + bool isThisDeclarationADesignatedInitializer() const; + + /// Returns true if the method selector resolves to a designated initializer + /// in the class's interface. + bool isDesignatedInitializerForTheInterface() const; + /// \brief Determine whether this method has a body. virtual bool hasBody() const { return Body.isValid(); } @@ -884,6 +892,18 @@ public: void getDesignatedInitializers( llvm::SmallVectorImpl &Methods) const; + /// Returns true if the given selector is a designated initializer for the + /// interface. + /// + /// If this declaration does not have methods marked as designated + /// initializers then the interface inherits the designated initializers of + /// its super class. + /// + /// \param InitMethod if non-null and the function returns true, it receives + /// the method that was marked as a designated initializer. + bool isDesignatedInitializer(Selector Sel, + const ObjCMethodDecl **InitMethod = 0) const; + /// \brief Determine whether this particular declaration of this class is /// actually also a definition. bool isThisDeclarationADefinition() const { diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index ec1eb1a8b59d..7f1c5dc9c16b 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -232,6 +232,7 @@ def BadFunctionCast : DiagGroup<"bad-function-cast">; def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">; def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">; def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">; +def ObjCDesignatedInit : DiagGroup<"objc-designated-initializers">; def ObjCRetainBlockProperty : DiagGroup<"objc-noncopy-retain-block-property">; def ObjCReadonlyPropertyHasSetter : DiagGroup<"objc-readonly-with-setter-property">; def ObjCInvalidIBOutletProperty : DiagGroup<"invalid-iboutlet">; @@ -502,6 +503,7 @@ def Most : DiagGroup<"most", [ Unused, VolatileRegisterVar, ObjCMissingSuperCalls, + ObjCDesignatedInit, OverloadedVirtual, PrivateExtern, SelTypeCast, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8315c78f259e..314d0375e671 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2432,6 +2432,11 @@ def err_attr_objc_designated_not_init_family : Error< "'objc_designated_initializer' only applies to methods of the init family">; def err_attr_objc_designated_not_interface : Error< "'objc_designated_initializer' only applies to methods of interface declarations">; +def warn_objc_designated_init_missing_super_call : Warning< + "designated initializer missing a 'super' call to a designated initializer of the super class">, + InGroup; +def note_objc_designated_init_marked_here : Note< + "method marked as designated initializer of the class here">; def err_ns_bridged_not_interface : Error< "parameter of 'ns_bridged' attribute does not name an Objective-C class">; diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 06afe1a8ae9d..409e6158b502 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -110,6 +110,13 @@ public: /// with \c __attribute__((objc_requires_super)). bool ObjCShouldCallSuper; + /// True when this is a method marked as a designated initializer. + bool ObjCIsDesignatedInit; + /// This starts true for a method marked as designated initializer and will + /// be set to false if there is an invocation to a designated initializer of + /// the super class. + bool ObjCWarnForNoDesignatedInitChain; + /// \brief Used to determine if errors occurred in this function or block. DiagnosticErrorTrap ErrorTrap; @@ -318,6 +325,8 @@ public: HasIndirectGoto(false), HasDroppedStmt(false), ObjCShouldCallSuper(false), + ObjCIsDesignatedInit(false), + ObjCWarnForNoDesignatedInitChain(false), ErrorTrap(Diag) { } virtual ~FunctionScopeInfo(); diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 528b52fe5d5b..cb58ad482e10 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -398,12 +398,39 @@ void ObjCInterfaceDecl::getDesignatedInitializers( for (instmeth_iterator I = IFace->instmeth_begin(), E = IFace->instmeth_end(); I != E; ++I) { const ObjCMethodDecl *MD = *I; - if (MD->getMethodFamily() == OMF_init && - MD->hasAttr()) + if (MD->isThisDeclarationADesignatedInitializer()) Methods.push_back(MD); } } +bool ObjCInterfaceDecl::isDesignatedInitializer(Selector Sel, + const ObjCMethodDecl **InitMethod) const { + assert(hasDefinition()); + if (data().ExternallyCompleted) + LoadExternalDefinition(); + + const ObjCInterfaceDecl *IFace = this; + while (IFace) { + if (IFace->data().HasDesignatedInitializers) + break; + IFace = IFace->getSuperClass(); + } + + if (!IFace) + return false; + + if (const ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/true, + /*shallowCategoryLookup=*/true, + /*followSuper=*/false)) { + if (MD->isThisDeclarationADesignatedInitializer()) { + if (InitMethod) + *InitMethod = MD; + return true; + } + } + return false; +} + void ObjCInterfaceDecl::allocateDefinitionData() { assert(!hasDefinition() && "ObjC class already has a definition"); Data.setPointer(new (getASTContext()) DefinitionData()); @@ -623,6 +650,20 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { Selector(), QualType(), 0, 0); } +bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const { + return getMethodFamily() == OMF_init && + hasAttr(); +} + +bool ObjCMethodDecl::isDesignatedInitializerForTheInterface() const { + const DeclContext *DC = getDeclContext(); + if (isa(DC)) + return false; + if (const ObjCInterfaceDecl *ID = getClassInterface()) + return ID->isDesignatedInitializer(getSelector()); + return false; +} + Stmt *ObjCMethodDecl::getBody() const { return Body.get(getASTContext().getExternalSource()); } diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index 8b3493ebfef8..cf2b5ebe0e5c 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -26,6 +26,10 @@ void FunctionScopeInfo::Clear() { HasBranchProtectedScope = false; HasBranchIntoScope = false; HasIndirectGoto = false; + HasDroppedStmt = false; + ObjCShouldCallSuper = false; + ObjCIsDesignatedInit = false; + ObjCWarnForNoDesignatedInitChain = false; SwitchStack.clear(); Returns.clear(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8229a6224a2c..c14cc4b8619f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9814,6 +9814,18 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, << MD->getSelector().getAsString(); getCurFunction()->ObjCShouldCallSuper = false; } + if (getCurFunction()->ObjCWarnForNoDesignatedInitChain) { + const ObjCMethodDecl *InitMethod = 0; + bool isDesignated = MD->getClassInterface() + ->isDesignatedInitializer(MD->getSelector(), &InitMethod); + assert(isDesignated && InitMethod); + (void)isDesignated; + Diag(MD->getLocation(), + diag::warn_objc_designated_init_missing_super_call); + Diag(InitMethod->getLocation(), + diag::note_objc_designated_init_marked_here); + getCurFunction()->ObjCWarnForNoDesignatedInitChain = false; + } } else { return 0; } diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index d9b6378269ad..53c11d59e666 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -391,6 +391,9 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { MDecl->getLocation(), 0); } + if (MDecl->isDesignatedInitializerForTheInterface()) + getCurFunction()->ObjCIsDesignatedInit = true; + // If this is "dealloc" or "finalize", set some bit here. // Then in ActOnSuperMessage() (SemaExprObjC), set it back to false. // Finally, in ActOnFinishFunctionBody() (SemaDecl), warn if flag is set. @@ -413,6 +416,9 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { getCurFunction()->ObjCShouldCallSuper = (SuperMethod && SuperMethod->hasAttr()); } + + if (getCurFunction()->ObjCIsDesignatedInit) + getCurFunction()->ObjCWarnForNoDesignatedInitChain = true; } } } diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index a60749b0224f..20f4b4f7248a 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -2447,6 +2447,16 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } } + if (SuperLoc.isValid() && getCurFunction()->ObjCIsDesignatedInit) { + if (const ObjCObjectPointerType * + OCIType = ReceiverType->getAsObjCInterfacePointerType()) { + if (const ObjCInterfaceDecl *ID = OCIType->getInterfaceDecl()) { + if (ID->isDesignatedInitializer(Sel)) + getCurFunction()->ObjCWarnForNoDesignatedInitChain = false; + } + } + } + // Check the message arguments. unsigned NumArgs = ArgsIn.size(); Expr **Args = ArgsIn.data(); diff --git a/clang/test/SemaObjC/attr-designated-init.m b/clang/test/SemaObjC/attr-designated-init.m index f607bb694472..3190d8a97fed 100644 --- a/clang/test/SemaObjC/attr-designated-init.m +++ b/clang/test/SemaObjC/attr-designated-init.m @@ -30,3 +30,99 @@ __attribute__((objc_root_class)) -(id)init3 { return 0; } -(id)init4 NS_DESIGNATED_INITIALIZER { return 0; } // expected-error {{only applies to methods of interface declarations}} @end + +__attribute__((objc_root_class)) +@interface B1 +-(id)initB1 NS_DESIGNATED_INITIALIZER; // expected-note 2 {{method marked as designated initializer of the class here}} +-(id)initB2; +-(id)initB3 NS_DESIGNATED_INITIALIZER; +@end + +@implementation B1 +-(id)initB1 { return 0; } +-(id)initB2 { return 0; } +-(id)initB3 { return 0; } +@end + +@interface S1 : B1 +-(id)initS1 NS_DESIGNATED_INITIALIZER; // expected-note {{method marked as designated initializer of the class here}} +-(id)initS2 NS_DESIGNATED_INITIALIZER; +-(id)initS3 NS_DESIGNATED_INITIALIZER; // expected-note {{method marked as designated initializer of the class here}} +-(id)initS4 NS_DESIGNATED_INITIALIZER; // expected-note {{method marked as designated initializer of the class here}} +-(id)initB1; +@end + +@implementation S1 +-(id)initS1 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return 0; +} +-(id)initS2 { + return [super initB1]; +} +-(id)initS3 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return [super initB2]; +} +-(id)initS4 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return [self initB1]; +} +-(id)initB1 { + return [self initS1]; +} +-(id)initB3 { + return [self initS1]; +} +@end + +@interface S2 : B1 +-(id)initB1; +@end + +@interface SS2 : S2 +-(id)initSS1 NS_DESIGNATED_INITIALIZER; +@end + +@implementation SS2 +-(id)initSS1 { + return [super initB1]; +} +@end + +@interface S3 : B1 +-(id)initS1 NS_DESIGNATED_INITIALIZER; +@end + +@interface SS3 : S3 +-(id)initSS1 NS_DESIGNATED_INITIALIZER; // expected-note {{method marked as designated initializer of the class here}} +@end + +@implementation SS3 +-(id)initSS1 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return [super initB1]; +} +@end + +@interface S4 : B1 +-(id)initB1; +-(id)initB3; +@end + +@implementation S4 +-(id)initB1 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return 0; +} +-(id)initB3 { + return [super initB3]; +} +@end + +@interface S5 : B1 +@end + +@implementation S5 +-(id)initB1 { // expected-warning {{designated initializer missing a 'super' call to a designated initializer of the super class}} + return 0; +} +-(id)initB3 { + return [super initB3]; +} +@end