TransRetainReleaseDealloc.cpp revision 5f9e272e632e951b1efe824cd16acb4d96077930
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 25using namespace clang; 26using namespace arcmt; 27using namespace trans; 28 29namespace { 30 31class 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 41public: 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 if (Pass.Ctx.Idents.get("nil").hasMacroDefinition()) 133 Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil"); 134 else 135 Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0"); 136 return true; 137 } 138 139 if (!hasSideEffects(E, Pass.Ctx)) { 140 if (tryRemoving(E)) 141 return true; 142 } 143 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); 144 145 return true; 146 } 147 148private: 149 void clearDiagnostics(SourceLocation loc) const { 150 Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, 151 diag::err_unavailable, 152 diag::err_unavailable_message, 153 loc); 154 } 155 156 bool isDelegateMessage(Expr *E) const { 157 if (!E) return false; 158 159 E = E->IgnoreParenCasts(); 160 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) 161 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); 162 163 if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) 164 return propE->getGetterSelector() == DelegateSel; 165 166 return false; 167 } 168 169 bool isInAtFinally(Expr *E) const { 170 assert(E); 171 Stmt *S = E; 172 while (S) { 173 if (isa<ObjCAtFinallyStmt>(S)) 174 return true; 175 S = StmtMap->getParent(S); 176 } 177 178 return false; 179 } 180 181 bool isRemovable(Expr *E) const { 182 return Removables.count(E); 183 } 184 185 bool tryRemoving(Expr *E) const { 186 if (isRemovable(E)) { 187 Pass.TA.removeStmt(E); 188 return true; 189 } 190 191 Stmt *parent = StmtMap->getParent(E); 192 193 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) 194 return tryRemoving(castE); 195 196 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) 197 return tryRemoving(parenE); 198 199 if (BinaryOperator * 200 bopE = dyn_cast_or_null<BinaryOperator>(parent)) { 201 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && 202 isRemovable(bopE)) { 203 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); 204 return true; 205 } 206 } 207 208 return false; 209 } 210 211}; 212 213} // anonymous namespace 214 215void trans::removeRetainReleaseDealloc(MigrationPass &pass) { 216 BodyTransform<RetainReleaseDeallocRemover> trans(pass); 217 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 218} 219