ARCMT.cpp revision 261991
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"
11249423Sdim#include "clang/AST/ASTConsumer.h"
12249423Sdim#include "clang/Basic/DiagnosticCategories.h"
13224135Sdim#include "clang/Frontend/ASTUnit.h"
14224135Sdim#include "clang/Frontend/CompilerInstance.h"
15234353Sdim#include "clang/Frontend/FrontendAction.h"
16226633Sdim#include "clang/Frontend/TextDiagnosticPrinter.h"
17224135Sdim#include "clang/Frontend/Utils.h"
18249423Sdim#include "clang/Lex/Preprocessor.h"
19243830Sdim#include "clang/Rewrite/Core/Rewriter.h"
20224135Sdim#include "clang/Sema/SemaDiagnostic.h"
21249423Sdim#include "clang/Serialization/ASTReader.h"
22249423Sdim#include "llvm/ADT/Triple.h"
23224135Sdim#include "llvm/Support/MemoryBuffer.h"
24224135Sdimusing namespace clang;
25224135Sdimusing namespace arcmt;
26224135Sdim
27226633Sdimbool 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++;
43249423Sdim      if (eraseS->getLevel() != DiagnosticsEngine::Note)
44249423Sdim        while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
45249423Sdim          ++I;
46224135Sdim      // Clear the diagnostic and any notes following it.
47243830Sdim      I = List.erase(eraseS, I);
48224135Sdim      continue;
49224135Sdim    }
50224135Sdim
51224135Sdim    ++I;
52224135Sdim  }
53224135Sdim
54224135Sdim  return cleared;
55224135Sdim}
56224135Sdim
57226633Sdimbool 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
79226633Sdimvoid 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)
86226633Sdim    if (I->getLevel() >= DiagnosticsEngine::Error)
87224135Sdim      return true;
88224135Sdim
89224135Sdim  return false;
90224135Sdim}
91224135Sdim
92224135Sdimnamespace {
93224135Sdim
94226633Sdimclass CaptureDiagnosticConsumer : public DiagnosticConsumer {
95226633Sdim  DiagnosticsEngine &Diags;
96239462Sdim  DiagnosticConsumer &DiagClient;
97224135Sdim  CapturedDiagList &CapturedDiags;
98239462Sdim  bool HasBegunSourceFile;
99224135Sdimpublic:
100226633Sdim  CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
101239462Sdim                            DiagnosticConsumer &client,
102239462Sdim                            CapturedDiagList &capturedDiags)
103239462Sdim    : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
104239462Sdim      HasBegunSourceFile(false) { }
105224135Sdim
106239462Sdim  virtual void BeginSourceFile(const LangOptions &Opts,
107239462Sdim                               const Preprocessor *PP) {
108239462Sdim    // Pass BeginSourceFile message onto DiagClient on first call.
109239462Sdim    // The corresponding EndSourceFile call will be made from an
110239462Sdim    // explicit call to FinishCapture.
111239462Sdim    if (!HasBegunSourceFile) {
112239462Sdim      DiagClient.BeginSourceFile(Opts, PP);
113239462Sdim      HasBegunSourceFile = true;
114239462Sdim    }
115239462Sdim  }
116239462Sdim
117239462Sdim  void FinishCapture() {
118239462Sdim    // Call EndSourceFile on DiagClient on completion of capture to
119239462Sdim    // enable VerifyDiagnosticConsumer to check diagnostics *after*
120239462Sdim    // it has received the diagnostic list.
121239462Sdim    if (HasBegunSourceFile) {
122239462Sdim      DiagClient.EndSourceFile();
123239462Sdim      HasBegunSourceFile = false;
124239462Sdim    }
125239462Sdim  }
126239462Sdim
127239462Sdim  virtual ~CaptureDiagnosticConsumer() {
128239462Sdim    assert(!HasBegunSourceFile && "FinishCapture not called!");
129239462Sdim  }
130239462Sdim
131226633Sdim  virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
132226633Sdim                                const Diagnostic &Info) {
133234353Sdim    if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
134226633Sdim        level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
135249423Sdim      if (Info.getLocation().isValid())
136249423Sdim        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
153261991Sdim  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
168226633Sdimstatic CompilerInvocation *
169226633SdimcreateInvocationForMigration(CompilerInvocation &origCI) {
170234353Sdim  OwningPtr<CompilerInvocation> CInvok;
171224135Sdim  CInvok.reset(new CompilerInvocation(origCI));
172249423Sdim  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
173249423Sdim  if (!PPOpts.ImplicitPCHInclude.empty()) {
174249423Sdim    // We can't use a PCH because it was likely built in non-ARC mode and we
175249423Sdim    // want to parse in ARC. Include the original header.
176249423Sdim    FileManager FileMgr(origCI.getFileSystemOpts());
177249423Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
178249423Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
179249423Sdim        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
180249423Sdim                              new IgnoringDiagConsumer()));
181249423Sdim    std::string OriginalFile =
182249423Sdim        ASTReader::getOriginalSourceFile(PPOpts.ImplicitPCHInclude,
183249423Sdim                                         FileMgr, *Diags);
184249423Sdim    if (!OriginalFile.empty())
185249423Sdim      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
186249423Sdim    PPOpts.ImplicitPCHInclude.clear();
187249423Sdim  }
188249423Sdim  // FIXME: Get the original header of a PTH as well.
189249423Sdim  CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear();
190224135Sdim  std::string define = getARCMTMacroName();
191224135Sdim  define += '=';
192224135Sdim  CInvok->getPreprocessorOpts().addMacroDef(define);
193234353Sdim  CInvok->getLangOpts()->ObjCAutoRefCount = true;
194234353Sdim  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
195224135Sdim  CInvok->getDiagnosticOpts().ErrorLimit = 0;
196239462Sdim  CInvok->getDiagnosticOpts().PedanticErrors = 0;
197239462Sdim
198239462Sdim  // Ignore -Werror flags when migrating.
199239462Sdim  std::vector<std::string> WarnOpts;
200239462Sdim  for (std::vector<std::string>::iterator
201239462Sdim         I = CInvok->getDiagnosticOpts().Warnings.begin(),
202239462Sdim         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
203239462Sdim    if (!StringRef(*I).startswith("error"))
204239462Sdim      WarnOpts.push_back(*I);
205239462Sdim  }
206239462Sdim  WarnOpts.push_back("error=arc-unsafe-retained-assign");
207239462Sdim  CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts);
208239462Sdim
209243830Sdim  CInvok->getLangOpts()->ObjCARCWeak = HasARCRuntime(origCI);
210224135Sdim
211224135Sdim  return CInvok.take();
212224135Sdim}
213224135Sdim
214226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags,
215243830Sdim                                   DiagnosticOptions *diagOpts,
216226633Sdim                                   Preprocessor &PP) {
217226633Sdim  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
218234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
219234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
220243830Sdim      new DiagnosticsEngine(DiagID, diagOpts, &printer,
221243830Sdim                            /*ShouldOwnClient=*/false));
222226633Sdim  Diags->setSourceManager(&PP.getSourceManager());
223226633Sdim
224234353Sdim  printer.BeginSourceFile(PP.getLangOpts(), &PP);
225226633Sdim  arcDiags.reportDiagnostics(*Diags);
226226633Sdim  printer.EndSourceFile();
227226633Sdim}
228226633Sdim
229224135Sdim//===----------------------------------------------------------------------===//
230224135Sdim// checkForManualIssues.
231224135Sdim//===----------------------------------------------------------------------===//
232224135Sdim
233224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI,
234234353Sdim                                 const FrontendInputFile &Input,
235226633Sdim                                 DiagnosticConsumer *DiagClient,
236226633Sdim                                 bool emitPremigrationARCErrors,
237226633Sdim                                 StringRef plistOut) {
238234353Sdim  if (!origCI.getLangOpts()->ObjC1)
239224135Sdim    return false;
240224135Sdim
241234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
242234353Sdim  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
243234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
244234353Sdim
245234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
246234353Sdim                                                                     NoFinalizeRemoval);
247224135Sdim  assert(!transforms.empty());
248224135Sdim
249234353Sdim  OwningPtr<CompilerInvocation> CInvok;
250224135Sdim  CInvok.reset(createInvocationForMigration(origCI));
251224135Sdim  CInvok->getFrontendOpts().Inputs.clear();
252234353Sdim  CInvok->getFrontendOpts().Inputs.push_back(Input);
253224135Sdim
254224135Sdim  CapturedDiagList capturedDiags;
255224135Sdim
256224135Sdim  assert(DiagClient);
257234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
258234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
259243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
260243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
261224135Sdim
262224135Sdim  // Filter of all diagnostics.
263239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
264224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
265224135Sdim
266234353Sdim  OwningPtr<ASTUnit> Unit(
267224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
268239462Sdim  if (!Unit) {
269239462Sdim    errRec.FinishCapture();
270224135Sdim    return true;
271239462Sdim  }
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();
280234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
281224135Sdim    capturedDiags.reportDiagnostics(*Diags);
282224135Sdim    DiagClient->EndSourceFile();
283239462Sdim    errRec.FinishCapture();
284224135Sdim    return true;
285224135Sdim  }
286224135Sdim
287226633Sdim  if (emitPremigrationARCErrors)
288243830Sdim    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
289226633Sdim                           Unit->getPreprocessor());
290226633Sdim  if (!plistOut.empty()) {
291226633Sdim    SmallVector<StoredDiagnostic, 8> arcDiags;
292226633Sdim    for (CapturedDiagList::iterator
293226633Sdim           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
294226633Sdim      arcDiags.push_back(*I);
295226633Sdim    writeARCDiagsToPlist(plistOut, arcDiags,
296234353Sdim                         Ctx.getSourceManager(), Ctx.getLangOpts());
297226633Sdim  }
298226633Sdim
299224135Sdim  // After parsing of source files ended, we want to reuse the
300224135Sdim  // diagnostics objects to emit further diagnostics.
301226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
302224135Sdim  // diagnostics with source range information are emitted only in between
303224135Sdim  // BeginSourceFile() and EndSourceFile().
304234353Sdim  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());
311249423Sdim  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
312249423Sdim                     ARCMTMacroLocs);
313234353Sdim  pass.setNSAllocReallocError(NoNSAllocReallocError);
314234353Sdim  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();
322239462Sdim  errRec.FinishCapture();
323224135Sdim
324226633Sdim  return capturedDiags.hasErrors() || testAct.hasReportedErrors();
325224135Sdim}
326224135Sdim
327224135Sdim//===----------------------------------------------------------------------===//
328224135Sdim// applyTransformations.
329224135Sdim//===----------------------------------------------------------------------===//
330224135Sdim
331224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI,
332234353Sdim                            const FrontendInputFile &Input,
333226633Sdim                            DiagnosticConsumer *DiagClient,
334226633Sdim                            StringRef outputDir,
335226633Sdim                            bool emitPremigrationARCErrors,
336226633Sdim                            StringRef plistOut) {
337234353Sdim  if (!origCI.getLangOpts()->ObjC1)
338224135Sdim    return false;
339224135Sdim
340234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
341234353Sdim
342224135Sdim  // Make sure checking is successful first.
343224135Sdim  CompilerInvocation CInvokForCheck(origCI);
344234353Sdim  if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient,
345226633Sdim                                  emitPremigrationARCErrors, plistOut))
346224135Sdim    return true;
347224135Sdim
348224135Sdim  CompilerInvocation CInvok(origCI);
349224135Sdim  CInvok.getFrontendOpts().Inputs.clear();
350234353Sdim  CInvok.getFrontendOpts().Inputs.push_back(Input);
351224135Sdim
352224135Sdim  MigrationProcess migration(CInvok, DiagClient, outputDir);
353234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
354224135Sdim
355234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
356234353Sdim                                                                     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
364234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
365234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
366243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
367243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
368224135Sdim
369224135Sdim  if (outputDir.empty()) {
370234353Sdim    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,
378234353Sdim                                 const FrontendInputFile &Input,
379226633Sdim                                 DiagnosticConsumer *DiagClient) {
380234353Sdim  return applyTransforms(origCI, Input, DiagClient,
381226633Sdim                         StringRef(), false, StringRef());
382224135Sdim}
383224135Sdim
384224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
385234353Sdim                                      const FrontendInputFile &Input,
386226633Sdim                                      DiagnosticConsumer *DiagClient,
387226633Sdim                                      StringRef outputDir,
388226633Sdim                                      bool emitPremigrationARCErrors,
389226633Sdim                                      StringRef plistOut) {
390224135Sdim  assert(!outputDir.empty() && "Expected output directory path");
391234353Sdim  return applyTransforms(origCI, Input, DiagClient,
392226633Sdim                         outputDir, emitPremigrationARCErrors, plistOut);
393224135Sdim}
394224135Sdim
395224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
396224135Sdim                                  remap,
397226633Sdim                              StringRef outputDir,
398226633Sdim                              DiagnosticConsumer *DiagClient) {
399224135Sdim  assert(!outputDir.empty());
400224135Sdim
401234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
402234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
403243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
404243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
405224135Sdim
406224135Sdim  FileRemapper remapper;
407224135Sdim  bool err = remapper.initFromDisk(outputDir, *Diags,
408224135Sdim                                   /*ignoreIfFilesChanged=*/true);
409224135Sdim  if (err)
410224135Sdim    return true;
411224135Sdim
412234353Sdim  PreprocessorOptions PPOpts;
413234353Sdim  remapper.applyMappings(PPOpts);
414234353Sdim  remap = PPOpts.RemappedFiles;
415224135Sdim
416224135Sdim  return false;
417224135Sdim}
418224135Sdim
419234353Sdimbool arcmt::getFileRemappingsFromFileList(
420234353Sdim                        std::vector<std::pair<std::string,std::string> > &remap,
421234353Sdim                        ArrayRef<StringRef> remapFiles,
422234353Sdim                        DiagnosticConsumer *DiagClient) {
423234353Sdim  bool hasErrorOccurred = false;
424234353Sdim  llvm::StringMap<bool> Uniquer;
425234353Sdim
426249423Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
427249423Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
428243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
429243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
430234353Sdim
431234353Sdim  for (ArrayRef<StringRef>::iterator
432234353Sdim         I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
433234353Sdim    StringRef file = *I;
434234353Sdim
435234353Sdim    FileRemapper remapper;
436234353Sdim    bool err = remapper.initFromFile(file, *Diags,
437234353Sdim                                     /*ignoreIfFilesChanged=*/true);
438234353Sdim    hasErrorOccurred = hasErrorOccurred || err;
439234353Sdim    if (err)
440234353Sdim      continue;
441234353Sdim
442234353Sdim    PreprocessorOptions PPOpts;
443234353Sdim    remapper.applyMappings(PPOpts);
444234353Sdim    for (PreprocessorOptions::remapped_file_iterator
445234353Sdim           RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end();
446234353Sdim           RI != RE; ++RI) {
447234353Sdim      bool &inserted = Uniquer[RI->first];
448234353Sdim      if (inserted)
449234353Sdim        continue;
450234353Sdim      inserted = true;
451234353Sdim      remap.push_back(*RI);
452234353Sdim    }
453234353Sdim  }
454234353Sdim
455234353Sdim  return hasErrorOccurred;
456234353Sdim}
457234353Sdim
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
471249423Sdim  virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD,
472251662Sdim                            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,
486226633Sdim                                         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)
500239462Sdim    : rewriter(rewriter), Listener(listener) {
501224135Sdim    if (Listener)
502224135Sdim      Listener->start(ctx);
503224135Sdim  }
504224135Sdim  ~RewritesApplicator() {
505224135Sdim    if (Listener)
506224135Sdim      Listener->finish();
507224135Sdim  }
508224135Sdim
509226633Sdim  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,
539226633Sdim                                   DiagnosticConsumer *diagClient,
540226633Sdim                                   StringRef outputDir)
541261991Sdim  : OrigCI(CI), DiagClient(diagClient), HadARCErrors(false) {
542224135Sdim  if (!outputDir.empty()) {
543234353Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
544234353Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
545243830Sdim      new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
546243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
547224135Sdim    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
548224135Sdim  }
549224135Sdim}
550224135Sdim
551224135Sdimbool MigrationProcess::applyTransform(TransformFn trans,
552224135Sdim                                      RewriteListener *listener) {
553234353Sdim  OwningPtr<CompilerInvocation> CInvok;
554224135Sdim  CInvok.reset(createInvocationForMigration(OrigCI));
555224135Sdim  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
556224135Sdim
557234353Sdim  Remapper.applyMappings(CInvok->getPreprocessorOpts());
558224135Sdim
559224135Sdim  CapturedDiagList capturedDiags;
560224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
561224135Sdim
562224135Sdim  assert(DiagClient);
563234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
564234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
565243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
566243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
567224135Sdim
568224135Sdim  // Filter of all diagnostics.
569239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
570224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
571224135Sdim
572234353Sdim  OwningPtr<ARCMTMacroTrackerAction> ASTAction;
573224135Sdim  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
574224135Sdim
575234353Sdim  OwningPtr<ASTUnit> Unit(
576224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
577224135Sdim                                                ASTAction.get()));
578239462Sdim  if (!Unit) {
579239462Sdim    errRec.FinishCapture();
580224135Sdim    return true;
581239462Sdim  }
582224135Sdim  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
583224135Sdim
584261991Sdim  HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
585261991Sdim
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();
593234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
594224135Sdim    capturedDiags.reportDiagnostics(*Diags);
595224135Sdim    DiagClient->EndSourceFile();
596239462Sdim    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.
602226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
603224135Sdim  // diagnostics with source range information are emitted only in between
604224135Sdim  // BeginSourceFile() and EndSourceFile().
605234353Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
606224135Sdim
607234353Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
608224135Sdim  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
609234353Sdim  MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
610249423Sdim                     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();
620239462Sdim  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";
633234353Sdim    SmallString<512> newText;
634224135Sdim    llvm::raw_svector_ostream vecOS(newText);
635224135Sdim    buf.write(vecOS);
636224135Sdim    vecOS.flush();
637224135Sdim    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
638226633Sdim                   StringRef(newText.data(), newText.size()), newFname);
639234353Sdim    SmallString<64> filePath(file->getName());
640224135Sdim    Unit->getFileManager().FixupRelativePath(filePath);
641224135Sdim    Remapper.remap(filePath.str(), memBuf);
642224135Sdim  }
643224135Sdim
644224135Sdim  return false;
645224135Sdim}
646