[objc] Emit a warning when the implementation of a designated initializer does not chain to

an init method that is a designated initializer for the superclass.

llvm-svn: 196316
This commit is contained in:
Argyrios Kyrtzidis 2013-12-03 21:11:36 +00:00
parent 9ed9e5f31c
commit 22bfa2c28b
10 changed files with 207 additions and 2 deletions

View File

@ -451,6 +451,14 @@ public:
return ImplementationControl(DeclImplementation); 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. /// \brief Determine whether this method has a body.
virtual bool hasBody() const { return Body.isValid(); } virtual bool hasBody() const { return Body.isValid(); }
@ -884,6 +892,18 @@ public:
void getDesignatedInitializers( void getDesignatedInitializers(
llvm::SmallVectorImpl<const ObjCMethodDecl *> &Methods) const; llvm::SmallVectorImpl<const ObjCMethodDecl *> &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 /// \brief Determine whether this particular declaration of this class is
/// actually also a definition. /// actually also a definition.
bool isThisDeclarationADefinition() const { bool isThisDeclarationADefinition() const {

View File

@ -232,6 +232,7 @@ def BadFunctionCast : DiagGroup<"bad-function-cast">;
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">; def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">; def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">; def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">;
def ObjCDesignatedInit : DiagGroup<"objc-designated-initializers">;
def ObjCRetainBlockProperty : DiagGroup<"objc-noncopy-retain-block-property">; def ObjCRetainBlockProperty : DiagGroup<"objc-noncopy-retain-block-property">;
def ObjCReadonlyPropertyHasSetter : DiagGroup<"objc-readonly-with-setter-property">; def ObjCReadonlyPropertyHasSetter : DiagGroup<"objc-readonly-with-setter-property">;
def ObjCInvalidIBOutletProperty : DiagGroup<"invalid-iboutlet">; def ObjCInvalidIBOutletProperty : DiagGroup<"invalid-iboutlet">;
@ -502,6 +503,7 @@ def Most : DiagGroup<"most", [
Unused, Unused,
VolatileRegisterVar, VolatileRegisterVar,
ObjCMissingSuperCalls, ObjCMissingSuperCalls,
ObjCDesignatedInit,
OverloadedVirtual, OverloadedVirtual,
PrivateExtern, PrivateExtern,
SelTypeCast, SelTypeCast,

View File

@ -2432,6 +2432,11 @@ def err_attr_objc_designated_not_init_family : Error<
"'objc_designated_initializer' only applies to methods of the init family">; "'objc_designated_initializer' only applies to methods of the init family">;
def err_attr_objc_designated_not_interface : Error< def err_attr_objc_designated_not_interface : Error<
"'objc_designated_initializer' only applies to methods of interface declarations">; "'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<ObjCDesignatedInit>;
def note_objc_designated_init_marked_here : Note<
"method marked as designated initializer of the class here">;
def err_ns_bridged_not_interface : Error< def err_ns_bridged_not_interface : Error<
"parameter of 'ns_bridged' attribute does not name an Objective-C class">; "parameter of 'ns_bridged' attribute does not name an Objective-C class">;

View File

@ -110,6 +110,13 @@ public:
/// with \c __attribute__((objc_requires_super)). /// with \c __attribute__((objc_requires_super)).
bool ObjCShouldCallSuper; 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. /// \brief Used to determine if errors occurred in this function or block.
DiagnosticErrorTrap ErrorTrap; DiagnosticErrorTrap ErrorTrap;
@ -318,6 +325,8 @@ public:
HasIndirectGoto(false), HasIndirectGoto(false),
HasDroppedStmt(false), HasDroppedStmt(false),
ObjCShouldCallSuper(false), ObjCShouldCallSuper(false),
ObjCIsDesignatedInit(false),
ObjCWarnForNoDesignatedInitChain(false),
ErrorTrap(Diag) { } ErrorTrap(Diag) { }
virtual ~FunctionScopeInfo(); virtual ~FunctionScopeInfo();

View File

@ -398,12 +398,39 @@ void ObjCInterfaceDecl::getDesignatedInitializers(
for (instmeth_iterator I = IFace->instmeth_begin(), for (instmeth_iterator I = IFace->instmeth_begin(),
E = IFace->instmeth_end(); I != E; ++I) { E = IFace->instmeth_end(); I != E; ++I) {
const ObjCMethodDecl *MD = *I; const ObjCMethodDecl *MD = *I;
if (MD->getMethodFamily() == OMF_init && if (MD->isThisDeclarationADesignatedInitializer())
MD->hasAttr<ObjCDesignatedInitializerAttr>())
Methods.push_back(MD); 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() { void ObjCInterfaceDecl::allocateDefinitionData() {
assert(!hasDefinition() && "ObjC class already has a definition"); assert(!hasDefinition() && "ObjC class already has a definition");
Data.setPointer(new (getASTContext()) DefinitionData()); Data.setPointer(new (getASTContext()) DefinitionData());
@ -623,6 +650,20 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
Selector(), QualType(), 0, 0); Selector(), QualType(), 0, 0);
} }
bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
return getMethodFamily() == OMF_init &&
hasAttr<ObjCDesignatedInitializerAttr>();
}
bool ObjCMethodDecl::isDesignatedInitializerForTheInterface() const {
const DeclContext *DC = getDeclContext();
if (isa<ObjCProtocolDecl>(DC))
return false;
if (const ObjCInterfaceDecl *ID = getClassInterface())
return ID->isDesignatedInitializer(getSelector());
return false;
}
Stmt *ObjCMethodDecl::getBody() const { Stmt *ObjCMethodDecl::getBody() const {
return Body.get(getASTContext().getExternalSource()); return Body.get(getASTContext().getExternalSource());
} }

View File

@ -26,6 +26,10 @@ void FunctionScopeInfo::Clear() {
HasBranchProtectedScope = false; HasBranchProtectedScope = false;
HasBranchIntoScope = false; HasBranchIntoScope = false;
HasIndirectGoto = false; HasIndirectGoto = false;
HasDroppedStmt = false;
ObjCShouldCallSuper = false;
ObjCIsDesignatedInit = false;
ObjCWarnForNoDesignatedInitChain = false;
SwitchStack.clear(); SwitchStack.clear();
Returns.clear(); Returns.clear();

View File

@ -9814,6 +9814,18 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
<< MD->getSelector().getAsString(); << MD->getSelector().getAsString();
getCurFunction()->ObjCShouldCallSuper = false; 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 { } else {
return 0; return 0;
} }

View File

@ -391,6 +391,9 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
MDecl->getLocation(), 0); MDecl->getLocation(), 0);
} }
if (MDecl->isDesignatedInitializerForTheInterface())
getCurFunction()->ObjCIsDesignatedInit = true;
// If this is "dealloc" or "finalize", set some bit here. // If this is "dealloc" or "finalize", set some bit here.
// Then in ActOnSuperMessage() (SemaExprObjC), set it back to false. // Then in ActOnSuperMessage() (SemaExprObjC), set it back to false.
// Finally, in ActOnFinishFunctionBody() (SemaDecl), warn if flag is set. // Finally, in ActOnFinishFunctionBody() (SemaDecl), warn if flag is set.
@ -413,6 +416,9 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
getCurFunction()->ObjCShouldCallSuper = getCurFunction()->ObjCShouldCallSuper =
(SuperMethod && SuperMethod->hasAttr<ObjCRequiresSuperAttr>()); (SuperMethod && SuperMethod->hasAttr<ObjCRequiresSuperAttr>());
} }
if (getCurFunction()->ObjCIsDesignatedInit)
getCurFunction()->ObjCWarnForNoDesignatedInitChain = true;
} }
} }
} }

View File

@ -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. // Check the message arguments.
unsigned NumArgs = ArgsIn.size(); unsigned NumArgs = ArgsIn.size();
Expr **Args = ArgsIn.data(); Expr **Args = ArgsIn.data();

View File

@ -30,3 +30,99 @@ __attribute__((objc_root_class))
-(id)init3 { return 0; } -(id)init3 { return 0; }
-(id)init4 NS_DESIGNATED_INITIALIZER { return 0; } // expected-error {{only applies to methods of interface declarations}} -(id)init4 NS_DESIGNATED_INITIALIZER { return 0; } // expected-error {{only applies to methods of interface declarations}}
@end @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