diff options
author | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2012-05-23 21:50:04 +0000 |
---|---|---|
committer | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2012-05-23 21:50:04 +0000 |
commit | 1b8fbd3601e009803565e74d2ec54abecb5cbf73 (patch) | |
tree | e2078743be9ffbdc3ed4eac0e8b607984d4a93d8 /lib/ARCMigrate | |
parent | f0fab767c8c272fc77699e82fd581ddf4d9d71fa (diff) |
[arcmt] Remove an unused -autorelease, without failing with error, for this
idiom that is used commonly in setters:
[backingValue autorelease];
backingValue = [newValue retain]; // in general a +1 assign
rdar://9914061
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157347 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/ARCMigrate')
-rw-r--r-- | lib/ARCMigrate/TransProperties.cpp | 11 | ||||
-rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 92 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.cpp | 42 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.h | 2 |
4 files changed, 129 insertions, 18 deletions
diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index 8f65fc3a8b..5ec918fe93 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -309,17 +309,8 @@ private: if (RE->getDecl() != Ivar) return true; - if (ObjCMessageExpr * - ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) - if (ME->getMethodFamily() == OMF_retain) + if (isPlusOneAssign(E)) return false; - - ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); - while (implCE && implCE->getCastKind() == CK_BitCast) - implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); - - if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) - return false; } return true; diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 11a6553341..df3cd5858e 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -64,14 +64,16 @@ public: return true; case OMF_autorelease: if (isRemovable(E)) { - // An unused autorelease is badness. If we remove it the receiver - // will likely die immediately while previously it was kept alive - // by the autorelease pool. This is bad practice in general, leave it - // and emit an error to force the user to restructure his code. - Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " - "message; its receiver may be destroyed immediately", - E->getLocStart(), E->getSourceRange()); - return true; + if (!isCommonUnusedAutorelease(E)) { + // An unused autorelease is badness. If we remove it the receiver + // will likely die immediately while previously it was kept alive + // by the autorelease pool. This is bad practice in general, leave it + // and emit an error to force the user to restructure his code. + Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " + "message; its receiver may be destroyed immediately", + E->getLocStart(), E->getSourceRange()); + return true; + } } // Pass through. case OMF_retain: @@ -156,6 +158,80 @@ public: } private: + /// \brief Checks for idioms where an unused -autorelease is common. + /// + /// Currently only returns true for this idiom which is common in property + /// setters: + /// + /// [backingValue autorelease]; + /// backingValue = [newValue retain]; // in general a +1 assign + /// + bool isCommonUnusedAutorelease(ObjCMessageExpr *E) { + Expr *Rec = E->getInstanceReceiver(); + if (!Rec) + return false; + + Decl *RefD = getReferencedDecl(Rec); + 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; + if (!nextStmt) + return false; + nextStmt = nextStmt->IgnoreImplicit(); + + // Check for "RefD = [+1 retained object];". + + if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) { + if (RefD != getReferencedDecl(Bop->getLHS())) + return false; + if (isPlusOneAssign(Bop)) + return true; + } + return false; + } + + Decl *getReferencedDecl(Expr *E) { + if (!E) + return 0; + + E = E->IgnoreParenCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getDecl(); + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E)) + return IRE->getDecl(); + + return 0; + } + /// \brief Check if the retain/release is due to a GCD/XPC macro that are /// defined as: /// diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp index 50bea95f12..24b650561c 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -14,6 +14,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" #include "clang/Basic/SourceManager.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/DenseSet.h" #include <map> @@ -56,6 +57,47 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type, return true; } +bool trans::isPlusOneAssign(const BinaryOperator *E) { + if (E->getOpcode() != BO_Assign) + return false; + + if (const ObjCMessageExpr * + ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) + if (ME->getMethodFamily() == OMF_retain) + return true; + + if (const CallExpr * + callE = dyn_cast<CallExpr>(E->getRHS()->IgnoreParenCasts())) { + if (const FunctionDecl *FD = callE->getDirectCallee()) { + if (FD->getAttr<CFReturnsRetainedAttr>()) + return true; + + if (FD->isGlobal() && + FD->getIdentifier() && + FD->getParent()->isTranslationUnit() && + FD->getLinkage() == ExternalLinkage && + ento::cocoa::isRefType(callE->getType(), "CF", + FD->getIdentifier()->getName())) { + StringRef fname = FD->getIdentifier()->getName(); + if (fname.endswith("Retain") || + fname.find("Create") != StringRef::npos || + fname.find("Copy") != StringRef::npos) { + return true; + } + } + } + } + + const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); + while (implCE && implCE->getCastKind() == CK_BitCast) + implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); + + if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) + return true; + + return false; +} + /// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h index 445c3e599d..7abc0304b1 100644 --- a/lib/ARCMigrate/Transforms.h +++ b/lib/ARCMigrate/Transforms.h @@ -154,6 +154,8 @@ public: bool canApplyWeak(ASTContext &Ctx, QualType type, bool AllowOnUnknownClass = false); +bool isPlusOneAssign(const BinaryOperator *E); + /// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned |