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 29 namespace { 30 31 class RetainReleaseDeallocRemover : 32 public RecursiveASTVisitor<RetainReleaseDeallocRemover> { 33 Stmt *Body; 34 MigrationPass &Pass; 35 36 ExprSet Removables; 37 OwningPtr<ParentMap> StmtMap; 38 39 Selector DelegateSel, FinalizeSel; 40 41 public: 42 RetainReleaseDeallocRemover(MigrationPass &pass) 43 : Body(0), Pass(pass) { 44 DelegateSel = 45 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); 46 FinalizeSel = 47 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); 48 } 49 50 void transformBody(Stmt *body) { 51 Body = body; 52 collectRemovables(body, Removables); 53 StmtMap.reset(new ParentMap(body)); 54 TraverseStmt(body); 55 } 56 57 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 58 switch (E->getMethodFamily()) { 59 default: 60 if (E->isInstanceMessage() && E->getSelector() == FinalizeSel) 61 break; 62 return true; 63 case OMF_autorelease: 64 if (isRemovable(E)) { 65 // An unused autorelease is badness. If we remove it the receiver 66 // will likely die immediately while previously it was kept alive 67 // by the autorelease pool. This is bad practice in general, leave it 68 // and emit an error to force the user to restructure his code. 69 Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " 70 "message; its receiver may be destroyed immediately", 71 E->getLocStart(), E->getSourceRange()); 72 return true; 73 } 74 // Pass through. 75 case OMF_retain: 76 case OMF_release: 77 if (E->getReceiverKind() == ObjCMessageExpr::Instance) 78 if (Expr *rec = E->getInstanceReceiver()) { 79 rec = rec->IgnoreParenImpCasts(); 80 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && 81 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 82 std::string err = "it is not safe to remove '"; 83 err += E->getSelector().getAsString() + "' message on " 84 "an __unsafe_unretained type"; 85 Pass.TA.reportError(err, rec->getLocStart()); 86 return true; 87 } 88 89 if (isGlobalVar(rec) && 90 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 91 std::string err = "it is not safe to remove '"; 92 err += E->getSelector().getAsString() + "' message on " 93 "a global variable"; 94 Pass.TA.reportError(err, rec->getLocStart()); 95 return true; 96 } 97 98 if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { 99 Pass.TA.reportError("it is not safe to remove 'retain' " 100 "message on the result of a 'delegate' message; " 101 "the object that was passed to 'setDelegate:' may not be " 102 "properly retained", rec->getLocStart()); 103 return true; 104 } 105 } 106 case OMF_dealloc: 107 break; 108 } 109 110 switch (E->getReceiverKind()) { 111 default: 112 return true; 113 case ObjCMessageExpr::SuperInstance: { 114 Transaction Trans(Pass.TA); 115 clearDiagnostics(E->getSuperLoc()); 116 if (tryRemoving(E)) 117 return true; 118 Pass.TA.replace(E->getSourceRange(), "self"); 119 return true; 120 } 121 case ObjCMessageExpr::Instance: 122 break; 123 } 124 125 Expr *rec = E->getInstanceReceiver(); 126 if (!rec) return true; 127 128 Transaction Trans(Pass.TA); 129 clearDiagnostics(rec->getExprLoc()); 130 131 if (E->getMethodFamily() == OMF_release && 132 isRemovable(E) && isInAtFinally(E)) { 133 // Change the -release to "receiver = nil" in a finally to avoid a leak 134 // when an exception is thrown. 135 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); 136 std::string str = " = "; 137 str += getNilString(Pass.Ctx); 138 Pass.TA.insertAfterToken(rec->getLocEnd(), str); 139 return true; 140 } 141 142 if (!hasSideEffects(E, Pass.Ctx)) { 143 if (tryRemoving(E)) 144 return true; 145 } 146 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); 147 148 return true; 149 } 150 151 private: 152 void clearDiagnostics(SourceLocation loc) const { 153 Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, 154 diag::err_unavailable, 155 diag::err_unavailable_message, 156 loc); 157 } 158 159 bool isDelegateMessage(Expr *E) const { 160 if (!E) return false; 161 162 E = E->IgnoreParenCasts(); 163 164 // Also look through property-getter sugar. 165 if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) 166 E = pseudoOp->getResultExpr()->IgnoreImplicit(); 167 168 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) 169 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); 170 171 return false; 172 } 173 174 bool isInAtFinally(Expr *E) const { 175 assert(E); 176 Stmt *S = E; 177 while (S) { 178 if (isa<ObjCAtFinallyStmt>(S)) 179 return true; 180 S = StmtMap->getParent(S); 181 } 182 183 return false; 184 } 185 186 bool isRemovable(Expr *E) const { 187 return Removables.count(E); 188 } 189 190 bool tryRemoving(Expr *E) const { 191 if (isRemovable(E)) { 192 Pass.TA.removeStmt(E); 193 return true; 194 } 195 196 Stmt *parent = StmtMap->getParent(E); 197 198 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) 199 return tryRemoving(castE); 200 201 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) 202 return tryRemoving(parenE); 203 204 if (BinaryOperator * 205 bopE = dyn_cast_or_null<BinaryOperator>(parent)) { 206 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && 207 isRemovable(bopE)) { 208 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); 209 return true; 210 } 211 } 212 213 return false; 214 } 215 216 }; 217 218 } // anonymous namespace 219 220 void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { 221 BodyTransform<RetainReleaseDeallocRemover> trans(pass); 222 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 223 } 224