Coverage Report

Created: 2022-01-18 06:27

/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
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.29k
diagLoc == range.getEnd()3.29k
||
42
3.29k
         
diagLoc.isBeforeInTranslationUnitThan(range.getEnd())2.73k
)) {
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
      // Clear the diagnostic and any notes following it.
49
621
      I = List.erase(eraseS, I);
50
621
      continue;
51
621
    }
52
53
7.78k
    ++I;
54
7.78k
  }
55
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
64
810
  ListTy::const_iterator I = List.begin();
65
5.57k
  while (I != List.end()) {
66
5.31k
    FullSourceLoc diagLoc = I->getLocation();
67
5.31k
    if ((IDs.empty() || // empty means any diagnostic in the range.
68
5.31k
         llvm::is_contained(IDs, I->getID())) &&
69
5.31k
        
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin())1.34k
&&
70
5.31k
        
(878
diagLoc == range.getEnd()878
||
71
878
         
diagLoc.isBeforeInTranslationUnitThan(range.getEnd())384
)) {
72
550
      return true;
73
550
    }
74
75
4.76k
    ++I;
76
4.76k
  }
77
78
260
  return false;
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
91
45
  return false;
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
    // Pass BeginSourceFile message onto DiagClient on first call.
111
    // The corresponding EndSourceFile call will be made from an
112
    // 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
    // Call EndSourceFile on DiagClient on completion of capture to
121
    // enable VerifyDiagnosticConsumer to check diagnostics *after*
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
142
    // 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
  // This duplicates some functionality from Darwin::AddDeploymentTarget
151
  // but this function is well defined, so keep it decoupled from the driver
152
  // and avoid unrelated complications.
153
122
  llvm::Triple triple(origCI.getTargetOpts().Triple);
154
155
122
  if (triple.isiOS())
156
0
    return triple.getOSMajorVersion() >= 5;
157
158
122
  if (triple.isWatchOS())
159
0
    return true;
160
161
122
  if (triple.getOS() == llvm::Triple::Darwin)
162
94
    return triple.getOSMajorVersion() >= 11;
163
164
28
  if (triple.getOS() == llvm::Triple::MacOSX) {
165
28
    return triple.getOSVersion() >= VersionTuple(10, 7);
166
28
  }
167
168
0
  return false;
169
28
}
170
171
static CompilerInvocation *
172
createInvocationForMigration(CompilerInvocation &origCI,
173
122
                             const PCHContainerReader &PCHContainerRdr) {
174
122
  std::unique_ptr<CompilerInvocation> CInvok;
175
122
  CInvok.reset(new CompilerInvocation(origCI));
176
122
  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
177
122
  if (!PPOpts.ImplicitPCHInclude.empty()) {
178
    // We can't use a PCH because it was likely built in non-ARC mode and we
179
    // want to parse in ARC. Include the original header.
180
7
    FileManager FileMgr(origCI.getFileSystemOpts());
181
7
    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
182
7
    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
183
7
        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
184
7
                              new IgnoringDiagConsumer()));
185
7
    std::string OriginalFile = ASTReader::getOriginalSourceFile(
186
7
        PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
187
7
    if (!OriginalFile.empty())
188
7
      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
189
7
    PPOpts.ImplicitPCHInclude.clear();
190
7
  }
191
122
  std::string define = std::string(getARCMTMacroName());
192
122
  define += '=';
193
122
  CInvok->getPreprocessorOpts().addMacroDef(define);
194
122
  CInvok->getLangOpts()->ObjCAutoRefCount = true;
195
122
  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
196
122
  CInvok->getDiagnosticOpts().ErrorLimit = 0;
197
122
  CInvok->getDiagnosticOpts().PedanticErrors = 0;
198
199
  // Ignore -Werror flags when migrating.
200
122
  std::vector<std::string> WarnOpts;
201
122
  for (std::vector<std::string>::iterator
202
122
         I = CInvok->getDiagnosticOpts().Warnings.begin(),
203
125
         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; 
++I3
) {
204
3
    if (!StringRef(*I).startswith("error"))
205
0
      WarnOpts.push_back(*I);
206
3
  }
207
122
  WarnOpts.push_back("error=arc-unsafe-retained-assign");
208
122
  CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
209
210
122
  CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI);
211
122
  CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime;
212
213
122
  return CInvok.release();
214
122
}
215
216
static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
217
                                   DiagnosticOptions *diagOpts,
