ARCMT.cpp revision 226633
1224135Sdim//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
2224135Sdim//
3224135Sdim//                     The LLVM Compiler Infrastructure
4224135Sdim//
5224135Sdim// This file is distributed under the University of Illinois Open Source
6224135Sdim// License. See LICENSE.TXT for details.
7224135Sdim//
8224135Sdim//===----------------------------------------------------------------------===//
9224135Sdim
10224135Sdim#include "Internals.h"
11224135Sdim#include "clang/Frontend/ASTUnit.h"
12224135Sdim#include "clang/Frontend/CompilerInstance.h"
13226633Sdim#include "clang/Frontend/TextDiagnosticPrinter.h"
14224135Sdim#include "clang/Frontend/Utils.h"
15224135Sdim#include "clang/AST/ASTConsumer.h"
16224135Sdim#include "clang/Rewrite/Rewriter.h"
17224135Sdim#include "clang/Sema/SemaDiagnostic.h"
18224135Sdim#include "clang/Basic/DiagnosticCategories.h"
19224135Sdim#include "clang/Lex/Preprocessor.h"
20224135Sdim#include "llvm/Support/MemoryBuffer.h"
21224135Sdim#include "llvm/ADT/Triple.h"
22224135Sdimusing namespace clang;
23224135Sdimusing namespace arcmt;
24224135Sdim
25226633Sdimbool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
26224135Sdim                                       SourceRange range) {
27224135Sdim  if (range.isInvalid())
28224135Sdim    return false;
29224135Sdim
30224135Sdim  bool cleared = false;
31224135Sdim  ListTy::iterator I = List.begin();
32224135Sdim  while (I != List.end()) {
33224135Sdim    FullSourceLoc diagLoc = I->getLocation();
34224135Sdim    if ((IDs.empty() || // empty means clear all diagnostics in the range.
35224135Sdim         std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
36224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
37224135Sdim        (diagLoc == range.getEnd() ||
38224135Sdim           diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
39224135Sdim      cleared = true;
40224135Sdim      ListTy::iterator eraseS = I++;
41226633Sdim      while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
42224135Sdim        ++I;
43224135Sdim      // Clear the diagnostic and any notes following it.
44224135Sdim      List.erase(eraseS, I);
45224135Sdim      continue;
46224135Sdim    }
47224135Sdim
48224135Sdim    ++I;
49224135Sdim  }
50224135Sdim
51224135Sdim  return cleared;
52224135Sdim}
53224135Sdim
54226633Sdimbool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
55224135Sdim                                     SourceRange range) const {
56224135Sdim  if (range.isInvalid())
57224135Sdim    return false;
58224135Sdim
59224135Sdim  ListTy::const_iterator I = List.begin();
60224135Sdim  while (I != List.end()) {
61224135Sdim    FullSourceLoc diagLoc = I->getLocation();
62224135Sdim    if ((IDs.empty() || // empty means any diagnostic in the range.
63224135Sdim         std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
64224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
65224135Sdim        (diagLoc == range.getEnd() ||
66224135Sdim           diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
67224135Sdim      return true;
68224135Sdim    }
69224135Sdim
70224135Sdim    ++I;
71224135Sdim  }
72224135Sdim
73224135Sdim  return false;
74224135Sdim}
75224135Sdim
76226633Sdimvoid CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
77224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
78224135Sdim    Diags.Report(*I);
79224135Sdim}
80224135Sdim
81224135Sdimbool CapturedDiagList::hasErrors() const {
82224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
83226633Sdim    if (I->getLevel() >= DiagnosticsEngine::Error)
84224135Sdim      return true;
85224135Sdim
86224135Sdim  return false;
87224135Sdim}
88224135Sdim
89224135Sdimnamespace {
90224135Sdim
91226633Sdimclass CaptureDiagnosticConsumer : public DiagnosticConsumer {
92226633Sdim  DiagnosticsEngine &Diags;
93224135Sdim  CapturedDiagList &CapturedDiags;
94224135Sdimpublic:
95226633Sdim  CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
96226633Sdim                           CapturedDiagList &capturedDiags)
97224135Sdim    : Diags(diags), CapturedDiags(capturedDiags) { }
98224135Sdim
99226633Sdim  virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
100226633Sdim                                const Diagnostic &Info) {
101224135Sdim    if (arcmt::isARCDiagnostic(Info.getID(), Diags) ||
102226633Sdim        level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
103224135Sdim      CapturedDiags.push_back(StoredDiagnostic(level, Info));
104224135Sdim      return;
105224135Sdim    }
106224135Sdim
107224135Sdim    // Non-ARC warnings are ignored.
108224135Sdim    Diags.setLastDiagnosticIgnored();
109224135Sdim  }
110226633Sdim
111226633Sdim  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
112226633Sdim    // Just drop any diagnostics that come from cloned consumers; they'll
113226633Sdim    // have different source managers anyway.
114226633Sdim    return new IgnoringDiagConsumer();
115226633Sdim  }
116224135Sdim};
117224135Sdim
118224135Sdim} // end anonymous namespace
119224135Sdim
120226633Sdimstatic inline StringRef SimulatorVersionDefineName() {
121224135Sdim  return "__IPHONE_OS_VERSION_MIN_REQUIRED=";
122224135Sdim}
123224135Sdim
124224135Sdim/// \brief Parse the simulator version define:
125224135Sdim/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9])
126224135Sdim// and return the grouped values as integers, e.g:
127224135Sdim//   __IPHONE_OS_VERSION_MIN_REQUIRED=40201
128224135Sdim// will return Major=4, Minor=2, Micro=1.
129226633Sdimstatic bool GetVersionFromSimulatorDefine(StringRef define,
130224135Sdim                                          unsigned &Major, unsigned &Minor,
131224135Sdim                                          unsigned &Micro) {
132224135Sdim  assert(define.startswith(SimulatorVersionDefineName()));
133226633Sdim  StringRef name, version;
134224135Sdim  llvm::tie(name, version) = define.split('=');
135224135Sdim  if (version.empty())
136224135Sdim    return false;
137224135Sdim  std::string verstr = version.str();
138224135Sdim  char *end;
139224135Sdim  unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10);
140224135Sdim  if (*end != '\0')
141224135Sdim    return false;
142224135Sdim  Major = num / 10000;
143224135Sdim  num = num % 10000;
144224135Sdim  Minor = num / 100;
145224135Sdim  Micro = num % 100;
146224135Sdim  return true;
147224135Sdim}
148224135Sdim
149224135Sdimstatic bool HasARCRuntime(CompilerInvocation &origCI) {
150224135Sdim  // This duplicates some functionality from Darwin::AddDeploymentTarget
151224135Sdim  // but this function is well defined, so keep it decoupled from the driver
152224135Sdim  // and avoid unrelated complications.
153224135Sdim
154224135Sdim  for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size();
155224135Sdim         i != e; ++i) {
156224135Sdim    StringRef define = origCI.getPreprocessorOpts().Macros[i].first;
157224135Sdim    bool isUndef = origCI.getPreprocessorOpts().Macros[i].second;
158224135Sdim    if (isUndef)
159224135Sdim      continue;
160224135Sdim    if (!define.startswith(SimulatorVersionDefineName()))
161224135Sdim      continue;
162226633Sdim    unsigned Major = 0, Minor = 0, Micro = 0;
163224135Sdim    if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) &&
164224135Sdim        Major < 10 && Minor < 100 && Micro < 100)
165224135Sdim      return Major >= 5;
166224135Sdim  }
167224135Sdim
168224135Sdim  llvm::Triple triple(origCI.getTargetOpts().Triple);
169224135Sdim
170224135Sdim  if (triple.getOS() == llvm::Triple::IOS)
171224135Sdim    return triple.getOSMajorVersion() >= 5;
172224135Sdim
173224135Sdim  if (triple.getOS() == llvm::Triple::Darwin)
174224135Sdim    return triple.getOSMajorVersion() >= 11;
175224135Sdim
176224135Sdim  if (triple.getOS() == llvm::Triple::MacOSX) {
177224135Sdim    unsigned Major, Minor, Micro;
178224135Sdim    triple.getOSVersion(Major, Minor, Micro);
179224135Sdim    return Major > 10 || (Major == 10 && Minor >= 7);
180224135Sdim  }
181224135Sdim
182224135Sdim  return false;
183224135Sdim}
184224135Sdim
185226633Sdimstatic CompilerInvocation *
186226633SdimcreateInvocationForMigration(CompilerInvocation &origCI) {
187224135Sdim  llvm::OwningPtr<CompilerInvocation> CInvok;
188224135Sdim  CInvok.reset(new CompilerInvocation(origCI));
189224135Sdim  CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string();
190224135Sdim  CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string();
191224135Sdim  std::string define = getARCMTMacroName();
192224135Sdim  define += '=';
193224135Sdim  CInvok->getPreprocessorOpts().addMacroDef(define);
194224135Sdim  CInvok->getLangOpts().ObjCAutoRefCount = true;
195224135Sdim  CInvok->getDiagnosticOpts().ErrorLimit = 0;
196224135Sdim  CInvok->getDiagnosticOpts().Warnings.push_back(
197224135Sdim                                            "error=arc-unsafe-retained-assign");
198224135Sdim  CInvok->getLangOpts().ObjCRuntimeHasWeak = HasARCRuntime(origCI);
199224135Sdim
200224135Sdim  return CInvok.take();
201224135Sdim}
202224135Sdim
203226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags,
204226633Sdim                                   const DiagnosticOptions &diagOpts,
205226633Sdim                                   Preprocessor &PP) {
206226633Sdim  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
207226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
208226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
209226633Sdim      new DiagnosticsEngine(DiagID, &printer, /*ShouldOwnClient=*/false));
210226633Sdim  Diags->setSourceManager(&PP.getSourceManager());
211226633Sdim
212226633Sdim  printer.BeginSourceFile(PP.getLangOptions(), &PP);
213226633Sdim  arcDiags.reportDiagnostics(*Diags);
214226633Sdim  printer.EndSourceFile();
215226633Sdim}
216226633Sdim
217224135Sdim//===----------------------------------------------------------------------===//
218224135Sdim// checkForManualIssues.
219224135Sdim//===----------------------------------------------------------------------===//
220224135Sdim
221224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI,
222226633Sdim                                 StringRef Filename, InputKind Kind,
223226633Sdim                                 DiagnosticConsumer *DiagClient,
224226633Sdim                                 bool emitPremigrationARCErrors,
225226633Sdim                                 StringRef plistOut) {
226224135Sdim  if (!origCI.getLangOpts().ObjC1)
227224135Sdim    return false;
228224135Sdim
229224135Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations();
230224135Sdim  assert(!transforms.empty());
231224135Sdim
232224135Sdim  llvm::OwningPtr<CompilerInvocation> CInvok;
233224135Sdim  CInvok.reset(createInvocationForMigration(origCI));
234224135Sdim  CInvok->getFrontendOpts().Inputs.clear();
235224135Sdim  CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
236224135Sdim
237224135Sdim  CapturedDiagList capturedDiags;
238224135Sdim
239224135Sdim  assert(DiagClient);
240224135Sdim  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
241226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
242226633Sdim      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
243224135Sdim
244224135Sdim  // Filter of all diagnostics.
245226633Sdim  CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
246224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
247224135Sdim
248224135Sdim  llvm::OwningPtr<ASTUnit> Unit(
249224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
250224135Sdim  if (!Unit)
251224135Sdim    return true;
252224135Sdim
253224135Sdim  // Don't filter diagnostics anymore.
254224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
255224135Sdim
256224135Sdim  ASTContext &Ctx = Unit->getASTContext();
257224135Sdim
258224135Sdim  if (Diags->hasFatalErrorOccurred()) {
259224135Sdim    Diags->Reset();
260224135Sdim    DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
261224135Sdim    capturedDiags.reportDiagnostics(*Diags);
262224135Sdim    DiagClient->EndSourceFile();
263224135Sdim    return true;
264224135Sdim  }
265224135Sdim
266226633Sdim  if (emitPremigrationARCErrors)
267226633Sdim    emitPremigrationErrors(capturedDiags, origCI.getDiagnosticOpts(),
268226633Sdim                           Unit->getPreprocessor());
269226633Sdim  if (!plistOut.empty()) {
270226633Sdim    SmallVector<StoredDiagnostic, 8> arcDiags;
271226633Sdim    for (CapturedDiagList::iterator
272226633Sdim           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
273226633Sdim      arcDiags.push_back(*I);
274226633Sdim    writeARCDiagsToPlist(plistOut, arcDiags,
275226633Sdim                         Ctx.getSourceManager(), Ctx.getLangOptions());
276226633Sdim  }
277226633Sdim
278224135Sdim  // After parsing of source files ended, we want to reuse the
279224135Sdim  // diagnostics objects to emit further diagnostics.
280226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
281224135Sdim  // diagnostics with source range information are emitted only in between
282224135Sdim  // BeginSourceFile() and EndSourceFile().
283224135Sdim  DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
284224135Sdim
285224135Sdim  // No macros will be added since we are just checking and we won't modify
286224135Sdim  // source code.
287224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
288224135Sdim
289224135Sdim  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
290224135Sdim  MigrationPass pass(Ctx, Unit->getSema(), testAct, ARCMTMacroLocs);
291224135Sdim
292224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i)
293224135Sdim    transforms[i](pass);
294224135Sdim
295224135Sdim  capturedDiags.reportDiagnostics(*Diags);
296224135Sdim
297224135Sdim  DiagClient->EndSourceFile();
298224135Sdim
299224135Sdim  // If we are migrating code that gets the '-fobjc-arc' flag, make sure
300224135Sdim  // to remove it so that we don't get errors from normal compilation.
301224135Sdim  origCI.getLangOpts().ObjCAutoRefCount = false;
302224135Sdim
303226633Sdim  return capturedDiags.hasErrors() || testAct.hasReportedErrors();
304224135Sdim}
305224135Sdim
306224135Sdim//===----------------------------------------------------------------------===//
307224135Sdim// applyTransformations.
308224135Sdim//===----------------------------------------------------------------------===//
309224135Sdim
310224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI,
311226633Sdim                            StringRef Filename, InputKind Kind,
312226633Sdim                            DiagnosticConsumer *DiagClient,
313226633Sdim                            StringRef outputDir,
314226633Sdim                            bool emitPremigrationARCErrors,
315226633Sdim                            StringRef plistOut) {
316224135Sdim  if (!origCI.getLangOpts().ObjC1)
317224135Sdim    return false;
318224135Sdim
319224135Sdim  // Make sure checking is successful first.
320224135Sdim  CompilerInvocation CInvokForCheck(origCI);
321226633Sdim  if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient,
322226633Sdim                                  emitPremigrationARCErrors, plistOut))
323224135Sdim    return true;
324224135Sdim
325224135Sdim  CompilerInvocation CInvok(origCI);
326224135Sdim  CInvok.getFrontendOpts().Inputs.clear();
327224135Sdim  CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
328224135Sdim
329224135Sdim  MigrationProcess migration(CInvok, DiagClient, outputDir);
330224135Sdim
331224135Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations();
332224135Sdim  assert(!transforms.empty());
333224135Sdim
334224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i) {
335224135Sdim    bool err = migration.applyTransform(transforms[i]);
336224135Sdim    if (err) return true;
337224135Sdim  }
338224135Sdim
339224135Sdim  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
340226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
341226633Sdim      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
342224135Sdim
343224135Sdim  if (outputDir.empty()) {
344224135Sdim    origCI.getLangOpts().ObjCAutoRefCount = true;
345224135Sdim    return migration.getRemapper().overwriteOriginal(*Diags);
346224135Sdim  } else {
347224135Sdim    // If we are migrating code that gets the '-fobjc-arc' flag, make sure
348224135Sdim    // to remove it so that we don't get errors from normal compilation.
349224135Sdim    origCI.getLangOpts().ObjCAutoRefCount = false;
350224135Sdim    return migration.getRemapper().flushToDisk(outputDir, *Diags);
351224135Sdim  }
352224135Sdim}
353224135Sdim
354224135Sdimbool arcmt::applyTransformations(CompilerInvocation &origCI,
355226633Sdim                                 StringRef Filename, InputKind Kind,
356226633Sdim                                 DiagnosticConsumer *DiagClient) {
357226633Sdim  return applyTransforms(origCI, Filename, Kind, DiagClient,
358226633Sdim                         StringRef(), false, StringRef());
359224135Sdim}
360224135Sdim
361224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
362226633Sdim                                      StringRef Filename, InputKind Kind,
363226633Sdim                                      DiagnosticConsumer *DiagClient,
364226633Sdim                                      StringRef outputDir,
365226633Sdim                                      bool emitPremigrationARCErrors,
366226633Sdim                                      StringRef plistOut) {
367224135Sdim  assert(!outputDir.empty() && "Expected output directory path");
368226633Sdim  return applyTransforms(origCI, Filename, Kind, DiagClient,
369226633Sdim                         outputDir, emitPremigrationARCErrors, plistOut);
370224135Sdim}
371224135Sdim
372224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
373224135Sdim                                  remap,
374226633Sdim                              StringRef outputDir,
375226633Sdim                              DiagnosticConsumer *DiagClient) {
376224135Sdim  assert(!outputDir.empty());
377224135Sdim
378224135Sdim  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
379226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
380226633Sdim      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
381224135Sdim
382224135Sdim  FileRemapper remapper;
383224135Sdim  bool err = remapper.initFromDisk(outputDir, *Diags,
384224135Sdim                                   /*ignoreIfFilesChanged=*/true);
385224135Sdim  if (err)
386224135Sdim    return true;
387224135Sdim
388224135Sdim  CompilerInvocation CI;
389224135Sdim  remapper.applyMappings(CI);
390224135Sdim  remap = CI.getPreprocessorOpts().RemappedFiles;
391224135Sdim
392224135Sdim  return false;
393224135Sdim}
394224135Sdim
395224135Sdim//===----------------------------------------------------------------------===//
396224135Sdim// CollectTransformActions.
397224135Sdim//===----------------------------------------------------------------------===//
398224135Sdim
399224135Sdimnamespace {
400224135Sdim
401224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
402224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
403224135Sdim
404224135Sdimpublic:
405224135Sdim  ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
406224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
407224135Sdim
408226633Sdim  virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI,
409226633Sdim                            SourceRange Range) {
410224135Sdim    if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
411224135Sdim      ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
412224135Sdim  }
413224135Sdim};
414224135Sdim
415224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction {
416224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
417224135Sdim
418224135Sdimpublic:
419224135Sdim  ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
420224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
421224135Sdim
422224135Sdim  virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
423226633Sdim                                         StringRef InFile) {
424224135Sdim    CI.getPreprocessor().addPPCallbacks(
425224135Sdim                              new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
426224135Sdim    return new ASTConsumer();
427224135Sdim  }
428224135Sdim};
429224135Sdim
430224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver {
431224135Sdim  Rewriter &rewriter;
432224135Sdim  ASTContext &Ctx;
433224135Sdim  MigrationProcess::RewriteListener *Listener;
434224135Sdim
435224135Sdimpublic:
436224135Sdim  RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
437224135Sdim                     MigrationProcess::RewriteListener *listener)
438224135Sdim    : rewriter(rewriter), Ctx(ctx), Listener(listener) {
439224135Sdim    if (Listener)
440224135Sdim      Listener->start(ctx);
441224135Sdim  }
442224135Sdim  ~RewritesApplicator() {
443224135Sdim    if (Listener)
444224135Sdim      Listener->finish();
445224135Sdim  }
446224135Sdim
447226633Sdim  virtual void insert(SourceLocation loc, StringRef text) {
448224135Sdim    bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
449224135Sdim                                   /*indentNewLines=*/true);
450224135Sdim    if (!err && Listener)
451224135Sdim      Listener->insert(loc, text);
452224135Sdim  }
453224135Sdim
454224135Sdim  virtual void remove(CharSourceRange range) {
455224135Sdim    Rewriter::RewriteOptions removeOpts;
456224135Sdim    removeOpts.IncludeInsertsAtBeginOfRange = false;
457224135Sdim    removeOpts.IncludeInsertsAtEndOfRange = false;
458224135Sdim    removeOpts.RemoveLineIfEmpty = true;
459224135Sdim
460224135Sdim    bool err = rewriter.RemoveText(range, removeOpts);
461224135Sdim    if (!err && Listener)
462224135Sdim      Listener->remove(range);
463224135Sdim  }
464224135Sdim
465224135Sdim  virtual void increaseIndentation(CharSourceRange range,
466224135Sdim                                    SourceLocation parentIndent) {
467224135Sdim    rewriter.IncreaseIndentation(range, parentIndent);
468224135Sdim  }
469224135Sdim};
470224135Sdim
471224135Sdim} // end anonymous namespace.
472224135Sdim
473224135Sdim/// \brief Anchor for VTable.
474224135SdimMigrationProcess::RewriteListener::~RewriteListener() { }
475224135Sdim
476224135SdimMigrationProcess::MigrationProcess(const CompilerInvocation &CI,
477226633Sdim                                   DiagnosticConsumer *diagClient,
478226633Sdim                                   StringRef outputDir)
479224135Sdim  : OrigCI(CI), DiagClient(diagClient) {
480224135Sdim  if (!outputDir.empty()) {
481224135Sdim    llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
482226633Sdim    llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
483226633Sdim      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
484224135Sdim    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
485224135Sdim  }
486224135Sdim}
487224135Sdim
488224135Sdimbool MigrationProcess::applyTransform(TransformFn trans,
489224135Sdim                                      RewriteListener *listener) {
490224135Sdim  llvm::OwningPtr<CompilerInvocation> CInvok;
491224135Sdim  CInvok.reset(createInvocationForMigration(OrigCI));
492224135Sdim  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
493224135Sdim
494224135Sdim  Remapper.applyMappings(*CInvok);
495224135Sdim
496224135Sdim  CapturedDiagList capturedDiags;
497224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
498224135Sdim
499224135Sdim  assert(DiagClient);
500224135Sdim  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
501226633Sdim  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
502226633Sdim      new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
503224135Sdim
504224135Sdim  // Filter of all diagnostics.
505226633Sdim  CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
506224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
507224135Sdim
508224135Sdim  llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction;
509224135Sdim  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
510224135Sdim
511224135Sdim  llvm::OwningPtr<ASTUnit> Unit(
512224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
513224135Sdim                                                ASTAction.get()));
514224135Sdim  if (!Unit)
515224135Sdim    return true;
516224135Sdim  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
517224135Sdim
518224135Sdim  // Don't filter diagnostics anymore.
519224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
520224135Sdim
521224135Sdim  ASTContext &Ctx = Unit->getASTContext();
522224135Sdim
523224135Sdim  if (Diags->hasFatalErrorOccurred()) {
524224135Sdim    Diags->Reset();
525224135Sdim    DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
526224135Sdim    capturedDiags.reportDiagnostics(*Diags);
527224135Sdim    DiagClient->EndSourceFile();
528224135Sdim    return true;
529224135Sdim  }
530224135Sdim
531224135Sdim  // After parsing of source files ended, we want to reuse the
532224135Sdim  // diagnostics objects to emit further diagnostics.
533226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
534224135Sdim  // diagnostics with source range information are emitted only in between
535224135Sdim  // BeginSourceFile() and EndSourceFile().
536224135Sdim  DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
537224135Sdim
538224135Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions());
539224135Sdim  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
540224135Sdim  MigrationPass pass(Ctx, Unit->getSema(), TA, ARCMTMacroLocs);
541224135Sdim
542224135Sdim  trans(pass);
543224135Sdim
544224135Sdim  {
545224135Sdim    RewritesApplicator applicator(rewriter, Ctx, listener);
546224135Sdim    TA.applyRewrites(applicator);
547224135Sdim  }
548224135Sdim
549224135Sdim  DiagClient->EndSourceFile();
550224135Sdim
551224135Sdim  if (DiagClient->getNumErrors())
552224135Sdim    return true;
553224135Sdim
554224135Sdim  for (Rewriter::buffer_iterator
555224135Sdim        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
556224135Sdim    FileID FID = I->first;
557224135Sdim    RewriteBuffer &buf = I->second;
558224135Sdim    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
559224135Sdim    assert(file);
560224135Sdim    std::string newFname = file->getName();
561224135Sdim    newFname += "-trans";
562224135Sdim    llvm::SmallString<512> newText;
563224135Sdim    llvm::raw_svector_ostream vecOS(newText);
564224135Sdim    buf.write(vecOS);
565224135Sdim    vecOS.flush();
566224135Sdim    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
567226633Sdim                   StringRef(newText.data(), newText.size()), newFname);
568224135Sdim    llvm::SmallString<64> filePath(file->getName());
569224135Sdim    Unit->getFileManager().FixupRelativePath(filePath);
570224135Sdim    Remapper.remap(filePath.str(), memBuf);
571224135Sdim  }
572224135Sdim
573224135Sdim  return false;
574224135Sdim}
575224135Sdim
576224135Sdim//===----------------------------------------------------------------------===//
577224135Sdim// isARCDiagnostic.
578224135Sdim//===----------------------------------------------------------------------===//
579224135Sdim
580226633Sdimbool arcmt::isARCDiagnostic(unsigned diagID, DiagnosticsEngine &Diag) {
581224135Sdim  return Diag.getDiagnosticIDs()->getCategoryNumberForDiag(diagID) ==
582224135Sdim           diag::DiagCat_Automatic_Reference_Counting_Issue;
583224135Sdim}
584