]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / llvm / tools / clang / lib / ARCMigrate / TransRetainReleaseDealloc.cpp
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
29 namespace {
30
31 class RetainReleaseDeallocRemover :
32                        public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
33   Stmt *Body;
34   MigrationPass &Pass;
35
36   ExprSet Removables;
37   llvm::OwningPtr<ParentMap> StmtMap;
38
39   Selector DelegateSel;
40
41 public:
42   RetainReleaseDeallocRemover(MigrationPass &pass)
43     : Body(0), Pass(pass) {
44     DelegateSel =
45         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
46   }
47
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;
59     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.
65         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());
68         return true;
69       }
70       // Pass through.
71     case OMF_retain:
72     case OMF_release:
73       if (E->getReceiverKind() == ObjCMessageExpr::Instance)
74         if (Expr *rec = E->getInstanceReceiver()) {
75           rec = rec->IgnoreParenImpCasts();
76           if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
77               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
78             std::string err = "it is not safe to remove '";
79             err += E->getSelector().getAsString() + "' message on "
80                 "an __unsafe_unretained type";
81             Pass.TA.reportError(err, rec->getLocStart());
82             return true;
83           }
84
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           }
93
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           }
101         }
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);
111       clearDiagnostics(E->getSuperLoc());
112       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);
125     clearDiagnostics(rec->getExprLoc());
126
127     if (E->getMethodFamily() == OMF_release &&
128         isRemovable(E) && isInAtFinally(E)) {
129       // Change the -release to "receiver = nil" in a finally to avoid a leak
130       // when an exception is thrown.
131       Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
132       std::string str = " = ";
133       str += getNilString(Pass.Ctx);
134       Pass.TA.insertAfterToken(rec->getLocEnd(), str);
135       return true;
136     }
137
138     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
147 private:
148   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
155   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
168   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
180   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
190     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))
196       return tryRemoving(parenE);
197
198     if (BinaryOperator *
199           bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
200       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
214 void trans::removeRetainReleaseDealloc(MigrationPass &pass) {
215   BodyTransform<RetainReleaseDeallocRemover> trans(pass);
216   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
217 }