Coverage Report

Created: 2020-02-25 14:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ARCMigrate/ARCMT.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- ARCMT.cpp - Migration 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
#include "Internals.h"
10
#include "clang/ARCMigrate/ARCMT.h"
11
#include "clang/AST/ASTConsumer.h"
12
#include "clang/Basic/DiagnosticCategories.h"
13
#include "clang/Frontend/ASTUnit.h"
14
#include "clang/Frontend/CompilerInstance.h"
15
#include "clang/Frontend/FrontendAction.h"
16
#include "clang/Frontend/TextDiagnosticPrinter.h"
17
#include "clang/Frontend/Utils.h"
18
#include "clang/Lex/Preprocessor.h"
19
#include "clang/Lex/PreprocessorOptions.h"
20
#include "clang/Rewrite/Core/Rewriter.h"
21
#include "clang/Sema/SemaDiagnostic.h"
22
#include "clang/Serialization/ASTReader.h"
23
#include "llvm/ADT/Triple.h"
24
#include "llvm/Support/MemoryBuffer.h"
25
#include <utility>
26
using namespace clang;
27
using namespace arcmt;
28
29
bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
30
484
                                       SourceRange range) {
31
484
  if (range.isInvalid())
32
0
    return false;
33
484
34
484
  bool cleared = false;
35
484
  ListTy::iterator I = List.begin();
36
8.88k
  while (I != List.end()) {
37
8.40k
    FullSourceLoc diagLoc = I->getLocation();
38
8.40k
    if ((IDs.empty() || // empty means clear all diagnostics in the range.
39
8.40k
         llvm::is_contained(IDs, I->getID())) &&
40
8.40k
        
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin())3.63k
&&
41
8.40k
        
(3.30k
diagLoc == range.getEnd()3.30k
||
42
3.30k
         
diagLoc.isBeforeInTranslationUnitThan(range.getEnd())2.74k
)) {
43
621
      cleared = true;
44
621
      ListTy::iterator eraseS = I++;
45
621
      if (eraseS->getLevel() != DiagnosticsEngine::Note)
46
970
        
while (596
I != List.end() &&
I->getLevel() == DiagnosticsEngine::Note884
)
47
374
          ++I;
48
621
      // Clear the diagnostic and any notes following it.
49
621
      I = List.erase(eraseS, I);
50
621
      continue;
51
621
    }
52
7.78k
53
7.78k
    ++I;
54
7.78k
  }
55
484
56
484
  return cleared;
57
484
}
58
59
bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
60
810
                                     SourceRange range) const {
61
810
  if (range.isInvalid())
62
0
    return false;
63
810
64
810
  ListTy::const_iterator I = List.begin();
65
5.55k
  while (I != List.end()) {
66
5.29k
    FullSourceLoc diagLoc = I->getLocation();
67
5.29k
    if ((IDs.empty() || // empty means any diagnostic in the range.
68
5.29k
         llvm::find(IDs, I->getID()) != IDs.end()) &&
69
5.29k
        
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin())1.33k
&&
70
5.29k
        
(876
diagLoc == range.getEnd()876
||
71
876
         
diagLoc.isBeforeInTranslationUnitThan(range.getEnd())382
)) {
72
550
      return true;
73
550
    }
74
4.74k
75
4.74k
    ++I;
76
4.74k
  }
77
810
78
810
  
return false260
;
79
810
}
80
81
51
void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
82
201
  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; 
++I150
)
83
150
    Diags.Report(*I);
84
51
}
85
86
88
bool CapturedDiagList::hasErrors() const {
87
88
  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; 
++I0
)
88
43
    if (I->getLevel() >= DiagnosticsEngine::Error)
89
43
      return true;
90
88
91
88
  
