ARCMT.cpp revision 243830
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" 11224135Sdim#include "clang/Frontend/ASTUnit.h" 12224135Sdim#include "clang/Frontend/CompilerInstance.h" 13234353Sdim#include "clang/Frontend/FrontendAction.h" 14226633Sdim#include "clang/Frontend/TextDiagnosticPrinter.h" 15224135Sdim#include "clang/Frontend/Utils.h" 16224135Sdim#include "clang/AST/ASTConsumer.h" 17243830Sdim#include "clang/Rewrite/Core/Rewriter.h" 18224135Sdim#include "clang/Sema/SemaDiagnostic.h" 19224135Sdim#include "clang/Basic/DiagnosticCategories.h" 20224135Sdim#include "clang/Lex/Preprocessor.h" 21224135Sdim#include "llvm/Support/MemoryBuffer.h" 22224135Sdim#include "llvm/ADT/Triple.h" 23224135Sdimusing namespace clang; 24224135Sdimusing namespace arcmt; 25224135Sdim 26226633Sdimbool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs, 27224135Sdim SourceRange range) { 28224135Sdim if (range.isInvalid()) 29224135Sdim return false; 30224135Sdim 31224135Sdim bool cleared = false; 32224135Sdim ListTy::iterator I = List.begin(); 33224135Sdim while (I != List.end()) { 34224135Sdim FullSourceLoc diagLoc = I->getLocation(); 35224135Sdim if ((IDs.empty() || // empty means clear all diagnostics in the range. 36224135Sdim std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && 37224135Sdim !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && 38224135Sdim (diagLoc == range.getEnd() || 39224135Sdim diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { 40224135Sdim cleared = true; 41224135Sdim ListTy::iterator eraseS = I++; 42226633Sdim while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note) 43224135Sdim ++I; 44224135Sdim // Clear the diagnostic and any notes following it. 45243830Sdim I = List.erase(eraseS, I); 46224135Sdim continue; 47224135Sdim } 48224135Sdim 49224135Sdim ++I; 50224135Sdim } 51224135Sdim 52224135Sdim return cleared; 53224135Sdim} 54224135Sdim 55226633Sdimbool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs, 56224135Sdim SourceRange range) const { 57224135Sdim if (range.isInvalid()) 58224135Sdim return false; 59224135Sdim 60224135Sdim ListTy::const_iterator I = List.begin(); 61224135Sdim while (I != List.end()) { 62224135Sdim FullSourceLoc diagLoc = I->getLocation(); 63224135Sdim if ((IDs.empty() || // empty means any diagnostic in the range. 64224135Sdim std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && 65224135Sdim !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && 66224135Sdim (diagLoc == range.getEnd() || 67224135Sdim diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { 68224135Sdim return true; 69224135Sdim } 70224135Sdim 71224135Sdim ++I; 72224135Sdim } 73224135Sdim 74224135Sdim return false; 75224135Sdim} 76224135Sdim 77226633Sdimvoid CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const { 78224135Sdim for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) 79224135Sdim Diags.Report(*I); 80224135Sdim} 81224135Sdim 82224135Sdimbool CapturedDiagList::hasErrors() const { 83224135Sdim for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) 84226633Sdim if (I->getLevel() >= DiagnosticsEngine::Error) 85224135Sdim return true; 86224135Sdim 87224135Sdim return false; 88224135Sdim} 89224135Sdim 90224135Sdimnamespace { 91224135Sdim 92226633Sdimclass CaptureDiagnosticConsumer : public DiagnosticConsumer { 93226633Sdim DiagnosticsEngine &Diags; 94239462Sdim DiagnosticConsumer &DiagClient; 95224135Sdim CapturedDiagList &CapturedDiags; 96239462Sdim bool HasBegunSourceFile; 97224135Sdimpublic: 98226633Sdim CaptureDiagnosticConsumer(DiagnosticsEngine &diags, 99239462Sdim DiagnosticConsumer &client, 100239462Sdim CapturedDiagList &capturedDiags) 101239462Sdim : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags), 102239462Sdim HasBegunSourceFile(false) { } 103224135Sdim 104239462Sdim virtual void BeginSourceFile(const LangOptions &Opts, 105239462Sdim const Preprocessor *PP) { 106239462Sdim // Pass BeginSourceFile message onto DiagClient on first call. 107239462Sdim // The corresponding EndSourceFile call will be made from an 108239462Sdim // explicit call to FinishCapture. 109239462Sdim if (!HasBegunSourceFile) { 110239462Sdim DiagClient.BeginSourceFile(Opts, PP); 111239462Sdim HasBegunSourceFile = true; 112239462Sdim } 113239462Sdim } 114239462Sdim 115239462Sdim void FinishCapture() { 116239462Sdim // Call EndSourceFile on DiagClient on completion of capture to 117239462Sdim // enable VerifyDiagnosticConsumer to check diagnostics *after* 118239462Sdim // it has received the diagnostic list. 119239462Sdim if (HasBegunSourceFile) { 120239462Sdim DiagClient.EndSourceFile(); 121239462Sdim HasBegunSourceFile = false; 122239462Sdim } 123239462Sdim } 124239462Sdim 125239462Sdim virtual ~CaptureDiagnosticConsumer() { 126239462Sdim assert(!HasBegunSourceFile && "FinishCapture not called!"); 127239462Sdim } 128239462Sdim 129226633Sdim virtual void HandleDiagnostic(DiagnosticsEngine::Level level, 130226633Sdim const Diagnostic &Info) { 131234353Sdim if (DiagnosticIDs::isARCDiagnostic(Info.getID()) || 132226633Sdim level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) { 133224135Sdim CapturedDiags.push_back(StoredDiagnostic(level, Info)); 134224135Sdim return; 135224135Sdim } 136224135Sdim 137224135Sdim // Non-ARC warnings are ignored. 138224135Sdim Diags.setLastDiagnosticIgnored(); 139224135Sdim } 140226633Sdim 141226633Sdim DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { 142226633Sdim // Just drop any diagnostics that come from cloned consumers; they'll 143226633Sdim // have different source managers anyway. 144226633Sdim return new IgnoringDiagConsumer(); 145226633Sdim } 146224135Sdim}; 147224135Sdim 148224135Sdim} // end anonymous namespace 149224135Sdim 150224135Sdimstatic bool HasARCRuntime(CompilerInvocation &origCI) { 151224135Sdim // This duplicates some functionality from Darwin::AddDeploymentTarget 152224135Sdim // but this function is well defined, so keep it decoupled from the driver 153224135Sdim // and avoid unrelated complications. 154224135Sdim llvm::Triple triple(origCI.getTargetOpts().Triple); 155224135Sdim 156224135Sdim if (triple.getOS() == llvm::Triple::IOS) 157224135Sdim return triple.getOSMajorVersion() >= 5; 158224135Sdim 159224135Sdim if (triple.getOS() == llvm::Triple::Darwin) 160224135Sdim return triple.getOSMajorVersion() >= 11; 161224135Sdim 162224135Sdim if (triple.getOS() == llvm::Triple::MacOSX) { 163224135Sdim unsigned Major, Minor, Micro; 164224135Sdim triple.getOSVersion(Major, Minor, Micro); 165224135Sdim return Major > 10 || (Major == 10 && Minor >= 7); 166224135Sdim } 167224135Sdim 168224135Sdim return false; 169224135Sdim} 170224135Sdim 171226633Sdimstatic CompilerInvocation * 172226633SdimcreateInvocationForMigration(CompilerInvocation &origCI) { 173234353Sdim OwningPtr<CompilerInvocation> CInvok; 174224135Sdim CInvok.reset(new CompilerInvocation(origCI)); 175224135Sdim CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string(); 176224135Sdim CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string(); 177224135Sdim std::string define = getARCMTMacroName(); 178224135Sdim define += '='; 179224135Sdim CInvok->getPreprocessorOpts().addMacroDef(define); 180234353Sdim CInvok->getLangOpts()->ObjCAutoRefCount = true; 181234353Sdim CInvok->getLangOpts()->setGC(LangOptions::NonGC); 182224135Sdim CInvok->getDiagnosticOpts().ErrorLimit = 0; 183239462Sdim CInvok->getDiagnosticOpts().PedanticErrors = 0; 184239462Sdim 185239462Sdim // Ignore -Werror flags when migrating. 186239462Sdim std::vector<std::string> WarnOpts; 187239462Sdim for (std::vector<std::string>::iterator 188239462Sdim I = CInvok->getDiagnosticOpts().Warnings.begin(), 189239462Sdim E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) { 190239462Sdim if (!StringRef(*I).startswith("error")) 191239462Sdim WarnOpts.push_back(*I); 192239462Sdim } 193239462Sdim WarnOpts.push_back("error=arc-unsafe-retained-assign"); 194239462Sdim CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts); 195239462Sdim 196243830Sdim CInvok->getLangOpts()->ObjCARCWeak = HasARCRuntime(origCI); 197224135Sdim 198224135Sdim return CInvok.take(); 199224135Sdim} 200224135Sdim 201226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags, 202243830Sdim DiagnosticOptions *diagOpts, 203226633Sdim Preprocessor &PP) { 204226633Sdim TextDiagnosticPrinter printer(llvm::errs(), diagOpts); 205234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 206234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 207243830Sdim new DiagnosticsEngine(DiagID, diagOpts, &printer, 208243830Sdim /*ShouldOwnClient=*/false)); 209226633Sdim Diags->setSourceManager(&PP.getSourceManager()); 210226633Sdim 211234353Sdim printer.BeginSourceFile(PP.getLangOpts(), &PP); 212226633Sdim arcDiags.reportDiagnostics(*Diags); 213226633Sdim printer.EndSourceFile(); 214226633Sdim} 215226633Sdim 216224135Sdim//===----------------------------------------------------------------------===// 217224135Sdim// checkForManualIssues. 218224135Sdim//===----------------------------------------------------------------------===// 219224135Sdim 220224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI, 221234353Sdim const FrontendInputFile &Input, 222226633Sdim DiagnosticConsumer *DiagClient, 223226633Sdim bool emitPremigrationARCErrors, 224226633Sdim StringRef plistOut) { 225234353Sdim if (!origCI.getLangOpts()->ObjC1) 226224135Sdim return false; 227224135Sdim 228234353Sdim LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); 229234353Sdim bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; 230234353Sdim bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; 231234353Sdim 232234353Sdim std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, 233234353Sdim NoFinalizeRemoval); 234224135Sdim assert(!transforms.empty()); 235224135Sdim 236234353Sdim OwningPtr<CompilerInvocation> CInvok; 237224135Sdim CInvok.reset(createInvocationForMigration(origCI)); 238224135Sdim CInvok->getFrontendOpts().Inputs.clear(); 239234353Sdim CInvok->getFrontendOpts().Inputs.push_back(Input); 240224135Sdim 241224135Sdim CapturedDiagList capturedDiags; 242224135Sdim 243224135Sdim assert(DiagClient); 244234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 245234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 246243830Sdim new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), 247243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 248224135Sdim 249224135Sdim // Filter of all diagnostics. 250239462Sdim CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); 251224135Sdim Diags->setClient(&errRec, /*ShouldOwnClient=*/false); 252224135Sdim 253234353Sdim OwningPtr<ASTUnit> Unit( 254224135Sdim ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags)); 255239462Sdim if (!Unit) { 256239462Sdim errRec.FinishCapture(); 257224135Sdim return true; 258239462Sdim } 259224135Sdim 260224135Sdim // Don't filter diagnostics anymore. 261224135Sdim Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); 262224135Sdim 263224135Sdim ASTContext &Ctx = Unit->getASTContext(); 264224135Sdim 265224135Sdim if (Diags->hasFatalErrorOccurred()) { 266224135Sdim Diags->Reset(); 267234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 268224135Sdim capturedDiags.reportDiagnostics(*Diags); 269224135Sdim DiagClient->EndSourceFile(); 270239462Sdim errRec.FinishCapture(); 271224135Sdim return true; 272224135Sdim } 273224135Sdim 274226633Sdim if (emitPremigrationARCErrors) 275243830Sdim emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(), 276226633Sdim Unit->getPreprocessor()); 277226633Sdim if (!plistOut.empty()) { 278226633Sdim SmallVector<StoredDiagnostic, 8> arcDiags; 279226633Sdim for (CapturedDiagList::iterator 280226633Sdim I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I) 281226633Sdim arcDiags.push_back(*I); 282226633Sdim writeARCDiagsToPlist(plistOut, arcDiags, 283234353Sdim Ctx.getSourceManager(), Ctx.getLangOpts()); 284226633Sdim } 285226633Sdim 286224135Sdim // After parsing of source files ended, we want to reuse the 287224135Sdim // diagnostics objects to emit further diagnostics. 288226633Sdim // We call BeginSourceFile because DiagnosticConsumer requires that 289224135Sdim // diagnostics with source range information are emitted only in between 290224135Sdim // BeginSourceFile() and EndSourceFile(). 291234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 292224135Sdim 293224135Sdim // No macros will be added since we are just checking and we won't modify 294224135Sdim // source code. 295224135Sdim std::vector<SourceLocation> ARCMTMacroLocs; 296224135Sdim 297224135Sdim TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); 298234353Sdim MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, ARCMTMacroLocs); 299234353Sdim pass.setNSAllocReallocError(NoNSAllocReallocError); 300234353Sdim pass.setNoFinalizeRemoval(NoFinalizeRemoval); 301224135Sdim 302224135Sdim for (unsigned i=0, e = transforms.size(); i != e; ++i) 303224135Sdim transforms[i](pass); 304224135Sdim 305224135Sdim capturedDiags.reportDiagnostics(*Diags); 306224135Sdim 307224135Sdim DiagClient->EndSourceFile(); 308239462Sdim errRec.FinishCapture(); 309224135Sdim 310224135Sdim // If we are migrating code that gets the '-fobjc-arc' flag, make sure 311224135Sdim // to remove it so that we don't get errors from normal compilation. 312234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = false; 313224135Sdim 314226633Sdim return capturedDiags.hasErrors() || testAct.hasReportedErrors(); 315224135Sdim} 316224135Sdim 317224135Sdim//===----------------------------------------------------------------------===// 318224135Sdim// applyTransformations. 319224135Sdim//===----------------------------------------------------------------------===// 320224135Sdim 321224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI, 322234353Sdim const FrontendInputFile &Input, 323226633Sdim DiagnosticConsumer *DiagClient, 324226633Sdim StringRef outputDir, 325226633Sdim bool emitPremigrationARCErrors, 326226633Sdim StringRef plistOut) { 327234353Sdim if (!origCI.getLangOpts()->ObjC1) 328224135Sdim return false; 329224135Sdim 330234353Sdim LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); 331234353Sdim 332224135Sdim // Make sure checking is successful first. 333224135Sdim CompilerInvocation CInvokForCheck(origCI); 334234353Sdim if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient, 335226633Sdim emitPremigrationARCErrors, plistOut)) 336224135Sdim return true; 337224135Sdim 338224135Sdim CompilerInvocation CInvok(origCI); 339224135Sdim CInvok.getFrontendOpts().Inputs.clear(); 340234353Sdim CInvok.getFrontendOpts().Inputs.push_back(Input); 341224135Sdim 342224135Sdim MigrationProcess migration(CInvok, DiagClient, outputDir); 343234353Sdim bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; 344224135Sdim 345234353Sdim std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, 346234353Sdim NoFinalizeRemoval); 347224135Sdim assert(!transforms.empty()); 348224135Sdim 349224135Sdim for (unsigned i=0, e = transforms.size(); i != e; ++i) { 350224135Sdim bool err = migration.applyTransform(transforms[i]); 351224135Sdim if (err) return true; 352224135Sdim } 353224135Sdim 354234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 355234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 356243830Sdim new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), 357243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 358224135Sdim 359224135Sdim if (outputDir.empty()) { 360234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = true; 361224135Sdim return migration.getRemapper().overwriteOriginal(*Diags); 362224135Sdim } else { 363224135Sdim // If we are migrating code that gets the '-fobjc-arc' flag, make sure 364224135Sdim // to remove it so that we don't get errors from normal compilation. 365234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = false; 366224135Sdim return migration.getRemapper().flushToDisk(outputDir, *Diags); 367224135Sdim } 368224135Sdim} 369224135Sdim 370224135Sdimbool arcmt::applyTransformations(CompilerInvocation &origCI, 371234353Sdim const FrontendInputFile &Input, 372226633Sdim DiagnosticConsumer *DiagClient) { 373234353Sdim return applyTransforms(origCI, Input, DiagClient, 374226633Sdim StringRef(), false, StringRef()); 375224135Sdim} 376224135Sdim 377224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI, 378234353Sdim const FrontendInputFile &Input, 379226633Sdim DiagnosticConsumer *DiagClient, 380226633Sdim StringRef outputDir, 381226633Sdim bool emitPremigrationARCErrors, 382226633Sdim StringRef plistOut) { 383224135Sdim assert(!outputDir.empty() && "Expected output directory path"); 384234353Sdim return applyTransforms(origCI, Input, DiagClient, 385226633Sdim outputDir, emitPremigrationARCErrors, plistOut); 386224135Sdim} 387224135Sdim 388224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & 389224135Sdim remap, 390226633Sdim StringRef outputDir, 391226633Sdim DiagnosticConsumer *DiagClient) { 392224135Sdim assert(!outputDir.empty()); 393224135Sdim 394234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 395234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 396243830Sdim new DiagnosticsEngine(DiagID, new DiagnosticOptions, 397243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 398224135Sdim 399224135Sdim FileRemapper remapper; 400224135Sdim bool err = remapper.initFromDisk(outputDir, *Diags, 401224135Sdim /*ignoreIfFilesChanged=*/true); 402224135Sdim if (err) 403224135Sdim return true; 404224135Sdim 405234353Sdim PreprocessorOptions PPOpts; 406234353Sdim remapper.applyMappings(PPOpts); 407234353Sdim remap = PPOpts.RemappedFiles; 408224135Sdim 409224135Sdim return false; 410224135Sdim} 411224135Sdim 412234353Sdimbool arcmt::getFileRemappingsFromFileList( 413234353Sdim std::vector<std::pair<std::string,std::string> > &remap, 414234353Sdim ArrayRef<StringRef> remapFiles, 415234353Sdim DiagnosticConsumer *DiagClient) { 416234353Sdim bool hasErrorOccurred = false; 417234353Sdim llvm::StringMap<bool> Uniquer; 418234353Sdim 419234353Sdim llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 420234353Sdim llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 421243830Sdim new DiagnosticsEngine(DiagID, new DiagnosticOptions, 422243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 423234353Sdim 424234353Sdim for (ArrayRef<StringRef>::iterator 425234353Sdim I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { 426234353Sdim StringRef file = *I; 427234353Sdim 428234353Sdim FileRemapper remapper; 429234353Sdim bool err = remapper.initFromFile(file, *Diags, 430234353Sdim /*ignoreIfFilesChanged=*/true); 431234353Sdim hasErrorOccurred = hasErrorOccurred || err; 432234353Sdim if (err) 433234353Sdim continue; 434234353Sdim 435234353Sdim PreprocessorOptions PPOpts; 436234353Sdim remapper.applyMappings(PPOpts); 437234353Sdim for (PreprocessorOptions::remapped_file_iterator 438234353Sdim RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end(); 439234353Sdim RI != RE; ++RI) { 440234353Sdim bool &inserted = Uniquer[RI->first]; 441234353Sdim if (inserted) 442234353Sdim continue; 443234353Sdim inserted = true; 444234353Sdim remap.push_back(*RI); 445234353Sdim } 446234353Sdim } 447234353Sdim 448234353Sdim return hasErrorOccurred; 449234353Sdim} 450234353Sdim 451224135Sdim//===----------------------------------------------------------------------===// 452224135Sdim// CollectTransformActions. 453224135Sdim//===----------------------------------------------------------------------===// 454224135Sdim 455224135Sdimnamespace { 456224135Sdim 457224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks { 458224135Sdim std::vector<SourceLocation> &ARCMTMacroLocs; 459224135Sdim 460224135Sdimpublic: 461224135Sdim ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) 462224135Sdim : ARCMTMacroLocs(ARCMTMacroLocs) { } 463224135Sdim 464226633Sdim virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI, 465226633Sdim SourceRange Range) { 466224135Sdim if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) 467224135Sdim ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); 468224135Sdim } 469224135Sdim}; 470224135Sdim 471224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction { 472224135Sdim std::vector<SourceLocation> &ARCMTMacroLocs; 473224135Sdim 474224135Sdimpublic: 475224135Sdim ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) 476224135Sdim : ARCMTMacroLocs(ARCMTMacroLocs) { } 477224135Sdim 478224135Sdim virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 479226633Sdim StringRef InFile) { 480224135Sdim CI.getPreprocessor().addPPCallbacks( 481224135Sdim new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs)); 482224135Sdim return new ASTConsumer(); 483224135Sdim } 484224135Sdim}; 485224135Sdim 486224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver { 487224135Sdim Rewriter &rewriter; 488224135Sdim MigrationProcess::RewriteListener *Listener; 489224135Sdim 490224135Sdimpublic: 491224135Sdim RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, 492224135Sdim MigrationProcess::RewriteListener *listener) 493239462Sdim : rewriter(rewriter), Listener(listener) { 494224135Sdim if (Listener) 495224135Sdim Listener->start(ctx); 496224135Sdim } 497224135Sdim ~RewritesApplicator() { 498224135Sdim if (Listener) 499224135Sdim Listener->finish(); 500224135Sdim } 501224135Sdim 502226633Sdim virtual void insert(SourceLocation loc, StringRef text) { 503224135Sdim bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true, 504224135Sdim /*indentNewLines=*/true); 505224135Sdim if (!err && Listener) 506224135Sdim Listener->insert(loc, text); 507224135Sdim } 508224135Sdim 509224135Sdim virtual void remove(CharSourceRange range) { 510224135Sdim Rewriter::RewriteOptions removeOpts; 511224135Sdim removeOpts.IncludeInsertsAtBeginOfRange = false; 512224135Sdim removeOpts.IncludeInsertsAtEndOfRange = false; 513224135Sdim removeOpts.RemoveLineIfEmpty = true; 514224135Sdim 515224135Sdim bool err = rewriter.RemoveText(range, removeOpts); 516224135Sdim if (!err && Listener) 517224135Sdim Listener->remove(range); 518224135Sdim } 519224135Sdim 520224135Sdim virtual void increaseIndentation(CharSourceRange range, 521224135Sdim SourceLocation parentIndent) { 522224135Sdim rewriter.IncreaseIndentation(range, parentIndent); 523224135Sdim } 524224135Sdim}; 525224135Sdim 526224135Sdim} // end anonymous namespace. 527224135Sdim 528224135Sdim/// \brief Anchor for VTable. 529224135SdimMigrationProcess::RewriteListener::~RewriteListener() { } 530224135Sdim 531224135SdimMigrationProcess::MigrationProcess(const CompilerInvocation &CI, 532226633Sdim DiagnosticConsumer *diagClient, 533226633Sdim StringRef outputDir) 534224135Sdim : OrigCI(CI), DiagClient(diagClient) { 535224135Sdim if (!outputDir.empty()) { 536234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 537234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 538243830Sdim new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), 539243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 540224135Sdim Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); 541224135Sdim } 542224135Sdim} 543224135Sdim 544224135Sdimbool MigrationProcess::applyTransform(TransformFn trans, 545224135Sdim RewriteListener *listener) { 546234353Sdim OwningPtr<CompilerInvocation> CInvok; 547224135Sdim CInvok.reset(createInvocationForMigration(OrigCI)); 548224135Sdim CInvok->getDiagnosticOpts().IgnoreWarnings = true; 549224135Sdim 550234353Sdim Remapper.applyMappings(CInvok->getPreprocessorOpts()); 551224135Sdim 552224135Sdim CapturedDiagList capturedDiags; 553224135Sdim std::vector<SourceLocation> ARCMTMacroLocs; 554224135Sdim 555224135Sdim assert(DiagClient); 556234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 557234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 558243830Sdim new DiagnosticsEngine(DiagID, new DiagnosticOptions, 559243830Sdim DiagClient, /*ShouldOwnClient=*/false)); 560224135Sdim 561224135Sdim // Filter of all diagnostics. 562239462Sdim CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); 563224135Sdim Diags->setClient(&errRec, /*ShouldOwnClient=*/false); 564224135Sdim 565234353Sdim OwningPtr<ARCMTMacroTrackerAction> ASTAction; 566224135Sdim ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); 567224135Sdim 568234353Sdim OwningPtr<ASTUnit> Unit( 569224135Sdim ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags, 570224135Sdim ASTAction.get())); 571239462Sdim if (!Unit) { 572239462Sdim errRec.FinishCapture(); 573224135Sdim return true; 574239462Sdim } 575224135Sdim Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. 576224135Sdim 577224135Sdim // Don't filter diagnostics anymore. 578224135Sdim Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); 579224135Sdim 580224135Sdim ASTContext &Ctx = Unit->getASTContext(); 581224135Sdim 582224135Sdim if (Diags->hasFatalErrorOccurred()) { 583224135Sdim Diags->Reset(); 584234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 585224135Sdim capturedDiags.reportDiagnostics(*Diags); 586224135Sdim DiagClient->EndSourceFile(); 587239462Sdim errRec.FinishCapture(); 588224135Sdim return true; 589224135Sdim } 590224135Sdim 591224135Sdim // After parsing of source files ended, we want to reuse the 592224135Sdim // diagnostics objects to emit further diagnostics. 593226633Sdim // We call BeginSourceFile because DiagnosticConsumer requires that 594224135Sdim // diagnostics with source range information are emitted only in between 595224135Sdim // BeginSourceFile() and EndSourceFile(). 596234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 597224135Sdim 598234353Sdim Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 599224135Sdim TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); 600234353Sdim MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(), 601234353Sdim Unit->getSema(), TA, ARCMTMacroLocs); 602224135Sdim 603224135Sdim trans(pass); 604224135Sdim 605224135Sdim { 606224135Sdim RewritesApplicator applicator(rewriter, Ctx, listener); 607224135Sdim TA.applyRewrites(applicator); 608224135Sdim } 609224135Sdim 610224135Sdim DiagClient->EndSourceFile(); 611239462Sdim errRec.FinishCapture(); 612224135Sdim 613224135Sdim if (DiagClient->getNumErrors()) 614224135Sdim return true; 615224135Sdim 616224135Sdim for (Rewriter::buffer_iterator 617224135Sdim I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 618224135Sdim FileID FID = I->first; 619224135Sdim RewriteBuffer &buf = I->second; 620224135Sdim const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 621224135Sdim assert(file); 622224135Sdim std::string newFname = file->getName(); 623224135Sdim newFname += "-trans"; 624234353Sdim SmallString<512> newText; 625224135Sdim llvm::raw_svector_ostream vecOS(newText); 626224135Sdim buf.write(vecOS); 627224135Sdim vecOS.flush(); 628224135Sdim llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( 629226633Sdim StringRef(newText.data(), newText.size()), newFname); 630234353Sdim SmallString<64> filePath(file->getName()); 631224135Sdim Unit->getFileManager().FixupRelativePath(filePath); 632224135Sdim Remapper.remap(filePath.str(), memBuf); 633224135Sdim } 634224135Sdim 635224135Sdim return false; 636224135Sdim} 637