1 //===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // removeRetainReleaseDealloc:
12 // Removes retain/release/autorelease/dealloc messages.
14 // return [[foo retain] autorelease];
18 //===----------------------------------------------------------------------===//
20 #include "Transforms.h"
21 #include "Internals.h"
22 #include "clang/Sema/SemaDiagnostic.h"
23 #include "clang/AST/ParentMap.h"
25 using namespace clang;
26 using namespace arcmt;
27 using namespace trans;
31 class RetainReleaseDeallocRemover :
32 public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
37 llvm::OwningPtr<ParentMap> StmtMap;
42 RetainReleaseDeallocRemover(MigrationPass &pass)
43 : Body(0), Pass(pass) {
45 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
48 void transformBody(Stmt *body) {
50 collectRemovables(body, Removables);
51 StmtMap.reset(new ParentMap(body));
55 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
56 switch (E->getMethodFamily()) {
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());
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());
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 "
90 Pass.TA.reportError(err, rec->getLocStart());
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());
106 switch (E->getReceiverKind()) {
109 case ObjCMessageExpr::SuperInstance: {
110 Transaction Trans(Pass.TA);
111 clearDiagnostics(E->getSuperLoc());
114 Pass.TA.replace(E->getSourceRange(), "self");
117 case ObjCMessageExpr::Instance:
121 Expr *rec = E->getInstanceReceiver();
122 if (!rec) return true;
124 Transaction Trans(Pass.TA);
125 clearDiagnostics(rec->getExprLoc());
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);
138 if (!hasSideEffects(E, Pass.Ctx)) {
142 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
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,
155 bool isDelegateMessage(Expr *E) const {
156 if (!E) return false;
158 E = E->IgnoreParenCasts();
159 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
160 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
162 if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E))
163 return propE->getGetterSelector() == DelegateSel;
168 bool isInAtFinally(Expr *E) const {
172 if (isa<ObjCAtFinallyStmt>(S))
174 S = StmtMap->getParent(S);
180 bool isRemovable(Expr *E) const {
181 return Removables.count(E);
184 bool tryRemoving(Expr *E) const {
185 if (isRemovable(E)) {
186 Pass.TA.removeStmt(E);
190 Stmt *parent = StmtMap->getParent(E);
192 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
193 return tryRemoving(castE);
195 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
196 return tryRemoving(parenE);
199 bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
200 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
202 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
212 } // anonymous namespace
214 void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
215 BodyTransform<RetainReleaseDeallocRemover> trans(pass);
216 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());