return false45
;
92
88
}
93
94
namespace {
95
96
class CaptureDiagnosticConsumer : public DiagnosticConsumer {
97
  DiagnosticsEngine &Diags;
98
  DiagnosticConsumer &DiagClient;
99
  CapturedDiagList &CapturedDiags;
100
  bool HasBegunSourceFile;
101
public:
102
  CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
103
                            DiagnosticConsumer &client,
104
                            CapturedDiagList &capturedDiags)
105
    : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
106
122
      HasBegunSourceFile(false) { }
107
108
  void BeginSourceFile(const LangOptions &Opts,
109
122
                       const Preprocessor *PP) override {
110
122
    // Pass BeginSourceFile message onto DiagClient on first call.
111
122
    // The corresponding EndSourceFile call will be made from an
112
122
    // explicit call to FinishCapture.
113
122
    if (!HasBegunSourceFile) {
114
122
      DiagClient.BeginSourceFile(Opts, PP);
115
122
      HasBegunSourceFile = true;
116
122
    }
117
122
  }
118
119
122
  void FinishCapture() {
120
122
    // Call EndSourceFile on DiagClient on completion of capture to
121
122
    // enable VerifyDiagnosticConsumer to check diagnostics *after*
122
122
    // it has received the diagnostic list.
123
122
    if (HasBegunSourceFile) {
124
122
      DiagClient.EndSourceFile();
125
122
      HasBegunSourceFile = false;
126
122
    }
127
122
  }
128
129
122
  ~CaptureDiagnosticConsumer() override {
130
122
    assert(!HasBegunSourceFile && "FinishCapture not called!");
131
122
  }
132
133
  void HandleDiagnostic(DiagnosticsEngine::Level level,
134
1.26k
                        const Diagnostic &Info) override {
135
1.26k
    if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
136
1.26k
        
level >= DiagnosticsEngine::Error590
||
level == DiagnosticsEngine::Note367
) {
137
1.18k
      if (Info.getLocation().isValid())
138
1.18k
        CapturedDiags.push_back(StoredDiagnostic(level, Info));
139
1.18k
      return;
140
1.18k
    }
141
82
142
82
    // Non-ARC warnings are ignored.
143
82
    Diags.setLastDiagnosticIgnored(true);
144
82
  }
145
};
146
147
} // end anonymous namespace
148
149
122
static bool HasARCRuntime(CompilerInvocation &origCI) {
150
122
  // This duplicates some functionality from Darwin::AddDeploymentTarget
151
122
  // but this function is well defined, so keep it decoupled from the driver
152
122
  // and avoid unrelated complications.
153
122
  llvm::Triple triple(origCI.getTargetOpts().Triple);
154
122
155
122
  if (triple.isiOS())
156
0
    return triple.getOSMajorVersion() >= 5;
157
122
158
122
  if (triple.isWatchOS())
159
0
    return true;
160
122
161
122
  if (triple.getOS() == llvm::Triple::Darwin)
162
94
    return triple.getOSMajorVersion() >= 11;
163
28
164
28
  if (triple.getOS() == llvm::Triple::MacOSX) {
165
28
    unsigned Major, Minor, Micro;
166
28
    triple.getOSVersion(Major, Minor, Micro);
167
28
    return Major > 10 || (Major == 10 && Minor >= 7);
168
28
  }
169
0
170
0
  return false;
171
0
}
172
173
static CompilerInvocation *
174
createInvocationForMigration(CompilerInvocation &origCI,
175
122
                             const PCHContainerReader &PCHContainerRdr) {
176
122
  std::unique_ptr<CompilerInvocation> CInvok;
177
122
  CInvok.reset(new CompilerInvocation(origCI));
178
122
  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
179
122
  if (!PPOpts.ImplicitPCHInclude.empty()) {
180
7
    // We can't use a PCH because it was likely built in non-ARC mode and we
181
7
    // want to parse in ARC. Include the original header.
182
7
    FileManager FileMgr(origCI.getFileSystemOpts());
183
7
    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
184
7
    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
185
7
        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
186
7
                              new IgnoringDiagConsumer()));
