When debugging a boost build with a modified version of Clang, I discovered that the PTH implementation stores TokenKind in 8 bits. However, we currently have 368 TokenKinds. The result is that the value gets truncated and the wrong token gets picked up when including PTH files. It seems that this will go wrong every time someone uses a token that uses the 9th bit. Upon asking on IRC, it was brought up that this was a highly experimental features that was considered a failure. I discovered via googling that BoostBuild (mostly Boost.Math) is the only user of this feature, using the CC1 flag directly. I believe that this can be transferred over to normal PCH with minimal effort: https://github.com/boostorg/build/issues/367 Based on advice on IRC and research showing that this is a nearly completely unused feature, this patch removes it entirely. Note: I considered leaving the build-flags in place and making them emit an error/warning, however since I've basically identified and warned the only user, it seemed better to just remove them. Differential Revision: https://reviews.llvm.org/D54547 Change-Id: If32744275ef1f585357bd6c1c813d96973c4d8d9 llvm-svn: 348266
617 lines
22 KiB
C++
617 lines
22 KiB
C++
//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Internals.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/Basic/DiagnosticCategories.h"
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendAction.h"
|
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
#include "clang/Rewrite/Core/Rewriter.h"
|
|
#include "clang/Sema/SemaDiagnostic.h"
|
|
#include "clang/Serialization/ASTReader.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include <utility>
|
|
using namespace clang;
|
|
using namespace arcmt;
|
|
|
|
bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
|
|
SourceRange range) {
|
|
if (range.isInvalid())
|
|
return false;
|
|
|
|
bool cleared = false;
|
|
ListTy::iterator I = List.begin();
|
|
while (I != List.end()) {
|
|
FullSourceLoc diagLoc = I->getLocation();
|
|
if ((IDs.empty() || // empty means clear all diagnostics in the range.
|
|
std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
|
|
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
|
|
(diagLoc == range.getEnd() ||
|
|
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
|
|
cleared = true;
|
|
ListTy::iterator eraseS = I++;
|
|
if (eraseS->getLevel() != DiagnosticsEngine::Note)
|
|
while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
|
|
++I;
|
|
// Clear the diagnostic and any notes following it.
|
|
I = List.erase(eraseS, I);
|
|
continue;
|
|
}
|
|
|
|
++I;
|
|
}
|
|
|
|
return cleared;
|
|
}
|
|
|
|
bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
|
|
SourceRange range) const {
|
|
if (range.isInvalid())
|
|
return false;
|
|
|
|
ListTy::const_iterator I = List.begin();
|
|
while (I != List.end()) {
|
|
FullSourceLoc diagLoc = I->getLocation();
|
|
if ((IDs.empty() || // empty means any diagnostic in the range.
|
|
std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
|
|
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
|
|
(diagLoc == range.getEnd() ||
|
|
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
|
|
return true;
|
|
}
|
|
|
|
++I;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
|
|
for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
|
|
Diags.Report(*I);
|
|
}
|
|
|
|
bool CapturedDiagList::hasErrors() const {
|
|
for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
|
|
if (I->getLevel() >= DiagnosticsEngine::Error)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class CaptureDiagnosticConsumer : public DiagnosticConsumer {
|
|
DiagnosticsEngine &Diags;
|
|
DiagnosticConsumer &DiagClient;
|
|
CapturedDiagList &CapturedDiags;
|
|
bool HasBegunSourceFile;
|
|
public:
|
|
CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
|
|
DiagnosticConsumer &client,
|
|
CapturedDiagList &capturedDiags)
|
|
: Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
|
|
HasBegunSourceFile(false) { }
|
|
|
|
void BeginSourceFile(const LangOptions &Opts,
|
|
const Preprocessor *PP) override {
|
|
// Pass BeginSourceFile message onto DiagClient on first call.
|
|
// The corresponding EndSourceFile call will be made from an
|
|
// explicit call to FinishCapture.
|
|
if (!HasBegunSourceFile) {
|
|
DiagClient.BeginSourceFile(Opts, PP);
|
|
HasBegunSourceFile = true;
|
|
}
|
|
}
|
|
|
|
void FinishCapture() {
|
|
// Call EndSourceFile on DiagClient on completion of capture to
|
|
// enable VerifyDiagnosticConsumer to check diagnostics *after*
|
|
// it has received the diagnostic list.
|
|
if (HasBegunSourceFile) {
|
|
DiagClient.EndSourceFile();
|
|
HasBegunSourceFile = false;
|
|
}
|
|
}
|
|
|
|
~CaptureDiagnosticConsumer() override {
|
|
assert(!HasBegunSourceFile && "FinishCapture not called!");
|
|
}
|
|
|
|
void HandleDiagnostic(DiagnosticsEngine::Level level,
|
|
const Diagnostic &Info) override {
|
|
if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
|
|
level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
|
|
if (Info.getLocation().isValid())
|
|
CapturedDiags.push_back(StoredDiagnostic(level, Info));
|
|
return;
|
|
}
|
|
|
|
// Non-ARC warnings are ignored.
|
|
Diags.setLastDiagnosticIgnored();
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static bool HasARCRuntime(CompilerInvocation &origCI) {
|
|
// This duplicates some functionality from Darwin::AddDeploymentTarget
|
|
// but this function is well defined, so keep it decoupled from the driver
|
|
// and avoid unrelated complications.
|
|
llvm::Triple triple(origCI.getTargetOpts().Triple);
|
|
|
|
if (triple.isiOS())
|
|
return triple.getOSMajorVersion() >= 5;
|
|
|
|
if (triple.isWatchOS())
|
|
return true;
|
|
|
|
if (triple.getOS() == llvm::Triple::Darwin)
|
|
return triple.getOSMajorVersion() >= 11;
|
|
|
|
if (triple.getOS() == llvm::Triple::MacOSX) {
|
|
unsigned Major, Minor, Micro;
|
|
triple.getOSVersion(Major, Minor, Micro);
|
|
return Major > 10 || (Major == 10 && Minor >= 7);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static CompilerInvocation *
|
|
createInvocationForMigration(CompilerInvocation &origCI,
|
|
const PCHContainerReader &PCHContainerRdr) {
|
|
std::unique_ptr<CompilerInvocation> CInvok;
|
|
CInvok.reset(new CompilerInvocation(origCI));
|
|
PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
|
|
if (!PPOpts.ImplicitPCHInclude.empty()) {
|
|
// We can't use a PCH because it was likely built in non-ARC mode and we
|
|
// want to parse in ARC. Include the original header.
|
|
FileManager FileMgr(origCI.getFileSystemOpts());
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
|
|
new IgnoringDiagConsumer()));
|
|
std::string OriginalFile = ASTReader::getOriginalSourceFile(
|
|
PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
|
|
if (!OriginalFile.empty())
|
|
PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
|
|
PPOpts.ImplicitPCHInclude.clear();
|
|
}
|
|
std::string define = getARCMTMacroName();
|
|
define += '=';
|
|
CInvok->getPreprocessorOpts().addMacroDef(define);
|
|
CInvok->getLangOpts()->ObjCAutoRefCount = true;
|
|
CInvok->getLangOpts()->setGC(LangOptions::NonGC);
|
|
CInvok->getDiagnosticOpts().ErrorLimit = 0;
|
|
CInvok->getDiagnosticOpts().PedanticErrors = 0;
|
|
|
|
// Ignore -Werror flags when migrating.
|
|
std::vector<std::string> WarnOpts;
|
|
for (std::vector<std::string>::iterator
|
|
I = CInvok->getDiagnosticOpts().Warnings.begin(),
|
|
E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
|
|
if (!StringRef(*I).startswith("error"))
|
|
WarnOpts.push_back(*I);
|
|
}
|
|
WarnOpts.push_back("error=arc-unsafe-retained-assign");
|
|
CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
|
|
|
|
CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI);
|
|
CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime;
|
|
|
|
return CInvok.release();
|
|
}
|
|
|
|
static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
|
|
DiagnosticOptions *diagOpts,
|
|
Preprocessor &PP) {
|
|
TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, diagOpts, &printer,
|
|
/*ShouldOwnClient=*/false));
|
|
Diags->setSourceManager(&PP.getSourceManager());
|
|
|
|
printer.BeginSourceFile(PP.getLangOpts(), &PP);
|
|
arcDiags.reportDiagnostics(*Diags);
|
|
printer.EndSourceFile();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// checkForManualIssues.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool arcmt::checkForManualIssues(
|
|
CompilerInvocation &origCI, const FrontendInputFile &Input,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
|
DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
|
|
StringRef plistOut) {
|
|
if (!origCI.getLangOpts()->ObjC)
|
|
return false;
|
|
|
|
LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
|
|
bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
|
|
bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
|
|
|
|
std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
|
|
NoFinalizeRemoval);
|
|
assert(!transforms.empty());
|
|
|
|
std::unique_ptr<CompilerInvocation> CInvok;
|
|
CInvok.reset(
|
|
createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
|
|
CInvok->getFrontendOpts().Inputs.clear();
|
|
CInvok->getFrontendOpts().Inputs.push_back(Input);
|
|
|
|
CapturedDiagList capturedDiags;
|
|
|
|
assert(DiagClient);
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
|
|
DiagClient, /*ShouldOwnClient=*/false));
|
|
|
|
// Filter of all diagnostics.
|
|
CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
|
|
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
|
|
|
|
std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
|
|
std::move(CInvok), PCHContainerOps, Diags));
|
|
if (!Unit) {
|
|
errRec.FinishCapture();
|
|
return true;
|
|
}
|
|
|
|
// Don't filter diagnostics anymore.
|
|
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
|
|
|
|
ASTContext &Ctx = Unit->getASTContext();
|
|
|
|
if (Diags->hasFatalErrorOccurred()) {
|
|
Diags->Reset();
|
|
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
|
|
capturedDiags.reportDiagnostics(*Diags);
|
|
DiagClient->EndSourceFile();
|
|
errRec.FinishCapture();
|
|
return true;
|
|
}
|
|
|
|
if (emitPremigrationARCErrors)
|
|
emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
|
|
Unit->getPreprocessor());
|
|
if (!plistOut.empty()) {
|
|
SmallVector<StoredDiagnostic, 8> arcDiags;
|
|
for (CapturedDiagList::iterator
|
|
I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
|
|
arcDiags.push_back(*I);
|
|
writeARCDiagsToPlist(plistOut, arcDiags,
|
|
Ctx.getSourceManager(), Ctx.getLangOpts());
|
|
}
|
|
|
|
// After parsing of source files ended, we want to reuse the
|
|
// diagnostics objects to emit further diagnostics.
|
|
// We call BeginSourceFile because DiagnosticConsumer requires that
|
|
// diagnostics with source range information are emitted only in between
|
|
// BeginSourceFile() and EndSourceFile().
|
|
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
|
|
|
|
// No macros will be added since we are just checking and we won't modify
|
|
// source code.
|
|
std::vector<SourceLocation> ARCMTMacroLocs;
|
|
|
|
TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
|
|
MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
|
|
ARCMTMacroLocs);
|
|
pass.setNoFinalizeRemoval(NoFinalizeRemoval);
|
|
if (!NoNSAllocReallocError)
|
|
Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
|
|
SourceLocation());
|
|
|
|
for (unsigned i=0, e = transforms.size(); i != e; ++i)
|
|
transforms[i](pass);
|
|
|
|
capturedDiags.reportDiagnostics(*Diags);
|
|
|
|
DiagClient->EndSourceFile();
|
|
errRec.FinishCapture();
|
|
|
|
return capturedDiags.hasErrors() || testAct.hasReportedErrors();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// applyTransformations.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static bool
|
|
applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
|
DiagnosticConsumer *DiagClient, StringRef outputDir,
|
|
bool emitPremigrationARCErrors, StringRef plistOut) {
|
|
if (!origCI.getLangOpts()->ObjC)
|
|
return false;
|
|
|
|
LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
|
|
|
|
// Make sure checking is successful first.
|
|
CompilerInvocation CInvokForCheck(origCI);
|
|
if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
|
|
DiagClient, emitPremigrationARCErrors,
|
|
plistOut))
|
|
return true;
|
|
|
|
CompilerInvocation CInvok(origCI);
|
|
CInvok.getFrontendOpts().Inputs.clear();
|
|
CInvok.getFrontendOpts().Inputs.push_back(Input);
|
|
|
|
MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
|
|
bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
|
|
|
|
std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
|
|
NoFinalizeRemoval);
|
|
assert(!transforms.empty());
|
|
|
|
for (unsigned i=0, e = transforms.size(); i != e; ++i) {
|
|
bool err = migration.applyTransform(transforms[i]);
|
|
if (err) return true;
|
|
}
|
|
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
|
|
DiagClient, /*ShouldOwnClient=*/false));
|
|
|
|
if (outputDir.empty()) {
|
|
origCI.getLangOpts()->ObjCAutoRefCount = true;
|
|
return migration.getRemapper().overwriteOriginal(*Diags);
|
|
} else {
|
|
return migration.getRemapper().flushToDisk(outputDir, *Diags);
|
|
}
|
|
}
|
|
|
|
bool arcmt::applyTransformations(
|
|
CompilerInvocation &origCI, const FrontendInputFile &Input,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
|
DiagnosticConsumer *DiagClient) {
|
|
return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
|
|
StringRef(), false, StringRef());
|
|
}
|
|
|
|
bool arcmt::migrateWithTemporaryFiles(
|
|
CompilerInvocation &origCI, const FrontendInputFile &Input,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
|
DiagnosticConsumer *DiagClient, StringRef outputDir,
|
|
bool emitPremigrationARCErrors, StringRef plistOut) {
|
|
assert(!outputDir.empty() && "Expected output directory path");
|
|
return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
|
|
emitPremigrationARCErrors, plistOut);
|
|
}
|
|
|
|
bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
|
|
remap,
|
|
StringRef outputDir,
|
|
DiagnosticConsumer *DiagClient) {
|
|
assert(!outputDir.empty());
|
|
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, new DiagnosticOptions,
|
|
DiagClient, /*ShouldOwnClient=*/false));
|
|
|
|
FileRemapper remapper;
|
|
bool err = remapper.initFromDisk(outputDir, *Diags,
|
|
/*ignoreIfFilesChanged=*/true);
|
|
if (err)
|
|
return true;
|
|
|
|
PreprocessorOptions PPOpts;
|
|
remapper.applyMappings(PPOpts);
|
|
remap = PPOpts.RemappedFiles;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CollectTransformActions.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
|
|
std::vector<SourceLocation> &ARCMTMacroLocs;
|
|
|
|
public:
|
|
ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
|
|
: ARCMTMacroLocs(ARCMTMacroLocs) { }
|
|
|
|
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
|
|
SourceRange Range, const MacroArgs *Args) override {
|
|
if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
|
|
ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
|
|
}
|
|
};
|
|
|
|
class ARCMTMacroTrackerAction : public ASTFrontendAction {
|
|
std::vector<SourceLocation> &ARCMTMacroLocs;
|
|
|
|
public:
|
|
ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
|
|
: ARCMTMacroLocs(ARCMTMacroLocs) { }
|
|
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
|
StringRef InFile) override {
|
|
CI.getPreprocessor().addPPCallbacks(
|
|
llvm::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
|
|
return llvm::make_unique<ASTConsumer>();
|
|
}
|
|
};
|
|
|
|
class RewritesApplicator : public TransformActions::RewriteReceiver {
|
|
Rewriter &rewriter;
|
|
MigrationProcess::RewriteListener *Listener;
|
|
|
|
public:
|
|
RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
|
|
MigrationProcess::RewriteListener *listener)
|
|
: rewriter(rewriter), Listener(listener) {
|
|
if (Listener)
|
|
Listener->start(ctx);
|
|
}
|
|
~RewritesApplicator() override {
|
|
if (Listener)
|
|
Listener->finish();
|
|
}
|
|
|
|
void insert(SourceLocation loc, StringRef text) override {
|
|
bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
|
|
/*indentNewLines=*/true);
|
|
if (!err && Listener)
|
|
Listener->insert(loc, text);
|
|
}
|
|
|
|
void remove(CharSourceRange range) override {
|
|
Rewriter::RewriteOptions removeOpts;
|
|
removeOpts.IncludeInsertsAtBeginOfRange = false;
|
|
removeOpts.IncludeInsertsAtEndOfRange = false;
|
|
removeOpts.RemoveLineIfEmpty = true;
|
|
|
|
bool err = rewriter.RemoveText(range, removeOpts);
|
|
if (!err && Listener)
|
|
Listener->remove(range);
|
|
}
|
|
|
|
void increaseIndentation(CharSourceRange range,
|
|
SourceLocation parentIndent) override {
|
|
rewriter.IncreaseIndentation(range, parentIndent);
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
/// Anchor for VTable.
|
|
MigrationProcess::RewriteListener::~RewriteListener() { }
|
|
|
|
MigrationProcess::MigrationProcess(
|
|
const CompilerInvocation &CI,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
|
DiagnosticConsumer *diagClient, StringRef outputDir)
|
|
: OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)),
|
|
DiagClient(diagClient), HadARCErrors(false) {
|
|
if (!outputDir.empty()) {
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
|
|
DiagClient, /*ShouldOwnClient=*/false));
|
|
Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
|
|
}
|
|
}
|
|
|
|
bool MigrationProcess::applyTransform(TransformFn trans,
|
|
RewriteListener *listener) {
|
|
std::unique_ptr<CompilerInvocation> CInvok;
|
|
CInvok.reset(
|
|
createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader()));
|
|
CInvok->getDiagnosticOpts().IgnoreWarnings = true;
|
|
|
|
Remapper.applyMappings(CInvok->getPreprocessorOpts());
|
|
|
|
CapturedDiagList capturedDiags;
|
|
std::vector<SourceLocation> ARCMTMacroLocs;
|
|
|
|
assert(DiagClient);
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
new DiagnosticsEngine(DiagID, new DiagnosticOptions,
|
|
DiagClient, /*ShouldOwnClient=*/false));
|
|
|
|
// Filter of all diagnostics.
|
|
CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
|
|
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
|
|
|
|
std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
|
|
ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
|
|
|
|
std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
|
|
std::move(CInvok), PCHContainerOps, Diags, ASTAction.get()));
|
|
if (!Unit) {
|
|
errRec.FinishCapture();
|
|
return true;
|
|
}
|
|
Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
|
|
|
|
HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
|
|
|
|
// Don't filter diagnostics anymore.
|
|
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
|
|
|
|
ASTContext &Ctx = Unit->getASTContext();
|
|
|
|
if (Diags->hasFatalErrorOccurred()) {
|
|
Diags->Reset();
|
|
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
|
|
capturedDiags.reportDiagnostics(*Diags);
|
|
DiagClient->EndSourceFile();
|
|
errRec.FinishCapture();
|
|
return true;
|
|
}
|
|
|
|
// After parsing of source files ended, we want to reuse the
|
|
// diagnostics objects to emit further diagnostics.
|
|
// We call BeginSourceFile because DiagnosticConsumer requires that
|
|
// diagnostics with source range information are emitted only in between
|
|
// BeginSourceFile() and EndSourceFile().
|
|
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
|
|
|
|
Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
|
|
TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
|
|
MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
|
|
Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
|
|
|
|
trans(pass);
|
|
|
|
{
|
|
RewritesApplicator applicator(rewriter, Ctx, listener);
|
|
TA.applyRewrites(applicator);
|
|
}
|
|
|
|
DiagClient->EndSourceFile();
|
|
errRec.FinishCapture();
|
|
|
|
if (DiagClient->getNumErrors())
|
|
return true;
|
|
|
|
for (Rewriter::buffer_iterator
|
|
I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
|
|
FileID FID = I->first;
|
|
RewriteBuffer &buf = I->second;
|
|
const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
|
|
assert(file);
|
|
std::string newFname = file->getName();
|
|
newFname += "-trans";
|
|
SmallString<512> newText;
|
|
llvm::raw_svector_ostream vecOS(newText);
|
|
buf.write(vecOS);
|
|
std::unique_ptr<llvm::MemoryBuffer> memBuf(
|
|
llvm::MemoryBuffer::getMemBufferCopy(
|
|
StringRef(newText.data(), newText.size()), newFname));
|
|
SmallString<64> filePath(file->getName());
|
|
Unit->getFileManager().FixupRelativePath(filePath);
|
|
Remapper.remap(filePath.str(), std::move(memBuf));
|
|
}
|
|
|
|
return false;
|
|
}
|