• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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