Home | History | Annotate | Download | only in ARCMigrate
      1 //===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // removeRetainReleaseDealloc:
     11 //
     12 // Removes retain/release/autorelease/dealloc messages.
     13 //
     14 //  return [[foo retain] autorelease];
     15 // ---->
     16 //  return foo;
     17 //
     18 //===----------------------------------------------------------------------===//
     19 
     20 #include "Transforms.h"
     21 #include "Internals.h"
     22 #include "clang/Sema/SemaDiagnostic.h"
     23 #include "clang/AST/ParentMap.h"
     24 
     25 using namespace clang;
     26 using namespace arcmt;
     27 using namespace trans;
     28 using llvm::StringRef;
     29 
     30 namespace {
     31 
     32 class RetainReleaseDeallocRemover :
     33                        public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
     34   Stmt *Body;
     35   MigrationPass &Pass;
     36 
     37   ExprSet Removables;
     38   llvm::OwningPtr<ParentMap> StmtMap;
     39 
     40   Selector DelegateSel;
     41 
     42 public:
     43   RetainReleaseDeallocRemover(MigrationPass &pass)
     44     : Body(0), Pass(pass) {
     45     DelegateSel =
     46         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
     47   }
     48 
     49   void transformBody(Stmt *body) {
     50     Body = body;
     51     collectRemovables(body, Removables);
     52     StmtMap.reset(new ParentMap(body));
     53     TraverseStmt(body);
     54   }
     55 
     56   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
     57     switch (E->getMethodFamily()) {
     58     default:
     59       return true;
     60     case OMF_autorelease:
     61       if (isRemovable(E)) {
     62         // An unused autorelease is badness. If we remove it the receiver
     63         // will likely die immediately while previously it was kept alive
     64         // by the autorelease pool. This is bad practice in general, leave it
     65         // and emit an error to force the user to restructure his code.
     66         Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
     67             "message; its receiver may be destroyed immediately",
     68             E->getLocStart(), E->getSourceRange());
     69         return true;
     70       }
     71       // Pass through.
     72     case OMF_retain:
     73     case OMF_release:
     74       if (E->getReceiverKind() == ObjCMessageExpr::Instance)
     75         if (Expr *rec = E->getInstanceReceiver()) {
     76           rec = rec->IgnoreParenImpCasts();
     77           if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
     78               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
     79             std::string err = "it is not safe to remove '";
     80             err += E->getSelector().getAsString() + "' message on "
     81                 "an __unsafe_unretained type";
     82             Pass.TA.reportError(err, rec->getLocStart());
     83             return true;
     84           }
     85 
     86           if (isGlobalVar(rec) &&
     87               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
     88             std::string err = "it is not safe to remove '";
     89             err += E->getSelector().getAsString() + "' message on "
     90                 "a global variable";
     91             Pass.TA.reportError(err, rec->getLocStart());
     92             return true;
     93           }
     94 
     95           if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
     96             Pass.TA.reportError("it is not safe to remove 'retain' "
     97                 "message on the result of a 'delegate' message; "
     98                 "the object that was passed to 'setDelegate:' may not be "
     99                 "properly retained", rec->getLocStart());
    100             return true;
    101           }
    102         }
    103     case OMF_dealloc:
    104       break;
    105     }
    106 
    107     switch (E->getReceiverKind()) {
    108     default:
    109       return true;
    110     case ObjCMessageExpr::SuperInstance: {
    111       Transaction Trans(Pass.TA);
    112       clearDiagnostics(E->getSuperLoc());
    113       if (tryRemoving(E))
    114         return true;
    115       Pass.TA.replace(E->getSourceRange(), "self");
    116       return true;
    117     }
    118     case ObjCMessageExpr::Instance:
    119       break;
    120     }
    121 
    122     Expr *rec = E->getInstanceReceiver();
    123     if (!rec) return true;
    124 
    125     Transaction Trans(Pass.TA);
    126     clearDiagnostics(rec->getExprLoc());
    127 
    128     if (E->getMethodFamily() == OMF_release &&
    129         isRemovable(E) && isInAtFinally(E)) {
    130       // Change the -release to "receiver = nil" in a finally to avoid a leak
    131       // when an exception is thrown.
    132       Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
    133       if (Pass.Ctx.Idents.get("nil").hasMacroDefinition())
    134         Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil");
    135       else
    136         Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0");
    137       return true;
    138     }
    139 
    140     if (!hasSideEffects(E, Pass.Ctx)) {
    141       if (tryRemoving(E))
    142         return true;
    143     }
    144     Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
    145 
    146     return true;
    147   }
    148 
    149 private:
    150   void clearDiagnostics(SourceLocation loc) const {
    151     Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
    152                             diag::err_unavailable,
    153                             diag::err_unavailable_message,
    154                             loc);
    155   }
    156 
    157   bool isDelegateMessage(Expr *E) const {
    158     if (!E) return false;
    159 
    160     E = E->IgnoreParenCasts();
    161     if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
    162       return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
    163 
    164     if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E))
    165       return propE->getGetterSelector() == DelegateSel;
    166 
    167     return false;
    168   }
    169 
    170   bool isInAtFinally(Expr *E) const {
    171     assert(E);
    172     Stmt *S = E;
    173     while (S) {
    174       if (isa<ObjCAtFinallyStmt>(S))
    175         return true;
    176       S = StmtMap->getParent(S);
    177     }
    178 
    179     return false;
    180   }
    181 
    182   bool isRemovable(Expr *E) const {
    183     return Removables.count(E);
    184   }
    185 
    186   bool tryRemoving(Expr *E) const {
    187     if (isRemovable(E)) {
    188       Pass.TA.removeStmt(E);
    189       return true;
    190     }
    191 
    192     Stmt *parent = StmtMap->getParent(E);
    193 
    194     if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
    195       return tryRemoving(castE);
    196 
    197     if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
    198       return tryRemoving(parenE);
    199 
    200     if (BinaryOperator *
    201           bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
    202       if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
    203           isRemovable(bopE)) {
    204         Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
    205         return true;
    206       }
    207     }
    208 
    209     return false;
    210   }
    211 
    212 };
    213 
    214 } // anonymous namespace
    215 
    216 void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
    217   BodyTransform<RetainReleaseDeallocRemover> trans(pass);
    218   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    219 }
    220