objc-arc: enforce performSelector rules in rejecting retaining selectors

passed to it, and unknown selectors causing potential leak.
// rdar://9659270

llvm-svn: 134449
This commit is contained in:
Fariborz Jahanian 2011-07-05 22:38:59 +00:00
parent 44f19ac3fb
commit b7a773626f
7 changed files with 132 additions and 1 deletions

View File

@ -484,6 +484,10 @@ def error_missing_property_ivar_decl : Error<
" ivar or must explicitly name an ivar">;
def error_synthesize_weak_non_arc_or_gc : Error<
"@synthesize of 'weak' property is only allowed in ARC or GC mode">;
def err_arc_perform_selector_retains : Error<
"performSelector names a selector which retains the object">;
def warn_arc_perform_selector_leaks : Warning<
"performSelector may cause a leak because its selector is unknown">;
def error_synthesized_ivar_yet_not_supported : Error<
"instance variable synthesis not yet supported"

View File

@ -488,7 +488,10 @@ enum ObjCMethodFamily {
OMF_release,
OMF_retain,
OMF_retainCount,
OMF_self
OMF_self,
// performSelector families
OMF_performSelector
};
/// Enough bits to store any enumerator in ObjCMethodFamily or

View File

@ -452,6 +452,34 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
if (!isInstanceMethod())
family = OMF_None;
break;
case OMF_performSelector:
if (!isInstanceMethod() ||
!getResultType()->isObjCIdType())
family = OMF_None;
else {
unsigned noParams = param_size();
if (noParams < 1 || noParams > 3)
family = OMF_None;
else {
ObjCMethodDecl::arg_type_iterator it = arg_type_begin();
QualType ArgT = (*it);
if (!ArgT->isObjCSelType()) {
family = OMF_None;
break;
}
while (--noParams) {
it++;
ArgT = (*it);
if (!ArgT->isObjCIdType()) {
family = OMF_None;
break;
}
}
}
}
break;
}
// Cache the result.

View File

@ -397,6 +397,8 @@ ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) {
if (name == "retainCount") return OMF_retainCount;
if (name == "self") return OMF_self;
}
if (name == "performSelector") return OMF_performSelector;
// The other method families may begin with a prefix of underscores.
while (!name.empty() && name.front() == '_')

View File

@ -286,6 +286,11 @@ static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) {
method->hasAttr<NSReturnsAutoreleasedAttr>())
return false;
break;
case OMF_performSelector:
// we don't annotate performSelector's
return true;
}
method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(),
@ -366,6 +371,7 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
case OMF_copy:
case OMF_new:
case OMF_self:
case OMF_performSelector:
break;
}
}
@ -1298,6 +1304,7 @@ static bool checkMethodFamilyMismatch(Sema &S, ObjCMethodDecl *impl,
case OMF_dealloc:
case OMF_retainCount:
case OMF_self:
case OMF_performSelector:
// Mismatches for these methods don't change ownership
// conventions, so we don't care.
return false;
@ -2467,6 +2474,7 @@ Decl *Sema::ActOnMethodDeclaration(
case OMF_mutableCopy:
case OMF_release:
case OMF_retainCount:
case OMF_performSelector:
break;
case OMF_alloc:

View File

@ -204,6 +204,7 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel,
case OMF_mutableCopy:
case OMF_new:
case OMF_self:
case OMF_performSelector:
break;
}
}
@ -1417,6 +1418,53 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
Diag(Loc, diag::err_arc_illegal_explicit_message)
<< Sel << SelectorLoc;
break;
case OMF_performSelector:
if (Method && NumArgs >= 1) {
if (ObjCSelectorExpr *SelExp = dyn_cast<ObjCSelectorExpr>(Args[0])) {
Selector ArgSel = SelExp->getSelector();
ObjCMethodDecl *SelMethod =
LookupInstanceMethodInGlobalPool(ArgSel,
SelExp->getSourceRange());
if (!SelMethod)
SelMethod =
LookupFactoryMethodInGlobalPool(ArgSel,
SelExp->getSourceRange());
if (SelMethod) {
ObjCMethodFamily SelFamily = SelMethod->getMethodFamily();
switch (SelFamily) {
case OMF_alloc:
case OMF_copy:
case OMF_mutableCopy:
case OMF_new:
case OMF_self:
case OMF_init:
// Issue error, unless ns_returns_not_retained.
if (!SelMethod->hasAttr<NSReturnsNotRetainedAttr>()) {
// selector names a +1 method
Diag(SelectorLoc,
diag::err_arc_perform_selector_retains);
Diag(SelMethod->getLocation(), diag::note_method_declared_at);
}
break;
default:
// +0 call. OK. unless ns_returns_retained.
if (SelMethod->hasAttr<NSReturnsRetainedAttr>()) {
// selector names a +1 method
Diag(SelectorLoc,
diag::err_arc_perform_selector_retains);
Diag(SelMethod->getLocation(), diag::note_method_declared_at);
}
break;
}
}
} else {
// error (may leak).
Diag(SelectorLoc, diag::warn_arc_perform_selector_leaks);
Diag(Args[0]->getExprLoc(), diag::note_used_here);
}
}
break;
}
}

View File

@ -0,0 +1,38 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s
// rdar://9659270
@interface NSObject
- (id)copy; // expected-note {{method declared here}}
- (id) test __attribute__((ns_returns_retained)); // expected-note {{method declared here}}
+ (id) new ; // expected-note {{method declared here}}
- (id) init __attribute__((ns_returns_not_retained));
- (id)PlusZero;
- (id)PlusOne __attribute__((ns_returns_retained)); // expected-note {{method declared here}}
@end
@interface I : NSObject
{
SEL sel1;
}
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
@end
@implementation I
- (id) Meth {
return [self performSelector : @selector(copy)]; // expected-error {{performSelector names a selector which retains the object}}
return [self performSelector : @selector(test)]; // expected-error {{performSelector names a selector which retains the object}}
return [self performSelector : @selector(new)]; // expected-error {{performSelector names a selector which retains the object}}
return [self performSelector : @selector(init)];
return [self performSelector : sel1]; // expected-warning {{performSelector may cause a leak because its selector is unknown}} \
// expected-note {{used here}}
return [self performSelector : @selector(PlusZero)];
return [self performSelector : @selector(PlusOne)]; // expected-error {{performSelector names a selector which retains the object}}
}
- (id)performSelector:(SEL)aSelector { return 0; }
- (id)performSelector:(SEL)aSelector withObject:(id)object { return 0; }
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 { return 0; }
@end