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