187
7
    std::string OriginalFile = ASTReader::getOriginalSourceFile(
188
7
        PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
189
7
    if (!OriginalFile.empty())
190
7
      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
191
7
    PPOpts.ImplicitPCHInclude.clear();
192
7
  }
193
122
  std::string define = std::string(getARCMTMacroName());
194
122
  define += '=';
195
122
  CInvok->getPreprocessorOpts().addMacroDef(define);
196
122
  CInvok->getLangOpts()->ObjCAutoRefCount = true;
197
122
  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
198
122
  CInvok->getDiagnosticOpts().ErrorLimit = 0;
199
122
  CInvok->getDiagnosticOpts().PedanticErrors = 0;
200
122
201
122
  // Ignore -Werror flags when migrating.
202
122
  std::vector<std::string> WarnOpts;
203
122
  for (std::vector<std::string>::iterator
204
122
         I = CInvok->getDiagnosticOpts().Warnings.begin(),
205
125
         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; 
++I3
) {
206
3
    if (!StringRef(*I).startswith("error"))
207
0
      WarnOpts.push_back(*I);
208
3
  }
209
122
  WarnOpts.push_back("error=arc-unsafe-retained-assign");
210
122
  CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
211
122
212
122
  CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI);
213
122
  CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime;
214
122
215
122
  return CInvok.release();
216
122
}
217
218
static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
219
                                   DiagnosticOptions *diagOpts,
220
1
                                   Preprocessor &PP) {
221
1
  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
222
1
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
223
1
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
224
1
      new DiagnosticsEngine(DiagID, diagOpts, &printer,
225
1
                            /*ShouldOwnClient=*/false));
226
1
  Diags->setSourceManager(&PP.getSourceManager());
227
1
228
1
  printer.BeginSourceFile(PP.getLangOpts(), &PP);
229
1
  arcDiags.reportDiagnostics(*Diags);
230
1
  printer.EndSourceFile();
231
1
}
232
233
//===----------------------------------------------------------------------===//
234
// checkForManualIssues.
235
//===----------------------------------------------------------------------===//
236
237
bool arcmt::checkForManualIssues(
238
    CompilerInvocation &origCI, const FrontendInputFile &Input,
239
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
240
    DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
241
51
    StringRef plistOut) {
242
51
  if (!origCI.getLangOpts()->ObjC)
243
1
    return false;
244
50
245
50
  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
246
50
  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
247
50
  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
248
50
249
50
  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
250
50
                                                                     NoFinalizeRemoval);
251
50
  assert(!transforms.empty());
252
50
253
50
  std::unique_ptr<CompilerInvocation> CInvok;
254
50
  CInvok.reset(
255
50
      createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
256
50
  CInvok->getFrontendOpts().Inputs.clear();
257
50
  CInvok->getFrontendOpts().Inputs.push_back(Input);
258
50
259
50
  CapturedDiagList capturedDiags;
260
50
261
50
  assert(DiagClient);
262
50
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
263
50
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
264
50
      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
265
50
                            DiagClient, /*ShouldOwnClient=*/false));
266
50
267
50
  // Filter of all diagnostics.
268
50
  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
269
50
  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
270
50
271
50
  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
272
50
      std::move(CInvok), PCHContainerOps, Diags));
273
50
  if (!Unit) {
274
0
    errRec.FinishCapture();
275
0
    return true;
276
0
  }
277
50
278
50
  // Don't filter diagnostics anymore.
279
50
  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
280
50
281
50
  ASTContext &Ctx = Unit->getASTContext();
282
50
283
50
  if (Diags->hasFatalErrorOccurred()) {
284
0
    Diags->Reset();
285
0
    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
286
0
    capturedDiags.reportDiagnostics(*Diags);
287
0
    DiagClient->EndSourceFile();
288
0
    errRec.FinishCapture();
289
0
    return true;
290
0
  }
291
50
292
50
  if (emitPremigrationARCErrors)
293
1
    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
294
1
                           Unit->getPreprocessor());
