Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/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
16
  }
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
403
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
39
91
39
      // Check that we can handle/rewrite all references of the pool.
92
39
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
39
103
39
      // Even if one reference is not handled we will not do anything about that
104
39
      // pool variable.
105
39
      if (info.Refs.empty())
106
33
        VarsToHandle.push_back(var);
107
39
    }
108
403
109
436
    for (unsigned i = 0, e = VarsToHandle.size(); i != e; 
++i33
) {
110
33
      PoolVarInfo &info = PoolVars[VarsToHandle[i]];
111
33
112
33
      Transaction Trans(Pass.TA);
113
33
114
33
      clearUnavailableDiags(info.Dcl);
115
33
      Pass.TA.removeStmt(info.Dcl);
116
33
117
33
      // 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
2
          // 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
33
148
33
      // 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
453
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
39
              // Does this statement follow the pattern:
177
39
              // 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
80
            // Does this statement follow the pattern:
191
80
            // 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
898
203
898
      if (Scopes.empty())
204
775
        continue;
205
123
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() : PoolVar(nullptr), CompoundParent(nullptr), Begin(), End(),
233
44
                  IsFollowedBySimpleReturnStmt(false) { }
234
235
37
    SourceRange getIndentedRange() const {
236
37
      Stmt::child_iterator rangeS = Begin;
237
37
      ++rangeS;
238
37
      if (rangeS == End)
239
4
        return SourceRange();
240
33
      Stmt::child_iterator rangeE = Begin;
241
90
      for (Stmt::child_iterator I = rangeS; I != End; 
++I57
)
242
57
        ++rangeE;
243
33
      return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc());
244
33
    }
245
  };
246
247
  class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
248
    ASTContext &Ctx;
249
    SourceRange ScopeRange;
250
    SourceLocation &referenceLoc, &declarationLoc;
251
252
  public:
253
    NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
254
                         SourceLocation &referenceLoc,
255
                         SourceLocation &declarationLoc)
256
      : Ctx(ctx), referenceLoc(referenceLoc),
257
37
        declarationLoc(declarationLoc) {
258
37
      ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(),
259
37
                               (*scope.End)->getBeginLoc());
260
37
    }
261
262
23
    bool VisitDeclRefExpr(DeclRefExpr *E) {
263
23
      return checkRef(E->getLocation(), E->getDecl()->getLocation());
264
23
    }
265
266
1
    bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
267
1
      return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
268
1
    }
269
270
1
    bool VisitTagTypeLoc(TagTypeLoc TL) {
271
1
      return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
272
1
    }
273
274
  private:
275
25
    bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
276
25
      if (isInScope(declLoc)) {
277
4
        referenceLoc = refLoc;
278
4
        declarationLoc = declLoc;
279
4
        return false;
280
4
      }
281
21
      return true;
282
21
    }
283
284
25
    bool isInScope(SourceLocation loc) {
285
25
      if (loc.isInvalid())
286
2
        return false;
287
23
288
23
      SourceManager &SM = Ctx.getSourceManager();
289
23
      if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
290
17
        return false;
291
6
      return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
292
6
    }
293
  };
294
295
43
  void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
296
43
    // Check that all names declared inside the scope are not used
297
43
    // outside the scope.
298
43
    {
299
43
      bool nameUsedOutsideScope = false;
300
43
      SourceLocation referenceLoc, declarationLoc;
301
43
      Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
302
43
      ++SI;
303
43
      // Check if the autoreleasepool scope is followed by a simple return
304
43
      // statement, in which case we will include the return in the scope.
305
43
      if (SI != SE)
306
30
        if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
307
11
          if ((retS->getRetValue() == nullptr ||
308
11
               isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
309
11
              
findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()2
) {
310
2
            scope.IsFollowedBySimpleReturnStmt = true;
311
2
            ++SI; // the return will be included in scope, don't check it.
312
2
          }
313
43
314
76
      for (; SI != SE; 
++SI33
) {
315
37
        nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
316
37
                                                     referenceLoc,
317
37
                                              declarationLoc).TraverseStmt(*SI);
318
37
        if (nameUsedOutsideScope)
319
4
          break;
320
37
      }
321
43
322
43
      // If not all references were cleared it means some variables/typenames/etc
323
43
      // declared inside the pool scope are used outside of it.
324
43
      // We won't try to rewrite the pool.
325
43
      if (nameUsedOutsideScope) {
326
4
        Pass.TA.reportError("a name is referenced outside the "
327
4
            "NSAutoreleasePool scope that it was declared in", referenceLoc);
328
4
        Pass.TA.reportNote("name declared here", declarationLoc);
329
4
        Pass.TA.reportNote("intended @autoreleasepool scope begins here",
330
4
                           (*scope.Begin)->getBeginLoc());
331
4
        Pass.TA.reportNote("intended @autoreleasepool scope ends here",
332
4
                           (*scope.End)->getBeginLoc());
333
4
        return;
334
4
      }
335
39
    }
