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