295
50
  if (!plistOut.empty()) {
296
1
    SmallVector<StoredDiagnostic, 8> arcDiags;
297
1
    for (CapturedDiagList::iterator
298
2
           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; 
++I1
)
299
1
      arcDiags.push_back(*I);
300
1
    writeARCDiagsToPlist(std::string(plistOut), arcDiags,
301
1
                         Ctx.getSourceManager(), Ctx.getLangOpts());
302
1
  }
303
50
304
50
  // After parsing of source files ended, we want to reuse the
305
50
  // diagnostics objects to emit further diagnostics.
306
50
  // We call BeginSourceFile because DiagnosticConsumer requires that
307
50
  // diagnostics with source range information are emitted only in between
308
50
  // BeginSourceFile() and EndSourceFile().
309
50
  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
310
50
311
50
  // No macros will be added since we are just checking and we won't modify
312
50
  // source code.
313
50
  std::vector<SourceLocation> ARCMTMacroLocs;
314
50
315
50
  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
316
50
  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
317
50
                     ARCMTMacroLocs);
318
50
  pass.setNoFinalizeRemoval(NoFinalizeRemoval);
319
50
  if (!NoNSAllocReallocError)
320
48
    Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
321
48
                       SourceLocation());
322
50
323
152
  for (unsigned i=0, e = transforms.size(); i != e; 
++i102
)
324
102
    transforms[i](pass);
325
50
326
50
  capturedDiags.reportDiagnostics(*Diags);
327
50
328
50
  DiagClient->EndSourceFile();
329
50
  errRec.FinishCapture();
330
50
331
50
  return capturedDiags.hasErrors() || 
testAct.hasReportedErrors()39
;
332
50
}
333
334
//===----------------------------------------------------------------------===//
335
// applyTransformations.
336
//===----------------------------------------------------------------------===//
337
338
static bool
339
applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
340
                std::shared_ptr<PCHContainerOperations> PCHContainerOps,
341
                DiagnosticConsumer *DiagClient, StringRef outputDir,
342
10
                bool emitPremigrationARCErrors, StringRef plistOut) {
343
10
  if (!origCI.getLangOpts()->ObjC)
344
0
    return false;
345
10
346
10
  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
347
10
348
10
  // Make sure checking is successful first.
349
10
  CompilerInvocation CInvokForCheck(origCI);
350
10
  if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
351
10
                                  DiagClient, emitPremigrationARCErrors,
352
10
                                  plistOut))
353
0
    return true;
354
10
355
10
  CompilerInvocation CInvok(origCI);
356
10
  CInvok.getFrontendOpts().Inputs.clear();
357
10
  CInvok.getFrontendOpts().Inputs.push_back(Input);
358
10
359
10
  MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
360
10
  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
361
10
362
10
  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
363
10
                                                                     NoFinalizeRemoval);
364
10
  assert(!transforms.empty());
365
10
366
30
  for (unsigned i=0, e = transforms.size(); i != e; 
++i20
) {
367
20
    bool err = migration.applyTransform(transforms[i]);
368
20
    if (err) 
return true0
;
369
20
  }
370
10
371
10
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
372
10
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
373
10
      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
374
10
                            DiagClient, /*ShouldOwnClient=*/false));
375
10
376
10
  if (outputDir.empty()) {
377
2
    origCI.getLangOpts()->ObjCAutoRefCount = true;
378
2
    return migration.getRemapper().overwriteOriginal(*Diags);
379
8
  } else {
380
8
    return migration.getRemapper().flushToDisk(outputDir, *Diags);
381
8
  }
382
10
}
383
384
bool arcmt::applyTransformations(
385
    CompilerInvocation &origCI, const FrontendInputFile &Input,
386
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
387
2
    DiagnosticConsumer *DiagClient) {
388
2
  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
389
2
                         StringRef(), false, StringRef());
