ARCMT.cpp revision 249423
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  }
143226633Sdim
144226633Sdim  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
145226633Sdim    // Just drop any diagnostics that come from cloned consumers; they'll
146226633Sdim    // have different source managers anyway.
147226633Sdim    return new IgnoringDiagConsumer();
148226633Sdim  }
149224135Sdim};
150224135Sdim
151224135Sdim} // end anonymous namespace
152224135Sdim
153224135Sdimstatic bool HasARCRuntime(CompilerInvocation &origCI) {
154224135Sdim  // This duplicates some functionality from Darwin::AddDeploymentTarget
155224135Sdim  // but this function is well defined, so keep it decoupled from the driver
156224135Sdim  // and avoid unrelated complications.
157224135Sdim  llvm::Triple triple(origCI.getTargetOpts().Triple);
158224135Sdim
159224135Sdim  if (triple.getOS() == llvm::Triple::IOS)
160224135Sdim    return triple.getOSMajorVersion() >= 5;
161224135Sdim
162224135Sdim  if (triple.getOS() == llvm::Triple::Darwin)
163224135Sdim    return triple.getOSMajorVersion() >= 11;
164224135Sdim
165224135Sdim  if (triple.getOS() == llvm::Triple::MacOSX) {
166224135Sdim    unsigned Major, Minor, Micro;
167224135Sdim    triple.getOSVersion(Major, Minor, Micro);
168224135Sdim    return Major > 10 || (Major == 10 && Minor >= 7);
169224135Sdim  }
170224135Sdim
171224135Sdim  return false;
172224135Sdim}
173224135Sdim
174226633Sdimstatic CompilerInvocation *
175226633SdimcreateInvocationForMigration(CompilerInvocation &origCI) {
176234353Sdim  OwningPtr<CompilerInvocation> CInvok;
177224135Sdim  CInvok.reset(new CompilerInvocation(origCI));
178249423Sdim  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
179249423Sdim  if (!PPOpts.ImplicitPCHInclude.empty()) {
180249423Sdim    // We can't use a PCH because it was likely built in non-ARC mode and we
181249423Sdim    // want to parse in ARC. Include the original header.
182249423Sdim    FileManager FileMgr(origCI.getFileSystemOpts());
183249423Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
184249423Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
185249423Sdim        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
186249423Sdim                              new IgnoringDiagConsumer()));
187249423Sdim    std::string OriginalFile =
188249423Sdim        ASTReader::getOriginalSourceFile(PPOpts.ImplicitPCHInclude,
189249423Sdim                                         FileMgr, *Diags);
190249423Sdim    if (!OriginalFile.empty())
191249423Sdim      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
192249423Sdim    PPOpts.ImplicitPCHInclude.clear();
193249423Sdim  }
194249423Sdim  // FIXME: Get the original header of a PTH as well.
195249423Sdim  CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear();
196224135Sdim  std::string define = getARCMTMacroName();
197224135Sdim  define += '=';
198224135Sdim  CInvok->getPreprocessorOpts().addMacroDef(define);
199234353Sdim  CInvok->getLangOpts()->ObjCAutoRefCount = true;
200234353Sdim  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
201224135Sdim  CInvok->getDiagnosticOpts().ErrorLimit = 0;
202239462Sdim  CInvok->getDiagnosticOpts().PedanticErrors = 0;
203239462Sdim
204239462Sdim  // Ignore -Werror flags when migrating.
205239462Sdim  std::vector<std::string> WarnOpts;
206239462Sdim  for (std::vector<std::string>::iterator
207239462Sdim         I = CInvok->getDiagnosticOpts().Warnings.begin(),
208239462Sdim         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
209239462Sdim    if (!StringRef(*I).startswith("error"))
210239462Sdim      WarnOpts.push_back(*I);
211239462Sdim  }
212239462Sdim  WarnOpts.push_back("error=arc-unsafe-retained-assign");
213239462Sdim  CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts);
214239462Sdim
215243830Sdim  CInvok->getLangOpts()->ObjCARCWeak = HasARCRuntime(origCI);
216224135Sdim
217224135Sdim  return CInvok.take();
218224135Sdim}
219224135Sdim
220226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags,
221243830Sdim                                   DiagnosticOptions *diagOpts,
222226633Sdim                                   Preprocessor &PP) {
223226633Sdim  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
224234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
225234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
226243830Sdim      new DiagnosticsEngine(DiagID, diagOpts, &printer,
227243830Sdim                            /*ShouldOwnClient=*/false));
228226633Sdim  Diags->setSourceManager(&PP.getSourceManager());
229226633Sdim
230234353Sdim  printer.BeginSourceFile(PP.getLangOpts(), &PP);
231226633Sdim  arcDiags.reportDiagnostics(*Diags);
232226633Sdim  printer.EndSourceFile();
233226633Sdim}
234226633Sdim
235224135Sdim//===----------------------------------------------------------------------===//
236224135Sdim// checkForManualIssues.
237224135Sdim//===----------------------------------------------------------------------===//
238224135Sdim
239224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI,
240234353Sdim                                 const FrontendInputFile &Input,
241226633Sdim                                 DiagnosticConsumer *DiagClient,
242226633Sdim                                 bool emitPremigrationARCErrors,
243226633Sdim                                 StringRef plistOut) {
244234353Sdim  if (!origCI.getLangOpts()->ObjC1)
245224135Sdim    return false;
246224135Sdim
247234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
248234353Sdim  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
249234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
250234353Sdim
251234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
252234353Sdim                                                                     NoFinalizeRemoval);
253224135Sdim  assert(!transforms.empty());
254224135Sdim
255234353Sdim  OwningPtr<CompilerInvocation> CInvok;
256224135Sdim  CInvok.reset(createInvocationForMigration(origCI));
257224135Sdim  CInvok->getFrontendOpts().Inputs.clear();
258234353Sdim  CInvok->getFrontendOpts().Inputs.push_back(Input);
259224135Sdim
260224135Sdim  CapturedDiagList capturedDiags;
261224135Sdim
262224135Sdim  assert(DiagClient);
263234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
264234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
265243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
266243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
267224135Sdim
268224135Sdim  // Filter of all diagnostics.
269239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
270224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
271224135Sdim
272234353Sdim  OwningPtr<ASTUnit> Unit(
273224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
274239462Sdim  if (!Unit) {
275239462Sdim    errRec.FinishCapture();
276224135Sdim    return true;
277239462Sdim  }
278224135Sdim
279224135Sdim  // Don't filter diagnostics anymore.
280224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
281224135Sdim
282224135Sdim  ASTContext &Ctx = Unit->getASTContext();
283224135Sdim
284224135Sdim  if (Diags->hasFatalErrorOccurred()) {
285224135Sdim    Diags->Reset();
286234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
287224135Sdim    capturedDiags.reportDiagnostics(*Diags);
288224135Sdim    DiagClient->EndSourceFile();
289239462Sdim    errRec.FinishCapture();
290224135Sdim    return true;
291224135Sdim  }
292224135Sdim
293226633Sdim  if (emitPremigrationARCErrors)
294243830Sdim    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
295226633Sdim                           Unit->getPreprocessor());
296226633Sdim  if (!plistOut.empty()) {
297226633Sdim    SmallVector<StoredDiagnostic, 8> arcDiags;
298226633Sdim    for (CapturedDiagList::iterator
299226633Sdim           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
300226633Sdim      arcDiags.push_back(*I);
301226633Sdim    writeARCDiagsToPlist(plistOut, arcDiags,
302234353Sdim                         Ctx.getSourceManager(), Ctx.getLangOpts());
303226633Sdim  }
304226633Sdim
305224135Sdim  // After parsing of source files ended, we want to reuse the
306224135Sdim  // diagnostics objects to emit further diagnostics.
307226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
308224135Sdim  // diagnostics with source range information are emitted only in between
309224135Sdim  // BeginSourceFile() and EndSourceFile().
310234353Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
311224135Sdim
312224135Sdim  // No macros will be added since we are just checking and we won't modify
313224135Sdim  // source code.
314224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
315224135Sdim
316224135Sdim  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
317249423Sdim  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
318249423Sdim                     ARCMTMacroLocs);
319234353Sdim  pass.setNSAllocReallocError(NoNSAllocReallocError);
320234353Sdim  pass.setNoFinalizeRemoval(NoFinalizeRemoval);
321224135Sdim
322224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i)
323224135Sdim    transforms[i](pass);
324224135Sdim
325224135Sdim  capturedDiags.reportDiagnostics(*Diags);
326224135Sdim
327224135Sdim  DiagClient->EndSourceFile();
328239462Sdim  errRec.FinishCapture();
329224135Sdim
330224135Sdim  // If we are migrating code that gets the '-fobjc-arc' flag, make sure
331224135Sdim  // to remove it so that we don't get errors from normal compilation.
332234353Sdim  origCI.getLangOpts()->ObjCAutoRefCount = false;
333224135Sdim
334226633Sdim  return capturedDiags.hasErrors() || testAct.hasReportedErrors();
335224135Sdim}
336224135Sdim
337224135Sdim//===----------------------------------------------------------------------===//
338224135Sdim// applyTransformations.
339224135Sdim//===----------------------------------------------------------------------===//
340224135Sdim
341224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI,
342234353Sdim                            const FrontendInputFile &Input,
343226633Sdim                            DiagnosticConsumer *DiagClient,
344226633Sdim                            StringRef outputDir,
345226633Sdim                            bool emitPremigrationARCErrors,
346226633Sdim                            StringRef plistOut) {
347234353Sdim  if (!origCI.getLangOpts()->ObjC1)
348224135Sdim    return false;
349224135Sdim
350234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
351234353Sdim
352224135Sdim  // Make sure checking is successful first.
353224135Sdim  CompilerInvocation CInvokForCheck(origCI);
354234353Sdim  if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient,
355226633Sdim                                  emitPremigrationARCErrors, plistOut))
356224135Sdim    return true;
357224135Sdim
358224135Sdim  CompilerInvocation CInvok(origCI);
359224135Sdim  CInvok.getFrontendOpts().Inputs.clear();
360234353Sdim  CInvok.getFrontendOpts().Inputs.push_back(Input);
361224135Sdim
362224135Sdim  MigrationProcess migration(CInvok, DiagClient, outputDir);
363234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
364224135Sdim
365234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
366234353Sdim                                                                     NoFinalizeRemoval);
367224135Sdim  assert(!transforms.empty());
368224135Sdim
369224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i) {
370224135Sdim    bool err = migration.applyTransform(transforms[i]);
371224135Sdim    if (err) return true;
372224135Sdim  }
373224135Sdim
374234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
375234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
376243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
377243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
378224135Sdim
379224135Sdim  if (outputDir.empty()) {
380234353Sdim    origCI.getLangOpts()->ObjCAutoRefCount = true;
381224135Sdim    return migration.getRemapper().overwriteOriginal(*Diags);
382224135Sdim  } else {
383224135Sdim    // If we are migrating code that gets the '-fobjc-arc' flag, make sure
384224135Sdim    // to remove it so that we don't get errors from normal compilation.
385234353Sdim    origCI.getLangOpts()->ObjCAutoRefCount = false;
386224135Sdim    return migration.getRemapper().flushToDisk(outputDir, *Diags);
387224135Sdim  }
388224135Sdim}
389224135Sdim
390224135Sdimbool arcmt::applyTransformations(CompilerInvocation &origCI,
391234353Sdim                                 const FrontendInputFile &Input,
392226633Sdim                                 DiagnosticConsumer *DiagClient) {
393234353Sdim  return applyTransforms(origCI, Input, DiagClient,
394226633Sdim                         StringRef(), false, StringRef());
395224135Sdim}
396224135Sdim
397224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
398234353Sdim                                      const FrontendInputFile &Input,
399226633Sdim                                      DiagnosticConsumer *DiagClient,
400226633Sdim                                      StringRef outputDir,
401226633Sdim                                      bool emitPremigrationARCErrors,
402226633Sdim                                      StringRef plistOut) {
403224135Sdim  assert(!outputDir.empty() && "Expected output directory path");
404234353Sdim  return applyTransforms(origCI, Input, DiagClient,
405226633Sdim                         outputDir, emitPremigrationARCErrors, plistOut);
406224135Sdim}
407224135Sdim
408224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
409224135Sdim                                  remap,
410226633Sdim                              StringRef outputDir,
411226633Sdim                              DiagnosticConsumer *DiagClient) {
412224135Sdim  assert(!outputDir.empty());
413224135Sdim
414234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
415234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
416243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
417243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
418224135Sdim
419224135Sdim  FileRemapper remapper;
420224135Sdim  bool err = remapper.initFromDisk(outputDir, *Diags,
421224135Sdim                                   /*ignoreIfFilesChanged=*/true);
422224135Sdim  if (err)
423224135Sdim    return true;
424224135Sdim
425234353Sdim  PreprocessorOptions PPOpts;
426234353Sdim  remapper.applyMappings(PPOpts);
427234353Sdim  remap = PPOpts.RemappedFiles;
428224135Sdim
429224135Sdim  return false;
430224135Sdim}
431224135Sdim
432234353Sdimbool arcmt::getFileRemappingsFromFileList(
433234353Sdim                        std::vector<std::pair<std::string,std::string> > &remap,
434234353Sdim                        ArrayRef<StringRef> remapFiles,
435234353Sdim                        DiagnosticConsumer *DiagClient) {
436234353Sdim  bool hasErrorOccurred = false;
437234353Sdim  llvm::StringMap<bool> Uniquer;
438234353Sdim
439249423Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
440249423Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
441243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
442243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
443234353Sdim
444234353Sdim  for (ArrayRef<StringRef>::iterator
445234353Sdim         I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
446234353Sdim    StringRef file = *I;
447234353Sdim
448234353Sdim    FileRemapper remapper;
449234353Sdim    bool err = remapper.initFromFile(file, *Diags,
450234353Sdim                                     /*ignoreIfFilesChanged=*/true);
451234353Sdim    hasErrorOccurred = hasErrorOccurred || err;
452234353Sdim    if (err)
453234353Sdim      continue;
454234353Sdim
455234353Sdim    PreprocessorOptions PPOpts;
456234353Sdim    remapper.applyMappings(PPOpts);
457234353Sdim    for (PreprocessorOptions::remapped_file_iterator
458234353Sdim           RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end();
459234353Sdim           RI != RE; ++RI) {
460234353Sdim      bool &inserted = Uniquer[RI->first];
461234353Sdim      if (inserted)
462234353Sdim        continue;
463234353Sdim      inserted = true;
464234353Sdim      remap.push_back(*RI);
465234353Sdim    }
466234353Sdim  }
467234353Sdim
468234353Sdim  return hasErrorOccurred;
469234353Sdim}
470234353Sdim
471224135Sdim//===----------------------------------------------------------------------===//
472224135Sdim// CollectTransformActions.
473224135Sdim//===----------------------------------------------------------------------===//
474224135Sdim
475224135Sdimnamespace {
476224135Sdim
477224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
478224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
479224135Sdim
480224135Sdimpublic:
481224135Sdim  ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
482224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
483224135Sdim
484249423Sdim  virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD,
485226633Sdim                            SourceRange Range) {
486224135Sdim    if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
487224135Sdim      ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
488224135Sdim  }
489224135Sdim};
490224135Sdim
491224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction {
492224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
493224135Sdim
494224135Sdimpublic:
495224135Sdim  ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
496224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
497224135Sdim
498224135Sdim  virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
499226633Sdim                                         StringRef InFile) {
500224135Sdim    CI.getPreprocessor().addPPCallbacks(
501224135Sdim                              new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
502224135Sdim    return new ASTConsumer();
503224135Sdim  }
504224135Sdim};
505224135Sdim
506224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver {
507224135Sdim  Rewriter &rewriter;
508224135Sdim  MigrationProcess::RewriteListener *Listener;
509224135Sdim
510224135Sdimpublic:
511224135Sdim  RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
512224135Sdim                     MigrationProcess::RewriteListener *listener)
513239462Sdim    : rewriter(rewriter), Listener(listener) {
514224135Sdim    if (Listener)
515224135Sdim      Listener->start(ctx);
516224135Sdim  }
517224135Sdim  ~RewritesApplicator() {
518224135Sdim    if (Listener)
519224135Sdim      Listener->finish();
520224135Sdim  }
521224135Sdim
522226633Sdim  virtual void insert(SourceLocation loc, StringRef text) {
523224135Sdim    bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
524224135Sdim                                   /*indentNewLines=*/true);
525224135Sdim    if (!err && Listener)
526224135Sdim      Listener->insert(loc, text);
527224135Sdim  }
528224135Sdim
529224135Sdim  virtual void remove(CharSourceRange range) {
530224135Sdim    Rewriter::RewriteOptions removeOpts;
531224135Sdim    removeOpts.IncludeInsertsAtBeginOfRange = false;
532224135Sdim    removeOpts.IncludeInsertsAtEndOfRange = false;
533224135Sdim    removeOpts.RemoveLineIfEmpty = true;
534224135Sdim
535224135Sdim    bool err = rewriter.RemoveText(range, removeOpts);
536224135Sdim    if (!err && Listener)
537224135Sdim      Listener->remove(range);
538224135Sdim  }
539224135Sdim
540224135Sdim  virtual void increaseIndentation(CharSourceRange range,
541224135Sdim                                    SourceLocation parentIndent) {
542224135Sdim    rewriter.IncreaseIndentation(range, parentIndent);
543224135Sdim  }
544224135Sdim};
545224135Sdim
546224135Sdim} // end anonymous namespace.
547224135Sdim
548224135Sdim/// \brief Anchor for VTable.
549224135SdimMigrationProcess::RewriteListener::~RewriteListener() { }
550224135Sdim
551224135SdimMigrationProcess::MigrationProcess(const CompilerInvocation &CI,
552226633Sdim                                   DiagnosticConsumer *diagClient,
553226633Sdim                                   StringRef outputDir)
554224135Sdim  : OrigCI(CI), DiagClient(diagClient) {
555224135Sdim  if (!outputDir.empty()) {
556234353Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
557234353Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
558243830Sdim      new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
559243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
560224135Sdim    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
561224135Sdim  }
562224135Sdim}
563224135Sdim
564224135Sdimbool MigrationProcess::applyTransform(TransformFn trans,
565224135Sdim                                      RewriteListener *listener) {
566234353Sdim  OwningPtr<CompilerInvocation> CInvok;
567224135Sdim  CInvok.reset(createInvocationForMigration(OrigCI));
568224135Sdim  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
569224135Sdim
570234353Sdim  Remapper.applyMappings(CInvok->getPreprocessorOpts());
571224135Sdim
572224135Sdim  CapturedDiagList capturedDiags;
573224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
574224135Sdim
575224135Sdim  assert(DiagClient);
576234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
577234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
578243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
579243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
580224135Sdim
581224135Sdim  // Filter of all diagnostics.
582239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
583224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
584224135Sdim
585234353Sdim  OwningPtr<ARCMTMacroTrackerAction> ASTAction;
586224135Sdim  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
587224135Sdim
588234353Sdim  OwningPtr<ASTUnit> Unit(
589224135Sdim      ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
590224135Sdim                                                ASTAction.get()));
591239462Sdim  if (!Unit) {
592239462Sdim    errRec.FinishCapture();
593224135Sdim    return true;
594239462Sdim  }
595224135Sdim  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
596224135Sdim
597224135Sdim  // Don't filter diagnostics anymore.
598224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
599224135Sdim
600224135Sdim  ASTContext &Ctx = Unit->getASTContext();
601224135Sdim
602224135Sdim  if (Diags->hasFatalErrorOccurred()) {
603224135Sdim    Diags->Reset();
604234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
605224135Sdim    capturedDiags.reportDiagnostics(*Diags);
606224135Sdim    DiagClient->EndSourceFile();
607239462Sdim    errRec.FinishCapture();
608224135Sdim    return true;
609224135Sdim  }
610224135Sdim
611224135Sdim  // After parsing of source files ended, we want to reuse the
612224135Sdim  // diagnostics objects to emit further diagnostics.
613226633Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
614224135Sdim  // diagnostics with source range information are emitted only in between
615224135Sdim  // BeginSourceFile() and EndSourceFile().
616234353Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
617224135Sdim
618234353Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
619224135Sdim  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
620234353Sdim  MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
621249423Sdim                     Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
622224135Sdim
623224135Sdim  trans(pass);
624224135Sdim
625224135Sdim  {
626224135Sdim    RewritesApplicator applicator(rewriter, Ctx, listener);
627224135Sdim    TA.applyRewrites(applicator);
628224135Sdim  }
629224135Sdim
630224135Sdim  DiagClient->EndSourceFile();
631239462Sdim  errRec.FinishCapture();
632224135Sdim
633224135Sdim  if (DiagClient->getNumErrors())
634224135Sdim    return true;
635224135Sdim
636224135Sdim  for (Rewriter::buffer_iterator
637224135Sdim        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
638224135Sdim    FileID FID = I->first;
639224135Sdim    RewriteBuffer &buf = I->second;
640224135Sdim    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
641224135Sdim    assert(file);
642224135Sdim    std::string newFname = file->getName();
643224135Sdim    newFname += "-trans";
644234353Sdim    SmallString<512> newText;
645224135Sdim    llvm::raw_svector_ostream vecOS(newText);
646224135Sdim    buf.write(vecOS);
647224135Sdim    vecOS.flush();
648224135Sdim    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
649226633Sdim                   StringRef(newText.data(), newText.size()), newFname);
650234353Sdim    SmallString<64> filePath(file->getName());
651224135Sdim    Unit->getFileManager().FixupRelativePath(filePath);
652224135Sdim    Remapper.remap(filePath.str(), memBuf);
653224135Sdim  }
654224135Sdim
655224135Sdim  return false;
656224135Sdim}
657