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/AST/ASTContext.h"
     23 #include "clang/AST/ParentMap.h"
     24 #include "clang/Basic/SourceManager.h"
     25 #include "clang/Lex/Lexer.h"
     26 #include "clang/Sema/SemaDiagnostic.h"
     27 
     28 using namespace clang;
     29 using namespace arcmt;
     30 using namespace trans;
     31 
     32 namespace {
     33 
     34 class RetainReleaseDeallocRemover :
     35                        public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
     36   Stmt *Body;
     37   MigrationPass &Pass;
     38 
     39   ExprSet Removables;
     40   OwningPtr<ParentMap> StmtMap;
     41 
     42   Selector DelegateSel, FinalizeSel;
     43 
     44 public:
     45   RetainReleaseDeallocRemover(MigrationPass &pass)
     46     : Body(0), Pass(pass) {
     47     DelegateSel =
     48         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
     49     FinalizeSel =
     50         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
     51   }
     52 
     53   void transformBody(Stmt *body, Decl *ParentD) {
     54     Body = body;
     55     collectRemovables(body, Removables);
     56     StmtMap.reset(new ParentMap(body));
     57     TraverseStmt(body);
     58   }
     59 
     60   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
     61     switch (E->getMethodFamily()) {
     62     default:
     63       if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)
     64         break;
     65       return true;
     66     case OMF_autorelease:
     67       if (isRemovable(E)) {
     68         if (!isCommonUnusedAutorelease(E)) {
     69           // An unused autorelease is badness. If we remove it the receiver
     70           // will likely die immediately while previously it was kept alive
     71           // by the autorelease pool. This is bad practice in general, leave it
     72           // and emit an error to force the user to restructure his code.
     73           Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
     74               "message; its receiver may be destroyed immediately",
     75               E->getLocStart(), E->getSourceRange());
     76           return true;
     77         }
     78       }
     79       // Pass through.
     80     case OMF_retain:
     81     case OMF_release:
     82       if (E->getReceiverKind() == ObjCMessageExpr::Instance)
     83         if (Expr *rec = E->getInstanceReceiver()) {
     84           rec = rec->IgnoreParenImpCasts();
     85           if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
     86               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
     87             std::string err = "it is not safe to remove '";
     88             err += E->getSelector().getAsString() + "' message on "
     89                 "an __unsafe_unretained type";
     90             Pass.TA.reportError(err, rec->getLocStart());
     91             return true;
     92           }
     93 
     94           if (isGlobalVar(rec) &&
     95               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
     96             std::string err = "it is not safe to remove '";
     97             err += E->getSelector().getAsString() + "' message on "
     98                 "a global variable";
     99             Pass.TA.reportError(err, rec->getLocStart());
    100             return true;
    101           }
    102 
    103           if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
    104             Pass.TA.reportError("it is not safe to remove 'retain' "
    105                 "message on the result of a 'delegate' message; "
    106                 "the object that was passed to 'setDelegate:' may not be "
    107                 "properly retained", rec->getLocStart());
    108             return true;
    109           }
    110         }
    111     case OMF_dealloc:
    112       break;
    113     }
    114 
    115     switch (E->getReceiverKind()) {
    116     default:
    117       return true;
    118     case ObjCMessageExpr::SuperInstance: {
    119       Transaction Trans(Pass.TA);
    120       clearDiagnostics(E->getSuperLoc());
    121       if (tryRemoving(E))
    122         return true;
    123       Pass.TA.replace(E->getSourceRange(), "self");
    124       return true;
    125     }
    126     case ObjCMessageExpr::Instance:
    127       break;
    128     }
    129 
    130     Expr *rec = E->getInstanceReceiver();
    131     if (!rec) return true;
    132 
    133     Transaction Trans(Pass.TA);
    134     clearDiagnostics(rec->getExprLoc());
    135 
    136     ObjCMessageExpr *Msg = E;
    137     Expr *RecContainer = Msg;
    138     SourceRange RecRange = rec->getSourceRange();
    139     checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);
    140 
    141     if (Msg->getMethodFamily() == OMF_release &&
    142         isRemovable(RecContainer) && isInAtFinally(RecContainer)) {
    143       // Change the -release to "receiver = nil" in a finally to avoid a leak
    144       // when an exception is thrown.
    145       Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
    146       std::string str = " = ";
    147       str += getNilString(Pass.Ctx);
    148       Pass.TA.insertAfterToken(RecRange.getEnd(), str);
    149       return true;
    150     }
    151 
    152     if (!hasSideEffects(rec, Pass.Ctx)) {
    153       if (tryRemoving(RecContainer))
    154         return true;
    155     }
    156     Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
    157 
    158     return true;
    159   }
    160 
    161 private:
    162   /// \brief Checks for idioms where an unused -autorelease is common.
    163   ///
    164   /// Currently only returns true for this idiom which is common in property
    165   /// setters:
    166   ///
    167   ///   [backingValue autorelease];
    168   ///   backingValue = [newValue retain]; // in general a +1 assign
    169   ///
    170   bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
    171     Expr *Rec = E->getInstanceReceiver();
    172     if (!Rec)
    173       return false;
    174 
    175     Decl *RefD = getReferencedDecl(Rec);
    176     if (!RefD)
    177       return false;
    178 
    179     Stmt *OuterS = E, *InnerS;
    180     do {
    181       InnerS = OuterS;
    182       OuterS = StmtMap->getParent(InnerS);
    183     }
    184     while (OuterS && (isa<ParenExpr>(OuterS) ||
    185                       isa<CastExpr>(OuterS) ||
    186                       isa<ExprWithCleanups>(OuterS)));
    187 
    188     if (!OuterS)
    189       return false;
    190 
    191     // Find next statement after the -autorelease.
    192 
    193     Stmt::child_iterator currChildS = OuterS->child_begin();
    194     Stmt::child_iterator childE = OuterS->child_end();
    195     for (; currChildS != childE; ++currChildS) {
    196       if (*currChildS == InnerS)
    197         break;
    198     }
    199     if (currChildS == childE)
    200       return false;
    201     ++currChildS;
    202     if (currChildS == childE)
    203       return false;
    204 
    205     Stmt *nextStmt = *currChildS;
    206     if (!nextStmt)
    207       return false;
    208     nextStmt = nextStmt->IgnoreImplicit();
    209 
    210     // Check for "RefD = [+1 retained object];".
    211 
    212     if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) {
    213       if (RefD != getReferencedDecl(Bop->getLHS()))
    214         return false;
    215       if (isPlusOneAssign(Bop))
    216         return true;
    217     }
    218     return false;
    219   }
    220 
    221   Decl *getReferencedDecl(Expr *E) {
    222     if (!E)
    223       return 0;
    224 
    225     E = E->IgnoreParenCasts();
    226     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
    227       return DRE->getDecl();
    228     if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
    229       return ME->getMemberDecl();
    230     if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
    231       return IRE->getDecl();
    232 
    233     return 0;
    234   }
    235 
    236   /// \brief Check if the retain/release is due to a GCD/XPC macro that are
    237   /// defined as:
    238   ///
    239   /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
    240   /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
    241   /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
    242   /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
    243   ///
    244   /// and return the top container which is the StmtExpr and the macro argument
    245   /// expression.
    246   void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer,
    247                         Expr *&Rec, SourceRange &RecRange) {
    248     SourceLocation Loc = Msg->getExprLoc();
    249     if (!Loc.isMacroID())
    250       return;
    251     SourceManager &SM = Pass.Ctx.getSourceManager();
    252     StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM,
    253                                                      Pass.Ctx.getLangOpts());
    254     bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)
    255         .Case("dispatch_retain", true)
    256         .Case("dispatch_release", true)
    257         .Case("xpc_retain", true)
    258         .Case("xpc_release", true)
    259         .Default(false);
    260     if (!isGCDOrXPC)
    261       return;
    262 
    263     StmtExpr *StmtE = 0;
    264     Stmt *S = Msg;
    265     while (S) {
    266       if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) {
    267         StmtE = SE;
    268         break;
    269       }
    270       S = StmtMap->getParent(S);
    271     }
    272 
    273     if (!StmtE)
    274       return;
    275 
    276     Stmt::child_range StmtExprChild = StmtE->children();
    277     if (!StmtExprChild)
    278       return;
    279     CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild);
    280     if (!CompS)
    281       return;
    282 
    283     Stmt::child_range CompStmtChild = CompS->children();
    284     if (!CompStmtChild)
    285       return;
    286     DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild);
    287     if (!DeclS)
    288       return;
    289     if (!DeclS->isSingleDecl())
    290       return;
    291     VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl());
    292     if (!VD)
    293       return;
    294     Expr *Init = VD->getInit();
    295     if (!Init)
    296       return;
    297 
    298     RecContainer = StmtE;
    299     Rec = Init->IgnoreParenImpCasts();
    300     if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec))
    301       Rec = EWC->getSubExpr()->IgnoreParenImpCasts();
    302     RecRange = Rec->getSourceRange();
    303     if (SM.isMacroArgExpansion(RecRange.getBegin()))
    304       RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));
    305     if (SM.isMacroArgExpansion(RecRange.getEnd()))
    306       RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));
    307   }
    308 
    309   void clearDiagnostics(SourceLocation loc) const {
    310     Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
    311                             diag::err_unavailable,
    312                             diag::err_unavailable_message,
    313                             loc);
    314   }
    315 
    316   bool isDelegateMessage(Expr *E) const {
    317     if (!E) return false;
    318 
    319     E = E->IgnoreParenCasts();
    320 
    321     // Also look through property-getter sugar.
    322     if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))
    323       E = pseudoOp->getResultExpr()->IgnoreImplicit();
    324 
    325     if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
    326       return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
    327 
    328     return false;
    329   }
    330 
    331   bool isInAtFinally(Expr *E) const {
    332     assert(E);
    333     Stmt *S = E;
    334     while (S) {
    335       if (isa<ObjCAtFinallyStmt>(S))
    336         return true;
    337       S = StmtMap->getParent(S);
    338     }
    339 
    340     return false;
    341   }
    342 
    343   bool isRemovable(Expr *E) const {
    344     return Removables.count(E);
    345   }
    346 
    347   bool tryRemoving(Expr *E) const {
    348     if (isRemovable(E)) {
    349       Pass.TA.removeStmt(E);
    350       return true;
    351     }
    352 
    353     Stmt *parent = StmtMap->getParent(E);
    354 
    355     if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
    356       return tryRemoving(castE);
    357 
    358     if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
    359       return tryRemoving(parenE);
    360 
    361     if (BinaryOperator *
    362           bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
    363       if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
    364           isRemovable(bopE)) {
    365         Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
    366         return true;
    367       }
    368     }
    369 
    370     return false;
    371   }
    372 
    373 };
    374 
    375 } // anonymous namespace
    376 
    377 void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
    378   BodyTransform<RetainReleaseDeallocRemover> trans(pass);
    379   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    380 }
    381