blob: 6eb82093cf892e0bf1b4310836d037079ff737a8 [file] [log] [blame]
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +00001//===--- 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;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000028
29namespace {
30
31class RetainReleaseDeallocRemover :
32 public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000033 Stmt *Body;
34 MigrationPass &Pass;
35
36 ExprSet Removables;
37 llvm::OwningPtr<ParentMap> StmtMap;
38
Argyrios Kyrtzidise7ef8552011-11-04 15:58:22 +000039 Selector DelegateSel, FinalizeSel;
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000040
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000041public:
Argyrios Kyrtzidisb1094a02011-06-23 21:21:33 +000042 RetainReleaseDeallocRemover(MigrationPass &pass)
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000043 : Body(0), Pass(pass) {
44 DelegateSel =
45 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
Argyrios Kyrtzidise7ef8552011-11-04 15:58:22 +000046 FinalizeSel =
47 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000048 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000049
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:
Argyrios Kyrtzidise7ef8552011-11-04 15:58:22 +000060 if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)
61 break;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000062 return true;
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000063 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.
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000069 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());
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000072 return true;
73 }
74 // Pass through.
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000075 case OMF_retain:
76 case OMF_release:
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000077 if (E->getReceiverKind() == ObjCMessageExpr::Instance)
78 if (Expr *rec = E->getInstanceReceiver()) {
79 rec = rec->IgnoreParenImpCasts();
Argyrios Kyrtzidis2cd53662011-07-14 22:46:12 +000080 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
81 (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000082 std::string err = "it is not safe to remove '";
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000083 err += E->getSelector().getAsString() + "' message on "
84 "an __unsafe_unretained type";
85 Pass.TA.reportError(err, rec->getLocStart());
86 return true;
87 }
Argyrios Kyrtzidis2c18ca02011-07-14 23:32:04 +000088
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 }
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000097
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 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000105 }
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);
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000115 clearDiagnostics(E->getSuperLoc());
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000116 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);
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000129 clearDiagnostics(rec->getExprLoc());
130
131 if (E->getMethodFamily() == OMF_release &&
132 isRemovable(E) && isInAtFinally(E)) {
Argyrios Kyrtzidis1aa60ff2011-07-15 22:04:00 +0000133 // Change the -release to "receiver = nil" in a finally to avoid a leak
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000134 // when an exception is thrown.
135 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000136 std::string str = " = ";
137 str += getNilString(Pass.Ctx);
138 Pass.TA.insertAfterToken(rec->getLocEnd(), str);
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000139 return true;
140 }
141
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000142 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
151private:
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000152 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
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +0000159 bool isDelegateMessage(Expr *E) const {
160 if (!E) return false;
161
162 E = E->IgnoreParenCasts();
John McCall4b9c2d22011-11-06 09:01:30 +0000163
164 // Also look through property-getter sugar.
165 if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))
166 E = pseudoOp->getResultExpr()->IgnoreImplicit();
167
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +0000168 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
169 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
170
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +0000171 return false;
172 }
173
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000174 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
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000186 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
John McCall7e5e5f42011-07-07 06:58:02 +0000196 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))
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000202 return tryRemoving(parenE);
203
204 if (BinaryOperator *
John McCall7e5e5f42011-07-07 06:58:02 +0000205 bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000206 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
Argyrios Kyrtzidise7ef8552011-11-04 15:58:22 +0000220void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000221 BodyTransform<RetainReleaseDeallocRemover> trans(pass);
222 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
223}