218
1
                                   Preprocessor &PP) {
219
1
  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
220
1
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
221
1
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
222
1
      new DiagnosticsEngine(DiagID, diagOpts, &printer,
223
1
                            /*ShouldOwnClient=*/false));
224
1
  Diags->setSourceManager(&PP.getSourceManager());
225
226
1
  printer.BeginSourceFile(PP.getLangOpts(), &PP);
227
1
  arcDiags.reportDiagnostics(*Diags);
228
1
  printer.EndSourceFile();
229
1
}
230
231
//===----------------------------------------------------------------------===//
232
// checkForManualIssues.
233
//===----------------------------------------------------------------------===//
234
235
bool arcmt::checkForManualIssues(
236
    CompilerInvocation &origCI, const FrontendInputFile &Input,
237
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
238
    DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
239
51
    StringRef plistOut) {
240
51
  if (!origCI.getLangOpts()->ObjC)
241
1
    return false;
242
243
50
  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
244
50
  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
245
50
  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
246
247
50
  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
248
50
                                                                     NoFinalizeRemoval);
249
50
  assert(!transforms.empty());
250
251
0
  std::unique_ptr<CompilerInvocation> CInvok;
252
50
  CInvok.reset(
253
50
      createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
254
50
  CInvok->getFrontendOpts().Inputs.clear();
255
50
  CInvok->getFrontendOpts().Inputs.push_back(Input);
256
257
50
  CapturedDiagList capturedDiags;
258
259
50
  assert(DiagClient);
260
0
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
261
50
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
262
50
      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
263
50
                            DiagClient, /*ShouldOwnClient=*/false));
264
265
  // Filter of all diagnostics.
266
50
  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
267
50
  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
268
269
50
  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
270
50
      std::move(CInvok), PCHContainerOps, Diags));
271
50
  if (!Unit) {
272
0
    errRec.FinishCapture();
273
0
    return true;
274
0
  }
275
276
  // Don't filter diagnostics anymore.
277
50
  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
278
279
50
  ASTContext &Ctx = Unit->getASTContext();
280
281
50
  if (Diags->hasFatalErrorOccurred()) {
282
0
    Diags->Reset();
283
0
    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
284
0
    capturedDiags.reportDiagnostics(*Diags);
285
0
    DiagClient->EndSourceFile();
286
0
    errRec.FinishCapture();
287
0
    return true;
288
0
  }
289
290
50
  if (emitPremigrationARCErrors)
291
1
    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
292
1
                           Unit->getPreprocessor());
293
50
  if (!plistOut.empty()) {
294
1
    SmallVector<StoredDiagnostic, 8> arcDiags;
295
1
    for (CapturedDiagList::iterator
296
2
           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; 
++I1
)
297
1
      arcDiags.push_back(*I);
298
1
    writeARCDiagsToPlist(std::string(plistOut), arcDiags,
299
1
                         Ctx.getSourceManager(), Ctx.getLangOpts());
300
1
  }
301
302
  // After parsing of source files ended, we want to reuse the
303
  // diagnostics objects to emit further diagnostics.
304
  // We call BeginSourceFile because DiagnosticConsumer requires that
305
  // diagnostics with source range information are emitted only in between
306
  // BeginSourceFile() and EndSourceFile().
307
50
  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
308
309
  // No macros will be added since we are just checking and we won't modify
310
  // source code.
311
50
  std::vector<SourceLocation> ARCMTMacroLocs;
312
313
50
  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
314
50
  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
315
50
                     ARCMTMacroLocs);
316
50
  pass.setNoFinalizeRemoval(NoFinalizeRemoval);
317
50
  if (!NoNSAllocReallocError)
318
48
    Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
319
48
                       SourceLocation());
320
321
152
  for (unsigned i=0, e = transforms.size(); i != e; 
++i102
)
322
102
    transforms[i](pass);
323
324
50
  capturedDiags.reportDiagnostics(*Diags);
325
326
50
  DiagClient->EndSourceFile();
327
50
  errRec.FinishCapture();
328
329
50
  return capturedDiags.hasErrors() || 
testAct.hasReportedErrors()39
;
330
50
}
331
332
//===----------------------------------------------------------------------===//
333
// applyTransformations.
334
//===----------------------------------------------------------------------===//
335
336
static bool
337
applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
338
                std::shared_ptr<PCHContainerOperations> PCHContainerOps,
339
                DiagnosticConsumer *DiagClient, StringRef outputDir,
