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