blob: 4a5cdf80566b9b2c01a02b785714dba6a5ebd30a [file] [log] [blame]
Argyrios Kyrtzidise5b475c2011-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 Kyrtzidise5b475c2011-06-21 20:20:39 +000028
29namespace {
30
31class RetainReleaseDeallocRemover :
32 public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000033 Stmt *Body;
34 MigrationPass &Pass;
35
36 ExprSet Removables;
37 llvm::OwningPtr<ParentMap> StmtMap;
38
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +000039 Selector DelegateSel;
40
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000041public:
Argyrios Kyrtzidis0b2bd862011-06-23 21:21:33 +000042 RetainReleaseDeallocRemover(MigrationPass &pass)
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +000043 : Body(0), Pass(pass) {
44 DelegateSel =
45 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
46 }
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000047
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;
Argyrios Kyrtzidis84b528f2011-07-14 21:26:49 +000059 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.
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +000065 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());
Argyrios Kyrtzidis84b528f2011-07-14 21:26:49 +000068 return true;
69 }
70 // Pass through.
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000071 case OMF_retain:
72 case OMF_release:
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000073 if (E->getReceiverKind() == ObjCMessageExpr::Instance)
74 if (Expr *rec = E->getInstanceReceiver()) {
75 rec = rec->IgnoreParenImpCasts();
Argyrios Kyrtzidis08903e42011-07-14 22:46:12 +000076 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
77 (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
Argyrios Kyrtzidis84b528f2011-07-14 21:26:49 +000078 std::string err = "it is not safe to remove '";
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +000079 err += E->getSelector().getAsString() + "' message on "
80 "an __unsafe_unretained type";
81 Pass.TA.reportError(err, rec->getLocStart());
82 return true;
83 }
Argyrios Kyrtzidisf2a27f42011-07-14 23:32:04 +000084
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 }
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +000093
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 }
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000101 }
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);
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000111 clearDiagnostics(E->getSuperLoc());
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000112 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);
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000125 clearDiagnostics(rec->getExprLoc());
126
127 if (E->getMethodFamily() == OMF_release &&
128 isRemovable(E) && isInAtFinally(E)) {
Argyrios Kyrtzidisf59daff2011-07-15 22:04:00 +0000129 // Change the -release to "receiver = nil" in a finally to avoid a leak
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000130 // when an exception is thrown.
131 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +0000132 if (Pass.Ctx.Idents.get("nil").hasMacroDefinition())
Argyrios Kyrtzidisf59daff2011-07-15 22:04:00 +0000133 Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil");
134 else
135 Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0");
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000136 return true;
137 }
138
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000139 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:
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000149 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
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +0000156 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
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000169 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
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000181 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
John McCall4db5c3c2011-07-07 06:58:02 +0000191 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))
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000197 return tryRemoving(parenE);
198
199 if (BinaryOperator *
John McCall4db5c3c2011-07-07 06:58:02 +0000200 bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000201 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}