340
10
                bool emitPremigrationARCErrors, StringRef plistOut) {
341
10
  if (!origCI.getLangOpts()->ObjC)
342
0
    return false;
343
344
10
  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
345
346
  // Make sure checking is successful first.
347
10
  CompilerInvocation CInvokForCheck(origCI);
348
10
  if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
349
10
                                  DiagClient, emitPremigrationARCErrors,
350
10
                                  plistOut))
351
0
    return true;
352
353
10
  CompilerInvocation CInvok(origCI);
354
10
  CInvok.getFrontendOpts().Inputs.clear();
355
10
  CInvok.getFrontendOpts().Inputs.push_back(Input);
356
357
10
  MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
358
10
  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
359
360
10
  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
361
10
                                                                     NoFinalizeRemoval);
362
10
  assert(!transforms.empty());
363
364
30
  for (unsigned i=0, e = transforms.size(); i != e; 
++i20
) {
365
20
    bool err = migration.applyTransform(transforms[i]);
366
20
    if (err) 
return true0
;
367
20
  }
368
369
10
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
370
10
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
371
10
      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
372
10
                            DiagClient, /*ShouldOwnClient=*/false));
373
374
10
  if (outputDir.empty()) {
375
2
    origCI.getLangOpts()->ObjCAutoRefCount = true;
376
2
    return migration.getRemapper().overwriteOriginal(*Diags);
377
8
  } else {
378
8
    return migration.getRemapper().flushToDisk(outputDir, *Diags);
379
8
  }
380
10
}
381
382
bool arcmt::applyTransformations(
383
    CompilerInvocation &origCI, const FrontendInputFile &Input,
384
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
385
2
    DiagnosticConsumer *DiagClient) {
386
2
  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
387
2
                         StringRef(), false, StringRef());
388
2
}
389
390
bool arcmt::migrateWithTemporaryFiles(
391
    CompilerInvocation &origCI, const FrontendInputFile &Input,
392
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
393
    DiagnosticConsumer *DiagClient, StringRef outputDir,
394
8
    bool emitPremigrationARCErrors, StringRef plistOut) {
395
8
  assert(!outputDir.empty() && "Expected output directory path");
396
0
  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
397
8
                         emitPremigrationARCErrors, plistOut);
398
8
}
399
400
bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
401
                                  remap,
402
                              StringRef outputDir,
403
24
                              DiagnosticConsumer *DiagClient) {
404
24
  assert(!outputDir.empty());
405
406
0
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
407
24
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
408
24
      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
409
24
                            DiagClient, /*ShouldOwnClient=*/false));
410
411
24
  FileRemapper remapper;
412
24
  bool err = remapper.initFromDisk(outputDir, *Diags,
413
24
                                   /*ignoreIfFilesChanged=*/true);
414
24
  if (err)
415
0
    return true;
416
417
24
  remapper.forEachMapping(
418
30
      [&](StringRef From, StringRef To) {
419
30
        remap.push_back(std::make_pair(From.str(), To.str()));
420
30
      },
421
24
      [](StringRef, const llvm::MemoryBufferRef &) 
{}0
);
422
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
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
529
72
  Remapper.applyMappings(CInvok->getPreprocessorOpts());
530
531
72
  CapturedDiagList capturedDiags;
532
72
  std::vector<SourceLocation> ARCMTMacroLocs;
533
534
72
  assert(DiagClient);
535
0
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
536
72
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
537
72
      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
538
72
                            DiagClient, /*ShouldOwnClient=*/false));
539
540
  // Filter of all diagnostics.
541
72
  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
542
72
  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
543
544
72
  std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
545
72
  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
546
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
555
72
  HadARCErrors = HadARCErrors || 
capturedDiags.hasErrors()38
;
556
557
  // Don't filter diagnostics anymore.
558
72
  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
559
560
72
  ASTContext &Ctx = Unit->getASTContext();
561
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
571
  // After parsing of source files ended, we want to reuse the
572
  // diagnostics objects to emit further diagnostics.
573
  // We call BeginSourceFile because DiagnosticConsumer requires that
574
  // diagnostics with source range information are emitted only in between
575
  // BeginSourceFile() and EndSourceFile().
576
72
  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
577
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
583
72
  trans(pass);
584
585
72
  {
586
72
    RewritesApplicator applicator(rewriter, Ctx, listener);
587
72
    TA.applyRewrites(applicator);
588
72
  }
589
590
72
  DiagClient->EndSourceFile();
591
72
  errRec.FinishCapture();
592
593
72
  if (DiagClient->getNumErrors())
594
0
    return true;
595
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
0
    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
615
72
  return false;
616
72
}