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