]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
Dtrace: Add SUN MDB-like type-aware print() action.
[FreeBSD/FreeBSD.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/AST/ASTContext.h"
23 #include "clang/AST/ParentMap.h"
24 #include "clang/Basic/SourceManager.h"
25 #include "clang/Lex/Lexer.h"
26 #include "clang/Sema/SemaDiagnostic.h"
27
28 using namespace clang;
29 using namespace arcmt;
30 using namespace trans;
31
32 namespace {
33
34 class RetainReleaseDeallocRemover :
35                        public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
36   Stmt *Body;
37   MigrationPass &Pass;
38
39   ExprSet Removables;
40   OwningPtr<ParentMap> StmtMap;
41
42   Selector DelegateSel, FinalizeSel;
43
44 public:
45   RetainReleaseDeallocRemover(MigrationPass &pass)
46     : Body(0), Pass(pass) {
47     DelegateSel =
48         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
49     FinalizeSel =
50         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
51   }
52
53   void transformBody(Stmt *body, Decl *ParentD) {
54     Body = body;
55     collectRemovables(body, Removables);
56     StmtMap.reset(new ParentMap(body));
57     TraverseStmt(body);
58   }
59
60   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
61     switch (E->getMethodFamily()) {
62     default:
63       if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)
64         break;
65       return true;
66     case OMF_autorelease:
67       if (isRemovable(E)) {
68         if (!isCommonUnusedAutorelease(E)) {
69           // An unused autorelease is badness. If we remove it the receiver
70           // will likely die immediately while previously it was kept alive
71           // by the autorelease pool. This is bad practice in general, leave it
72           // and emit an error to force the user to restructure his code.
73           Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
74               "message; its receiver may be destroyed immediately",
75               E->getLocStart(), E->getSourceRange());
76           return true;
77         }
78       }
79       // Pass through.
80     case OMF_retain:
81     case OMF_release:
82       if (E->getReceiverKind() == ObjCMessageExpr::Instance)
83         if (Expr *rec = E->getInstanceReceiver()) {
84           rec = rec->IgnoreParenImpCasts();
85           if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
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                 "an __unsafe_unretained type";
90             Pass.TA.reportError(err, rec->getLocStart());
91             return true;
92           }
93
94           if (isGlobalVar(rec) &&
95               (E->getMethodFamily() != OMF_retain || isRemovable(E))) {
96             std::string err = "it is not safe to remove '";
97             err += E->getSelector().getAsString() + "' message on "
98                 "a global variable";
99             Pass.TA.reportError(err, rec->getLocStart());
100             return true;
101           }
102
103           if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
104             Pass.TA.reportError("it is not safe to remove 'retain' "
105                 "message on the result of a 'delegate' message; "
106                 "the object that was passed to 'setDelegate:' may not be "
107                 "properly retained", rec->getLocStart());
108             return true;
109           }
110         }
111     case OMF_dealloc:
112       break;
113     }
114
115     switch (E->getReceiverKind()) {
116     default:
117       return true;
118     case ObjCMessageExpr::SuperInstance: {
119       Transaction Trans(Pass.TA);
120       clearDiagnostics(E->getSuperLoc());
121       if (tryRemoving(E))
122         return true;
123       Pass.TA.replace(E->getSourceRange(), "self");
124       return true;
125     }
126     case ObjCMessageExpr::Instance:
127       break;
128     }
129
130     Expr *rec = E->getInstanceReceiver();
131     if (!rec) return true;
132
133     Transaction Trans(Pass.TA);
134     clearDiagnostics(rec->getExprLoc());
135
136     ObjCMessageExpr *Msg = E;
137     Expr *RecContainer = Msg;
138     SourceRange RecRange = rec->getSourceRange();
139     checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);
140
141     if (Msg->getMethodFamily() == OMF_release &&
142         isRemovable(RecContainer) && isInAtFinally(RecContainer)) {
143       // Change the -release to "receiver = nil" in a finally to avoid a leak
144       // when an exception is thrown.
145       Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
146       std::string str = " = ";
147       str += getNilString(Pass.Ctx);
148       Pass.TA.insertAfterToken(RecRange.getEnd(), str);
149       return true;
150     }
151
152     if (!hasSideEffects(rec, Pass.Ctx)) {
153       if (tryRemoving(RecContainer))
154         return true;
155     }
156     Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
157
158     return true;
159   }
160
161 private:
162   /// \brief Checks for idioms where an unused -autorelease is common.
163   ///
164   /// Currently only returns true for this idiom which is common in property
165   /// setters:
166   ///
167   ///   [backingValue autorelease];
168   ///   backingValue = [newValue retain]; // in general a +1 assign
169   ///
170   bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
171     Expr *Rec = E->getInstanceReceiver();
172     if (!Rec)
173       return false;
174
175     Decl *RefD = getReferencedDecl(Rec);
176     if (!RefD)
177       return false;
178
179     Stmt *OuterS = E, *InnerS;
180     do {
181       InnerS = OuterS;
182       OuterS = StmtMap->getParent(InnerS);
183     }
184     while (OuterS && (isa<ParenExpr>(OuterS) ||
185                       isa<CastExpr>(OuterS) ||
186                       isa<ExprWithCleanups>(OuterS)));
187     
188     if (!OuterS)
189       return false;
190
191     // Find next statement after the -autorelease.
192
193     Stmt::child_iterator currChildS = OuterS->child_begin();
194     Stmt::child_iterator childE = OuterS->child_end();
195     for (; currChildS != childE; ++currChildS) {
196       if (*currChildS == InnerS)
197         break;
198     }
199     if (currChildS == childE)
200       return false;
201     ++currChildS;
202     if (currChildS == childE)
203       return false;
204
205     Stmt *nextStmt = *currChildS;
206     if (!nextStmt)
207       return false;
208     nextStmt = nextStmt->IgnoreImplicit();
209
210     // Check for "RefD = [+1 retained object];".
211     
212     if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) {
213       if (RefD != getReferencedDecl(Bop->getLHS()))
214         return false;
215       if (isPlusOneAssign(Bop))
216         return true;
217     }
218     return false;
219   }
220
221   Decl *getReferencedDecl(Expr *E) {
222     if (!E)
223       return 0;
224
225     E = E->IgnoreParenCasts();
226     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
227       return DRE->getDecl();
228     if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
229       return ME->getMemberDecl();
230     if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
231       return IRE->getDecl();
232
233     return 0;
234   }
235
236   /// \brief Check if the retain/release is due to a GCD/XPC macro that are
237   /// defined as:
238   ///
239   /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
240   /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
241   /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
242   /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
243   ///
244   /// and return the top container which is the StmtExpr and the macro argument
245   /// expression.
246   void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer,
247                         Expr *&Rec, SourceRange &RecRange) {
248     SourceLocation Loc = Msg->getExprLoc();
249     if (!Loc.isMacroID())
250       return;
251     SourceManager &SM = Pass.Ctx.getSourceManager();
252     StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM,
253                                                      Pass.Ctx.getLangOpts());
254     bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)
255         .Case("dispatch_retain", true)
256         .Case("dispatch_release", true)
257         .Case("xpc_retain", true)
258         .Case("xpc_release", true)
259         .Default(false);
260     if (!isGCDOrXPC)
261       return;
262
263     StmtExpr *StmtE = 0;
264     Stmt *S = Msg;
265     while (S) {
266       if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) {
267         StmtE = SE;
268         break;
269       }
270       S = StmtMap->getParent(S);
271     }
272
273     if (!StmtE)
274       return;
275
276     Stmt::child_range StmtExprChild = StmtE->children();
277     if (!StmtExprChild)
278       return;
279     CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild);
280     if (!CompS)
281       return;
282
283     Stmt::child_range CompStmtChild = CompS->children();
284     if (!CompStmtChild)
285       return;
286     DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild);
287     if (!DeclS)
288       return;
289     if (!DeclS->isSingleDecl())
290       return;
291     VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl());
292     if (!VD)
293       return;
294     Expr *Init = VD->getInit();
295     if (!Init)
296       return;
297
298     RecContainer = StmtE;
299     Rec = Init->IgnoreParenImpCasts();
300     if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec))
301       Rec = EWC->getSubExpr()->IgnoreParenImpCasts();
302     RecRange = Rec->getSourceRange();
303     if (SM.isMacroArgExpansion(RecRange.getBegin()))
304       RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));
305     if (SM.isMacroArgExpansion(RecRange.getEnd()))
306       RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));
307   }
308
309   void clearDiagnostics(SourceLocation loc) const {
310     Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
311                             diag::err_unavailable,
312                             diag::err_unavailable_message,
313                             loc);
314   }
315
316   bool isDelegateMessage(Expr *E) const {
317     if (!E) return false;
318
319     E = E->IgnoreParenCasts();
320
321     // Also look through property-getter sugar.
322     if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))
323       E = pseudoOp->getResultExpr()->IgnoreImplicit();
324
325     if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
326       return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
327
328     return false;
329   }
330
331   bool isInAtFinally(Expr *E) const {
332     assert(E);
333     Stmt *S = E;
334     while (S) {
335       if (isa<ObjCAtFinallyStmt>(S))
336         return true;
337       S = StmtMap->getParent(S);
338     }
339
340     return false;
341   }
342
343   bool isRemovable(Expr *E) const {
344     return Removables.count(E);
345   }
346   
347   bool tryRemoving(Expr *E) const {
348     if (isRemovable(E)) {
349       Pass.TA.removeStmt(E);
350       return true;
351     }
352
353     Stmt *parent = StmtMap->getParent(E);
354
355     if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
356       return tryRemoving(castE);
357
358     if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
359       return tryRemoving(parenE);
360
361     if (BinaryOperator *
362           bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
363       if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
364           isRemovable(bopE)) {
365         Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
366         return true;
367       }
368     }
369
370     return false;
371   }
372
373 };
374
375 } // anonymous namespace
376
377 void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
378   BodyTransform<RetainReleaseDeallocRemover> trans(pass);
379   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
380 }