390
2
}
391
392
bool arcmt::migrateWithTemporaryFiles(
393
    CompilerInvocation &origCI, const FrontendInputFile &Input,
394
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
395
    DiagnosticConsumer *DiagClient, StringRef outputDir,
396
8
    bool emitPremigrationARCErrors, StringRef plistOut) {
397
8
  assert(!outputDir.empty() && "Expected output directory path");
398
8
  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
399
8
                         emitPremigrationARCErrors, plistOut);
400
8
}
401
402
bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
403
                                  remap,
404
                              StringRef outputDir,
405
24
                              DiagnosticConsumer *DiagClient) {
406
24
  assert(!outputDir.empty());
407
24
408
24
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
409
24
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
410
24
      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
411
24
                            DiagClient, /*ShouldOwnClient=*/false));
412
24
413
24
  FileRemapper remapper;
414
24
  bool err = remapper.initFromDisk(outputDir, *Diags,
415
24
                                   /*ignoreIfFilesChanged=*/true);
416
24
  if (err)
417
0
    return true;
418
24
419
24
  PreprocessorOptions PPOpts;
420
24
  remapper.applyMappings(PPOpts);
421
24
  remap = PPOpts.RemappedFiles;
422
24
423
24
  return false;
424
24
}
425
426
427
//===----------------------------------------------------------------------===//
428
// CollectTransformActions.
429
//===----------------------------------------------------------------------===//
430
431
namespace {
432
433
class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
434
  std::vector<SourceLocation> &ARCMTMacroLocs;
435
436
public:
437
  ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
438
72
    : ARCMTMacroLocs(ARCMTMacroLocs) { }
439
440
  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
441
964
                    SourceRange Range, const MacroArgs *Args) override {
442
964
    if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
443
92
      ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
444
964
  }
445
};
446
447
class ARCMTMacroTrackerAction : public ASTFrontendAction {
448
  std::vector<SourceLocation> &ARCMTMacroLocs;
449
450
public:
451
  ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
452
72
    : ARCMTMacroLocs(ARCMTMacroLocs) { }
453
454
  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
455
72
                                                 StringRef InFile) override {
456
72
    CI.getPreprocessor().addPPCallbacks(
457
72
               std::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
458
72
    return std::make_unique<ASTConsumer>();
459
72
  }
460
};
461
462
class RewritesApplicator : public TransformActions::RewriteReceiver {
463
  Rewriter &rewriter;
464
  MigrationProcess::RewriteListener *Listener;
465
466
public:
467
  RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
468
                     MigrationProcess::RewriteListener *listener)
469
72
    : rewriter(rewriter), Listener(listener) {
470
72
    if (Listener)
471
0
      Listener->start(ctx);
472
72
  }
473
72
  ~RewritesApplicator() override {
474
72
    if (Listener)
475
0
      Listener->finish();
476
72
  }
477
478
313
  void insert(SourceLocation loc, StringRef text) override {
479
313
    bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
480
313
                                   /*indentNewLines=*/true);
481
313
    if (!err && Listener)
482
0
      Listener->insert(loc, text);
483
313
  }
484
485
402
  void remove(CharSourceRange range) override {
486
402
    Rewriter::RewriteOptions removeOpts;
487
402
    removeOpts.IncludeInsertsAtBeginOfRange = false;
488
402
    removeOpts.IncludeInsertsAtEndOfRange = false;
489
402
    removeOpts.RemoveLineIfEmpty = true;
490
402
491
402
    bool err = rewriter.RemoveText(range, removeOpts);
492
402
    if (!err && Listener)
493
0
      Listener->remove(range);
494
402
  }
495
496
  void increaseIndentation(CharSourceRange range,
497
16
                            SourceLocation parentIndent) override {
498
16
    rewriter.IncreaseIndentation(range, parentIndent);
499
16
  }
500
};
501
502
} // end anonymous namespace.
503
504
/// Anchor for VTable.
505
0
MigrationProcess::RewriteListener::~RewriteListener() { }
506
507
MigrationProcess::MigrationProcess(
508
    const CompilerInvocation &CI,
509
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
510
    DiagnosticConsumer *diagClient, StringRef outputDir)
