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:
RetainReleaseDeallocRemover(MigrationPass & pass)43 RetainReleaseDeallocRemover(MigrationPass &pass)
44 : Body(0), Pass(pass) {
45 DelegateSel =
46 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
47 }
48
transformBody(Stmt * body)49 void transformBody(Stmt *body) {
50 Body = body;
51 collectRemovables(body, Removables);
52 StmtMap.reset(new ParentMap(body));
53 TraverseStmt(body);
54 }
55
VisitObjCMessageExpr(ObjCMessageExpr * E)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:
clearDiagnostics(SourceLocation loc) const150 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
isDelegateMessage(Expr * E) const157 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
isInAtFinally(Expr * E) const170 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
isRemovable(Expr * E) const182 bool isRemovable(Expr *E) const {
183 return Removables.count(E);
184 }
185
tryRemoving(Expr * E) const186 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
removeRetainReleaseDealloc(MigrationPass & pass)216 void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
217 BodyTransform<RetainReleaseDeallocRemover> trans(pass);
218 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
219 }
220