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