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