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