diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ARCMigrate/ARCMT.cpp | 484 | ||||
-rw-r--r-- | lib/ARCMigrate/CMakeLists.txt | 14 | ||||
-rw-r--r-- | lib/ARCMigrate/FileRemapper.cpp | 290 | ||||
-rw-r--r-- | lib/ARCMigrate/Internals.h | 146 | ||||
-rw-r--r-- | lib/ARCMigrate/Makefile | 18 | ||||
-rw-r--r-- | lib/ARCMigrate/TransformActions.cpp | 699 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.cpp | 2094 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Driver/Tools.cpp | 20 | ||||
-rw-r--r-- | lib/Frontend/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Frontend/CompilerInstance.cpp | 29 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 32 | ||||
-rwxr-xr-x | lib/Makefile | 4 |
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 @@ |