diff options
Diffstat (limited to 'lib/ARCMigrate/TransRetainReleaseDealloc.cpp')
-rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp new file mode 100644 index 0000000..ed6ed0a --- /dev/null +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -0,0 +1,219 @@ +//===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// removeRetainReleaseDealloc: +// +// Removes retain/release/autorelease/dealloc messages. +// +// return [[foo retain] autorelease]; +// ----> +// return foo; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class RetainReleaseDeallocRemover : + public RecursiveASTVisitor<RetainReleaseDeallocRemover> { + Stmt *Body; + MigrationPass &Pass; + + ExprSet Removables; + llvm::OwningPtr<ParentMap> StmtMap; + + Selector DelegateSel; + +public: + RetainReleaseDeallocRemover(MigrationPass &pass) + : Body(0), Pass(pass) { + DelegateSel = + Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); + } + + void transformBody(Stmt *body) { + Body = body; + collectRemovables(body, Removables); + StmtMap.reset(new ParentMap(body)); + TraverseStmt(body); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + switch (E->getMethodFamily()) { + default: + 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; + } + // Pass through. + case OMF_retain: + case OMF_release: + if (E->getReceiverKind() == ObjCMessageExpr::Instance) + if (Expr *rec = E->getInstanceReceiver()) { + rec = rec->IgnoreParenImpCasts(); + if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && + (E->getMethodFamily() != OMF_retain || isRemovable(E))) { + std::string err = "it is not safe to remove '"; + err += E->getSelector().getAsString() + "' message on " + "an __unsafe_unretained type"; + Pass.TA.reportError(err, rec->getLocStart()); + return true; + } + + if (isGlobalVar(rec) && + (E->getMethodFamily() != OMF_retain || isRemovable(E))) { + std::string err = "it is not safe to remove '"; + err += E->getSelector().getAsString() + "' message on " + "a global variable"; + Pass.TA.reportError(err, rec->getLocStart()); + return true; + } + + if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { + Pass.TA.reportError("it is not safe to remove 'retain' " + "message on the result of a 'delegate' message; " + "the object that was passed to 'setDelegate:' may not be " + "properly retained", rec->getLocStart()); + return true; + } + } + case OMF_dealloc: + break; + } + + switch (E->getReceiverKind()) { + default: + return true; + case ObjCMessageExpr::SuperInstance: { + Transaction Trans(Pass.TA); + clearDiagnostics(E->getSuperLoc()); + if (tryRemoving(E)) + return true; + Pass.TA.replace(E->getSourceRange(), "self"); + return true; + } + case ObjCMessageExpr::Instance: + break; + } + + Expr *rec = E->getInstanceReceiver(); + if (!rec) return true; + + Transaction Trans(Pass.TA); + clearDiagnostics(rec->getExprLoc()); + + if (E->getMethodFamily() == OMF_release && + isRemovable(E) && isInAtFinally(E)) { + // Change the -release to "receiver = nil" in a finally to avoid a leak + // when an exception is thrown. + Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + if (Pass.Ctx.Idents.get("nil").hasMacroDefinition()) + Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil"); + else + Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0"); + return true; + } + + if (!hasSideEffects(E, Pass.Ctx)) { + if (tryRemoving(E)) + return true; + } + Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + + return true; + } + +private: + void clearDiagnostics(SourceLocation loc) const { + Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, + diag::err_unavailable, + diag::err_unavailable_message, + loc); + } + + bool isDelegateMessage(Expr *E) const { + if (!E) return false; + + E = E->IgnoreParenCasts(); + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) + return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); + + if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) + return propE->getGetterSelector() == DelegateSel; + + return false; + } + + bool isInAtFinally(Expr *E) const { + assert(E); + Stmt *S = E; + while (S) { + if (isa<ObjCAtFinallyStmt>(S)) + return true; + S = StmtMap->getParent(S); + } + + return false; + } + + bool isRemovable(Expr *E) const { + return Removables.count(E); + } + + bool tryRemoving(Expr *E) const { + if (isRemovable(E)) { + Pass.TA.removeStmt(E); + return true; + } + + Stmt *parent = StmtMap->getParent(E); + + if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) + return tryRemoving(castE); + + if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) + return tryRemoving(parenE); + + if (BinaryOperator * + bopE = dyn_cast_or_null<BinaryOperator>(parent)) { + if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && + isRemovable(bopE)) { + Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); + return true; + } + } + + return false; + } + +}; + +} // anonymous namespace + +void trans::removeRetainReleaseDealloc(MigrationPass &pass) { + BodyTransform<RetainReleaseDeallocRemover> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} |