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