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"
11252723Sdim#include "clang/AST/ASTConsumer.h"
12252723Sdim#include "clang/Basic/DiagnosticCategories.h"
13224135Sdim#include "clang/Frontend/ASTUnit.h"
14224135Sdim#include "clang/Frontend/CompilerInstance.h"
15235633Sdim#include "clang/Frontend/FrontendAction.h"
16226890Sdim#include "clang/Frontend/TextDiagnosticPrinter.h"
17224135Sdim#include "clang/Frontend/Utils.h"
18252723Sdim#include "clang/Lex/Preprocessor.h"
19245431Sdim#include "clang/Rewrite/Core/Rewriter.h"
20224135Sdim#include "clang/Sema/SemaDiagnostic.h"
21252723Sdim#include "clang/Serialization/ASTReader.h"
22252723Sdim#include "llvm/ADT/Triple.h"
23224135Sdim#include "llvm/Support/MemoryBuffer.h"
24224135Sdimusing namespace clang;
25224135Sdimusing namespace arcmt;
26224135Sdim
27226890Sdimbool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
28224135Sdim                                       SourceRange range) {
29224135Sdim  if (range.isInvalid())
30224135Sdim    return false;
31224135Sdim
32224135Sdim  bool cleared = false;
33224135Sdim  ListTy::iterator I = List.begin();
34224135Sdim  while (I != List.end()) {
35224135Sdim    FullSourceLoc diagLoc = I->getLocation();
36224135Sdim    if ((IDs.empty() || // empty means clear all diagnostics in the range.
37224135Sdim         std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
38224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
39224135Sdim        (diagLoc == range.getEnd() ||
40224135Sdim           diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
41224135Sdim      cleared = true;
42224135Sdim      ListTy::iterator eraseS = I++;
43252723Sdim      if (eraseS->getLevel() != DiagnosticsEngine::Note)
44252723Sdim        while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
45252723Sdim          ++I;
46224135Sdim      // Clear the diagnostic and any notes following it.
47245431Sdim      I = List.erase(eraseS, I);
48224135Sdim      continue;
49224135Sdim    }
50224135Sdim
51224135Sdim    ++I;
52224135Sdim  }
53224135Sdim
54224135Sdim  return cleared;
55224135Sdim}
56224135Sdim
57226890Sdimbool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
58224135Sdim                                     SourceRange range) const {
59224135Sdim  if (range.isInvalid())
60224135Sdim    return false;
61224135Sdim
62224135Sdim  ListTy::const_iterator I = List.begin();
63224135Sdim  while (I != List.end()) {
64224135Sdim    FullSourceLoc diagLoc = I->getLocation();
65224135Sdim    if ((IDs.empty() || // empty means any diagnostic in the range.
66224135Sdim         std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
67224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
68224135Sdim        (diagLoc == range.getEnd() ||
69224135Sdim           diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
70224135Sdim      return true;
71224135Sdim    }
72224135Sdim
73224135Sdim    ++I;
74224135Sdim  }
75224135Sdim
76224135Sdim  return false;
77224135Sdim}
78224135Sdim
79226890Sdimvoid CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
80224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
81224135Sdim    Diags.Report(*I);
82224135Sdim}
83224135Sdim
84224135Sdimbool CapturedDiagList::hasErrors() const {
85224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
86226890Sdim    if (I->getLevel() >= DiagnosticsEngine::Error)
87224135Sdim      return true;
88224135Sdim
89224135Sdim  return false;
90224135Sdim}
91224135Sdim
92224135Sdimnamespace {
93224135Sdim
94226890Sdimclass CaptureDiagnosticConsumer : public DiagnosticConsumer {
95226890Sdim  DiagnosticsEngine &Diags;
96245431Sdim  DiagnosticConsumer &DiagClient;
97224135Sdim  CapturedDiagList &CapturedDiags;
98245431Sdim  bool HasBegunSourceFile;
99224135Sdimpublic:
100226890Sdim  CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
101245431Sdim                            DiagnosticConsumer &client,
102245431Sdim                            CapturedDiagList &capturedDiags)
103245431Sdim    : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
104245431Sdim      HasBegunSourceFile(false) { }
105224135Sdim
106245431Sdim  virtual void BeginSourceFile(const LangOptions &Opts,
107245431Sdim                               const Preprocessor *PP) {
108245431Sdim    // Pass BeginSourceFile message onto DiagClient on first call.
109245431Sdim    // The corresponding EndSourceFile call will be made from an
110245431Sdim    // explicit call to FinishCapture.
111245431Sdim    if (!HasBegunSourceFile) {
112245431Sdim      DiagClient.BeginSourceFile(Opts, PP);
113245431Sdim      HasBegunSourceFile = true;
114245431Sdim    }
115245431Sdim  }
116245431Sdim
117245431Sdim  void FinishCapture() {
118245431Sdim    // Call EndSourceFile on DiagClient on completion of capture to
119245431Sdim    // enable VerifyDiagnosticConsumer to check diagnostics *after*
120245431Sdim    // it has received the diagnostic list.
121245431Sdim    if (HasBegunSourceFile) {
122245431Sdim      DiagClient.EndSourceFile();
123245431Sdim      HasBegunSourceFile = false;
124245431Sdim    }
125245431Sdim  }
126245431Sdim
127245431Sdim  virtual ~CaptureDiagnosticConsumer() {
128245431Sdim    assert(!HasBegunSourceFile && "FinishCapture not called!");
129245431Sdim  }
130245431Sdim
131226890Sdim  virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
132226890Sdim                                const Diagnostic &Info) {
133235633Sdim    if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
134226890Sdim        level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
135252723Sdim      if (Info.getLocation().isValid())
136252723Sdim        CapturedDiags.push_back(StoredDiagnostic(level, Info));
137224135Sdim      return;
138224135Sdim    }
139224135Sdim
140224135Sdim    // Non-ARC warnings are ignored.
141224135Sdim    Diags.setLastDiagnosticIgnored();
142224135Sdim  }
143224135Sdim};
144224135Sdim
145224135Sdim} // end anonymous namespace
146224135Sdim
147224135Sdimstatic bool HasARCRuntime(CompilerInvocation &origCI) {
148224135Sdim  // This duplicates some functionality from Darwin::AddDeploymentTarget
149224135Sdim  // but this function is well defined, so keep it decoupled from the driver
150224135Sdim  // and avoid unrelated complications.
151224135Sdim  llvm::Triple triple(origCI.getTargetOpts().Triple);
152224135Sdim
153263509Sdim  if (triple.isiOS())
154224135Sdim    return triple.getOSMajorVersion() >= 5;
155224135Sdim
156224135Sdim  if (triple.getOS() == llvm::Triple::Darwin)
157224135Sdim    return triple.getOSMajorVersion() >= 11;
158224135Sdim
159224135Sdim  if (triple.getOS() == llvm::Triple::MacOSX) {
160224135Sdim    unsigned Major, Minor, Micro;
161224135Sdim    triple.getOSVersion(Major, Minor, Micro);
162224135Sdim    return Major > 10 || (Major == 10 && Minor >= 7);
163224135Sdim  }
164224135Sdim
165224135Sdim  return false;
166224135Sdim}
167224135Sdim
168226890Sdimstatic CompilerInvocation *
169226890SdimcreateInvocationForMigration(CompilerInvocation &origCI) {
170235633Sdim  OwningPtr<CompilerInvocation> CInvok;
171224135Sdim  CInvok.reset(new CompilerInvocation(origCI));
172252723Sdim  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
173252723Sdim  if (!PPOpts.ImplicitPCHInclude.empty()) {
174252723Sdim    // We can't use a PCH because it was likely built in non-ARC mode and we
175252723Sdim    // want to parse in ARC. Include the original header.
176252723Sdim    FileManager FileMgr(origCI.getFileSystemOpts());
177252723Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
178252723Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
179252723Sdim        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
180252723Sdim                              new IgnoringDiagConsumer()));
181252723Sdim    std::string OriginalFile =
182252723Sdim        ASTReader::getOriginalSourceFile(PPOpts.ImplicitPCHInclude,
183252723Sdim                                         FileMgr, *Diags);
184252723Sdim    if (!OriginalFile.empty())
185252723Sdim      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
186252723Sdim    PPOpts.ImplicitPCHInclude.clear();
187252723Sdim  }
188252723Sdim  // FIXME: Get the original header of a PTH as well.
189252723Sdim  CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear();
190224135Sdim  std::string define = getARCMTMacroName();
191224135Sdim  define += '=';
192224135Sdim  CInvok->getPreprocessorOpts().addMacroDef(define);
193235633Sdim  CInvok->getLangOpts()->ObjCAutoRefCount = true;
194235633Sdim  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
195224135Sdim  CInvok->getDiagnosticOpts().ErrorLimit = 0;
196245431Sdim  CInvok->getDiagnosticOpts().PedanticErrors = 0;
197224135Sdim
198245431Sdim  // Ignore -Werror flags when migrating.
199245431Sdim  std::vector<std::string> WarnOpts;
200245431Sdim  for (std::vector<std::string>::iterator
201245431Sdim         I = CInvok->getDiagnosticOpts().Warnings.begin(),
202245431Sdim         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
203245431Sdim    if (!StringRef(*I).startswith("error"))
204245431Sdim      WarnOpts.push_back(*I);
205245431Sdim  }
206245431Sdim  WarnOpts.push_back("error=arc-unsafe-retained-assign");
207245431Sdim  CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts);
208245431Sdim
209245431Sdim  CInvok->getLangOpts()->ObjCARCWeak = HasARCRuntime(origCI);
210245431Sdim
211224135Sdim  return CInvok.take();
212224135Sdim}
213224135Sdim
214226890Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags,
215245431Sdim                                   DiagnosticOptions *diagOpts,
216226890Sdim                                   Preprocessor &PP) {
217226890Sdim  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
218235633Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
219235633Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
220245431Sdim      new DiagnosticsEngine(DiagID, diagOpts, &printer,
221245431Sdim                            /*ShouldOwnClient=*/false));
222226890Sdim  Diags->setSourceManager(&PP.getSourceManager());
223226890Sdim
224235633Sdim  printer.BeginSourceFile(PP.getLangOpts(), &PP);
225226890Sdim  arcDiags.reportDiagnostics(*Diags);
226226890Sdim  printer.EndSourceFile();
227226890Sdim}
228226890Sdim
229224135Sdim//===----------------------------------------------------------------------===//
230224135Sdim// checkForManualIssues.
231224135Sdim//===----------------------------------------------------------------------===//
232224135Sdim
233224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI,
234235633Sdim                                 const FrontendInputFile &Input,
235226890Sdim                                 DiagnosticConsumer *DiagClient,
236226890Sdim                                 bool emitPremigrationARCErrors,
237226890Sdim                                 StringRef plistOut) {
238235633Sdim  if (!origCI.getLangOpts()->ObjC1)
239224135Sdim    return false;
240224135Sdim
241235633Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
242235633Sdim  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
243235633Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
244235633Sdim
245235633Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
246235633Sdim                                                                     NoFinalizeRemoval);
247224135Sdim  assert(!transforms.empty());
248224135Sdim
249235633Sdim  OwningPtr<CompilerInvocation> CInvok;
250224135Sdim  CInvok.reset(createInvocationForMigration(origCI));
251224135Sdim  CInvok->getFrontendOpts().Inputs.clear();
252235633Sdim  CInvok->getFrontendOpts().Inputs.push_back(Input);
253224135Sdim
254224135Sdim  CapturedDiagList capturedDiags;
255224135Sdim
256224135Sdim  assert(DiagClient);
257235633Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
258235633Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
259245431Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
260245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
261224135Sdim
262224135Sdim  // Filter of all diagnostics.
263245431Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
264224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
265224135Sdim
266235633Sdim  OwningPtr<ASTUnit> Unit(
267224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
268245431Sdim  if (!Unit) {
269245431Sdim    errRec.FinishCapture();
270224135Sdim    return true;
271245431Sdim  }
272224135Sdim
273224135Sdim  // Don't filter diagnostics anymore.
274224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
275224135Sdim
276224135Sdim  ASTContext &Ctx = Unit->getASTContext();
277224135Sdim
278224135Sdim  if (Diags->hasFatalErrorOccurred()) {
279224135Sdim    Diags->Reset();
280235633Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
281224135Sdim    capturedDiags.reportDiagnostics(*Diags);
282224135Sdim    DiagClient->EndSourceFile();
283245431Sdim    errRec.FinishCapture();
284224135Sdim    return true;
285224135Sdim  }
286224135Sdim
287226890Sdim  if (emitPremigrationARCErrors)
288245431Sdim    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
289226890Sdim                           Unit->getPreprocessor());
290226890Sdim  if (!plistOut.empty()) {
291226890Sdim    SmallVector<StoredDiagnostic, 8> arcDiags;
292226890Sdim    for (CapturedDiagList::iterator
293226890Sdim           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
294226890Sdim      arcDiags.push_back(*I);
295226890Sdim    writeARCDiagsToPlist(plistOut, arcDiags,
296235633Sdim                         Ctx.getSourceManager(), Ctx.getLangOpts());
297226890Sdim  }
298226890Sdim
299224135Sdim  // After parsing of source files ended, we want to reuse the
300224135Sdim  // diagnostics objects to emit further diagnostics.
301226890Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
302224135Sdim  // diagnostics with source range information are emitted only in between
303224135Sdim  // BeginSourceFile() and EndSourceFile().
304235633Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
305224135Sdim
306224135Sdim  // No macros will be added since we are just checking and we won't modify
307224135Sdim  // source code.
308224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
309224135Sdim
310224135Sdim  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
311252723Sdim  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
312252723Sdim                     ARCMTMacroLocs);
313235633Sdim  pass.setNSAllocReallocError(NoNSAllocReallocError);
314235633Sdim  pass.setNoFinalizeRemoval(NoFinalizeRemoval);
315224135Sdim
316224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i)
317224135Sdim    transforms[i](pass);
318224135Sdim
319224135Sdim  capturedDiags.reportDiagnostics(*Diags);
320224135Sdim
321224135Sdim  DiagClient->EndSourceFile();
322245431Sdim  errRec.FinishCapture();
323224135Sdim
324226890Sdim  return capturedDiags.hasErrors() || testAct.hasReportedErrors();
325224135Sdim}
326224135Sdim
327224135Sdim//===----------------------------------------------------------------------===//
328224135Sdim// applyTransformations.
329224135Sdim//===----------------------------------------------------------------------===//
330224135Sdim
331224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI,
332235633Sdim                            const FrontendInputFile &Input,
333226890Sdim                            DiagnosticConsumer *DiagClient,
334226890Sdim                            StringRef outputDir,
335226890Sdim                            bool emitPremigrationARCErrors,
336226890Sdim                            StringRef plistOut) {
337235633Sdim  if (!origCI.getLangOpts()->ObjC1)
338224135Sdim    return false;
339224135Sdim
340235633Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
341235633Sdim
342224135Sdim  // Make sure checking is successful first.
343224135Sdim  CompilerInvocation CInvokForCheck(origCI);
344235633Sdim  if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient,
345226890Sdim                                  emitPremigrationARCErrors, plistOut))
346224135Sdim    return true;
347224135Sdim
348224135Sdim  CompilerInvocation CInvok(origCI);
349224135Sdim  CInvok.getFrontendOpts().Inputs.clear();
350235633Sdim  CInvok.getFrontendOpts().Inputs.push_back(Input);
351224135Sdim
352224135Sdim  MigrationProcess migration(CInvok, DiagClient, outputDir);
353235633Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
354224135Sdim
355235633Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
356235633Sdim                                                                     NoFinalizeRemoval);
357224135Sdim  assert(!transforms.empty());
358224135Sdim
359224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i) {
360224135Sdim    bool err = migration.applyTransform(transforms[i]);
361224135Sdim    if (err) return true;
362224135Sdim  }
363224135Sdim
364235633Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
365235633Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
366245431Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
367245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
368224135Sdim
369224135Sdim  if (outputDir.empty()) {
370235633Sdim    origCI.getLangOpts()->ObjCAutoRefCount = true;
371224135Sdim    return migration.getRemapper().overwriteOriginal(*Diags);
372224135Sdim  } else {
373224135Sdim    return migration.getRemapper().flushToDisk(outputDir, *Diags);
374224135Sdim  }
375224135Sdim}
376224135Sdim
377224135Sdimbool arcmt::applyTransformations(CompilerInvocation &origCI,
378235633Sdim                                 const FrontendInputFile &Input,
379226890Sdim                                 DiagnosticConsumer *DiagClient) {
380235633Sdim  return applyTransforms(origCI, Input, DiagClient,
381226890Sdim                         StringRef(), false, StringRef());
382224135Sdim}
383224135Sdim
384224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
385235633Sdim                                      const FrontendInputFile &Input,
386226890Sdim                                      DiagnosticConsumer *DiagClient,
387226890Sdim                                      StringRef outputDir,
388226890Sdim                                      bool emitPremigrationARCErrors,
389226890Sdim                                      StringRef plistOut) {
390224135Sdim  assert(!outputDir.empty() && "Expected output directory path");
391235633Sdim  return applyTransforms(origCI, Input, DiagClient,
392226890Sdim                         outputDir, emitPremigrationARCErrors, plistOut);
393224135Sdim}
394224135Sdim
395224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
396224135Sdim                                  remap,
397226890Sdim                              StringRef outputDir,
398226890Sdim                              DiagnosticConsumer *DiagClient) {
399224135Sdim  assert(!outputDir.empty());
400224135Sdim
401235633Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
402235633Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
403245431Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
404245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
405224135Sdim
406224135Sdim  FileRemapper remapper;
407224135Sdim  bool err = remapper.initFromDisk(outputDir, *Diags,
408224135Sdim                                   /*ignoreIfFilesChanged=*/true);
409224135Sdim  if (err)
410224135Sdim    return true;
411224135Sdim
412235633Sdim  PreprocessorOptions PPOpts;
413235633Sdim  remapper.applyMappings(PPOpts);
414235633Sdim  remap = PPOpts.RemappedFiles;
415224135Sdim
416224135Sdim  return false;
417224135Sdim}
418224135Sdim
419235633Sdimbool arcmt::getFileRemappingsFromFileList(
420235633Sdim                        std::vector<std::pair<std::string,std::string> > &remap,
421235633Sdim                        ArrayRef<StringRef> remapFiles,
422235633Sdim                        DiagnosticConsumer *DiagClient) {
423235633Sdim  bool hasErrorOccurred = false;
424235633Sdim  llvm::StringMap<bool> Uniquer;
425235633Sdim
426252723Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
427252723Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
428245431Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
429245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
430235633Sdim
431235633Sdim  for (ArrayRef<StringRef>::iterator
432235633Sdim         I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
433235633Sdim    StringRef file = *I;
434235633Sdim
435235633Sdim    FileRemapper remapper;
436235633Sdim    bool err = remapper.initFromFile(file, *Diags,
437235633Sdim                                     /*ignoreIfFilesChanged=*/true);
438235633Sdim    hasErrorOccurred = hasErrorOccurred || err;
439235633Sdim    if (err)
440235633Sdim      continue;
441235633Sdim
442235633Sdim    PreprocessorOptions PPOpts;
443235633Sdim    remapper.applyMappings(PPOpts);
444235633Sdim    for (PreprocessorOptions::remapped_file_iterator
445235633Sdim           RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end();
446235633Sdim           RI != RE; ++RI) {
447235633Sdim      bool &inserted = Uniquer[RI->first];
448235633Sdim      if (inserted)
449235633Sdim        continue;
450235633Sdim      inserted = true;
451235633Sdim      remap.push_back(*RI);
452235633Sdim    }
453235633Sdim  }
454235633Sdim
455235633Sdim  return hasErrorOccurred;
456235633Sdim}
457235633Sdim
458224135Sdim//===----------------------------------------------------------------------===//
459224135Sdim// CollectTransformActions.
460224135Sdim//===----------------------------------------------------------------------===//
461224135Sdim
462224135Sdimnamespace {
463224135Sdim
464224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
465224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
466224135Sdim
467224135Sdimpublic:
468224135Sdim  ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
469224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
470224135Sdim
471252723Sdim  virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD,
472252723Sdim                            SourceRange Range, const MacroArgs *Args) {
473224135Sdim    if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
474224135Sdim      ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
475224135Sdim  }
476224135Sdim};
477224135Sdim
478224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction {
479224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
480224135Sdim
481224135Sdimpublic:
482224135Sdim  ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
483224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
484224135Sdim
485224135Sdim  virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
486226890Sdim                                         StringRef InFile) {
487224135Sdim    CI.getPreprocessor().addPPCallbacks(
488224135Sdim                              new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
489224135Sdim    return new ASTConsumer();
490224135Sdim  }
491224135Sdim};
492224135Sdim
493224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver {
494224135Sdim  Rewriter &rewriter;
495224135Sdim  MigrationProcess::RewriteListener *Listener;
496224135Sdim
497224135Sdimpublic:
498224135Sdim  RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
499224135Sdim                     MigrationProcess::RewriteListener *listener)
500245431Sdim    : rewriter(rewriter), Listener(listener) {
501224135Sdim    if (Listener)
502224135Sdim      Listener->start(ctx);
503224135Sdim  }
504224135Sdim  ~RewritesApplicator() {
505224135Sdim    if (Listener)
506224135Sdim      Listener->finish();
507224135Sdim  }
508224135Sdim
509226890Sdim  virtual void insert(SourceLocation loc, StringRef text) {
510224135Sdim    bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
511224135Sdim                                   /*indentNewLines=*/true);
512224135Sdim    if (!err && Listener)
513224135Sdim      Listener->insert(loc, text);
514224135Sdim  }
515224135Sdim
516224135Sdim  virtual void remove(CharSourceRange range) {
517224135Sdim    Rewriter::RewriteOptions removeOpts;
518224135Sdim    removeOpts.IncludeInsertsAtBeginOfRange = false;
519224135Sdim    removeOpts.IncludeInsertsAtEndOfRange = false;
520224135Sdim    removeOpts.RemoveLineIfEmpty = true;
521224135Sdim
522224135Sdim    bool err = rewriter.RemoveText(range, removeOpts);
523224135Sdim    if (!err && Listener)
524224135Sdim      Listener->remove(range);
525224135Sdim  }
526224135Sdim
527224135Sdim  virtual void increaseIndentation(CharSourceRange range,
528224135Sdim                                    SourceLocation parentIndent) {
529224135Sdim    rewriter.IncreaseIndentation(range, parentIndent);
530224135Sdim  }
531224135Sdim};
532224135Sdim
533224135Sdim} // end anonymous namespace.
534224135Sdim
535224135Sdim/// \brief Anchor for VTable.
536224135SdimMigrationProcess::RewriteListener::~RewriteListener() { }
537224135Sdim
538224135SdimMigrationProcess::MigrationProcess(const CompilerInvocation &CI,
539226890Sdim                                   DiagnosticConsumer *diagClient,
540226890Sdim                                   StringRef outputDir)
541263509Sdim  : OrigCI(CI), DiagClient(diagClient), HadARCErrors(false) {
542224135Sdim  if (!outputDir.empty()) {
543235633Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
544235633Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
545245431Sdim      new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
546245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
547224135Sdim    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
548224135Sdim  }
549224135Sdim}
550224135Sdim
551224135Sdimbool MigrationProcess::applyTransform(TransformFn trans,
552224135Sdim                                      RewriteListener *listener) {
553235633Sdim  OwningPtr<CompilerInvocation> CInvok;
554224135Sdim  CInvok.reset(createInvocationForMigration(OrigCI));
555224135Sdim  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
556224135Sdim
557235633Sdim  Remapper.applyMappings(CInvok->getPreprocessorOpts());
558224135Sdim
559224135Sdim  CapturedDiagList capturedDiags;
560224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
561224135Sdim
562224135Sdim  assert(DiagClient);
563235633Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
564235633Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
565245431Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
566245431Sdim                            DiagClient, /*ShouldOwnClient=*/false));
567224135Sdim
568224135Sdim  // Filter of all diagnostics.
569245431Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
570224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
571224135Sdim
572235633Sdim  OwningPtr<ARCMTMacroTrackerAction> ASTAction;
573224135Sdim  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
574224135Sdim
575235633Sdim  OwningPtr<ASTUnit> Unit(
576224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
577224135Sdim                                                ASTAction.get()));
578245431Sdim  if (!Unit) {
579245431Sdim    errRec.FinishCapture();
580224135Sdim    return true;
581245431Sdim  }
582224135Sdim  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
583224135Sdim
584263509Sdim  HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
585263509Sdim
586224135Sdim  // Don't filter diagnostics anymore.
587224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
588224135Sdim
589224135Sdim  ASTContext &Ctx = Unit->getASTContext();
590224135Sdim
591224135Sdim  if (Diags->hasFatalErrorOccurred()) {
592224135Sdim    Diags->Reset();
593235633Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
594224135Sdim    capturedDiags.reportDiagnostics(*Diags);
595224135Sdim    DiagClient->EndSourceFile();
596245431Sdim    errRec.FinishCapture();
597224135Sdim    return true;
598224135Sdim  }
599224135Sdim
600224135Sdim  // After parsing of source files ended, we want to reuse the
601224135Sdim  // diagnostics objects to emit further diagnostics.
602226890Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
603224135Sdim  // diagnostics with source range information are emitted only in between
604224135Sdim  // BeginSourceFile() and EndSourceFile().
605235633Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
606224135Sdim
607235633Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
608224135Sdim  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
609235633Sdim  MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
610252723Sdim                     Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
611224135Sdim
612224135Sdim  trans(pass);
613224135Sdim
614224135Sdim  {
615224135Sdim    RewritesApplicator applicator(rewriter, Ctx, listener);
616224135Sdim    TA.applyRewrites(applicator);
617224135Sdim  }
618224135Sdim
619224135Sdim  DiagClient->EndSourceFile();
620245431Sdim  errRec.FinishCapture();
621224135Sdim
622224135Sdim  if (DiagClient->getNumErrors())
623224135Sdim    return true;
624224135Sdim
625224135Sdim  for (Rewriter::buffer_iterator
626224135Sdim        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
627224135Sdim    FileID FID = I->first;
628224135Sdim    RewriteBuffer &buf = I->second;
629224135Sdim    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
630224135Sdim    assert(file);
631224135Sdim    std::string newFname = file->getName();
632224135Sdim    newFname += "-trans";
633235633Sdim    SmallString<512> newText;
634224135Sdim    llvm::raw_svector_ostream vecOS(newText);
635224135Sdim    buf.write(vecOS);
636224135Sdim    vecOS.flush();
637224135Sdim    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
638226890Sdim                   StringRef(newText.data(), newText.size()), newFname);
639235633Sdim    SmallString<64> filePath(file->getName());
640224135Sdim    Unit->getFileManager().FixupRelativePath(filePath);
641224135Sdim    Remapper.remap(filePath.str(), memBuf);
642224135Sdim  }
643224135Sdim
644224135Sdim  return false;
645224135Sdim}
646