blob: 394f8480e112eb69dbaf9bc14bf8a8d2185c529c [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 Kyrtzidis93907472011-07-27 05:28:18 +0000132 std::string str = " = ";
133 str += getNilString(Pass.Ctx);
134 Pass.TA.insertAfterToken(rec->getLocEnd(), str);
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000135 return true;
136 }
137
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000138 if (!hasSideEffects(E, Pass.Ctx)) {
139 if (tryRemoving(E))
140 return true;
141 }
142 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
143
144 return true;
145 }
146
147private:
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000148 void clearDiagnostics(SourceLocation loc) const {
149 Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
150 diag::err_unavailable,
151 diag::err_unavailable_message,
152 loc);
153 }
154
Argyrios Kyrtzidisa6fe4bf2011-07-15 23:48:56 +0000155 bool isDelegateMessage(Expr *E) const {
156 if (!E) return false;
157
158 E = E->IgnoreParenCasts();
159 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
160 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
161
162 if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E))
163 return propE->getGetterSelector() == DelegateSel;
164
165 return false;
166 }
167
Argyrios Kyrtzidis6a8a14d2011-07-15 21:11:23 +0000168 bool isInAtFinally(Expr *E) const {
169 assert(E);
170 Stmt *S = E;
171 while (S) {
172 if (isa<ObjCAtFinallyStmt>(S))
173 return true;
174 S = StmtMap->getParent(S);
175 }
176
177 return false;
178 }
179
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000180 bool isRemovable(Expr *E) const {
181 return Removables.count(E);
182 }
183
184 bool tryRemoving(Expr *E) const {
185 if (isRemovable(E)) {
186 Pass.TA.removeStmt(E);
187 return true;
188 }
189
John McCall4db5c3c2011-07-07 06:58:02 +0000190 Stmt *parent = StmtMap->getParent(E);
191
192 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
193 return tryRemoving(castE);
194
195 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000196 return tryRemoving(parenE);
197
198 if (BinaryOperator *
John McCall4db5c3c2011-07-07 06:58:02 +0000199 bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +0000200 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
201 isRemovable(bopE)) {
202 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
203 return true;
204 }
205 }
206
207 return false;
208 }
209
210};
211
212} // anonymous namespace
213
214void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
215 BodyTransform<RetainReleaseDeallocRemover> trans(pass);
216 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
217}