ARCMT.cpp revision 234353
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" 17224135Sdim#include "clang/Rewrite/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. 45224135Sdim 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; 94224135Sdim CapturedDiagList &CapturedDiags; 95224135Sdimpublic: 96226633Sdim CaptureDiagnosticConsumer(DiagnosticsEngine &diags, 97226633Sdim CapturedDiagList &capturedDiags) 98224135Sdim : Diags(diags), CapturedDiags(capturedDiags) { } 99224135Sdim 100226633Sdim virtual void HandleDiagnostic(DiagnosticsEngine::Level level, 101226633Sdim const Diagnostic &Info) { 102234353Sdim if (DiagnosticIDs::isARCDiagnostic(Info.getID()) || 103226633Sdim level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) { 104224135Sdim CapturedDiags.push_back(StoredDiagnostic(level, Info)); 105224135Sdim return; 106224135Sdim } 107224135Sdim 108224135Sdim // Non-ARC warnings are ignored. 109224135Sdim Diags.setLastDiagnosticIgnored(); 110224135Sdim } 111226633Sdim 112226633Sdim DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { 113226633Sdim // Just drop any diagnostics that come from cloned consumers; they'll 114226633Sdim // have different source managers anyway. 115226633Sdim return new IgnoringDiagConsumer(); 116226633Sdim } 117224135Sdim}; 118224135Sdim 119224135Sdim} // end anonymous namespace 120224135Sdim 121226633Sdimstatic inline StringRef SimulatorVersionDefineName() { 122224135Sdim return "__IPHONE_OS_VERSION_MIN_REQUIRED="; 123224135Sdim} 124224135Sdim 125224135Sdim/// \brief Parse the simulator version define: 126224135Sdim/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9]) 127224135Sdim// and return the grouped values as integers, e.g: 128224135Sdim// __IPHONE_OS_VERSION_MIN_REQUIRED=40201 129224135Sdim// will return Major=4, Minor=2, Micro=1. 130226633Sdimstatic bool GetVersionFromSimulatorDefine(StringRef define, 131224135Sdim unsigned &Major, unsigned &Minor, 132224135Sdim unsigned &Micro) { 133224135Sdim assert(define.startswith(SimulatorVersionDefineName())); 134226633Sdim StringRef name, version; 135224135Sdim llvm::tie(name, version) = define.split('='); 136224135Sdim if (version.empty()) 137224135Sdim return false; 138224135Sdim std::string verstr = version.str(); 139224135Sdim char *end; 140224135Sdim unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10); 141224135Sdim if (*end != '\0') 142224135Sdim return false; 143224135Sdim Major = num / 10000; 144224135Sdim num = num % 10000; 145224135Sdim Minor = num / 100; 146224135Sdim Micro = num % 100; 147224135Sdim return true; 148224135Sdim} 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 155224135Sdim for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size(); 156224135Sdim i != e; ++i) { 157224135Sdim StringRef define = origCI.getPreprocessorOpts().Macros[i].first; 158224135Sdim bool isUndef = origCI.getPreprocessorOpts().Macros[i].second; 159224135Sdim if (isUndef) 160224135Sdim continue; 161224135Sdim if (!define.startswith(SimulatorVersionDefineName())) 162224135Sdim continue; 163226633Sdim unsigned Major = 0, Minor = 0, Micro = 0; 164224135Sdim if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && 165224135Sdim Major < 10 && Minor < 100 && Micro < 100) 166224135Sdim return Major >= 5; 167224135Sdim } 168224135Sdim 169224135Sdim llvm::Triple triple(origCI.getTargetOpts().Triple); 170224135Sdim 171224135Sdim if (triple.getOS() == llvm::Triple::IOS) 172224135Sdim return triple.getOSMajorVersion() >= 5; 173224135Sdim 174224135Sdim if (triple.getOS() == llvm::Triple::Darwin) 175224135Sdim return triple.getOSMajorVersion() >= 11; 176224135Sdim 177224135Sdim if (triple.getOS() == llvm::Triple::MacOSX) { 178224135Sdim unsigned Major, Minor, Micro; 179224135Sdim triple.getOSVersion(Major, Minor, Micro); 180224135Sdim return Major > 10 || (Major == 10 && Minor >= 7); 181224135Sdim } 182224135Sdim 183224135Sdim return false; 184224135Sdim} 185224135Sdim 186226633Sdimstatic CompilerInvocation * 187226633SdimcreateInvocationForMigration(CompilerInvocation &origCI) { 188234353Sdim OwningPtr<CompilerInvocation> CInvok; 189224135Sdim CInvok.reset(new CompilerInvocation(origCI)); 190224135Sdim CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string(); 191224135Sdim CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string(); 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; 198224135Sdim CInvok->getDiagnosticOpts().Warnings.push_back( 199224135Sdim "error=arc-unsafe-retained-assign"); 200234353Sdim CInvok->getLangOpts()->ObjCRuntimeHasWeak = HasARCRuntime(origCI); 201224135Sdim 202224135Sdim return CInvok.take(); 203224135Sdim} 204224135Sdim 205226633Sdimstatic void emitPremigrationErrors(const CapturedDiagList &arcDiags, 206226633Sdim const DiagnosticOptions &diagOpts, 207226633Sdim Preprocessor &PP) { 208226633Sdim TextDiagnosticPrinter printer(llvm::errs(), diagOpts); 209234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 210234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 211226633Sdim new DiagnosticsEngine(DiagID, &printer, /*ShouldOwnClient=*/false)); 212226633Sdim Diags->setSourceManager(&PP.getSourceManager()); 213226633Sdim 214234353Sdim printer.BeginSourceFile(PP.getLangOpts(), &PP); 215226633Sdim arcDiags.reportDiagnostics(*Diags); 216226633Sdim printer.EndSourceFile(); 217226633Sdim} 218226633Sdim 219224135Sdim//===----------------------------------------------------------------------===// 220224135Sdim// checkForManualIssues. 221224135Sdim//===----------------------------------------------------------------------===// 222224135Sdim 223224135Sdimbool arcmt::checkForManualIssues(CompilerInvocation &origCI, 224234353Sdim const FrontendInputFile &Input, 225226633Sdim DiagnosticConsumer *DiagClient, 226226633Sdim bool emitPremigrationARCErrors, 227226633Sdim StringRef plistOut) { 228234353Sdim if (!origCI.getLangOpts()->ObjC1) 229224135Sdim return false; 230224135Sdim 231234353Sdim LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); 232234353Sdim bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; 233234353Sdim bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; 234234353Sdim 235234353Sdim std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, 236234353Sdim NoFinalizeRemoval); 237224135Sdim assert(!transforms.empty()); 238224135Sdim 239234353Sdim OwningPtr<CompilerInvocation> CInvok; 240224135Sdim CInvok.reset(createInvocationForMigration(origCI)); 241224135Sdim CInvok->getFrontendOpts().Inputs.clear(); 242234353Sdim CInvok->getFrontendOpts().Inputs.push_back(Input); 243224135Sdim 244224135Sdim CapturedDiagList capturedDiags; 245224135Sdim 246224135Sdim assert(DiagClient); 247234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 248234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 249226633Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 250224135Sdim 251224135Sdim // Filter of all diagnostics. 252226633Sdim CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); 253224135Sdim Diags->setClient(&errRec, /*ShouldOwnClient=*/false); 254224135Sdim 255234353Sdim OwningPtr<ASTUnit> Unit( 256224135Sdim ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags)); 257224135Sdim if (!Unit) 258224135Sdim return true; 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(); 270224135Sdim return true; 271224135Sdim } 272224135Sdim 273226633Sdim if (emitPremigrationARCErrors) 274226633Sdim emitPremigrationErrors(capturedDiags, origCI.getDiagnosticOpts(), 275226633Sdim Unit->getPreprocessor()); 276226633Sdim if (!plistOut.empty()) { 277226633Sdim SmallVector<StoredDiagnostic, 8> arcDiags; 278226633Sdim for (CapturedDiagList::iterator 279226633Sdim I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I) 280226633Sdim arcDiags.push_back(*I); 281226633Sdim writeARCDiagsToPlist(plistOut, arcDiags, 282234353Sdim Ctx.getSourceManager(), Ctx.getLangOpts()); 283226633Sdim } 284226633Sdim 285224135Sdim // After parsing of source files ended, we want to reuse the 286224135Sdim // diagnostics objects to emit further diagnostics. 287226633Sdim // We call BeginSourceFile because DiagnosticConsumer requires that 288224135Sdim // diagnostics with source range information are emitted only in between 289224135Sdim // BeginSourceFile() and EndSourceFile(). 290234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 291224135Sdim 292224135Sdim // No macros will be added since we are just checking and we won't modify 293224135Sdim // source code. 294224135Sdim std::vector<SourceLocation> ARCMTMacroLocs; 295224135Sdim 296224135Sdim TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); 297234353Sdim MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, ARCMTMacroLocs); 298234353Sdim pass.setNSAllocReallocError(NoNSAllocReallocError); 299234353Sdim pass.setNoFinalizeRemoval(NoFinalizeRemoval); 300224135Sdim 301224135Sdim for (unsigned i=0, e = transforms.size(); i != e; ++i) 302224135Sdim transforms[i](pass); 303224135Sdim 304224135Sdim capturedDiags.reportDiagnostics(*Diags); 305224135Sdim 306224135Sdim DiagClient->EndSourceFile(); 307224135Sdim 308224135Sdim // If we are migrating code that gets the '-fobjc-arc' flag, make sure 309224135Sdim // to remove it so that we don't get errors from normal compilation. 310234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = false; 311224135Sdim 312226633Sdim return capturedDiags.hasErrors() || testAct.hasReportedErrors(); 313224135Sdim} 314224135Sdim 315224135Sdim//===----------------------------------------------------------------------===// 316224135Sdim// applyTransformations. 317224135Sdim//===----------------------------------------------------------------------===// 318224135Sdim 319224135Sdimstatic bool applyTransforms(CompilerInvocation &origCI, 320234353Sdim const FrontendInputFile &Input, 321226633Sdim DiagnosticConsumer *DiagClient, 322226633Sdim StringRef outputDir, 323226633Sdim bool emitPremigrationARCErrors, 324226633Sdim StringRef plistOut) { 325234353Sdim if (!origCI.getLangOpts()->ObjC1) 326224135Sdim return false; 327224135Sdim 328234353Sdim LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); 329234353Sdim 330224135Sdim // Make sure checking is successful first. 331224135Sdim CompilerInvocation CInvokForCheck(origCI); 332234353Sdim if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient, 333226633Sdim emitPremigrationARCErrors, plistOut)) 334224135Sdim return true; 335224135Sdim 336224135Sdim CompilerInvocation CInvok(origCI); 337224135Sdim CInvok.getFrontendOpts().Inputs.clear(); 338234353Sdim CInvok.getFrontendOpts().Inputs.push_back(Input); 339224135Sdim 340224135Sdim MigrationProcess migration(CInvok, DiagClient, outputDir); 341234353Sdim bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; 342224135Sdim 343234353Sdim std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, 344234353Sdim NoFinalizeRemoval); 345224135Sdim assert(!transforms.empty()); 346224135Sdim 347224135Sdim for (unsigned i=0, e = transforms.size(); i != e; ++i) { 348224135Sdim bool err = migration.applyTransform(transforms[i]); 349224135Sdim if (err) return true; 350224135Sdim } 351224135Sdim 352234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 353234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 354226633Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 355224135Sdim 356224135Sdim if (outputDir.empty()) { 357234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = true; 358224135Sdim return migration.getRemapper().overwriteOriginal(*Diags); 359224135Sdim } else { 360224135Sdim // If we are migrating code that gets the '-fobjc-arc' flag, make sure 361224135Sdim // to remove it so that we don't get errors from normal compilation. 362234353Sdim origCI.getLangOpts()->ObjCAutoRefCount = false; 363224135Sdim return migration.getRemapper().flushToDisk(outputDir, *Diags); 364224135Sdim } 365224135Sdim} 366224135Sdim 367224135Sdimbool arcmt::applyTransformations(CompilerInvocation &origCI, 368234353Sdim const FrontendInputFile &Input, 369226633Sdim DiagnosticConsumer *DiagClient) { 370234353Sdim return applyTransforms(origCI, Input, DiagClient, 371226633Sdim StringRef(), false, StringRef()); 372224135Sdim} 373224135Sdim 374224135Sdimbool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI, 375234353Sdim const FrontendInputFile &Input, 376226633Sdim DiagnosticConsumer *DiagClient, 377226633Sdim StringRef outputDir, 378226633Sdim bool emitPremigrationARCErrors, 379226633Sdim StringRef plistOut) { 380224135Sdim assert(!outputDir.empty() && "Expected output directory path"); 381234353Sdim return applyTransforms(origCI, Input, DiagClient, 382226633Sdim outputDir, emitPremigrationARCErrors, plistOut); 383224135Sdim} 384224135Sdim 385224135Sdimbool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & 386224135Sdim remap, 387226633Sdim StringRef outputDir, 388226633Sdim DiagnosticConsumer *DiagClient) { 389224135Sdim assert(!outputDir.empty()); 390224135Sdim 391234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 392234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 393226633Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 394224135Sdim 395224135Sdim FileRemapper remapper; 396224135Sdim bool err = remapper.initFromDisk(outputDir, *Diags, 397224135Sdim /*ignoreIfFilesChanged=*/true); 398224135Sdim if (err) 399224135Sdim return true; 400224135Sdim 401234353Sdim PreprocessorOptions PPOpts; 402234353Sdim remapper.applyMappings(PPOpts); 403234353Sdim remap = PPOpts.RemappedFiles; 404224135Sdim 405224135Sdim return false; 406224135Sdim} 407224135Sdim 408234353Sdimbool arcmt::getFileRemappingsFromFileList( 409234353Sdim std::vector<std::pair<std::string,std::string> > &remap, 410234353Sdim ArrayRef<StringRef> remapFiles, 411234353Sdim DiagnosticConsumer *DiagClient) { 412234353Sdim bool hasErrorOccurred = false; 413234353Sdim llvm::StringMap<bool> Uniquer; 414234353Sdim 415234353Sdim llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 416234353Sdim llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 417234353Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 418234353Sdim 419234353Sdim for (ArrayRef<StringRef>::iterator 420234353Sdim I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { 421234353Sdim StringRef file = *I; 422234353Sdim 423234353Sdim FileRemapper remapper; 424234353Sdim bool err = remapper.initFromFile(file, *Diags, 425234353Sdim /*ignoreIfFilesChanged=*/true); 426234353Sdim hasErrorOccurred = hasErrorOccurred || err; 427234353Sdim if (err) 428234353Sdim continue; 429234353Sdim 430234353Sdim PreprocessorOptions PPOpts; 431234353Sdim remapper.applyMappings(PPOpts); 432234353Sdim for (PreprocessorOptions::remapped_file_iterator 433234353Sdim RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end(); 434234353Sdim RI != RE; ++RI) { 435234353Sdim bool &inserted = Uniquer[RI->first]; 436234353Sdim if (inserted) 437234353Sdim continue; 438234353Sdim inserted = true; 439234353Sdim remap.push_back(*RI); 440234353Sdim } 441234353Sdim } 442234353Sdim 443234353Sdim return hasErrorOccurred; 444234353Sdim} 445234353Sdim 446224135Sdim//===----------------------------------------------------------------------===// 447224135Sdim// CollectTransformActions. 448224135Sdim//===----------------------------------------------------------------------===// 449224135Sdim 450224135Sdimnamespace { 451224135Sdim 452224135Sdimclass ARCMTMacroTrackerPPCallbacks : public PPCallbacks { 453224135Sdim std::vector<SourceLocation> &ARCMTMacroLocs; 454224135Sdim 455224135Sdimpublic: 456224135Sdim ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) 457224135Sdim : ARCMTMacroLocs(ARCMTMacroLocs) { } 458224135Sdim 459226633Sdim virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI, 460226633Sdim SourceRange Range) { 461224135Sdim if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) 462224135Sdim ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); 463224135Sdim } 464224135Sdim}; 465224135Sdim 466224135Sdimclass ARCMTMacroTrackerAction : public ASTFrontendAction { 467224135Sdim std::vector<SourceLocation> &ARCMTMacroLocs; 468224135Sdim 469224135Sdimpublic: 470224135Sdim ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) 471224135Sdim : ARCMTMacroLocs(ARCMTMacroLocs) { } 472224135Sdim 473224135Sdim virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 474226633Sdim StringRef InFile) { 475224135Sdim CI.getPreprocessor().addPPCallbacks( 476224135Sdim new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs)); 477224135Sdim return new ASTConsumer(); 478224135Sdim } 479224135Sdim}; 480224135Sdim 481224135Sdimclass RewritesApplicator : public TransformActions::RewriteReceiver { 482224135Sdim Rewriter &rewriter; 483224135Sdim ASTContext &Ctx; 484224135Sdim MigrationProcess::RewriteListener *Listener; 485224135Sdim 486224135Sdimpublic: 487224135Sdim RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, 488224135Sdim MigrationProcess::RewriteListener *listener) 489224135Sdim : rewriter(rewriter), Ctx(ctx), Listener(listener) { 490224135Sdim if (Listener) 491224135Sdim Listener->start(ctx); 492224135Sdim } 493224135Sdim ~RewritesApplicator() { 494224135Sdim if (Listener) 495224135Sdim Listener->finish(); 496224135Sdim } 497224135Sdim 498226633Sdim virtual void insert(SourceLocation loc, StringRef text) { 499224135Sdim bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true, 500224135Sdim /*indentNewLines=*/true); 501224135Sdim if (!err && Listener) 502224135Sdim Listener->insert(loc, text); 503224135Sdim } 504224135Sdim 505224135Sdim virtual void remove(CharSourceRange range) { 506224135Sdim Rewriter::RewriteOptions removeOpts; 507224135Sdim removeOpts.IncludeInsertsAtBeginOfRange = false; 508224135Sdim removeOpts.IncludeInsertsAtEndOfRange = false; 509224135Sdim removeOpts.RemoveLineIfEmpty = true; 510224135Sdim 511224135Sdim bool err = rewriter.RemoveText(range, removeOpts); 512224135Sdim if (!err && Listener) 513224135Sdim Listener->remove(range); 514224135Sdim } 515224135Sdim 516224135Sdim virtual void increaseIndentation(CharSourceRange range, 517224135Sdim SourceLocation parentIndent) { 518224135Sdim rewriter.IncreaseIndentation(range, parentIndent); 519224135Sdim } 520224135Sdim}; 521224135Sdim 522224135Sdim} // end anonymous namespace. 523224135Sdim 524224135Sdim/// \brief Anchor for VTable. 525224135SdimMigrationProcess::RewriteListener::~RewriteListener() { } 526224135Sdim 527224135SdimMigrationProcess::MigrationProcess(const CompilerInvocation &CI, 528226633Sdim DiagnosticConsumer *diagClient, 529226633Sdim StringRef outputDir) 530224135Sdim : OrigCI(CI), DiagClient(diagClient) { 531224135Sdim if (!outputDir.empty()) { 532234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 533234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 534226633Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 535224135Sdim Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); 536224135Sdim } 537224135Sdim} 538224135Sdim 539224135Sdimbool MigrationProcess::applyTransform(TransformFn trans, 540224135Sdim RewriteListener *listener) { 541234353Sdim OwningPtr<CompilerInvocation> CInvok; 542224135Sdim CInvok.reset(createInvocationForMigration(OrigCI)); 543224135Sdim CInvok->getDiagnosticOpts().IgnoreWarnings = true; 544224135Sdim 545234353Sdim Remapper.applyMappings(CInvok->getPreprocessorOpts()); 546224135Sdim 547224135Sdim CapturedDiagList capturedDiags; 548224135Sdim std::vector<SourceLocation> ARCMTMacroLocs; 549224135Sdim 550224135Sdim assert(DiagClient); 551234353Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 552234353Sdim IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 553226633Sdim new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); 554224135Sdim 555224135Sdim // Filter of all diagnostics. 556226633Sdim CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); 557224135Sdim Diags->setClient(&errRec, /*ShouldOwnClient=*/false); 558224135Sdim 559234353Sdim OwningPtr<ARCMTMacroTrackerAction> ASTAction; 560224135Sdim ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); 561224135Sdim 562234353Sdim OwningPtr<ASTUnit> Unit( 563224135Sdim ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags, 564224135Sdim ASTAction.get())); 565224135Sdim if (!Unit) 566224135Sdim return true; 567224135Sdim Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. 568224135Sdim 569224135Sdim // Don't filter diagnostics anymore. 570224135Sdim Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); 571224135Sdim 572224135Sdim ASTContext &Ctx = Unit->getASTContext(); 573224135Sdim 574224135Sdim if (Diags->hasFatalErrorOccurred()) { 575224135Sdim Diags->Reset(); 576234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 577224135Sdim capturedDiags.reportDiagnostics(*Diags); 578224135Sdim DiagClient->EndSourceFile(); 579224135Sdim return true; 580224135Sdim } 581224135Sdim 582224135Sdim // After parsing of source files ended, we want to reuse the 583224135Sdim // diagnostics objects to emit further diagnostics. 584226633Sdim // We call BeginSourceFile because DiagnosticConsumer requires that 585224135Sdim // diagnostics with source range information are emitted only in between 586224135Sdim // BeginSourceFile() and EndSourceFile(). 587234353Sdim DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); 588224135Sdim 589234353Sdim Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 590224135Sdim TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); 591234353Sdim MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(), 592234353Sdim Unit->getSema(), TA, ARCMTMacroLocs); 593224135Sdim 594224135Sdim trans(pass); 595224135Sdim 596224135Sdim { 597224135Sdim RewritesApplicator applicator(rewriter, Ctx, listener); 598224135Sdim TA.applyRewrites(applicator); 599224135Sdim } 600224135Sdim 601224135Sdim DiagClient->EndSourceFile(); 602224135Sdim 603224135Sdim if (DiagClient->getNumErrors()) 604224135Sdim return true; 605224135Sdim 606224135Sdim for (Rewriter::buffer_iterator 607224135Sdim I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 608224135Sdim FileID FID = I->first; 609224135Sdim RewriteBuffer &buf = I->second; 610224135Sdim const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 611224135Sdim assert(file); 612224135Sdim std::string newFname = file->getName(); 613224135Sdim newFname += "-trans"; 614234353Sdim SmallString<512> newText; 615224135Sdim llvm::raw_svector_ostream vecOS(newText); 616224135Sdim buf.write(vecOS); 617224135Sdim vecOS.flush(); 618224135Sdim llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( 619226633Sdim StringRef(newText.data(), newText.size()), newFname); 620234353Sdim SmallString<64> filePath(file->getName()); 621224135Sdim Unit->getFileManager().FixupRelativePath(filePath); 622224135Sdim Remapper.remap(filePath.str(), memBuf); 623224135Sdim } 624224135Sdim 625224135Sdim return false; 626224135Sdim} 627