xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/ARCMigrate/TransAutoreleasePool.cpp (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1*7330f729Sjoerg //===--- TransAutoreleasePool.cpp - Transformations to ARC mode -----------===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg //
9*7330f729Sjoerg // rewriteAutoreleasePool:
10*7330f729Sjoerg //
11*7330f729Sjoerg // Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
12*7330f729Sjoerg //
13*7330f729Sjoerg //  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
14*7330f729Sjoerg //  ...
15*7330f729Sjoerg //  [pool release];
16*7330f729Sjoerg // ---->
17*7330f729Sjoerg //  @autorelease {
18*7330f729Sjoerg //  ...
19*7330f729Sjoerg //  }
20*7330f729Sjoerg //
21*7330f729Sjoerg // An NSAutoreleasePool will not be touched if:
22*7330f729Sjoerg // - There is not a corresponding -release/-drain in the same scope
23*7330f729Sjoerg // - Not all references of the NSAutoreleasePool variable can be removed
24*7330f729Sjoerg // - There is a variable that is declared inside the intended @autorelease scope
25*7330f729Sjoerg //   which is also used outside it.
26*7330f729Sjoerg //
27*7330f729Sjoerg //===----------------------------------------------------------------------===//
28*7330f729Sjoerg 
29*7330f729Sjoerg #include "Transforms.h"
30*7330f729Sjoerg #include "Internals.h"
31*7330f729Sjoerg #include "clang/AST/ASTContext.h"
32*7330f729Sjoerg #include "clang/Basic/SourceManager.h"
33*7330f729Sjoerg #include "clang/Sema/SemaDiagnostic.h"
34*7330f729Sjoerg #include <map>
35*7330f729Sjoerg 
36*7330f729Sjoerg using namespace clang;
37*7330f729Sjoerg using namespace arcmt;
38*7330f729Sjoerg using namespace trans;
39*7330f729Sjoerg 
40*7330f729Sjoerg namespace {
41*7330f729Sjoerg 
42*7330f729Sjoerg class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
43*7330f729Sjoerg   Decl *Dcl;
44*7330f729Sjoerg   SmallVectorImpl<ObjCMessageExpr *> &Releases;
45*7330f729Sjoerg 
46*7330f729Sjoerg public:
ReleaseCollector(Decl * D,SmallVectorImpl<ObjCMessageExpr * > & releases)47*7330f729Sjoerg   ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
48*7330f729Sjoerg     : Dcl(D), Releases(releases) { }
49*7330f729Sjoerg 
VisitObjCMessageExpr(ObjCMessageExpr * E)50*7330f729Sjoerg   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
51*7330f729Sjoerg     if (!E->isInstanceMessage())
52*7330f729Sjoerg       return true;
53*7330f729Sjoerg     if (E->getMethodFamily() != OMF_release)
54*7330f729Sjoerg       return true;
55*7330f729Sjoerg     Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
56*7330f729Sjoerg     if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
57*7330f729Sjoerg       if (DE->getDecl() == Dcl)
58*7330f729Sjoerg         Releases.push_back(E);
59*7330f729Sjoerg     }
60*7330f729Sjoerg     return true;
61*7330f729Sjoerg   }
62*7330f729Sjoerg };
63*7330f729Sjoerg 
64*7330f729Sjoerg }
65*7330f729Sjoerg 
66*7330f729Sjoerg namespace {
67*7330f729Sjoerg 
68*7330f729Sjoerg class AutoreleasePoolRewriter
69*7330f729Sjoerg                          : public RecursiveASTVisitor<AutoreleasePoolRewriter> {
70*7330f729Sjoerg public:
AutoreleasePoolRewriter(MigrationPass & pass)71*7330f729Sjoerg   AutoreleasePoolRewriter(MigrationPass &pass)
72*7330f729Sjoerg     : Body(nullptr), Pass(pass) {
73*7330f729Sjoerg     PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
74*7330f729Sjoerg     DrainSel = pass.Ctx.Selectors.getNullarySelector(
75*7330f729Sjoerg                                                  &pass.Ctx.Idents.get("drain"));
76*7330f729Sjoerg   }
77*7330f729Sjoerg 
transformBody(Stmt * body,Decl * ParentD)78*7330f729Sjoerg   void transformBody(Stmt *body, Decl *ParentD) {
79*7330f729Sjoerg     Body = body;
80*7330f729Sjoerg     TraverseStmt(body);
81*7330f729Sjoerg   }
82*7330f729Sjoerg 
~AutoreleasePoolRewriter()83*7330f729Sjoerg   ~AutoreleasePoolRewriter() {
84*7330f729Sjoerg     SmallVector<VarDecl *, 8> VarsToHandle;
85*7330f729Sjoerg 
86*7330f729Sjoerg     for (std::map<VarDecl *, PoolVarInfo>::iterator
87*7330f729Sjoerg            I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
88*7330f729Sjoerg       VarDecl *var = I->first;
89*7330f729Sjoerg       PoolVarInfo &info = I->second;
90*7330f729Sjoerg 
91*7330f729Sjoerg       // Check that we can handle/rewrite all references of the pool.
92*7330f729Sjoerg 
93*7330f729Sjoerg       clearRefsIn(info.Dcl, info.Refs);
94*7330f729Sjoerg       for (SmallVectorImpl<PoolScope>::iterator
95*7330f729Sjoerg              scpI = info.Scopes.begin(),
96*7330f729Sjoerg              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
97*7330f729Sjoerg         PoolScope &scope = *scpI;
98*7330f729Sjoerg         clearRefsIn(*scope.Begin, info.Refs);
99*7330f729Sjoerg         clearRefsIn(*scope.End, info.Refs);
100*7330f729Sjoerg         clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
101*7330f729Sjoerg       }
102*7330f729Sjoerg 
103*7330f729Sjoerg       // Even if one reference is not handled we will not do anything about that
104*7330f729Sjoerg       // pool variable.
105*7330f729Sjoerg       if (info.Refs.empty())
106*7330f729Sjoerg         VarsToHandle.push_back(var);
107*7330f729Sjoerg     }
108*7330f729Sjoerg 
109*7330f729Sjoerg     for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
110*7330f729Sjoerg       PoolVarInfo &info = PoolVars[VarsToHandle[i]];
111*7330f729Sjoerg 
112*7330f729Sjoerg       Transaction Trans(Pass.TA);
113*7330f729Sjoerg 
114*7330f729Sjoerg       clearUnavailableDiags(info.Dcl);
115*7330f729Sjoerg       Pass.TA.removeStmt(info.Dcl);
116*7330f729Sjoerg 
117*7330f729Sjoerg       // Add "@autoreleasepool { }"
118*7330f729Sjoerg       for (SmallVectorImpl<PoolScope>::iterator
119*7330f729Sjoerg              scpI = info.Scopes.begin(),
120*7330f729Sjoerg              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
121*7330f729Sjoerg         PoolScope &scope = *scpI;
122*7330f729Sjoerg         clearUnavailableDiags(*scope.Begin);
123*7330f729Sjoerg         clearUnavailableDiags(*scope.End);
124*7330f729Sjoerg         if (scope.IsFollowedBySimpleReturnStmt) {
125*7330f729Sjoerg           // Include the return in the scope.
126*7330f729Sjoerg           Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
127*7330f729Sjoerg           Pass.TA.removeStmt(*scope.End);
128*7330f729Sjoerg           Stmt::child_iterator retI = scope.End;
129*7330f729Sjoerg           ++retI;
130*7330f729Sjoerg           SourceLocation afterSemi =
131*7330f729Sjoerg               findLocationAfterSemi((*retI)->getEndLoc(), Pass.Ctx);
132*7330f729Sjoerg           assert(afterSemi.isValid() &&
133*7330f729Sjoerg                  "Didn't we check before setting IsFollowedBySimpleReturnStmt "
134*7330f729Sjoerg                  "to true?");
135*7330f729Sjoerg           Pass.TA.insertAfterToken(afterSemi, "\n}");
136*7330f729Sjoerg           Pass.TA.increaseIndentation(
137*7330f729Sjoerg               SourceRange(scope.getIndentedRange().getBegin(),
138*7330f729Sjoerg                           (*retI)->getEndLoc()),
139*7330f729Sjoerg               scope.CompoundParent->getBeginLoc());
140*7330f729Sjoerg         } else {
141*7330f729Sjoerg           Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
142*7330f729Sjoerg           Pass.TA.replaceStmt(*scope.End, "}");
143*7330f729Sjoerg           Pass.TA.increaseIndentation(scope.getIndentedRange(),
144*7330f729Sjoerg                                       scope.CompoundParent->getBeginLoc());
145*7330f729Sjoerg         }
146*7330f729Sjoerg       }
147*7330f729Sjoerg 
148*7330f729Sjoerg       // Remove rest of pool var references.
149*7330f729Sjoerg       for (SmallVectorImpl<PoolScope>::iterator
150*7330f729Sjoerg              scpI = info.Scopes.begin(),
151*7330f729Sjoerg              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
152*7330f729Sjoerg         PoolScope &scope = *scpI;
153*7330f729Sjoerg         for (SmallVectorImpl<ObjCMessageExpr *>::iterator
154*7330f729Sjoerg                relI = scope.Releases.begin(),
155*7330f729Sjoerg                relE = scope.Releases.end(); relI != relE; ++relI) {
156*7330f729Sjoerg           clearUnavailableDiags(*relI);
157*7330f729Sjoerg           Pass.TA.removeStmt(*relI);
158*7330f729Sjoerg         }
159*7330f729Sjoerg       }
160*7330f729Sjoerg     }
161*7330f729Sjoerg   }
162*7330f729Sjoerg 
VisitCompoundStmt(CompoundStmt * S)163*7330f729Sjoerg   bool VisitCompoundStmt(CompoundStmt *S) {
164*7330f729Sjoerg     SmallVector<PoolScope, 4> Scopes;
165*7330f729Sjoerg 
166*7330f729Sjoerg     for (Stmt::child_iterator
167*7330f729Sjoerg            I = S->body_begin(), E = S->body_end(); I != E; ++I) {
168*7330f729Sjoerg       Stmt *child = getEssential(*I);
169*7330f729Sjoerg       if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
170*7330f729Sjoerg         if (DclS->isSingleDecl()) {
171*7330f729Sjoerg           if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
172*7330f729Sjoerg             if (isNSAutoreleasePool(VD->getType())) {
173*7330f729Sjoerg               PoolVarInfo &info = PoolVars[VD];
174*7330f729Sjoerg               info.Dcl = DclS;
175*7330f729Sjoerg               collectRefs(VD, S, info.Refs);
176*7330f729Sjoerg               // Does this statement follow the pattern:
177*7330f729Sjoerg               // NSAutoreleasePool * pool = [NSAutoreleasePool  new];
178*7330f729Sjoerg               if (isPoolCreation(VD->getInit())) {
179*7330f729Sjoerg                 Scopes.push_back(PoolScope());
180*7330f729Sjoerg                 Scopes.back().PoolVar = VD;
181*7330f729Sjoerg                 Scopes.back().CompoundParent = S;
182*7330f729Sjoerg                 Scopes.back().Begin = I;
183*7330f729Sjoerg               }
184*7330f729Sjoerg             }
185*7330f729Sjoerg           }
186*7330f729Sjoerg         }
187*7330f729Sjoerg       } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
188*7330f729Sjoerg         if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
189*7330f729Sjoerg           if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
190*7330f729Sjoerg             // Does this statement follow the pattern:
191*7330f729Sjoerg             // pool = [NSAutoreleasePool  new];
192*7330f729Sjoerg             if (isNSAutoreleasePool(VD->getType()) &&
193*7330f729Sjoerg                 isPoolCreation(bop->getRHS())) {
194*7330f729Sjoerg               Scopes.push_back(PoolScope());
195*7330f729Sjoerg               Scopes.back().PoolVar = VD;
196*7330f729Sjoerg               Scopes.back().CompoundParent = S;
197*7330f729Sjoerg               Scopes.back().Begin = I;
198*7330f729Sjoerg             }
199*7330f729Sjoerg           }
200*7330f729Sjoerg         }
201*7330f729Sjoerg       }
202*7330f729Sjoerg 
203*7330f729Sjoerg       if (Scopes.empty())
204*7330f729Sjoerg         continue;
205*7330f729Sjoerg 
206*7330f729Sjoerg       if (isPoolDrain(Scopes.back().PoolVar, child)) {
207*7330f729Sjoerg         PoolScope &scope = Scopes.back();
208*7330f729Sjoerg         scope.End = I;
209*7330f729Sjoerg         handlePoolScope(scope, S);
210*7330f729Sjoerg         Scopes.pop_back();
211*7330f729Sjoerg       }
212*7330f729Sjoerg     }
213*7330f729Sjoerg     return true;
214*7330f729Sjoerg   }
215*7330f729Sjoerg 
216*7330f729Sjoerg private:
clearUnavailableDiags(Stmt * S)217*7330f729Sjoerg   void clearUnavailableDiags(Stmt *S) {
218*7330f729Sjoerg     if (S)
219*7330f729Sjoerg       Pass.TA.clearDiagnostic(diag::err_unavailable,
220*7330f729Sjoerg                               diag::err_unavailable_message,
221*7330f729Sjoerg                               S->getSourceRange());
222*7330f729Sjoerg   }
223*7330f729Sjoerg 
224*7330f729Sjoerg   struct PoolScope {
225*7330f729Sjoerg     VarDecl *PoolVar;
226*7330f729Sjoerg     CompoundStmt *CompoundParent;
227*7330f729Sjoerg     Stmt::child_iterator Begin;
228*7330f729Sjoerg     Stmt::child_iterator End;
229*7330f729Sjoerg     bool IsFollowedBySimpleReturnStmt;
230*7330f729Sjoerg     SmallVector<ObjCMessageExpr *, 4> Releases;
231*7330f729Sjoerg 
PoolScope__anon70d3f9af0211::AutoreleasePoolRewriter::PoolScope232*7330f729Sjoerg     PoolScope() : PoolVar(nullptr), CompoundParent(nullptr), Begin(), End(),
233*7330f729Sjoerg                   IsFollowedBySimpleReturnStmt(false) { }
234*7330f729Sjoerg 
getIndentedRange__anon70d3f9af0211::AutoreleasePoolRewriter::PoolScope235*7330f729Sjoerg     SourceRange getIndentedRange() const {
236*7330f729Sjoerg       Stmt::child_iterator rangeS = Begin;
237*7330f729Sjoerg       ++rangeS;
238*7330f729Sjoerg       if (rangeS == End)
239*7330f729Sjoerg         return SourceRange();
240*7330f729Sjoerg       Stmt::child_iterator rangeE = Begin;
241*7330f729Sjoerg       for (Stmt::child_iterator I = rangeS; I != End; ++I)
242*7330f729Sjoerg         ++rangeE;
243*7330f729Sjoerg       return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc());
244*7330f729Sjoerg     }
245*7330f729Sjoerg   };
246*7330f729Sjoerg 
247*7330f729Sjoerg   class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
248*7330f729Sjoerg     ASTContext &Ctx;
249*7330f729Sjoerg     SourceRange ScopeRange;
250*7330f729Sjoerg     SourceLocation &referenceLoc, &declarationLoc;
251*7330f729Sjoerg 
252*7330f729Sjoerg   public:
NameReferenceChecker(ASTContext & ctx,PoolScope & scope,SourceLocation & referenceLoc,SourceLocation & declarationLoc)253*7330f729Sjoerg     NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
254*7330f729Sjoerg                          SourceLocation &referenceLoc,
255*7330f729Sjoerg                          SourceLocation &declarationLoc)
256*7330f729Sjoerg       : Ctx(ctx), referenceLoc(referenceLoc),
257*7330f729Sjoerg         declarationLoc(declarationLoc) {
258*7330f729Sjoerg       ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(),
259*7330f729Sjoerg                                (*scope.End)->getBeginLoc());
260*7330f729Sjoerg     }
261*7330f729Sjoerg 
VisitDeclRefExpr(DeclRefExpr * E)262*7330f729Sjoerg     bool VisitDeclRefExpr(DeclRefExpr *E) {
263*7330f729Sjoerg       return checkRef(E->getLocation(), E->getDecl()->getLocation());
264*7330f729Sjoerg     }
265*7330f729Sjoerg 
VisitTypedefTypeLoc(TypedefTypeLoc TL)266*7330f729Sjoerg     bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
267*7330f729Sjoerg       return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
268*7330f729Sjoerg     }
269*7330f729Sjoerg 
VisitTagTypeLoc(TagTypeLoc TL)270*7330f729Sjoerg     bool VisitTagTypeLoc(TagTypeLoc TL) {
271*7330f729Sjoerg       return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
272*7330f729Sjoerg     }
273*7330f729Sjoerg 
274*7330f729Sjoerg   private:
checkRef(SourceLocation refLoc,SourceLocation declLoc)275*7330f729Sjoerg     bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
276*7330f729Sjoerg       if (isInScope(declLoc)) {
277*7330f729Sjoerg         referenceLoc = refLoc;
278*7330f729Sjoerg         declarationLoc = declLoc;
279*7330f729Sjoerg         return false;
280*7330f729Sjoerg       }
281*7330f729Sjoerg       return true;
282*7330f729Sjoerg     }
283*7330f729Sjoerg 
isInScope(SourceLocation loc)284*7330f729Sjoerg     bool isInScope(SourceLocation loc) {
285*7330f729Sjoerg       if (loc.isInvalid())
286*7330f729Sjoerg         return false;
287*7330f729Sjoerg 
288*7330f729Sjoerg       SourceManager &SM = Ctx.getSourceManager();
289*7330f729Sjoerg       if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
290*7330f729Sjoerg         return false;
291*7330f729Sjoerg       return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
292*7330f729Sjoerg     }
293*7330f729Sjoerg   };
294*7330f729Sjoerg 
handlePoolScope(PoolScope & scope,CompoundStmt * compoundS)295*7330f729Sjoerg   void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
296*7330f729Sjoerg     // Check that all names declared inside the scope are not used
297*7330f729Sjoerg     // outside the scope.
298*7330f729Sjoerg     {
299*7330f729Sjoerg       bool nameUsedOutsideScope = false;
300*7330f729Sjoerg       SourceLocation referenceLoc, declarationLoc;
301*7330f729Sjoerg       Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
302*7330f729Sjoerg       ++SI;
303*7330f729Sjoerg       // Check if the autoreleasepool scope is followed by a simple return
304*7330f729Sjoerg       // statement, in which case we will include the return in the scope.
305*7330f729Sjoerg       if (SI != SE)
306*7330f729Sjoerg         if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
307*7330f729Sjoerg           if ((retS->getRetValue() == nullptr ||
308*7330f729Sjoerg                isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
309*7330f729Sjoerg               findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()) {
310*7330f729Sjoerg             scope.IsFollowedBySimpleReturnStmt = true;
311*7330f729Sjoerg             ++SI; // the return will be included in scope, don't check it.
312*7330f729Sjoerg           }
313*7330f729Sjoerg 
314*7330f729Sjoerg       for (; SI != SE; ++SI) {
315*7330f729Sjoerg         nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
316*7330f729Sjoerg                                                      referenceLoc,
317*7330f729Sjoerg                                               declarationLoc).TraverseStmt(*SI);
318*7330f729Sjoerg         if (nameUsedOutsideScope)
319*7330f729Sjoerg           break;
320*7330f729Sjoerg       }
321*7330f729Sjoerg 
322*7330f729Sjoerg       // If not all references were cleared it means some variables/typenames/etc
323*7330f729Sjoerg       // declared inside the pool scope are used outside of it.
324*7330f729Sjoerg       // We won't try to rewrite the pool.
325*7330f729Sjoerg       if (nameUsedOutsideScope) {
326*7330f729Sjoerg         Pass.TA.reportError("a name is referenced outside the "
327*7330f729Sjoerg             "NSAutoreleasePool scope that it was declared in", referenceLoc);
328*7330f729Sjoerg         Pass.TA.reportNote("name declared here", declarationLoc);
329*7330f729Sjoerg         Pass.TA.reportNote("intended @autoreleasepool scope begins here",
330*7330f729Sjoerg                            (*scope.Begin)->getBeginLoc());
331*7330f729Sjoerg         Pass.TA.reportNote("intended @autoreleasepool scope ends here",
332*7330f729Sjoerg                            (*scope.End)->getBeginLoc());
333*7330f729Sjoerg         return;
334*7330f729Sjoerg       }
335*7330f729Sjoerg     }
336*7330f729Sjoerg 
337*7330f729Sjoerg     // Collect all releases of the pool; they will be removed.
338*7330f729Sjoerg     {
339*7330f729Sjoerg       ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
340*7330f729Sjoerg       Stmt::child_iterator I = scope.Begin;
341*7330f729Sjoerg       ++I;
342*7330f729Sjoerg       for (; I != scope.End; ++I)
343*7330f729Sjoerg         releaseColl.TraverseStmt(*I);
344*7330f729Sjoerg     }
345*7330f729Sjoerg 
346*7330f729Sjoerg     PoolVars[scope.PoolVar].Scopes.push_back(scope);
347*7330f729Sjoerg   }
348*7330f729Sjoerg 
isPoolCreation(Expr * E)349*7330f729Sjoerg   bool isPoolCreation(Expr *E) {
350*7330f729Sjoerg     if (!E) return false;
351*7330f729Sjoerg     E = getEssential(E);
352*7330f729Sjoerg     ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
353*7330f729Sjoerg     if (!ME) return false;
354*7330f729Sjoerg     if (ME->getMethodFamily() == OMF_new &&
355*7330f729Sjoerg         ME->getReceiverKind() == ObjCMessageExpr::Class &&
356*7330f729Sjoerg         isNSAutoreleasePool(ME->getReceiverInterface()))
357*7330f729Sjoerg       return true;
358*7330f729Sjoerg     if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
359*7330f729Sjoerg         ME->getMethodFamily() == OMF_init) {
360*7330f729Sjoerg       Expr *rec = getEssential(ME->getInstanceReceiver());
361*7330f729Sjoerg       if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
362*7330f729Sjoerg         if (recME->getMethodFamily() == OMF_alloc &&
363*7330f729Sjoerg             recME->getReceiverKind() == ObjCMessageExpr::Class &&
364*7330f729Sjoerg             isNSAutoreleasePool(recME->getReceiverInterface()))
365*7330f729Sjoerg           return true;
366*7330f729Sjoerg       }
367*7330f729Sjoerg     }
368*7330f729Sjoerg 
369*7330f729Sjoerg     return false;
370*7330f729Sjoerg   }
371*7330f729Sjoerg 
isPoolDrain(VarDecl * poolVar,Stmt * S)372*7330f729Sjoerg   bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
373*7330f729Sjoerg     if (!S) return false;
374*7330f729Sjoerg     S = getEssential(S);
375*7330f729Sjoerg     ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
376*7330f729Sjoerg     if (!ME) return false;
377*7330f729Sjoerg     if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
378*7330f729Sjoerg       Expr *rec = getEssential(ME->getInstanceReceiver());
379*7330f729Sjoerg       if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
380*7330f729Sjoerg         if (dref->getDecl() == poolVar)
381*7330f729Sjoerg           return ME->getMethodFamily() == OMF_release ||
382*7330f729Sjoerg                  ME->getSelector() == DrainSel;
383*7330f729Sjoerg     }
384*7330f729Sjoerg 
385*7330f729Sjoerg     return false;
386*7330f729Sjoerg   }
387*7330f729Sjoerg 
isNSAutoreleasePool(ObjCInterfaceDecl * IDecl)388*7330f729Sjoerg   bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
389*7330f729Sjoerg     return IDecl && IDecl->getIdentifier() == PoolII;
390*7330f729Sjoerg   }
391*7330f729Sjoerg 
isNSAutoreleasePool(QualType Ty)392*7330f729Sjoerg   bool isNSAutoreleasePool(QualType Ty) {
393*7330f729Sjoerg     QualType pointee = Ty->getPointeeType();
394*7330f729Sjoerg     if (pointee.isNull())
395*7330f729Sjoerg       return false;
396*7330f729Sjoerg     if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
397*7330f729Sjoerg       return isNSAutoreleasePool(interT->getDecl());
398*7330f729Sjoerg     return false;
399*7330f729Sjoerg   }
400*7330f729Sjoerg 
getEssential(Expr * E)401*7330f729Sjoerg   static Expr *getEssential(Expr *E) {
402*7330f729Sjoerg     return cast<Expr>(getEssential((Stmt*)E));
403*7330f729Sjoerg   }
getEssential(Stmt * S)404*7330f729Sjoerg   static Stmt *getEssential(Stmt *S) {
405*7330f729Sjoerg     if (FullExpr *FE = dyn_cast<FullExpr>(S))
406*7330f729Sjoerg       S = FE->getSubExpr();
407*7330f729Sjoerg     if (Expr *E = dyn_cast<Expr>(S))
408*7330f729Sjoerg       S = E->IgnoreParenCasts();
409*7330f729Sjoerg     return S;
410*7330f729Sjoerg   }
411*7330f729Sjoerg 
412*7330f729Sjoerg   Stmt *Body;
413*7330f729Sjoerg   MigrationPass &Pass;
414*7330f729Sjoerg 
415*7330f729Sjoerg   IdentifierInfo *PoolII;
416*7330f729Sjoerg   Selector DrainSel;
417*7330f729Sjoerg 
418*7330f729Sjoerg   struct PoolVarInfo {
419*7330f729Sjoerg     DeclStmt *Dcl;
420*7330f729Sjoerg     ExprSet Refs;
421*7330f729Sjoerg     SmallVector<PoolScope, 2> Scopes;
422*7330f729Sjoerg 
PoolVarInfo__anon70d3f9af0211::AutoreleasePoolRewriter::PoolVarInfo423*7330f729Sjoerg     PoolVarInfo() : Dcl(nullptr) { }
424*7330f729Sjoerg   };
425*7330f729Sjoerg 
426*7330f729Sjoerg   std::map<VarDecl *, PoolVarInfo> PoolVars;
427*7330f729Sjoerg };
428*7330f729Sjoerg 
429*7330f729Sjoerg } // anonymous namespace
430*7330f729Sjoerg 
rewriteAutoreleasePool(MigrationPass & pass)431*7330f729Sjoerg void trans::rewriteAutoreleasePool(MigrationPass &pass) {
432*7330f729Sjoerg   BodyTransform<AutoreleasePoolRewriter> trans(pass);
433*7330f729Sjoerg   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
434*7330f729Sjoerg }
435