[arcmt] Don't error if an autoreleased variable is returned after the -autorelease.

rdar://12952025

llvm-svn: 171482
This commit is contained in:
Argyrios Kyrtzidis 2013-01-04 18:29:59 +00:00
parent dde168b8b5
commit b7b89b5ebc
3 changed files with 79 additions and 18 deletions

View File

@ -162,13 +162,26 @@ public:
private:
/// \brief Checks for idioms where an unused -autorelease is common.
///
/// Currently only returns true for this idiom which is common in property
/// Returns true for this idiom which is common in property
/// setters:
///
/// [backingValue autorelease];
/// backingValue = [newValue retain]; // in general a +1 assign
///
/// For these as well:
///
/// [[var retain] autorelease];
/// return var;
///
bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
if (isPlusOneAssignAfterAutorelease(E))
return true;
if (isReturnedAfterAutorelease(E))
return true;
return false;
}
bool isReturnedAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
@ -177,36 +190,30 @@ private:
if (!RefD)
return false;
Stmt *OuterS = E, *InnerS;
do {
InnerS = OuterS;
OuterS = StmtMap->getParent(InnerS);
}
while (OuterS && (isa<ParenExpr>(OuterS) ||
isa<CastExpr>(OuterS) ||
isa<ExprWithCleanups>(OuterS)));
if (!OuterS)
return false;
// Find next statement after the -autorelease.
Stmt::child_iterator currChildS = OuterS->child_begin();
Stmt::child_iterator childE = OuterS->child_end();
for (; currChildS != childE; ++currChildS) {
if (*currChildS == InnerS)
break;
}
if (currChildS == childE)
return false;
++currChildS;
if (currChildS == childE)
return false;
Stmt *nextStmt = *currChildS;
Stmt *nextStmt = getNextStmt(E);
if (!nextStmt)
return false;
// Check for "return <variable>;".
if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(nextStmt))
return RefD == getReferencedDecl(RetS->getRetValue());
return false;
}
bool isPlusOneAssignAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *nextStmt = getNextStmt(E);
if (!nextStmt)
return false;
nextStmt = nextStmt->IgnoreImplicit();
// Check for "RefD = [+1 retained object];".
@ -219,11 +226,56 @@ private:
return false;
}
Stmt *getNextStmt(Expr *E) {
if (!E)
return 0;
Stmt *OuterS = E, *InnerS;
do {
InnerS = OuterS;
OuterS = StmtMap->getParent(InnerS);
}
while (OuterS && (isa<ParenExpr>(OuterS) ||
isa<CastExpr>(OuterS) ||
isa<ExprWithCleanups>(OuterS)));
if (!OuterS)
return 0;
Stmt::child_iterator currChildS = OuterS->child_begin();
Stmt::child_iterator childE = OuterS->child_end();
for (; currChildS != childE; ++currChildS) {
if (*currChildS == InnerS)
break;
}
if (currChildS == childE)
return 0;
++currChildS;
if (currChildS == childE)
return 0;
Stmt *nextStmt = *currChildS;
if (!nextStmt)
return 0;
return nextStmt->IgnoreImplicit();
}
Decl *getReferencedDecl(Expr *E) {
if (!E)
return 0;
E = E->IgnoreParenCasts();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
switch (ME->getMethodFamily()) {
case OMF_copy:
case OMF_autorelease:
case OMF_release:
case OMF_retain:
return getReferencedDecl(ME->getInstanceReceiver());
default:
return 0;
}
}
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl();
if (MemberExpr *ME = dyn_cast<MemberExpr>(E))

View File

@ -64,3 +64,8 @@ void test(A *prevVal, A *newVal) {
[prevVal autorelease];
prevVal = [newVal retain];
}
id test2(A* val) {
[[val retain] autorelease];
return val;
}

View File

@ -60,3 +60,7 @@ int main (int argc, const char * argv[]) {
void test(A *prevVal, A *newVal) {
prevVal = newVal;
}
id test2(A* val) {
return val;
}