10b57cec5SDimitry Andric //===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
95ffd83dbSDimitry Andric #include "clang/ARCMigrate/ARCMT.h"
1006c3fb27SDimitry Andric #include "Internals.h"
110b57cec5SDimitry Andric #include "clang/AST/ASTConsumer.h"
120b57cec5SDimitry Andric #include "clang/Basic/DiagnosticCategories.h"
130b57cec5SDimitry Andric #include "clang/Frontend/ASTUnit.h"
140b57cec5SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
150b57cec5SDimitry Andric #include "clang/Frontend/FrontendAction.h"
160b57cec5SDimitry Andric #include "clang/Frontend/TextDiagnosticPrinter.h"
170b57cec5SDimitry Andric #include "clang/Frontend/Utils.h"
180b57cec5SDimitry Andric #include "clang/Lex/Preprocessor.h"
190b57cec5SDimitry Andric #include "clang/Lex/PreprocessorOptions.h"
200b57cec5SDimitry Andric #include "clang/Rewrite/Core/Rewriter.h"
210b57cec5SDimitry Andric #include "clang/Sema/SemaDiagnostic.h"
220b57cec5SDimitry Andric #include "clang/Serialization/ASTReader.h"
230b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
2406c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h"
250b57cec5SDimitry Andric #include <utility>
260b57cec5SDimitry Andric using namespace clang;
270b57cec5SDimitry Andric using namespace arcmt;
280b57cec5SDimitry Andric
clearDiagnostic(ArrayRef<unsigned> IDs,SourceRange range)290b57cec5SDimitry Andric bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
300b57cec5SDimitry Andric SourceRange range) {
310b57cec5SDimitry Andric if (range.isInvalid())
320b57cec5SDimitry Andric return false;
330b57cec5SDimitry Andric
340b57cec5SDimitry Andric bool cleared = false;
350b57cec5SDimitry Andric ListTy::iterator I = List.begin();
360b57cec5SDimitry Andric while (I != List.end()) {
370b57cec5SDimitry Andric FullSourceLoc diagLoc = I->getLocation();
380b57cec5SDimitry Andric if ((IDs.empty() || // empty means clear all diagnostics in the range.
390b57cec5SDimitry Andric llvm::is_contained(IDs, I->getID())) &&
400b57cec5SDimitry Andric !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
410b57cec5SDimitry Andric (diagLoc == range.getEnd() ||
420b57cec5SDimitry Andric diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
430b57cec5SDimitry Andric cleared = true;
440b57cec5SDimitry Andric ListTy::iterator eraseS = I++;
450b57cec5SDimitry Andric if (eraseS->getLevel() != DiagnosticsEngine::Note)
460b57cec5SDimitry Andric while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
470b57cec5SDimitry Andric ++I;
480b57cec5SDimitry Andric // Clear the diagnostic and any notes following it.
490b57cec5SDimitry Andric I = List.erase(eraseS, I);
500b57cec5SDimitry Andric continue;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric ++I;
540b57cec5SDimitry Andric }
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric return cleared;
570b57cec5SDimitry Andric }
580b57cec5SDimitry Andric
hasDiagnostic(ArrayRef<unsigned> IDs,SourceRange range) const590b57cec5SDimitry Andric bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
600b57cec5SDimitry Andric SourceRange range) const {
610b57cec5SDimitry Andric if (range.isInvalid())
620b57cec5SDimitry Andric return false;
630b57cec5SDimitry Andric
640b57cec5SDimitry Andric ListTy::const_iterator I = List.begin();
650b57cec5SDimitry Andric while (I != List.end()) {
660b57cec5SDimitry Andric FullSourceLoc diagLoc = I->getLocation();
670b57cec5SDimitry Andric if ((IDs.empty() || // empty means any diagnostic in the range.
68349cc55cSDimitry Andric llvm::is_contained(IDs, I->getID())) &&
690b57cec5SDimitry Andric !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
700b57cec5SDimitry Andric (diagLoc == range.getEnd() ||
710b57cec5SDimitry Andric diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
720b57cec5SDimitry Andric return true;
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric
750b57cec5SDimitry Andric ++I;
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric
780b57cec5SDimitry Andric return false;
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric
reportDiagnostics(DiagnosticsEngine & Diags) const810b57cec5SDimitry Andric void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
820b57cec5SDimitry Andric for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
830b57cec5SDimitry Andric Diags.Report(*I);
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric
hasErrors() const860b57cec5SDimitry Andric bool CapturedDiagList::hasErrors() const {
870b57cec5SDimitry Andric for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
880b57cec5SDimitry Andric if (I->getLevel() >= DiagnosticsEngine::Error)
890b57cec5SDimitry Andric return true;
900b57cec5SDimitry Andric
910b57cec5SDimitry Andric return false;
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric
940b57cec5SDimitry Andric namespace {
950b57cec5SDimitry Andric
960b57cec5SDimitry Andric class CaptureDiagnosticConsumer : public DiagnosticConsumer {
970b57cec5SDimitry Andric DiagnosticsEngine &Diags;
980b57cec5SDimitry Andric DiagnosticConsumer &DiagClient;
990b57cec5SDimitry Andric CapturedDiagList &CapturedDiags;
1000b57cec5SDimitry Andric bool HasBegunSourceFile;
1010b57cec5SDimitry Andric public:
CaptureDiagnosticConsumer(DiagnosticsEngine & diags,DiagnosticConsumer & client,CapturedDiagList & capturedDiags)1020b57cec5SDimitry Andric CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
1030b57cec5SDimitry Andric DiagnosticConsumer &client,
1040b57cec5SDimitry Andric CapturedDiagList &capturedDiags)
1050b57cec5SDimitry Andric : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
1060b57cec5SDimitry Andric HasBegunSourceFile(false) { }
1070b57cec5SDimitry Andric
BeginSourceFile(const LangOptions & Opts,const Preprocessor * PP)1080b57cec5SDimitry Andric void BeginSourceFile(const LangOptions &Opts,
1090b57cec5SDimitry Andric const Preprocessor *PP) override {
1100b57cec5SDimitry Andric // Pass BeginSourceFile message onto DiagClient on first call.
1110b57cec5SDimitry Andric // The corresponding EndSourceFile call will be made from an
1120b57cec5SDimitry Andric // explicit call to FinishCapture.
1130b57cec5SDimitry Andric if (!HasBegunSourceFile) {
1140b57cec5SDimitry Andric DiagClient.BeginSourceFile(Opts, PP);
1150b57cec5SDimitry Andric HasBegunSourceFile = true;
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric
FinishCapture()1190b57cec5SDimitry Andric void FinishCapture() {
1200b57cec5SDimitry Andric // Call EndSourceFile on DiagClient on completion of capture to
1210b57cec5SDimitry Andric // enable VerifyDiagnosticConsumer to check diagnostics *after*
1220b57cec5SDimitry Andric // it has received the diagnostic list.
1230b57cec5SDimitry Andric if (HasBegunSourceFile) {
1240b57cec5SDimitry Andric DiagClient.EndSourceFile();
1250b57cec5SDimitry Andric HasBegunSourceFile = false;
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric }
1280b57cec5SDimitry Andric
~CaptureDiagnosticConsumer()1290b57cec5SDimitry Andric ~CaptureDiagnosticConsumer() override {
1300b57cec5SDimitry Andric assert(!HasBegunSourceFile && "FinishCapture not called!");
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric
HandleDiagnostic(DiagnosticsEngine::Level level,const Diagnostic & Info)1330b57cec5SDimitry Andric void HandleDiagnostic(DiagnosticsEngine::Level level,
1340b57cec5SDimitry Andric const Diagnostic &Info) override {
1350b57cec5SDimitry Andric if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
1360b57cec5SDimitry Andric level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
1370b57cec5SDimitry Andric if (Info.getLocation().isValid())
1380b57cec5SDimitry Andric CapturedDiags.push_back(StoredDiagnostic(level, Info));
1390b57cec5SDimitry Andric return;
1400b57cec5SDimitry Andric }
1410b57cec5SDimitry Andric
1420b57cec5SDimitry Andric // Non-ARC warnings are ignored.
143a7dea167SDimitry Andric Diags.setLastDiagnosticIgnored(true);
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric };
1460b57cec5SDimitry Andric
1470b57cec5SDimitry Andric } // end anonymous namespace
1480b57cec5SDimitry Andric
HasARCRuntime(CompilerInvocation & origCI)1490b57cec5SDimitry Andric static bool HasARCRuntime(CompilerInvocation &origCI) {
1500b57cec5SDimitry Andric // This duplicates some functionality from Darwin::AddDeploymentTarget
1510b57cec5SDimitry Andric // but this function is well defined, so keep it decoupled from the driver
1520b57cec5SDimitry Andric // and avoid unrelated complications.
1530b57cec5SDimitry Andric llvm::Triple triple(origCI.getTargetOpts().Triple);
1540b57cec5SDimitry Andric
1550b57cec5SDimitry Andric if (triple.isiOS())
1560b57cec5SDimitry Andric return triple.getOSMajorVersion() >= 5;
1570b57cec5SDimitry Andric
1580b57cec5SDimitry Andric if (triple.isWatchOS())
1590b57cec5SDimitry Andric return true;
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric if (triple.getOS() == llvm::Triple::Darwin)
1620b57cec5SDimitry Andric return triple.getOSMajorVersion() >= 11;
1630b57cec5SDimitry Andric
1640b57cec5SDimitry Andric if (triple.getOS() == llvm::Triple::MacOSX) {
1650eae32dcSDimitry Andric return triple.getOSVersion() >= VersionTuple(10, 7);
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric
1680b57cec5SDimitry Andric return false;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric static CompilerInvocation *
createInvocationForMigration(CompilerInvocation & origCI,const PCHContainerReader & PCHContainerRdr)1720b57cec5SDimitry Andric createInvocationForMigration(CompilerInvocation &origCI,
1730b57cec5SDimitry Andric const PCHContainerReader &PCHContainerRdr) {
1740b57cec5SDimitry Andric std::unique_ptr<CompilerInvocation> CInvok;
1750b57cec5SDimitry Andric CInvok.reset(new CompilerInvocation(origCI));
1760b57cec5SDimitry Andric PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
1770b57cec5SDimitry Andric if (!PPOpts.ImplicitPCHInclude.empty()) {
1780b57cec5SDimitry Andric // We can't use a PCH because it was likely built in non-ARC mode and we
1790b57cec5SDimitry Andric // want to parse in ARC. Include the original header.
1800b57cec5SDimitry Andric FileManager FileMgr(origCI.getFileSystemOpts());
1810b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
1820b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
1830b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
1840b57cec5SDimitry Andric new IgnoringDiagConsumer()));
1850b57cec5SDimitry Andric std::string OriginalFile = ASTReader::getOriginalSourceFile(
1860b57cec5SDimitry Andric PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
1870b57cec5SDimitry Andric if (!OriginalFile.empty())
1880b57cec5SDimitry Andric PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
1890b57cec5SDimitry Andric PPOpts.ImplicitPCHInclude.clear();
1900b57cec5SDimitry Andric }
1915ffd83dbSDimitry Andric std::string define = std::string(getARCMTMacroName());
1920b57cec5SDimitry Andric define += '=';
1930b57cec5SDimitry Andric CInvok->getPreprocessorOpts().addMacroDef(define);
1945f757f3fSDimitry Andric CInvok->getLangOpts().ObjCAutoRefCount = true;
1955f757f3fSDimitry Andric CInvok->getLangOpts().setGC(LangOptions::NonGC);
1960b57cec5SDimitry Andric CInvok->getDiagnosticOpts().ErrorLimit = 0;
1970b57cec5SDimitry Andric CInvok->getDiagnosticOpts().PedanticErrors = 0;
1980b57cec5SDimitry Andric
1990b57cec5SDimitry Andric // Ignore -Werror flags when migrating.
2000b57cec5SDimitry Andric std::vector<std::string> WarnOpts;
2010b57cec5SDimitry Andric for (std::vector<std::string>::iterator
2020b57cec5SDimitry Andric I = CInvok->getDiagnosticOpts().Warnings.begin(),
2030b57cec5SDimitry Andric E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
2045f757f3fSDimitry Andric if (!StringRef(*I).starts_with("error"))
2050b57cec5SDimitry Andric WarnOpts.push_back(*I);
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric WarnOpts.push_back("error=arc-unsafe-retained-assign");
2080b57cec5SDimitry Andric CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
2090b57cec5SDimitry Andric
2105f757f3fSDimitry Andric CInvok->getLangOpts().ObjCWeakRuntime = HasARCRuntime(origCI);
2115f757f3fSDimitry Andric CInvok->getLangOpts().ObjCWeak = CInvok->getLangOpts().ObjCWeakRuntime;
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric return CInvok.release();
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric
emitPremigrationErrors(const CapturedDiagList & arcDiags,DiagnosticOptions * diagOpts,Preprocessor & PP)2160b57cec5SDimitry Andric static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
2170b57cec5SDimitry Andric DiagnosticOptions *diagOpts,
2180b57cec5SDimitry Andric Preprocessor &PP) {
2190b57cec5SDimitry Andric TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
2200b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
2210b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
2220b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, diagOpts, &printer,
2230b57cec5SDimitry Andric /*ShouldOwnClient=*/false));
2240b57cec5SDimitry Andric Diags->setSourceManager(&PP.getSourceManager());
2250b57cec5SDimitry Andric
2260b57cec5SDimitry Andric printer.BeginSourceFile(PP.getLangOpts(), &PP);
2270b57cec5SDimitry Andric arcDiags.reportDiagnostics(*Diags);
2280b57cec5SDimitry Andric printer.EndSourceFile();
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric
2310b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2320b57cec5SDimitry Andric // checkForManualIssues.
2330b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2340b57cec5SDimitry Andric
checkForManualIssues(CompilerInvocation & origCI,const FrontendInputFile & Input,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagClient,bool emitPremigrationARCErrors,StringRef plistOut)2350b57cec5SDimitry Andric bool arcmt::checkForManualIssues(
2360b57cec5SDimitry Andric CompilerInvocation &origCI, const FrontendInputFile &Input,
2370b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps,
2380b57cec5SDimitry Andric DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
2390b57cec5SDimitry Andric StringRef plistOut) {
2405f757f3fSDimitry Andric if (!origCI.getLangOpts().ObjC)
2410b57cec5SDimitry Andric return false;
2420b57cec5SDimitry Andric
2435f757f3fSDimitry Andric LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
2440b57cec5SDimitry Andric bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
2450b57cec5SDimitry Andric bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
2460b57cec5SDimitry Andric
2470b57cec5SDimitry Andric std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
2480b57cec5SDimitry Andric NoFinalizeRemoval);
2490b57cec5SDimitry Andric assert(!transforms.empty());
2500b57cec5SDimitry Andric
2510b57cec5SDimitry Andric std::unique_ptr<CompilerInvocation> CInvok;
2520b57cec5SDimitry Andric CInvok.reset(
2530b57cec5SDimitry Andric createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
2540b57cec5SDimitry Andric CInvok->getFrontendOpts().Inputs.clear();
2550b57cec5SDimitry Andric CInvok->getFrontendOpts().Inputs.push_back(Input);
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric CapturedDiagList capturedDiags;
2580b57cec5SDimitry Andric
2590b57cec5SDimitry Andric assert(DiagClient);
2600b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
2610b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
2620b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
2630b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false));
2640b57cec5SDimitry Andric
2650b57cec5SDimitry Andric // Filter of all diagnostics.
2660b57cec5SDimitry Andric CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
2670b57cec5SDimitry Andric Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
2680b57cec5SDimitry Andric
2690b57cec5SDimitry Andric std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
2700b57cec5SDimitry Andric std::move(CInvok), PCHContainerOps, Diags));
2710b57cec5SDimitry Andric if (!Unit) {
2720b57cec5SDimitry Andric errRec.FinishCapture();
2730b57cec5SDimitry Andric return true;
2740b57cec5SDimitry Andric }
2750b57cec5SDimitry Andric
2760b57cec5SDimitry Andric // Don't filter diagnostics anymore.
2770b57cec5SDimitry Andric Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
2780b57cec5SDimitry Andric
2790b57cec5SDimitry Andric ASTContext &Ctx = Unit->getASTContext();
2800b57cec5SDimitry Andric
2810b57cec5SDimitry Andric if (Diags->hasFatalErrorOccurred()) {
2820b57cec5SDimitry Andric Diags->Reset();
2830b57cec5SDimitry Andric DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
2840b57cec5SDimitry Andric capturedDiags.reportDiagnostics(*Diags);
2850b57cec5SDimitry Andric DiagClient->EndSourceFile();
2860b57cec5SDimitry Andric errRec.FinishCapture();
2870b57cec5SDimitry Andric return true;
2880b57cec5SDimitry Andric }
2890b57cec5SDimitry Andric
2900b57cec5SDimitry Andric if (emitPremigrationARCErrors)
2910b57cec5SDimitry Andric emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
2920b57cec5SDimitry Andric Unit->getPreprocessor());
2930b57cec5SDimitry Andric if (!plistOut.empty()) {
2940b57cec5SDimitry Andric SmallVector<StoredDiagnostic, 8> arcDiags;
2950b57cec5SDimitry Andric for (CapturedDiagList::iterator
2960b57cec5SDimitry Andric I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
2970b57cec5SDimitry Andric arcDiags.push_back(*I);
2985ffd83dbSDimitry Andric writeARCDiagsToPlist(std::string(plistOut), arcDiags,
2990b57cec5SDimitry Andric Ctx.getSourceManager(), Ctx.getLangOpts());
3000b57cec5SDimitry Andric }
3010b57cec5SDimitry Andric
3020b57cec5SDimitry Andric // After parsing of source files ended, we want to reuse the
3030b57cec5SDimitry Andric // diagnostics objects to emit further diagnostics.
3040b57cec5SDimitry Andric // We call BeginSourceFile because DiagnosticConsumer requires that
3050b57cec5SDimitry Andric // diagnostics with source range information are emitted only in between
3060b57cec5SDimitry Andric // BeginSourceFile() and EndSourceFile().
3070b57cec5SDimitry Andric DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
3080b57cec5SDimitry Andric
3090b57cec5SDimitry Andric // No macros will be added since we are just checking and we won't modify
3100b57cec5SDimitry Andric // source code.
3110b57cec5SDimitry Andric std::vector<SourceLocation> ARCMTMacroLocs;
3120b57cec5SDimitry Andric
3130b57cec5SDimitry Andric TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
3140b57cec5SDimitry Andric MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
3150b57cec5SDimitry Andric ARCMTMacroLocs);
3160b57cec5SDimitry Andric pass.setNoFinalizeRemoval(NoFinalizeRemoval);
3170b57cec5SDimitry Andric if (!NoNSAllocReallocError)
3180b57cec5SDimitry Andric Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
3190b57cec5SDimitry Andric SourceLocation());
3200b57cec5SDimitry Andric
3210b57cec5SDimitry Andric for (unsigned i=0, e = transforms.size(); i != e; ++i)
3220b57cec5SDimitry Andric transforms[i](pass);
3230b57cec5SDimitry Andric
3240b57cec5SDimitry Andric capturedDiags.reportDiagnostics(*Diags);
3250b57cec5SDimitry Andric
3260b57cec5SDimitry Andric DiagClient->EndSourceFile();
3270b57cec5SDimitry Andric errRec.FinishCapture();
3280b57cec5SDimitry Andric
3290b57cec5SDimitry Andric return capturedDiags.hasErrors() || testAct.hasReportedErrors();
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric
3320b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3330b57cec5SDimitry Andric // applyTransformations.
3340b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3350b57cec5SDimitry Andric
3360b57cec5SDimitry Andric static bool
applyTransforms(CompilerInvocation & origCI,const FrontendInputFile & Input,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagClient,StringRef outputDir,bool emitPremigrationARCErrors,StringRef plistOut)3370b57cec5SDimitry Andric applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
3380b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps,
3390b57cec5SDimitry Andric DiagnosticConsumer *DiagClient, StringRef outputDir,
3400b57cec5SDimitry Andric bool emitPremigrationARCErrors, StringRef plistOut) {
3415f757f3fSDimitry Andric if (!origCI.getLangOpts().ObjC)
3420b57cec5SDimitry Andric return false;
3430b57cec5SDimitry Andric
3445f757f3fSDimitry Andric LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
3450b57cec5SDimitry Andric
3460b57cec5SDimitry Andric // Make sure checking is successful first.
3470b57cec5SDimitry Andric CompilerInvocation CInvokForCheck(origCI);
3480b57cec5SDimitry Andric if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
3490b57cec5SDimitry Andric DiagClient, emitPremigrationARCErrors,
3500b57cec5SDimitry Andric plistOut))
3510b57cec5SDimitry Andric return true;
3520b57cec5SDimitry Andric
3530b57cec5SDimitry Andric CompilerInvocation CInvok(origCI);
3540b57cec5SDimitry Andric CInvok.getFrontendOpts().Inputs.clear();
3550b57cec5SDimitry Andric CInvok.getFrontendOpts().Inputs.push_back(Input);
3560b57cec5SDimitry Andric
3570b57cec5SDimitry Andric MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
3580b57cec5SDimitry Andric bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
3590b57cec5SDimitry Andric
3600b57cec5SDimitry Andric std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
3610b57cec5SDimitry Andric NoFinalizeRemoval);
3620b57cec5SDimitry Andric assert(!transforms.empty());
3630b57cec5SDimitry Andric
3640b57cec5SDimitry Andric for (unsigned i=0, e = transforms.size(); i != e; ++i) {
3650b57cec5SDimitry Andric bool err = migration.applyTransform(transforms[i]);
3660b57cec5SDimitry Andric if (err) return true;
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric
3690b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
3700b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
3710b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
3720b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false));
3730b57cec5SDimitry Andric
3740b57cec5SDimitry Andric if (outputDir.empty()) {
3755f757f3fSDimitry Andric origCI.getLangOpts().ObjCAutoRefCount = true;
3760b57cec5SDimitry Andric return migration.getRemapper().overwriteOriginal(*Diags);
3770b57cec5SDimitry Andric } else {
3780b57cec5SDimitry Andric return migration.getRemapper().flushToDisk(outputDir, *Diags);
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric }
3810b57cec5SDimitry Andric
applyTransformations(CompilerInvocation & origCI,const FrontendInputFile & Input,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagClient)3820b57cec5SDimitry Andric bool arcmt::applyTransformations(
3830b57cec5SDimitry Andric CompilerInvocation &origCI, const FrontendInputFile &Input,
3840b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps,
3850b57cec5SDimitry Andric DiagnosticConsumer *DiagClient) {
3860b57cec5SDimitry Andric return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
3870b57cec5SDimitry Andric StringRef(), false, StringRef());
3880b57cec5SDimitry Andric }
3890b57cec5SDimitry Andric
migrateWithTemporaryFiles(CompilerInvocation & origCI,const FrontendInputFile & Input,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagClient,StringRef outputDir,bool emitPremigrationARCErrors,StringRef plistOut)3900b57cec5SDimitry Andric bool arcmt::migrateWithTemporaryFiles(
3910b57cec5SDimitry Andric CompilerInvocation &origCI, const FrontendInputFile &Input,
3920b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps,
3930b57cec5SDimitry Andric DiagnosticConsumer *DiagClient, StringRef outputDir,
3940b57cec5SDimitry Andric bool emitPremigrationARCErrors, StringRef plistOut) {
3950b57cec5SDimitry Andric assert(!outputDir.empty() && "Expected output directory path");
3960b57cec5SDimitry Andric return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
3970b57cec5SDimitry Andric emitPremigrationARCErrors, plistOut);
3980b57cec5SDimitry Andric }
3990b57cec5SDimitry Andric
getFileRemappings(std::vector<std::pair<std::string,std::string>> & remap,StringRef outputDir,DiagnosticConsumer * DiagClient)4000b57cec5SDimitry Andric bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
4010b57cec5SDimitry Andric remap,
4020b57cec5SDimitry Andric StringRef outputDir,
4030b57cec5SDimitry Andric DiagnosticConsumer *DiagClient) {
4040b57cec5SDimitry Andric assert(!outputDir.empty());
4050b57cec5SDimitry Andric
4060b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
4070b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
4080b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, new DiagnosticOptions,
4090b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false));
4100b57cec5SDimitry Andric
4110b57cec5SDimitry Andric FileRemapper remapper;
4120b57cec5SDimitry Andric bool err = remapper.initFromDisk(outputDir, *Diags,
4130b57cec5SDimitry Andric /*ignoreIfFilesChanged=*/true);
4140b57cec5SDimitry Andric if (err)
4150b57cec5SDimitry Andric return true;
4160b57cec5SDimitry Andric
417e8d8bef9SDimitry Andric remapper.forEachMapping(
418e8d8bef9SDimitry Andric [&](StringRef From, StringRef To) {
419e8d8bef9SDimitry Andric remap.push_back(std::make_pair(From.str(), To.str()));
420e8d8bef9SDimitry Andric },
421e8d8bef9SDimitry Andric [](StringRef, const llvm::MemoryBufferRef &) {});
4220b57cec5SDimitry Andric
4230b57cec5SDimitry Andric return false;
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric
4260b57cec5SDimitry Andric
4270b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4280b57cec5SDimitry Andric // CollectTransformActions.
4290b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4300b57cec5SDimitry Andric
4310b57cec5SDimitry Andric namespace {
4320b57cec5SDimitry Andric
4330b57cec5SDimitry Andric class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
4340b57cec5SDimitry Andric std::vector<SourceLocation> &ARCMTMacroLocs;
4350b57cec5SDimitry Andric
4360b57cec5SDimitry Andric public:
ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> & ARCMTMacroLocs)4370b57cec5SDimitry Andric ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
4380b57cec5SDimitry Andric : ARCMTMacroLocs(ARCMTMacroLocs) { }
4390b57cec5SDimitry Andric
MacroExpands(const Token & MacroNameTok,const MacroDefinition & MD,SourceRange Range,const MacroArgs * Args)4400b57cec5SDimitry Andric void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
4410b57cec5SDimitry Andric SourceRange Range, const MacroArgs *Args) override {
4420b57cec5SDimitry Andric if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
4430b57cec5SDimitry Andric ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric };
4460b57cec5SDimitry Andric
4470b57cec5SDimitry Andric class ARCMTMacroTrackerAction : public ASTFrontendAction {
4480b57cec5SDimitry Andric std::vector<SourceLocation> &ARCMTMacroLocs;
4490b57cec5SDimitry Andric
4500b57cec5SDimitry Andric public:
ARCMTMacroTrackerAction(std::vector<SourceLocation> & ARCMTMacroLocs)4510b57cec5SDimitry Andric ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
4520b57cec5SDimitry Andric : ARCMTMacroLocs(ARCMTMacroLocs) { }
4530b57cec5SDimitry Andric
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)4540b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
4550b57cec5SDimitry Andric StringRef InFile) override {
4560b57cec5SDimitry Andric CI.getPreprocessor().addPPCallbacks(
457a7dea167SDimitry Andric std::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
458a7dea167SDimitry Andric return std::make_unique<ASTConsumer>();
4590b57cec5SDimitry Andric }
4600b57cec5SDimitry Andric };
4610b57cec5SDimitry Andric
4620b57cec5SDimitry Andric class RewritesApplicator : public TransformActions::RewriteReceiver {
4630b57cec5SDimitry Andric Rewriter &rewriter;
4640b57cec5SDimitry Andric MigrationProcess::RewriteListener *Listener;
4650b57cec5SDimitry Andric
4660b57cec5SDimitry Andric public:
RewritesApplicator(Rewriter & rewriter,ASTContext & ctx,MigrationProcess::RewriteListener * listener)4670b57cec5SDimitry Andric RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
4680b57cec5SDimitry Andric MigrationProcess::RewriteListener *listener)
4690b57cec5SDimitry Andric : rewriter(rewriter), Listener(listener) {
4700b57cec5SDimitry Andric if (Listener)
4710b57cec5SDimitry Andric Listener->start(ctx);
4720b57cec5SDimitry Andric }
~RewritesApplicator()4730b57cec5SDimitry Andric ~RewritesApplicator() override {
4740b57cec5SDimitry Andric if (Listener)
4750b57cec5SDimitry Andric Listener->finish();
4760b57cec5SDimitry Andric }
4770b57cec5SDimitry Andric
insert(SourceLocation loc,StringRef text)4780b57cec5SDimitry Andric void insert(SourceLocation loc, StringRef text) override {
4790b57cec5SDimitry Andric bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
4800b57cec5SDimitry Andric /*indentNewLines=*/true);
4810b57cec5SDimitry Andric if (!err && Listener)
4820b57cec5SDimitry Andric Listener->insert(loc, text);
4830b57cec5SDimitry Andric }
4840b57cec5SDimitry Andric
remove(CharSourceRange range)4850b57cec5SDimitry Andric void remove(CharSourceRange range) override {
4860b57cec5SDimitry Andric Rewriter::RewriteOptions removeOpts;
4870b57cec5SDimitry Andric removeOpts.IncludeInsertsAtBeginOfRange = false;
4880b57cec5SDimitry Andric removeOpts.IncludeInsertsAtEndOfRange = false;
4890b57cec5SDimitry Andric removeOpts.RemoveLineIfEmpty = true;
4900b57cec5SDimitry Andric
4910b57cec5SDimitry Andric bool err = rewriter.RemoveText(range, removeOpts);
4920b57cec5SDimitry Andric if (!err && Listener)
4930b57cec5SDimitry Andric Listener->remove(range);
4940b57cec5SDimitry Andric }
4950b57cec5SDimitry Andric
increaseIndentation(CharSourceRange range,SourceLocation parentIndent)4960b57cec5SDimitry Andric void increaseIndentation(CharSourceRange range,
4970b57cec5SDimitry Andric SourceLocation parentIndent) override {
4980b57cec5SDimitry Andric rewriter.IncreaseIndentation(range, parentIndent);
4990b57cec5SDimitry Andric }
5000b57cec5SDimitry Andric };
5010b57cec5SDimitry Andric
5020b57cec5SDimitry Andric } // end anonymous namespace.
5030b57cec5SDimitry Andric
5040b57cec5SDimitry Andric /// Anchor for VTable.
~RewriteListener()5050b57cec5SDimitry Andric MigrationProcess::RewriteListener::~RewriteListener() { }
5060b57cec5SDimitry Andric
MigrationProcess(CompilerInvocation & CI,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * diagClient,StringRef outputDir)5070b57cec5SDimitry Andric MigrationProcess::MigrationProcess(
5085f757f3fSDimitry Andric CompilerInvocation &CI,
5090b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps,
5100b57cec5SDimitry Andric DiagnosticConsumer *diagClient, StringRef outputDir)
5110b57cec5SDimitry Andric : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)),
5120b57cec5SDimitry Andric DiagClient(diagClient), HadARCErrors(false) {
5130b57cec5SDimitry Andric if (!outputDir.empty()) {
5140b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
5150b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
5160b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
5170b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false));
5180b57cec5SDimitry Andric Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanged=*/true);
5190b57cec5SDimitry Andric }
5200b57cec5SDimitry Andric }
5210b57cec5SDimitry Andric
applyTransform(TransformFn trans,RewriteListener * listener)5220b57cec5SDimitry Andric bool MigrationProcess::applyTransform(TransformFn trans,
5230b57cec5SDimitry Andric RewriteListener *listener) {
5240b57cec5SDimitry Andric std::unique_ptr<CompilerInvocation> CInvok;
5250b57cec5SDimitry Andric CInvok.reset(
5260b57cec5SDimitry Andric createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader()));
5270b57cec5SDimitry Andric CInvok->getDiagnosticOpts().IgnoreWarnings = true;
5280b57cec5SDimitry Andric
5290b57cec5SDimitry Andric Remapper.applyMappings(CInvok->getPreprocessorOpts());
5300b57cec5SDimitry Andric
5310b57cec5SDimitry Andric CapturedDiagList capturedDiags;
5320b57cec5SDimitry Andric std::vector<SourceLocation> ARCMTMacroLocs;
5330b57cec5SDimitry Andric
5340b57cec5SDimitry Andric assert(DiagClient);
5350b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
5360b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
5370b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, new DiagnosticOptions,
5380b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false));
5390b57cec5SDimitry Andric
5400b57cec5SDimitry Andric // Filter of all diagnostics.
5410b57cec5SDimitry Andric CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
5420b57cec5SDimitry Andric Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
5430b57cec5SDimitry Andric
5440b57cec5SDimitry Andric std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
5450b57cec5SDimitry Andric ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
5460b57cec5SDimitry Andric
5470b57cec5SDimitry Andric std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
5480b57cec5SDimitry Andric std::move(CInvok), PCHContainerOps, Diags, ASTAction.get()));
5490b57cec5SDimitry Andric if (!Unit) {
5500b57cec5SDimitry Andric errRec.FinishCapture();
5510b57cec5SDimitry Andric return true;
5520b57cec5SDimitry Andric }
5530b57cec5SDimitry Andric Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
5540b57cec5SDimitry Andric
5550b57cec5SDimitry Andric HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
5560b57cec5SDimitry Andric
5570b57cec5SDimitry Andric // Don't filter diagnostics anymore.
5580b57cec5SDimitry Andric Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
5590b57cec5SDimitry Andric
5600b57cec5SDimitry Andric ASTContext &Ctx = Unit->getASTContext();
5610b57cec5SDimitry Andric
5620b57cec5SDimitry Andric if (Diags->hasFatalErrorOccurred()) {
5630b57cec5SDimitry Andric Diags->Reset();
5640b57cec5SDimitry Andric DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
5650b57cec5SDimitry Andric capturedDiags.reportDiagnostics(*Diags);
5660b57cec5SDimitry Andric DiagClient->EndSourceFile();
5670b57cec5SDimitry Andric errRec.FinishCapture();
5680b57cec5SDimitry Andric return true;
5690b57cec5SDimitry Andric }
5700b57cec5SDimitry Andric
5710b57cec5SDimitry Andric // After parsing of source files ended, we want to reuse the
5720b57cec5SDimitry Andric // diagnostics objects to emit further diagnostics.
5730b57cec5SDimitry Andric // We call BeginSourceFile because DiagnosticConsumer requires that
5740b57cec5SDimitry Andric // diagnostics with source range information are emitted only in between
5750b57cec5SDimitry Andric // BeginSourceFile() and EndSourceFile().
5760b57cec5SDimitry Andric DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
5770b57cec5SDimitry Andric
5780b57cec5SDimitry Andric Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
5790b57cec5SDimitry Andric TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
5805f757f3fSDimitry Andric MigrationPass pass(Ctx, OrigCI.getLangOpts().getGC(),
5810b57cec5SDimitry Andric Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
5820b57cec5SDimitry Andric
5830b57cec5SDimitry Andric trans(pass);
5840b57cec5SDimitry Andric
5850b57cec5SDimitry Andric {
5860b57cec5SDimitry Andric RewritesApplicator applicator(rewriter, Ctx, listener);
5870b57cec5SDimitry Andric TA.applyRewrites(applicator);
5880b57cec5SDimitry Andric }
5890b57cec5SDimitry Andric
5900b57cec5SDimitry Andric DiagClient->EndSourceFile();
5910b57cec5SDimitry Andric errRec.FinishCapture();
5920b57cec5SDimitry Andric
5930b57cec5SDimitry Andric if (DiagClient->getNumErrors())
5940b57cec5SDimitry Andric return true;
5950b57cec5SDimitry Andric
5960b57cec5SDimitry Andric for (Rewriter::buffer_iterator
5970b57cec5SDimitry Andric I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
5980b57cec5SDimitry Andric FileID FID = I->first;
5990b57cec5SDimitry Andric RewriteBuffer &buf = I->second;
6005f757f3fSDimitry Andric OptionalFileEntryRef file =
6015f757f3fSDimitry Andric Ctx.getSourceManager().getFileEntryRefForID(FID);
6020b57cec5SDimitry Andric assert(file);
6035ffd83dbSDimitry Andric std::string newFname = std::string(file->getName());
6040b57cec5SDimitry Andric newFname += "-trans";
6050b57cec5SDimitry Andric SmallString<512> newText;
6060b57cec5SDimitry Andric llvm::raw_svector_ostream vecOS(newText);
6070b57cec5SDimitry Andric buf.write(vecOS);
6080b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> memBuf(
609*0fca6ea1SDimitry Andric llvm::MemoryBuffer::getMemBufferCopy(newText.str(), newFname));
6100b57cec5SDimitry Andric SmallString<64> filePath(file->getName());
6110b57cec5SDimitry Andric Unit->getFileManager().FixupRelativePath(filePath);
6120b57cec5SDimitry Andric Remapper.remap(filePath.str(), std::move(memBuf));
6130b57cec5SDimitry Andric }
6140b57cec5SDimitry Andric
6150b57cec5SDimitry Andric return false;
6160b57cec5SDimitry Andric }
617