336
39
337
39
    // Collect all releases of the pool; they will be removed.
338
39
    {
339
39
      ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
340
39
      Stmt::child_iterator I = scope.Begin;
341
39
      ++I;
342
98
      for (; I != scope.End; 
++I59
)
343
59
        releaseColl.TraverseStmt(*I);
344
39
    }
345
39
346
39
    PoolVars[scope.PoolVar].Scopes.push_back(scope);
347
39
  }
348
349
47
  bool isPoolCreation(Expr *E) {
350
47
    if (!E) 
return false0
;
351
47
    E = getEssential(E);
352
47
    ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
353
47
    if (!ME) 
return false3
;
354
44
    if (ME->getMethodFamily() == OMF_new &&
355
44
        
ME->getReceiverKind() == ObjCMessageExpr::Class8
&&
356
44
        
isNSAutoreleasePool(ME->getReceiverInterface())8
)
357
8
      return true;
358
36
    if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
359
36
        ME->getMethodFamily() == OMF_init) {
360
36
      Expr *rec = getEssential(ME->getInstanceReceiver());
361
36
      if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
362
36
        if (recME->getMethodFamily() == OMF_alloc &&
363
36
            recME->getReceiverKind() == ObjCMessageExpr::Class &&
364
36
            isNSAutoreleasePool(recME->getReceiverInterface()))
365
36
          return true;
366
0
      }
367
36
    }
368
0
369
0
    return false;
370
0
  }
371
372
123
  bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
373
123
    if (!S) 
return false0
;
374
123
    S = getEssential(S);
375
123
    ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
376
123
    if (!ME) 
return false78
;
377
45
    if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
378
45
      Expr *rec = getEssential(ME->getInstanceReceiver());
379
45
      if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
380
43
        if (dref->getDecl() == poolVar)
381
43
          return ME->getMethodFamily() == OMF_release ||
382
43
                 
ME->getSelector() == DrainSel28
;
383
2
    }
384
2
385
2
    return false;
386
2
  }
387
388
151
  bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
389
151
    return IDecl && IDecl->getIdentifier() == PoolII;
390
151
  }
391
392
244
  bool isNSAutoreleasePool(QualType Ty) {
393
244
    QualType pointee = Ty->getPointeeType();
394
244
    if (pointee.isNull())
395
12
      return false;
396
232
    if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
397
107
      return isNSAutoreleasePool(interT->getDecl());
398
125
    return false;
399
125
  }
400
401
128
  static Expr *getEssential(Expr *E) {
402
128
    return cast<Expr>(getEssential((Stmt*)E));
403
128
  }
404
1.14k
  static Stmt *getEssential(Stmt *S) {
405
1.14k
    if (FullExpr *FE = dyn_cast<FullExpr>(S))
406
203
      S = FE->getSubExpr();
407
1.14k
    if (Expr *E = dyn_cast<Expr>(S))
408
666
      S = E->IgnoreParenCasts();
409
1.14k
    return S;
410
1.14k
  }
411
412
  Stmt *Body;
413
  MigrationPass &Pass;
414
415
  IdentifierInfo *PoolII;
416
  Selector DrainSel;
417
418
  struct PoolVarInfo {
419
    DeclStmt *Dcl;
420
    ExprSet Refs;
421
    SmallVector<PoolScope, 2> Scopes;
422
423
39
    PoolVarInfo() : Dcl(nullptr) { }
424
  };
425
426
  std::map<VarDecl *, PoolVarInfo> PoolVars;
427
};
428
429
} // anonymous namespace
430
431
85
void trans::rewriteAutoreleasePool(MigrationPass &pass) {
432
85
  BodyTransform<AutoreleasePoolRewriter> trans(pass);
433
85
  trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
434
85
}