511
    : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)),
512
35
      DiagClient(diagClient), HadARCErrors(false) {
513
35
  if (!outputDir.empty()) {
514
8
    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
515
8
    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
516
8
      new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
517
8
                            DiagClient, /*ShouldOwnClient=*/false));
518
8
    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanged=*/true);
519
8
  }
520
35
}
521
522
bool MigrationProcess::applyTransform(TransformFn trans,
523
72
                                      RewriteListener *listener) {
524
72
  std::unique_ptr<CompilerInvocation> CInvok;
525
72
  CInvok.reset(
526
72
      createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader()));
527
72
  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
528
72
529
72
  Remapper.applyMappings(CInvok->getPreprocessorOpts());
530
72
531
72
  CapturedDiagList capturedDiags;
532
72
  std::vector<SourceLocation> ARCMTMacroLocs;
533
72
534
72
  assert(DiagClient);
535
72
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
536
72
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
537
72
      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
538
72
                            DiagClient, /*ShouldOwnClient=*/false));
539
72
540
72
  // Filter of all diagnostics.
541
72
  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
542
72
  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
543
72
544
72
  std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
545
72
  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
546
72
547
72
  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
548
72
      std::move(CInvok), PCHContainerOps, Diags, ASTAction.get()));
549
72
  if (!Unit) {
550
0
    errRec.FinishCapture();
551
0
    return true;
552
0
  }
553
72
  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
554
72
555
72
  HadARCErrors = HadARCErrors || 
capturedDiags.hasErrors()38
;
556
72
557
72
  // Don't filter diagnostics anymore.
558
72
  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
559
72
560
72
  ASTContext &Ctx = Unit->getASTContext();
561
72
562
72
  if (Diags->hasFatalErrorOccurred()) {
563
0
    Diags->Reset();
564
0
    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
565
0
    capturedDiags.reportDiagnostics(*Diags);
566
0
    DiagClient->EndSourceFile();
567
0
    errRec.FinishCapture();
568
0
    return true;
569
0
  }
570
72
571
72
  // After parsing of source files ended, we want to reuse the
572
72
  // diagnostics objects to emit further diagnostics.
573
72
  // We call BeginSourceFile because DiagnosticConsumer requires that
574
72
  // diagnostics with source range information are emitted only in between
575
72
  // BeginSourceFile() and EndSourceFile().
576
72
  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
577
72
578
72
  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
579
72
  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
580
72
  MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
581
72
                     Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
582
72
583
72
  trans(pass);
584
72
585
72
  {
586
72
    RewritesApplicator applicator(rewriter, Ctx, listener);
587
72
    TA.applyRewrites(applicator);
588
72
  }
589
72
590
72
  DiagClient->EndSourceFile();
591
72
  errRec.FinishCapture();
592
72
593
72
  if (DiagClient->getNumErrors())
594
0
    return true;
595
72
596
72
  for (Rewriter::buffer_iterator
597
149
        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; 
++I77
) {
598
77
    FileID FID = I->first;
599
77
    RewriteBuffer &buf = I->second;
600
77
    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
601
77
    assert(file);
602
77
    std::string newFname = std::string(file->getName());
603
77
    newFname += "-trans";
604
77
    SmallString<512> newText;
605
77
    llvm::raw_svector_ostream vecOS(newText);
606
77
    buf.write(vecOS);
607
77
    std::unique_ptr<llvm::MemoryBuffer> memBuf(
608
77
        llvm::MemoryBuffer::getMemBufferCopy(
609
77
            StringRef(newText.data(), newText.size()), newFname));
610
77
    SmallString<64> filePath(file->getName());
611
77
    Unit->getFileManager().FixupRelativePath(filePath);
612
77
    Remapper.remap(filePath.str(), std::move(memBuf));
613
77
  }
614
72
615
72
  return false;
616
72
}