1224135Sdim//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
2224135Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6224135Sdim//
7224135Sdim//===----------------------------------------------------------------------===//
8224135Sdim
9224135Sdim#include "Internals.h"
10249423Sdim#include "clang/AST/ASTConsumer.h"
11249423Sdim#include "clang/Basic/DiagnosticCategories.h"
12224135Sdim#include "clang/Frontend/ASTUnit.h"
13224135Sdim#include "clang/Frontend/CompilerInstance.h"
14234353Sdim#include "clang/Frontend/FrontendAction.h"
15226633Sdim#include "clang/Frontend/TextDiagnosticPrinter.h"
16224135Sdim#include "clang/Frontend/Utils.h"
17249423Sdim#include "clang/Lex/Preprocessor.h"
18314564Sdim#include "clang/Lex/PreprocessorOptions.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"
24309124Sdim#include <utility>
25224135Sdimusing namespace clang;
26224135Sdimusing namespace arcmt;
27224135Sdim
28226633Sdimbool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
29224135Sdim                                       SourceRange range) {
30224135Sdim  if (range.isInvalid())
31224135Sdim    return false;
32224135Sdim
33224135Sdim  bool cleared = false;
34224135Sdim  ListTy::iterator I = List.begin();
35224135Sdim  while (I != List.end()) {
36224135Sdim    FullSourceLoc diagLoc = I->getLocation();
37224135Sdim    if ((IDs.empty() || // empty means clear all diagnostics in the range.
38353358Sdim         llvm::is_contained(IDs, I->getID())) &&
39224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
40224135Sdim        (diagLoc == range.getEnd() ||
41353358Sdim         diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
42224135Sdim      cleared = true;
43224135Sdim      ListTy::iterator eraseS = I++;
44249423Sdim      if (eraseS->getLevel() != DiagnosticsEngine::Note)
45249423Sdim        while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
46249423Sdim          ++I;
47224135Sdim      // Clear the diagnostic and any notes following it.
48243830Sdim      I = List.erase(eraseS, I);
49224135Sdim      continue;
50224135Sdim    }
51224135Sdim
52224135Sdim    ++I;
53224135Sdim  }
54224135Sdim
55224135Sdim  return cleared;
56224135Sdim}
57224135Sdim
58226633Sdimbool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
59224135Sdim                                     SourceRange range) const {
60224135Sdim  if (range.isInvalid())
61224135Sdim    return false;
62224135Sdim
63224135Sdim  ListTy::const_iterator I = List.begin();
64224135Sdim  while (I != List.end()) {
65224135Sdim    FullSourceLoc diagLoc = I->getLocation();
66224135Sdim    if ((IDs.empty() || // empty means any diagnostic in the range.
67353358Sdim         llvm::find(IDs, I->getID()) != IDs.end()) &&
68224135Sdim        !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
69224135Sdim        (diagLoc == range.getEnd() ||
70353358Sdim         diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
71224135Sdim      return true;
72224135Sdim    }
73224135Sdim
74224135Sdim    ++I;
75224135Sdim  }
76224135Sdim
77224135Sdim  return false;
78224135Sdim}
79224135Sdim
80226633Sdimvoid CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
81224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
82224135Sdim    Diags.Report(*I);
83224135Sdim}
84224135Sdim
85224135Sdimbool CapturedDiagList::hasErrors() const {
86224135Sdim  for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
87226633Sdim    if (I->getLevel() >= DiagnosticsEngine::Error)
88224135Sdim      return true;
89224135Sdim
90224135Sdim  return false;
91224135Sdim}
92224135Sdim
93224135Sdimnamespace {
94224135Sdim
95226633Sdimclass CaptureDiagnosticConsumer : public DiagnosticConsumer {
96226633Sdim  DiagnosticsEngine &Diags;
97239462Sdim  DiagnosticConsumer &DiagClient;
98224135Sdim  CapturedDiagList &CapturedDiags;
99239462Sdim  bool HasBegunSourceFile;
100224135Sdimpublic:
101226633Sdim  CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
102239462Sdim                            DiagnosticConsumer &client,
103239462Sdim                            CapturedDiagList &capturedDiags)
104239462Sdim    : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
105239462Sdim      HasBegunSourceFile(false) { }
106224135Sdim
107276479Sdim  void BeginSourceFile(const LangOptions &Opts,
108276479Sdim                       const Preprocessor *PP) override {
109239462Sdim    // Pass BeginSourceFile message onto DiagClient on first call.
110239462Sdim    // The corresponding EndSourceFile call will be made from an
111239462Sdim    // explicit call to FinishCapture.
112239462Sdim    if (!HasBegunSourceFile) {
113239462Sdim      DiagClient.BeginSourceFile(Opts, PP);
114239462Sdim      HasBegunSourceFile = true;
115239462Sdim    }
116239462Sdim  }
117239462Sdim
118239462Sdim  void FinishCapture() {
119239462Sdim    // Call EndSourceFile on DiagClient on completion of capture to
120239462Sdim    // enable VerifyDiagnosticConsumer to check diagnostics *after*
121239462Sdim    // it has received the diagnostic list.
122239462Sdim    if (HasBegunSourceFile) {
123239462Sdim      DiagClient.EndSourceFile();
124239462Sdim      HasBegunSourceFile = false;
125239462Sdim    }
126239462Sdim  }
127239462Sdim
128288943Sdim  ~CaptureDiagnosticConsumer() override {
129239462Sdim    assert(!HasBegunSourceFile && "FinishCapture not called!");
130239462Sdim  }
131239462Sdim
132276479Sdim  void HandleDiagnostic(DiagnosticsEngine::Level level,
133276479Sdim                        const Diagnostic &Info) override {
134234353Sdim    if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
135226633Sdim        level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
136249423Sdim      if (Info.getLocation().isValid())
137249423Sdim        CapturedDiags.push_back(StoredDiagnostic(level, Info));
138224135Sdim      return;
139224135Sdim    }
140224135Sdim
141224135Sdim    // Non-ARC warnings are ignored.
142360784Sdim    Diags.setLastDiagnosticIgnored(true);
143224135Sdim  }
144224135Sdim};
145224135Sdim
146224135Sdim} // end anonymous namespace
147224135Sdim
148224135Sdimstatic bool HasARCRuntime(CompilerInvocation &origCI) {
149224135Sdim  // This duplicates some functionality from Darwin::AddDeploymentTarget
150224135Sdim  // but this function is well defined, so keep it decoupled from the driver
151224135Sdim  // and avoid unrelated complications.
152224135Sdim  llvm::Triple triple(origCI.getTargetOpts().Triple);
153224135Sdim
154261991Sdim  if (triple.isiOS())
155224135Sdim    return triple.getOSMajorVersion() >= 5;
156224135Sdim
157296417Sdim  if (triple.isWatchOS())
158296417Sdim    return true;
159296417Sdim
160224135Sdim  if (triple.getOS() == llvm::Triple::Darwin)
161224135Sdim    return triple.getOSMajorVersion() >= 11;
162224135Sdim
163224135Sdim  if (triple.getOS() == llvm::Triple::MacOSX) {
164224135Sdim    unsigned Major, Minor, Micro;
165224135Sdim    triple.getOSVersion(Major, Minor, Micro);
166224135Sdim    return Major > 10 || (Major == 10 && Minor >= 7);
167224135Sdim  }
168224135Sdim
169224135Sdim  return false;
170224135Sdim}
171224135Sdim
172226633Sdimstatic CompilerInvocation *
173288943SdimcreateInvocationForMigration(CompilerInvocation &origCI,
174288943Sdim                             const PCHContainerReader &PCHContainerRdr) {
175276479Sdim  std::unique_ptr<CompilerInvocation> CInvok;
176224135Sdim  CInvok.reset(new CompilerInvocation(origCI));
177249423Sdim  PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
178249423Sdim  if (!PPOpts.ImplicitPCHInclude.empty()) {
179249423Sdim    // We can't use a PCH because it was likely built in non-ARC mode and we
180249423Sdim    // want to parse in ARC. Include the original header.
181249423Sdim    FileManager FileMgr(origCI.getFileSystemOpts());
182249423Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
183249423Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
184249423Sdim        new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
185249423Sdim                              new IgnoringDiagConsumer()));
186288943Sdim    std::string OriginalFile = ASTReader::getOriginalSourceFile(
187288943Sdim        PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
188249423Sdim    if (!OriginalFile.empty())
189249423Sdim      PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
190249423Sdim    PPOpts.ImplicitPCHInclude.clear();
191249423Sdim  }
192224135Sdim  std::string define = getARCMTMacroName();
193224135Sdim  define += '=';
194224135Sdim  CInvok->getPreprocessorOpts().addMacroDef(define);
195234353Sdim  CInvok->getLangOpts()->ObjCAutoRefCount = true;
196234353Sdim  CInvok->getLangOpts()->setGC(LangOptions::NonGC);
197224135Sdim  CInvok->getDiagnosticOpts().ErrorLimit = 0;
198239462Sdim  CInvok->getDiagnosticOpts().PedanticErrors = 0;
199239462Sdim
200239462Sdim  // Ignore -Werror flags when migrating.
201239462Sdim  std::vector<std::string> WarnOpts;
202239462Sdim  for (std::vector<std::string>::iterator
203239462Sdim         I = CInvok->getDiagnosticOpts().Warnings.begin(),
204239462Sdim         E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
205239462Sdim    if (!StringRef(*I).startswith("error"))
206239462Sdim      WarnOpts.push_back(*I);
207239462Sdim  }
208239462Sdim  WarnOpts.push_back("error=arc-unsafe-retained-assign");
209276479Sdim  CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
210239462Sdim
211296417Sdim  CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI);
212296417Sdim  CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime;
213224135Sdim
214276479Sdim  return CInvok.release();
215224135Sdim}
216224135Sdim
217226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags,
218243830Sdim                                   DiagnosticOptions *diagOpts,
219226633Sdim                                   Preprocessor &PP) {
220226633Sdim  TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
221234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
222234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
223243830Sdim      new DiagnosticsEngine(DiagID, diagOpts, &printer,
224243830Sdim                            /*ShouldOwnClient=*/false));
225226633Sdim  Diags->setSourceManager(&PP.getSourceManager());
226341825Sdim
227234353Sdim  printer.BeginSourceFile(PP.getLangOpts(), &PP);
228226633Sdim  arcDiags.reportDiagnostics(*Diags);
229226633Sdim  printer.EndSourceFile();
230226633Sdim}
231226633Sdim
232224135Sdim//===----------------------------------------------------------------------===//
233224135Sdim// checkForManualIssues.
234224135Sdim//===----------------------------------------------------------------------===//
235224135Sdim
236288943Sdimbool arcmt::checkForManualIssues(
237288943Sdim    CompilerInvocation &origCI, const FrontendInputFile &Input,
238288943Sdim    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
239288943Sdim    DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
240288943Sdim    StringRef plistOut) {
241344779Sdim  if (!origCI.getLangOpts()->ObjC)
242224135Sdim    return false;
243224135Sdim
244234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
245234353Sdim  bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
246234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
247234353Sdim
248234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
249234353Sdim                                                                     NoFinalizeRemoval);
250224135Sdim  assert(!transforms.empty());
251224135Sdim
252276479Sdim  std::unique_ptr<CompilerInvocation> CInvok;
253288943Sdim  CInvok.reset(
254288943Sdim      createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
255224135Sdim  CInvok->getFrontendOpts().Inputs.clear();
256234353Sdim  CInvok->getFrontendOpts().Inputs.push_back(Input);
257224135Sdim
258224135Sdim  CapturedDiagList capturedDiags;
259224135Sdim
260224135Sdim  assert(DiagClient);
261234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
262234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
263243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
264243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
265224135Sdim
266224135Sdim  // Filter of all diagnostics.
267239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
268224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
269224135Sdim
270288943Sdim  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
271314564Sdim      std::move(CInvok), PCHContainerOps, Diags));
272239462Sdim  if (!Unit) {
273239462Sdim    errRec.FinishCapture();
274224135Sdim    return true;
275239462Sdim  }
276224135Sdim
277224135Sdim  // Don't filter diagnostics anymore.
278224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
279224135Sdim
280224135Sdim  ASTContext &Ctx = Unit->getASTContext();
281224135Sdim
282224135Sdim  if (Diags->hasFatalErrorOccurred()) {
283224135Sdim    Diags->Reset();
284234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
285224135Sdim    capturedDiags.reportDiagnostics(*Diags);
286224135Sdim    DiagClient->EndSourceFile();
287239462Sdim    errRec.FinishCapture();
288224135Sdim    return true;
289224135Sdim  }
290224135Sdim
291226633Sdim  if (emitPremigrationARCErrors)
292243830Sdim    emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
293226633Sdim                           Unit->getPreprocessor());
294226633Sdim  if (!plistOut.empty()) {
295226633Sdim    SmallVector<StoredDiagnostic, 8> arcDiags;
296226633Sdim    for (CapturedDiagList::iterator
297226633Sdim           I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
298226633Sdim      arcDiags.push_back(*I);
299226633Sdim    writeARCDiagsToPlist(plistOut, arcDiags,
300234353Sdim                         Ctx.getSourceManager(), Ctx.getLangOpts());
301226633Sdim  }
302226633Sdim
303224135Sdim  // After parsing of source files ended, we want to reuse the
304224135Sdim  // diagnostics objects to emit further diagnostics.
305341825Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
306224135Sdim  // diagnostics with source range information are emitted only in between
307224135Sdim  // BeginSourceFile() and EndSourceFile().
308234353Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
309224135Sdim
310224135Sdim  // No macros will be added since we are just checking and we won't modify
311224135Sdim  // source code.
312224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
313224135Sdim
314224135Sdim  TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
315249423Sdim  MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
316249423Sdim                     ARCMTMacroLocs);
317234353Sdim  pass.setNoFinalizeRemoval(NoFinalizeRemoval);
318276479Sdim  if (!NoNSAllocReallocError)
319276479Sdim    Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
320276479Sdim                       SourceLocation());
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
330226633Sdim  return capturedDiags.hasErrors() || testAct.hasReportedErrors();
331224135Sdim}
332224135Sdim
333224135Sdim//===----------------------------------------------------------------------===//
334224135Sdim// applyTransformations.
335224135Sdim//===----------------------------------------------------------------------===//
336224135Sdim
337288943Sdimstatic bool
338288943SdimapplyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
339288943Sdim                std::shared_ptr<PCHContainerOperations> PCHContainerOps,
340288943Sdim                DiagnosticConsumer *DiagClient, StringRef outputDir,
341288943Sdim                bool emitPremigrationARCErrors, StringRef plistOut) {
342344779Sdim  if (!origCI.getLangOpts()->ObjC)
343224135Sdim    return false;
344224135Sdim
345234353Sdim  LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
346234353Sdim
347224135Sdim  // Make sure checking is successful first.
348224135Sdim  CompilerInvocation CInvokForCheck(origCI);
349288943Sdim  if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
350288943Sdim                                  DiagClient, emitPremigrationARCErrors,
351288943Sdim                                  plistOut))
352224135Sdim    return true;
353224135Sdim
354224135Sdim  CompilerInvocation CInvok(origCI);
355224135Sdim  CInvok.getFrontendOpts().Inputs.clear();
356234353Sdim  CInvok.getFrontendOpts().Inputs.push_back(Input);
357288943Sdim
358288943Sdim  MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
359234353Sdim  bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
360224135Sdim
361234353Sdim  std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
362234353Sdim                                                                     NoFinalizeRemoval);
363224135Sdim  assert(!transforms.empty());
364224135Sdim
365224135Sdim  for (unsigned i=0, e = transforms.size(); i != e; ++i) {
366224135Sdim    bool err = migration.applyTransform(transforms[i]);
367224135Sdim    if (err) return true;
368224135Sdim  }
369224135Sdim
370234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
371234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
372243830Sdim      new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
373243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
374224135Sdim
375224135Sdim  if (outputDir.empty()) {
376234353Sdim    origCI.getLangOpts()->ObjCAutoRefCount = true;
377224135Sdim    return migration.getRemapper().overwriteOriginal(*Diags);
378224135Sdim  } else {
379224135Sdim    return migration.getRemapper().flushToDisk(outputDir, *Diags);
380224135Sdim  }
381224135Sdim}
382224135Sdim
383288943Sdimbool arcmt::applyTransformations(
384288943Sdim    CompilerInvocation &origCI, const FrontendInputFile &Input,
385288943Sdim    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
386288943Sdim    DiagnosticConsumer *DiagClient) {
387288943Sdim  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
388226633Sdim                         StringRef(), false, StringRef());
389224135Sdim}
390224135Sdim
391288943Sdimbool arcmt::migrateWithTemporaryFiles(
392288943Sdim    CompilerInvocation &origCI, const FrontendInputFile &Input,
393288943Sdim    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
394288943Sdim    DiagnosticConsumer *DiagClient, StringRef outputDir,
395288943Sdim    bool emitPremigrationARCErrors, StringRef plistOut) {
396224135Sdim  assert(!outputDir.empty() && "Expected output directory path");
397288943Sdim  return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
398288943Sdim                         emitPremigrationARCErrors, plistOut);
399224135Sdim}
400224135Sdim
401224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
402224135Sdim                                  remap,
403226633Sdim                              StringRef outputDir,
404226633Sdim                              DiagnosticConsumer *DiagClient) {
405224135Sdim  assert(!outputDir.empty());
406224135Sdim
407234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
408234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
409243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
410243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
411224135Sdim
412224135Sdim  FileRemapper remapper;
413224135Sdim  bool err = remapper.initFromDisk(outputDir, *Diags,
414224135Sdim                                   /*ignoreIfFilesChanged=*/true);
415224135Sdim  if (err)
416224135Sdim    return true;
417224135Sdim
418234353Sdim  PreprocessorOptions PPOpts;
419234353Sdim  remapper.applyMappings(PPOpts);
420234353Sdim  remap = PPOpts.RemappedFiles;
421224135Sdim
422224135Sdim  return false;
423224135Sdim}
424224135Sdim
425234353Sdim
426224135Sdim//===----------------------------------------------------------------------===//
427224135Sdim// CollectTransformActions.
428224135Sdim//===----------------------------------------------------------------------===//
429224135Sdim
430224135Sdimnamespace {
431224135Sdim
432224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
433224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
434224135Sdim
435224135Sdimpublic:
436224135Sdim  ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
437224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
438224135Sdim
439288943Sdim  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
440276479Sdim                    SourceRange Range, const MacroArgs *Args) override {
441224135Sdim    if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
442224135Sdim      ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
443224135Sdim  }
444224135Sdim};
445224135Sdim
446224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction {
447224135Sdim  std::vector<SourceLocation> &ARCMTMacroLocs;
448224135Sdim
449224135Sdimpublic:
450224135Sdim  ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
451224135Sdim    : ARCMTMacroLocs(ARCMTMacroLocs) { }
452224135Sdim
453280031Sdim  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
454280031Sdim                                                 StringRef InFile) override {
455224135Sdim    CI.getPreprocessor().addPPCallbacks(
456360784Sdim               std::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
457360784Sdim    return std::make_unique<ASTConsumer>();
458224135Sdim  }
459224135Sdim};
460224135Sdim
461224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver {
462224135Sdim  Rewriter &rewriter;
463224135Sdim  MigrationProcess::RewriteListener *Listener;
464224135Sdim
465224135Sdimpublic:
466224135Sdim  RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
467224135Sdim                     MigrationProcess::RewriteListener *listener)
468239462Sdim    : rewriter(rewriter), Listener(listener) {
469224135Sdim    if (Listener)
470224135Sdim      Listener->start(ctx);
471224135Sdim  }
472288943Sdim  ~RewritesApplicator() override {
473224135Sdim    if (Listener)
474224135Sdim      Listener->finish();
475224135Sdim  }
476224135Sdim
477276479Sdim  void insert(SourceLocation loc, StringRef text) override {
478224135Sdim    bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
479224135Sdim                                   /*indentNewLines=*/true);
480224135Sdim    if (!err && Listener)
481224135Sdim      Listener->insert(loc, text);
482224135Sdim  }
483224135Sdim
484276479Sdim  void remove(CharSourceRange range) override {
485224135Sdim    Rewriter::RewriteOptions removeOpts;
486224135Sdim    removeOpts.IncludeInsertsAtBeginOfRange = false;
487224135Sdim    removeOpts.IncludeInsertsAtEndOfRange = false;
488224135Sdim    removeOpts.RemoveLineIfEmpty = true;
489224135Sdim
490224135Sdim    bool err = rewriter.RemoveText(range, removeOpts);
491224135Sdim    if (!err && Listener)
492224135Sdim      Listener->remove(range);
493224135Sdim  }
494224135Sdim
495276479Sdim  void increaseIndentation(CharSourceRange range,
496276479Sdim                            SourceLocation parentIndent) override {
497224135Sdim    rewriter.IncreaseIndentation(range, parentIndent);
498224135Sdim  }
499224135Sdim};
500224135Sdim
501224135Sdim} // end anonymous namespace.
502224135Sdim
503341825Sdim/// Anchor for VTable.
504224135SdimMigrationProcess::RewriteListener::~RewriteListener() { }
505224135Sdim
506288943SdimMigrationProcess::MigrationProcess(
507288943Sdim    const CompilerInvocation &CI,
508288943Sdim    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
509288943Sdim    DiagnosticConsumer *diagClient, StringRef outputDir)
510309124Sdim    : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)),
511309124Sdim      DiagClient(diagClient), HadARCErrors(false) {
512224135Sdim  if (!outputDir.empty()) {
513234353Sdim    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
514234353Sdim    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
515243830Sdim      new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
516243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
517353358Sdim    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanged=*/true);
518224135Sdim  }
519224135Sdim}
520224135Sdim
521224135Sdimbool MigrationProcess::applyTransform(TransformFn trans,
522224135Sdim                                      RewriteListener *listener) {
523276479Sdim  std::unique_ptr<CompilerInvocation> CInvok;
524288943Sdim  CInvok.reset(
525288943Sdim      createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader()));
526224135Sdim  CInvok->getDiagnosticOpts().IgnoreWarnings = true;
527224135Sdim
528234353Sdim  Remapper.applyMappings(CInvok->getPreprocessorOpts());
529224135Sdim
530224135Sdim  CapturedDiagList capturedDiags;
531224135Sdim  std::vector<SourceLocation> ARCMTMacroLocs;
532224135Sdim
533224135Sdim  assert(DiagClient);
534234353Sdim  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
535234353Sdim  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
536243830Sdim      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
537243830Sdim                            DiagClient, /*ShouldOwnClient=*/false));
538224135Sdim
539224135Sdim  // Filter of all diagnostics.
540239462Sdim  CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
541224135Sdim  Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
542224135Sdim
543276479Sdim  std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
544224135Sdim  ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
545224135Sdim
546276479Sdim  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
547314564Sdim      std::move(CInvok), PCHContainerOps, Diags, ASTAction.get()));
548239462Sdim  if (!Unit) {
549239462Sdim    errRec.FinishCapture();
550224135Sdim    return true;
551239462Sdim  }
552224135Sdim  Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
553224135Sdim
554261991Sdim  HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
555261991Sdim
556224135Sdim  // Don't filter diagnostics anymore.
557224135Sdim  Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
558224135Sdim
559224135Sdim  ASTContext &Ctx = Unit->getASTContext();
560224135Sdim
561224135Sdim  if (Diags->hasFatalErrorOccurred()) {
562224135Sdim    Diags->Reset();
563234353Sdim    DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
564224135Sdim    capturedDiags.reportDiagnostics(*Diags);
565224135Sdim    DiagClient->EndSourceFile();
566239462Sdim    errRec.FinishCapture();
567224135Sdim    return true;
568224135Sdim  }
569224135Sdim
570224135Sdim  // After parsing of source files ended, we want to reuse the
571224135Sdim  // diagnostics objects to emit further diagnostics.
572341825Sdim  // We call BeginSourceFile because DiagnosticConsumer requires that
573224135Sdim  // diagnostics with source range information are emitted only in between
574224135Sdim  // BeginSourceFile() and EndSourceFile().
575234353Sdim  DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
576224135Sdim
577234353Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
578224135Sdim  TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
579234353Sdim  MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
580249423Sdim                     Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
581224135Sdim
582224135Sdim  trans(pass);
583224135Sdim
584224135Sdim  {
585224135Sdim    RewritesApplicator applicator(rewriter, Ctx, listener);
586224135Sdim    TA.applyRewrites(applicator);
587224135Sdim  }
588224135Sdim
589224135Sdim  DiagClient->EndSourceFile();
590239462Sdim  errRec.FinishCapture();
591224135Sdim
592224135Sdim  if (DiagClient->getNumErrors())
593224135Sdim    return true;
594224135Sdim
595224135Sdim  for (Rewriter::buffer_iterator
596224135Sdim        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
597224135Sdim    FileID FID = I->first;
598224135Sdim    RewriteBuffer &buf = I->second;
599224135Sdim    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
600224135Sdim    assert(file);
601224135Sdim    std::string newFname = file->getName();
602224135Sdim    newFname += "-trans";
603234353Sdim    SmallString<512> newText;
604224135Sdim    llvm::raw_svector_ostream vecOS(newText);
605224135Sdim    buf.write(vecOS);
606280031Sdim    std::unique_ptr<llvm::MemoryBuffer> memBuf(
607280031Sdim        llvm::MemoryBuffer::getMemBufferCopy(
608280031Sdim            StringRef(newText.data(), newText.size()), newFname));
609234353Sdim    SmallString<64> filePath(file->getName());
610224135Sdim    Unit->getFileManager().FixupRelativePath(filePath);
611280031Sdim    Remapper.remap(filePath.str(), std::move(memBuf));
612224135Sdim  }
613224135Sdim
614224135Sdim  return false;
615224135Sdim}
616