aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ARCMigrate/ARCMT.cpp484
-rw-r--r--lib/ARCMigrate/CMakeLists.txt14
-rw-r--r--lib/ARCMigrate/FileRemapper.cpp290
-rw-r--r--lib/ARCMigrate/Internals.h146
-rw-r--r--lib/ARCMigrate/Makefile18
-rw-r--r--lib/ARCMigrate/TransformActions.cpp699
-rw-r--r--lib/ARCMigrate/Transforms.cpp2094
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/Driver/Tools.cpp20
-rw-r--r--lib/Frontend/CMakeLists.txt1
-rw-r--r--lib/Frontend/CompilerInstance.cpp29
-rw-r--r--lib/Frontend/CompilerInvocation.cpp32
-rwxr-xr-xlib/Makefile4
13 files changed, 3830 insertions, 2 deletions
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp
new file mode 100644
index 0000000000..bb66da6a71
--- /dev/null
+++ b/lib/ARCMigrate/ARCMT.cpp
@@ -0,0 +1,484 @@
+//===--- 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/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Basic/DiagnosticCategories.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/ADT/Triple.h"
+
+using namespace clang;
+using namespace arcmt;
+using llvm::StringRef;
+
+bool CapturedDiagList::clearDiagnostic(llvm::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++;
+ while (I != List.end() && I->getLevel() == Diagnostic::Note)
+ ++I;
+ // Clear the diagnostic and any notes following it.
+ List.erase(eraseS, I);
+ continue;
+ }
+
+ ++I;
+ }
+
+ return cleared;
+}
+
+bool CapturedDiagList::hasDiagnostic(llvm::ArrayRef<unsigned> IDs,
+ SourceRange range) {
+ if (range.isInvalid())
+ return false;
+
+ ListTy::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(Diagnostic &Diags) {
+ for (ListTy::iterator I = List.begin(), E = List.end(); I != E; ++I)
+ Diags.Report(*I);
+}
+
+namespace {
+
+class CaptureDiagnosticClient : public DiagnosticClient {
+ Diagnostic &Diags;
+ CapturedDiagList &CapturedDiags;
+public:
+ CaptureDiagnosticClient(Diagnostic &diags,
+ CapturedDiagList &capturedDiags)
+ : Diags(diags), CapturedDiags(capturedDiags) { }
+
+ virtual void HandleDiagnostic(Diagnostic::Level level,
+ const DiagnosticInfo &Info) {
+ if (arcmt::isARCDiagnostic(Info.getID(), Diags) ||
+ level >= Diagnostic::Error || level == Diagnostic::Note) {
+ CapturedDiags.push_back(StoredDiagnostic(level, Info));
+ return;
+ }
+
+ // Non-ARC warnings are ignored.
+ Diags.setLastDiagnosticIgnored();
+ }
+};
+
+} // end anonymous namespace
+
+CompilerInvocation *createInvocationForMigration(CompilerInvocation &origCI) {
+ llvm::OwningPtr<CompilerInvocation> CInvok;
+ CInvok.reset(new CompilerInvocation(origCI));
+ CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string();
+ CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string();
+ std::string define = getARCMTMacroName();
+ define += '=';
+ CInvok->getPreprocessorOpts().addMacroDef(define);
+ CInvok->getLangOpts().ObjCAutoRefCount = true;
+ CInvok->getDiagnosticOpts().ErrorLimit = 0;
+
+ // FIXME: Hackety hack! Try to find out if there is an ARC runtime.
+ bool hasARCRuntime = false;
+ llvm::SmallVector<std::string, 16> args;
+ args.push_back("-x");
+ args.push_back("objective-c");
+ args.push_back("-fobjc-arc");
+
+ llvm::Triple triple(CInvok->getTargetOpts().Triple);
+ if (triple.getOS() == llvm::Triple::IOS ||
+ triple.getOS() == llvm::Triple::MacOSX) {
+ unsigned Major, Minor, Micro;
+ triple.getOSVersion(Major, Minor, Micro);
+ llvm::SmallString<100> flag;
+ if (triple.getOS() == llvm::Triple::IOS)
+ flag += "-miphoneos-version-min=";
+ else
+ flag += "-mmacosx-version-min=";
+ llvm::raw_svector_ostream(flag) << Major << '.' << Minor << '.' << Micro;
+ args.push_back(flag.str());
+ }
+
+ args.push_back(origCI.getFrontendOpts().Inputs[0].second.c_str());
+ // Also push all defines to deal with the iOS simulator hack.
+ for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size();
+ i != e; ++i) {
+ std::string &def = origCI.getPreprocessorOpts().Macros[i].first;
+ bool isUndef = origCI.getPreprocessorOpts().Macros[i].second;
+ if (!isUndef) {
+ std::string newdef = "-D";
+ newdef += def;
+ args.push_back(newdef);
+ }
+ }
+
+ llvm::SmallVector<const char *, 16> cargs;
+ for (unsigned i = 0, e = args.size(); i != e; ++i)
+ cargs.push_back(args[i].c_str());
+
+ llvm::OwningPtr<CompilerInvocation> checkCI;
+ checkCI.reset(clang::createInvocationFromCommandLine(cargs));
+ if (checkCI)
+ hasARCRuntime = !checkCI->getLangOpts().ObjCNoAutoRefCountRuntime;
+
+ CInvok->getLangOpts().ObjCNoAutoRefCountRuntime = !hasARCRuntime;
+
+ return CInvok.take();
+}
+
+//===----------------------------------------------------------------------===//
+// checkForManualIssues.
+//===----------------------------------------------------------------------===//
+
+bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
+ llvm::StringRef Filename, InputKind Kind,
+ DiagnosticClient *DiagClient) {
+ if (!origCI.getLangOpts().ObjC1)
+ return false;
+
+ std::vector<TransformFn> transforms = arcmt::getAllTransformations();
+ assert(!transforms.empty());
+
+ llvm::OwningPtr<CompilerInvocation> CInvok;
+ CInvok.reset(createInvocationForMigration(origCI));
+ CInvok->getFrontendOpts().Inputs.clear();
+ CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
+
+ CapturedDiagList capturedDiags;
+
+ assert(DiagClient);
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
+ new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+
+ // Filter of all diagnostics.
+ CaptureDiagnosticClient errRec(*Diags, capturedDiags);
+ Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
+
+ llvm::OwningPtr<ASTUnit> Unit(
+ ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
+ if (!Unit)
+ return true;
+
+ // Don't filter diagnostics anymore.
+ Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
+
+ ASTContext &Ctx = Unit->getASTContext();
+
+ if (Diags->hasFatalErrorOccurred()) {
+ Diags->Reset();
+ DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
+ capturedDiags.reportDiagnostics(*Diags);
+ DiagClient->EndSourceFile();
+ return true;
+ }
+
+ // After parsing of source files ended, we want to reuse the
+ // diagnostics objects to emit further diagnostics.
+ // We call BeginSourceFile because DiagnosticClient requires that
+ // diagnostics with source range information are emitted only in between
+ // BeginSourceFile() and EndSourceFile().
+ DiagClient->BeginSourceFile(Ctx.getLangOptions(), &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, Unit->getSema(), testAct, ARCMTMacroLocs);
+
+ for (unsigned i=0, e = transforms.size(); i != e; ++i)
+ transforms[i](pass);
+
+ capturedDiags.reportDiagnostics(*Diags);
+
+ DiagClient->EndSourceFile();
+
+ return Diags->getClient()->getNumErrors() > 0;
+}
+
+//===----------------------------------------------------------------------===//
+// applyTransformations.
+//===----------------------------------------------------------------------===//
+
+bool arcmt::applyTransformations(CompilerInvocation &origCI,
+ llvm::StringRef Filename, InputKind Kind,
+ DiagnosticClient *DiagClient) {
+ if (!origCI.getLangOpts().ObjC1)
+ return false;
+
+ // Make sure checking is successful first.
+ CompilerInvocation CInvokForCheck(origCI);
+ if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient))
+ return true;
+
+ CompilerInvocation CInvok(origCI);
+ CInvok.getFrontendOpts().Inputs.clear();
+ CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
+
+ MigrationProcess migration(CInvok, DiagClient);
+
+ std::vector<TransformFn> transforms = arcmt::getAllTransformations();
+ assert(!transforms.empty());
+
+ for (unsigned i=0, e = transforms.size(); i != e; ++i) {
+ bool err = migration.applyTransform(transforms[i]);
+ if (err) return true;
+ }
+
+ origCI.getLangOpts().ObjCAutoRefCount = true;
+
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
+ new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+ return migration.getRemapper().overwriteOriginal(*Diags);
+}
+
+//===----------------------------------------------------------------------===//
+// applyTransformationsInMemory.
+//===----------------------------------------------------------------------===//
+
+bool arcmt::applyTransformationsInMemory(CompilerInvocation &origCI,
+ llvm::StringRef Filename, InputKind Kind,
+ DiagnosticClient *DiagClient) {
+ if (!origCI.getLangOpts().ObjC1)
+ return false;
+
+ // Make sure checking is successful first.
+ CompilerInvocation CInvokForCheck(origCI);
+ if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient))
+ return true;
+
+ CompilerInvocation CInvok(origCI);
+ CInvok.getFrontendOpts().Inputs.clear();
+ CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
+
+ MigrationProcess migration(CInvok, DiagClient);
+
+ std::vector<TransformFn> transforms = arcmt::getAllTransformations();
+ assert(!transforms.empty());
+
+ for (unsigned i=0, e = transforms.size(); i != e; ++i) {
+ bool err = migration.applyTransform(transforms[i]);
+ if (err) return true;
+ }
+
+ origCI.getLangOpts().ObjCAutoRefCount = true;
+ migration.getRemapper().transferMappingsAndClear(origCI);
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// CollectTransformActions.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
+ std::vector<SourceLocation> &ARCMTMacroLocs;
+
+public:
+ ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
+ : ARCMTMacroLocs(ARCMTMacroLocs) { }
+
+ virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI) {
+ 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) { }
+
+ virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
+ llvm::StringRef InFile) {
+ CI.getPreprocessor().addPPCallbacks(
+ new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
+ return new ASTConsumer();
+ }
+};
+
+class RewritesApplicator : public TransformActions::RewriteReceiver {
+ Rewriter &rewriter;
+ ASTContext &Ctx;
+ MigrationProcess::RewriteListener *Listener;
+
+public:
+ RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
+ MigrationProcess::RewriteListener *listener)
+ : rewriter(rewriter), Ctx(ctx), Listener(listener) {
+ if (Listener)
+ Listener->start(ctx);
+ }
+ ~RewritesApplicator() {
+ if (Listener)
+ Listener->finish();
+ }
+
+ virtual void insert(SourceLocation loc, llvm::StringRef text) {
+ bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
+ /*indentNewLines=*/true);
+ if (!err && Listener)
+ Listener->insert(loc, text);
+ }
+
+ virtual void remove(CharSourceRange range) {
+ Rewriter::RewriteOptions removeOpts;
+ removeOpts.IncludeInsertsAtBeginOfRange = false;
+ removeOpts.IncludeInsertsAtEndOfRange = false;
+ removeOpts.RemoveLineIfEmpty = true;
+
+ bool err = rewriter.RemoveText(range, removeOpts);
+ if (!err && Listener)
+ Listener->remove(range);
+ }
+
+ virtual void increaseIndentation(CharSourceRange range,
+ SourceLocation parentIndent) {
+ rewriter.IncreaseIndentation(range, parentIndent);
+ }
+};
+
+} // end anonymous namespace.
+
+/// \brief Anchor for VTable.
+MigrationProcess::RewriteListener::~RewriteListener() { }
+
+bool MigrationProcess::applyTransform(TransformFn trans,
+ RewriteListener *listener) {
+ llvm::OwningPtr<CompilerInvocation> CInvok;
+ CInvok.reset(createInvocationForMigration(OrigCI));
+ CInvok->getDiagnosticOpts().IgnoreWarnings = true;
+
+ Remapper.applyMappings(*CInvok);
+
+ CapturedDiagList capturedDiags;
+ std::vector<SourceLocation> ARCMTMacroLocs;
+
+ assert(DiagClient);
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
+ new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
+
+ // Filter of all diagnostics.
+ CaptureDiagnosticClient errRec(*Diags, capturedDiags);
+ Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
+
+ llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction;
+ ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
+
+ llvm::OwningPtr<ASTUnit> Unit(
+ ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
+ ASTAction.get()));
+ if (!Unit)
+ return true;
+ Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
+
+ // Don't filter diagnostics anymore.
+ Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
+
+ ASTContext &Ctx = Unit->getASTContext();
+
+ if (Diags->hasFatalErrorOccurred()) {
+ Diags->Reset();
+ DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
+ capturedDiags.reportDiagnostics(*Diags);
+ DiagClient->EndSourceFile();
+ return true;
+ }
+
+ // After parsing of source files ended, we want to reuse the
+ // diagnostics objects to emit further diagnostics.
+ // We call BeginSourceFile because DiagnosticClient requires that
+ // diagnostics with source range information are emitted only in between
+ // BeginSourceFile() and EndSourceFile().
+ DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
+
+ Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions());
+ TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
+ MigrationPass pass(Ctx, Unit->getSema(), TA, ARCMTMacroLocs);
+
+ trans(pass);
+
+ {
+ RewritesApplicator applicator(rewriter, Ctx, listener);
+ TA.applyRewrites(applicator);
+ }
+
+ DiagClient->EndSourceFile();
+
+ 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";
+ llvm::SmallString<512> newText;
+ llvm::raw_svector_ostream vecOS(newText);
+ buf.write(vecOS);
+ vecOS.flush();
+ llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
+ llvm::StringRef(newText.data(), newText.size()), newFname);
+ llvm::SmallString<64> filePath(file->getName());
+ Unit->getFileManager().FixupRelativePath(filePath);
+ Remapper.remap(filePath.str(), memBuf);
+ }
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// isARCDiagnostic.
+//===----------------------------------------------------------------------===//
+
+bool arcmt::isARCDiagnostic(unsigned diagID, Diagnostic &Diag) {
+ return Diag.getDiagnosticIDs()->getCategoryNumberForDiag(diagID) ==
+ diag::DiagCat_Automatic_Reference_Counting_Issue;
+}
diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt
new file mode 100644
index 0000000000..3645a22356
--- /dev/null
+++ b/lib/ARCMigrate/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend clangRewrite)
+
+add_clang_library(clangARCMigrate
+ ARCMT.cpp
+ FileRemapper.cpp
+ TransformActions.cpp
+ Transforms.cpp
+ )
+
+add_dependencies(clangARCMigrate
+ ClangAttrClasses
+ ClangAttrList
+ ClangDeclNodes
+ ClangStmtNodes)
diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp
new file mode 100644
index 0000000000..ae5d3a3c93
--- /dev/null
+++ b/lib/ARCMigrate/FileRemapper.cpp
@@ -0,0 +1,290 @@
+//===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ARCMigrate/FileRemapper.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <fstream>
+
+using namespace clang;
+using namespace arcmt;
+
+FileRemapper::FileRemapper() {
+ FileMgr.reset(new FileManager(FileSystemOptions()));
+}
+
+FileRemapper::~FileRemapper() {
+ clear();
+}
+
+void FileRemapper::clear(llvm::StringRef outputDir) {
+ for (MappingsTy::iterator
+ I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
+ resetTarget(I->second);
+ FromToMappings.clear();
+ assert(ToFromMappings.empty());
+ if (!outputDir.empty()) {
+ std::string infoFile = getRemapInfoFile(outputDir);
+ bool existed;
+ llvm::sys::fs::remove(infoFile, existed);
+ }
+}
+
+std::string FileRemapper::getRemapInfoFile(llvm::StringRef outputDir) {
+ assert(!outputDir.empty());
+ llvm::sys::Path dir(outputDir);
+ llvm::sys::Path infoFile = dir;
+ infoFile.appendComponent("remap");
+ return infoFile.str();
+}
+
+bool FileRemapper::initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag,
+ bool ignoreIfFilesChanged) {
+ assert(FromToMappings.empty() &&
+ "initFromDisk should be called before any remap calls");
+ std::string infoFile = getRemapInfoFile(outputDir);
+ bool fileExists = false;
+ llvm::sys::fs::exists(infoFile, fileExists);
+ if (!fileExists)
+ return false;
+
+ std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
+
+ std::ifstream fin(infoFile.c_str());
+ if (!fin.good())
+ return report(std::string("Error opening file: ") + infoFile, Diag);
+
+ while (true) {
+ std::string fromFilename, toFilename;
+ uint64_t timeModified;
+
+ fin >> fromFilename >> timeModified >> toFilename;
+ if (fin.eof())
+ break;
+ if (!fin.good()) {
+ if (ignoreIfFilesChanged)
+ return false;
+ return report(std::string("Error in format of file: ") + infoFile, Diag);
+ }
+
+ const FileEntry *origFE = FileMgr->getFile(fromFilename);
+ if (!origFE) {
+ if (ignoreIfFilesChanged)
+ continue;
+ return report(std::string("File does not exist: ") + fromFilename, Diag);
+ }
+ const FileEntry *newFE = FileMgr->getFile(toFilename);
+ if (!newFE) {
+ if (ignoreIfFilesChanged)
+ continue;
+ return report(std::string("File does not exist: ") + toFilename, Diag);
+ }
+
+ if ((uint64_t)origFE->getModificationTime() != timeModified) {
+ if (ignoreIfFilesChanged)
+ continue;
+ return report(std::string("File was modified: ") + fromFilename, Diag);
+ }
+
+ pairs.push_back(std::make_pair(origFE, newFE));
+ }
+
+ for (unsigned i = 0, e = pairs.size(); i != e; ++i)
+ remap(pairs[i].first, pairs[i].second);
+
+ return false;
+}
+
+bool FileRemapper::flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag) {
+ using namespace llvm::sys;
+
+ bool existed;
+ if (fs::create_directory(outputDir, existed) != llvm::errc::success)
+ return report(std::string("Could not create directory: ") + outputDir.str(),
+ Diag);
+
+ std::string errMsg;
+ std::string infoFile = getRemapInfoFile(outputDir);
+ llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
+ llvm::raw_fd_ostream::F_Binary);
+ if (!errMsg.empty() || infoOut.has_error())
+ return report(errMsg, Diag);
+
+ for (MappingsTy::iterator
+ I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
+
+ const FileEntry *origFE = I->first;
+ infoOut << origFE->getName() << '\n';
+ infoOut << (uint64_t)origFE->getModificationTime() << '\n';
+
+ if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
+ infoOut << FE->getName() << '\n';
+ } else {
+
+ llvm::SmallString<64> tempPath;
+ tempPath = path::filename(origFE->getName());
+ tempPath += "-%%%%%%%%";
+ tempPath += path::extension(origFE->getName());
+ int fd;
+ if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success)
+ return report(std::string("Could not create file: ") + tempPath.c_str(),
+ Diag);
+
+ llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
+ llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
+ newOut.write(mem->getBufferStart(), mem->getBufferSize());
+ newOut.close();
+
+ const FileEntry *newE = FileMgr->getFile(tempPath);
+ remap(origFE, newE);
+ infoOut << newE->getName() << '\n';
+ }
+ }
+
+ infoOut.close();
+ return false;
+}
+
+bool FileRemapper::overwriteOriginal(Diagnostic &Diag,
+ llvm::StringRef outputDir) {
+ using namespace llvm::sys;
+
+ for (MappingsTy::iterator
+ I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
+ const FileEntry *origFE = I->first;
+ if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) {
+ if (fs::copy_file(newFE->getName(), origFE->getName(),
+ fs::copy_option::overwrite_if_exists) != llvm::errc::success) {
+ std::string err = "Could not copy file '";
+ llvm::raw_string_ostream os(err);
+ os << "Could not copy file '" << newFE->getName() << "' to file '"
+ << origFE->getName() << "'";
+ os.flush();
+ return report(err, Diag);
+ }
+ } else {
+
+ bool fileExists = false;
+ fs::exists(origFE->getName(), fileExists);
+ if (!fileExists)
+ return report(std::string("File does not exist: ") + origFE->getName(),
+ Diag);
+
+ std::string errMsg;
+ llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
+ llvm::raw_fd_ostream::F_Binary);
+ if (!errMsg.empty() || Out.has_error())
+ return report(errMsg, Diag);
+
+ llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
+ Out.write(mem->getBufferStart(), mem->getBufferSize());
+ Out.close();
+ }
+ }
+
+ clear(outputDir);
+ return false;
+}
+
+void FileRemapper::applyMappings(CompilerInvocation &CI) const {
+ PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
+ for (MappingsTy::const_iterator
+ I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
+ if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
+ PPOpts.addRemappedFile(I->first->getName(), FE->getName());
+ } else {
+ llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
+ PPOpts.addRemappedFile(I->first->getName(), mem);
+ }
+ }
+
+ PPOpts.RetainRemappedFileBuffers = true;
+}
+
+void FileRemapper::transferMappingsAndClear(CompilerInvocation &CI) {
+ PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
+ for (MappingsTy::iterator
+ I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
+ if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
+ PPOpts.addRemappedFile(I->first->getName(), FE->getName());
+ } else {
+ llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
+ PPOpts.addRemappedFile(I->first->getName(), mem);
+ }
+ I->second = Target();
+ }
+
+ PPOpts.RetainRemappedFileBuffers = false;
+ clear();
+}
+
+void FileRemapper::remap(llvm::StringRef filePath, llvm::MemoryBuffer *memBuf) {
+ remap(getOriginalFile(filePath), memBuf);
+}
+
+void FileRemapper::remap(llvm::StringRef filePath, llvm::StringRef newPath) {
+ const FileEntry *file = getOriginalFile(filePath);
+ const FileEntry *newfile = FileMgr->getFile(newPath);
+ remap(file, newfile);
+}
+
+void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
+ assert(file);
+ Target &targ = FromToMappings[file];
+ resetTarget(targ);
+ targ = memBuf;
+}
+
+void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
+ assert(file && newfile);
+ Target &targ = FromToMappings[file];
+ resetTarget(targ);
+ targ = newfile;
+ ToFromMappings[newfile] = file;
+}
+
+const FileEntry *FileRemapper::getOriginalFile(llvm::StringRef filePath) {
+ const FileEntry *file = FileMgr->getFile(filePath);
+ // If we are updating a file that overriden an original file,
+ // actually update the original file.
+ llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
+ I = ToFromMappings.find(file);
+ if (I != ToFromMappings.end()) {
+ file = I->second;
+ assert(FromToMappings.find(file) != FromToMappings.end() &&
+ "Original file not in mappings!");
+ }
+ return file;
+}
+
+void FileRemapper::resetTarget(Target &targ) {
+ if (!targ)
+ return;
+
+ if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
+ delete oldmem;
+ } else {
+ const FileEntry *toFE = targ.get<const FileEntry *>();
+ llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
+ I = ToFromMappings.find(toFE);
+ if (I != ToFromMappings.end())
+ ToFromMappings.erase(I);
+ }
+}
+
+bool FileRemapper::report(const std::string &err, Diagnostic &Diag) {
+ unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
+ err);
+ Diag.Report(ID);
+ return true;
+}
diff --git a/lib/ARCMigrate/Internals.h b/lib/ARCMigrate/Internals.h
new file mode 100644
index 0000000000..fdc0aad176
--- /dev/null
+++ b/lib/ARCMigrate/Internals.h
@@ -0,0 +1,146 @@
+//===-- Internals.h - Implementation Details---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
+#define LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
+
+#include "clang/ARCMigrate/ARCMT.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+ class Sema;
+ class Stmt;
+
+namespace arcmt {
+
+class CapturedDiagList {
+ typedef std::list<StoredDiagnostic> ListTy;
+ ListTy List;
+
+public:
+ void push_back(const StoredDiagnostic &diag) { List.push_back(diag); }
+
+ bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
+ bool hasDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
+
+ void reportDiagnostics(Diagnostic &diags);
+};
+
+class TransformActions {
+ Diagnostic &Diags;
+ CapturedDiagList &CapturedDiags;
+ void *Impl; // TransformActionsImpl.
+
+public:
+ TransformActions(Diagnostic &diag, CapturedDiagList &capturedDiags,
+ ASTContext &ctx, Preprocessor &PP);
+ ~TransformActions();
+
+ void startTransaction();
+ bool commitTransaction();
+ void abortTransaction();
+
+ void insert(SourceLocation loc, llvm::StringRef text);
+ void insertAfterToken(SourceLocation loc, llvm::StringRef text);
+ void remove(SourceRange range);
+ void removeStmt(Stmt *S);
+ void replace(SourceRange range, llvm::StringRef text);
+ void replace(SourceRange range, SourceRange replacementRange);
+ void replaceStmt(Stmt *S, llvm::StringRef text);
+ void replaceText(SourceLocation loc, llvm::StringRef text,
+ llvm::StringRef replacementText);
+ void increaseIndentation(SourceRange range,
+ SourceLocation parentIndent);
+
+ bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
+ bool clearAllDiagnostics(SourceRange range) {
+ return clearDiagnostic(llvm::ArrayRef<unsigned>(), range);
+ }
+ bool clearDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
+ unsigned IDs[] = { ID1, ID2 };
+ return clearDiagnostic(IDs, range);
+ }
+ bool clearDiagnostic(unsigned ID1, unsigned ID2, unsigned ID3,
+ SourceRange range) {
+ unsigned IDs[] = { ID1, ID2, ID3 };
+ return clearDiagnostic(IDs, range);
+ }
+
+ bool hasDiagnostic(unsigned ID, SourceRange range) {
+ return CapturedDiags.hasDiagnostic(ID, range);
+ }
+
+ bool hasDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
+ unsigned IDs[] = { ID1, ID2 };
+ return CapturedDiags.hasDiagnostic(IDs, range);
+ }
+
+ void reportError(llvm::StringRef error, SourceLocation loc,
+ SourceRange range = SourceRange());
+ void reportNote(llvm::StringRef note, SourceLocation loc,
+ SourceRange range = SourceRange());
+
+ class RewriteReceiver {
+ public:
+ virtual ~RewriteReceiver();
+
+ virtual void insert(SourceLocation loc, llvm::StringRef text) = 0;
+ virtual void remove(CharSourceRange range) = 0;
+ virtual void increaseIndentation(CharSourceRange range,
+ SourceLocation parentIndent) = 0;
+ };
+
+ void applyRewrites(RewriteReceiver &receiver);
+};
+
+class Transaction {
+ TransformActions &TA;
+ bool Aborted;
+
+public:
+ Transaction(TransformActions &TA) : TA(TA), Aborted(false) {
+ TA.startTransaction();
+ }
+
+ ~Transaction() {
+ if (!isAborted())
+ TA.commitTransaction();
+ }
+
+ void abort() {
+ TA.abortTransaction();
+ Aborted = true;
+ }
+
+ bool isAborted() const { return Aborted; }
+};
+
+class MigrationPass {
+public:
+ ASTContext &Ctx;
+ Sema &SemaRef;
+ TransformActions &TA;
+ std::vector<SourceLocation> &ARCMTMacroLocs;
+
+ MigrationPass(ASTContext &Ctx, Sema &sema, TransformActions &TA,
+ std::vector<SourceLocation> &ARCMTMacroLocs)
+ : Ctx(Ctx), SemaRef(sema), TA(TA), ARCMTMacroLocs(ARCMTMacroLocs) { }
+};
+
+bool isARCDiagnostic(unsigned diagID, Diagnostic &Diag);
+
+static inline llvm::StringRef getARCMTMacroName() {
+ return "__IMPL_ARCMT_REMOVED_EXPR__";
+}
+
+} // end namespace arcmt
+
+} // end namespace clang
+
+#endif
diff --git a/lib/ARCMigrate/Makefile b/lib/ARCMigrate/Makefile
new file mode 100644
index 0000000000..5232c5e5af
--- /dev/null
+++ b/lib/ARCMigrate/Makefile
@@ -0,0 +1,18 @@