blob: ed6ed0adfdf2fac4653e438076e1411a49b7e0e2 [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;
28using llvm::StringRef;
29
30namespace {
31
32class RetainReleaseDeallocRemover :
33 public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000034 Stmt *Body;
35 MigrationPass &Pass;
36
37 ExprSet Removables;
38 llvm::OwningPtr<ParentMap> StmtMap;
39
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000040 Selector DelegateSel;
41
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000042public:
Argyrios Kyrtzidisb1094a02011-06-23 21:21:33 +000043 RetainReleaseDeallocRemover(MigrationPass &pass)
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000044 : Body(0), Pass(pass) {
45 DelegateSel =
46 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
47 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000048
49 void transformBody(Stmt *body) {
50 Body = body;
51 collectRemovables(body, Removables);
52 StmtMap.reset(new ParentMap(body));
53 TraverseStmt(body);
54 }
55
56 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
57 switch (E->getMethodFamily()) {
58 default:
59 return true;
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000060 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.
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000066 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());
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000069 return true;
70 }
71 // Pass through.
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000072 case OMF_retain:
73 case OMF_release:
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000074 if (E->getReceiverKind() == ObjCMessageExpr::Instance)
75 if (Expr *rec = E->getInstanceReceiver()) {
76 rec = rec->IgnoreParenImpCasts();
Argyrios Kyrtzidis2cd53662011-07-14 22:46:12 +000077 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
78 (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
Argyrios Kyrtzidise0e40762011-07-14 21:26:49 +000079 std::string err = "it is not safe to remove '";
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000080 err += E->getSelector().getAsString() + "' message on "
81 "an __unsafe_unretained type";
82 Pass.TA.reportError(err, rec->getLocStart());
83 return true;
84 }
Argyrios Kyrtzidis2c18ca02011-07-14 23:32:04 +000085
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 }
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +000094
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 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000102 }
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);
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000112 clearDiagnostics(E->getSuperLoc());
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000113 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);
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000126 clearDiagnostics(rec->getExprLoc());
127
128 if (E->getMethodFamily() == OMF_release &&
129 isRemovable(E) && isInAtFinally(E)) {
Argyrios Kyrtzidis1aa60ff2011-07-15 22:04:00 +0000130 // Change the -release to "receiver = nil" in a finally to avoid a leak
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000131 // when an exception is thrown.
132 Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +0000133 if (Pass.Ctx.Idents.get("nil").hasMacroDefinition())
Argyrios Kyrtzidis1aa60ff2011-07-15 22:04:00 +0000134 Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil");
135 else
136 Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0");
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000137 return true;
138 }
139
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000140 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
149private:
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000150 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
Argyrios Kyrtzidis82a921a2011-07-15 23:48:56 +0000157 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
Argyrios Kyrtzidisf55a8692011-07-15 21:11:23 +0000170 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
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000182 bool isRemovable(Expr *E) const {
183 return Removables.count(E);
184 }
185
186 bool tryRemoving(Expr *E) const {
187 if (isRemovable(E)) {
188 Pass.TA.removeStmt(E);
189 return true;
190 }
191
John McCall7e5e5f42011-07-07 06:58:02 +0000192 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))
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000198 return tryRemoving(parenE);
199
200 if (BinaryOperator *
John McCall7e5e5f42011-07-07 06:58:02 +0000201 bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000202 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
216void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
217 BodyTransform<RetainReleaseDeallocRemover> trans(pass);
218 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
219}