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