diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ARCMigrate/ARCMT.cpp | 46 | ||||
-rw-r--r-- | lib/ARCMigrate/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/ARCMigrate/FileRemapper.cpp | 26 | ||||
-rw-r--r-- | lib/ARCMigrate/ObjCMT.cpp | 226 | ||||
-rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 94 | ||||
-rw-r--r-- | lib/Basic/DiagnosticIDs.cpp | 13 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Driver/Action.cpp | 7 | ||||
-rw-r--r-- | lib/Driver/Driver.cpp | 3 | ||||
-rw-r--r-- | lib/Driver/ToolChain.cpp | 2 | ||||
-rw-r--r-- | lib/Driver/ToolChains.cpp | 2 | ||||
-rw-r--r-- | lib/Driver/Tools.cpp | 25 | ||||
-rw-r--r-- | lib/Driver/WindowsToolChain.cpp | 1 | ||||
-rw-r--r-- | lib/Edit/CMakeLists.txt | 7 | ||||
-rw-r--r-- | lib/Edit/Commit.cpp | 345 | ||||
-rw-r--r-- | lib/Edit/EditedSource.cpp | 329 | ||||
-rw-r--r-- | lib/Edit/Makefile | 14 | ||||
-rw-r--r-- | lib/Edit/RewriteObjCFoundationAPI.cpp | 589 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 31 | ||||
-rw-r--r-- | lib/Frontend/DiagnosticRenderer.cpp | 57 | ||||
-rw-r--r-- | lib/FrontendTool/ExecuteCompilerInvocation.cpp | 9 | ||||
-rwxr-xr-x | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/Rewrite/FixItRewriter.cpp | 69 |
23 files changed, 1834 insertions, 65 deletions
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 61753074e8..07f22c4f36 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -398,13 +398,51 @@ bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & if (err) return true; - CompilerInvocation CI; - remapper.applyMappings(CI); - remap = CI.getPreprocessorOpts().RemappedFiles; + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + remap = PPOpts.RemappedFiles; return false; } +bool arcmt::getFileRemappingsFromFileList( + std::vector<std::pair<std::string,std::string> > &remap, + ArrayRef<StringRef> remapFiles, + DiagnosticConsumer *DiagClient) { + bool hasErrorOccurred = false; + llvm::StringMap<bool> Uniquer; + + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + for (ArrayRef<StringRef>::iterator + I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { + StringRef file = *I; + + FileRemapper remapper; + bool err = remapper.initFromFile(file, *Diags, + /*ignoreIfFilesChanged=*/true); + hasErrorOccurred = hasErrorOccurred || err; + if (err) + continue; + + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + for (PreprocessorOptions::remapped_file_iterator + RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end(); + RI != RE; ++RI) { + bool &inserted = Uniquer[RI->first]; + if (inserted) + continue; + inserted = true; + remap.push_back(*RI); + } + } + + return hasErrorOccurred; +} + //===----------------------------------------------------------------------===// // CollectTransformActions. //===----------------------------------------------------------------------===// @@ -504,7 +542,7 @@ bool MigrationProcess::applyTransform(TransformFn trans, CInvok.reset(createInvocationForMigration(OrigCI)); CInvok->getDiagnosticOpts().IgnoreWarnings = true; - Remapper.applyMappings(*CInvok); + Remapper.applyMappings(CInvok->getPreprocessorOpts()); CapturedDiagList capturedDiags; std::vector<SourceLocation> ARCMTMacroLocs; diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt index 1a64b12721..fcb7f72ee2 100644 --- a/lib/ARCMigrate/CMakeLists.txt +++ b/lib/ARCMigrate/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangARCMigrate ARCMT.cpp ARCMTActions.cpp FileRemapper.cpp + ObjCMT.cpp PlistReporter.cpp TransAPIUses.cpp TransARCAssign.cpp diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp index 0e6e35c748..1e97c9eed5 100644 --- a/lib/ARCMigrate/FileRemapper.cpp +++ b/lib/ARCMigrate/FileRemapper.cpp @@ -8,8 +8,9 @@ //===----------------------------------------------------------------------===// #include "clang/ARCMigrate/FileRemapper.h" -#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/PreprocessorOptions.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/Diagnostic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/FileSystem.h" @@ -50,9 +51,15 @@ std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged) { + std::string infoFile = getRemapInfoFile(outputDir); + return initFromFile(infoFile, Diag, ignoreIfFilesChanged); +} + +bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, + bool ignoreIfFilesChanged) { assert(FromToMappings.empty() && "initFromDisk should be called before any remap calls"); - std::string infoFile = getRemapInfoFile(outputDir); + std::string infoFile = filePath; bool fileExists = false; llvm::sys::fs::exists(infoFile, fileExists); if (!fileExists) @@ -108,8 +115,15 @@ bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { if (fs::create_directory(outputDir, existed) != llvm::errc::success) return report("Could not create directory: " + outputDir, Diag); - std::string errMsg; std::string infoFile = getRemapInfoFile(outputDir); + return flushToFile(infoFile, Diag); +} + +bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { + using namespace llvm::sys; + + std::string errMsg; + std::string infoFile = outputPath; llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, llvm::raw_fd_ostream::F_Binary); if (!errMsg.empty()) @@ -189,8 +203,7 @@ bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, return false; } -void FileRemapper::applyMappings(CompilerInvocation &CI) const { - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); +void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { for (MappingsTy::const_iterator I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { @@ -204,8 +217,7 @@ void FileRemapper::applyMappings(CompilerInvocation &CI) const { PPOpts.RetainRemappedFileBuffers = true; } -void FileRemapper::transferMappingsAndClear(CompilerInvocation &CI) { - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); +void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) { for (MappingsTy::iterator I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp new file mode 100644 index 0000000000..497377a394 --- /dev/null +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -0,0 +1,226 @@ +//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMTActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/NSAPI.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Edit/Rewriters.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; +using namespace arcmt; + +namespace { + +class ObjCMigrateASTConsumer : public ASTConsumer { + void migrateDecl(Decl *D); + +public: + std::string MigrateDir; + bool MigrateLiterals; + bool MigrateSubscripting; + llvm::OwningPtr<NSAPI> NSAPIObj; + llvm::OwningPtr<edit::EditedSource> Editor; + FileRemapper &Remapper; + FileManager &FileMgr; + const PreprocessingRecord *PPRec; + bool IsOutputFile; + + ObjCMigrateASTConsumer(StringRef migrateDir, + bool migrateLiterals, + bool migrateSubscripting, + FileRemapper &remapper, + FileManager &fileMgr, + const PreprocessingRecord *PPRec, + bool isOutputFile = false) + : MigrateDir(migrateDir), + MigrateLiterals(migrateLiterals), + MigrateSubscripting(migrateSubscripting), + Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), + IsOutputFile(isOutputFile) { } + +protected: + virtual void Initialize(ASTContext &Context) { + NSAPIObj.reset(new NSAPI(Context)); + Editor.reset(new edit::EditedSource(Context.getSourceManager(), + Context.getLangOptions(), + PPRec)); + } + + virtual bool HandleTopLevelDecl(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) + migrateDecl(*I); + return true; + } + virtual void HandleInterestingDecl(DeclGroupRef DG) { + // Ignore decls from the PCH. + } + virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { + ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); + } + + virtual void HandleTranslationUnit(ASTContext &Ctx); +}; + +} + +ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, + StringRef migrateDir, + bool migrateLiterals, + bool migrateSubscripting) + : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting), + CompInst(0) { + if (MigrateDir.empty()) + MigrateDir = "."; // user current directory if none is given. +} + +ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + ASTConsumer * + WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir, + MigrateLiterals, + MigrateSubscripting, + Remapper, + CompInst->getFileManager(), + CompInst->getPreprocessor().getPreprocessingRecord()); + ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer }; + return new MultiplexConsumer(Consumers); +} + +bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { + Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), + /*ignoreIfFilesChanges=*/true); + CompInst = &CI; + CI.getDiagnostics().setIgnoreAllWarnings(true); + CI.getPreprocessorOpts().DetailedRecord = true; + CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true; + return true; +} + +namespace { +class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { + ObjCMigrateASTConsumer &Consumer; + +public: + ObjCMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } + + bool shouldVisitTemplateInstantiations() const { return false; } + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (Consumer.MigrateLiterals) { + edit::Commit commit(*Consumer.Editor); + edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit); + Consumer.Editor->commit(commit); + } + + if (Consumer.MigrateSubscripting) { + edit::Commit commit(*Consumer.Editor); + edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); + Consumer.Editor->commit(commit); + } + + return true; + } + + bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { + // Do depth first; we want to rewrite the subexpressions first so that if + // we have to move expressions we will move them already rewritten. + for (Stmt::child_range range = E->children(); range; ++range) + if (!TraverseStmt(*range)) + return false; + + return WalkUpFromObjCMessageExpr(E); + } +}; +} + +void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { + if (!D) + return; + if (isa<ObjCMethodDecl>(D)) + return; // Wait for the ObjC container declaration. + + ObjCMigrator(*this).TraverseDecl(D); +} + +namespace { + +class RewritesReceiver : public edit::EditsReceiver { + Rewriter &Rewrite; + +public: + RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } + + virtual void insert(SourceLocation loc, StringRef text) { + Rewrite.InsertText(loc, text); + } + virtual void replace(CharSourceRange range, StringRef text) { + Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); + } +}; + +} + +void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions()); + RewritesReceiver Rec(rewriter); + Editor->applyRewrites(Rec); + + 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); + llvm::SmallString<512> newText; + llvm::raw_svector_ostream vecOS(newText); + buf.write(vecOS); + vecOS.flush(); + llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(newText.data(), newText.size()), file->getName()); + llvm::SmallString<64> filePath(file->getName()); + FileMgr.FixupRelativePath(filePath); + Remapper.remap(filePath.str(), memBuf); + } + + if (IsOutputFile) { + Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); + } else { + Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); + } +} + +bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { + CI.getPreprocessorOpts().DetailedRecord = true; + CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true; + return true; +} + +ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, + /*MigrateLiterals=*/true, + /*MigrateSubscripting=*/true, + Remapper, + CI.getFileManager(), + CI.getPreprocessor().getPreprocessingRecord(), + /*isOutputFile=*/true); +} diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 7bb7b5eeea..15669ac973 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -21,6 +21,8 @@ #include "Internals.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/AST/ParentMap.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" using namespace clang; using namespace arcmt; @@ -128,27 +130,105 @@ public: Transaction Trans(Pass.TA); clearDiagnostics(rec->getExprLoc()); - if (E->getMethodFamily() == OMF_release && - isRemovable(E) && isInAtFinally(E)) { + ObjCMessageExpr *Msg = E; + Expr *RecContainer = Msg; + SourceRange RecRange = rec->getSourceRange(); + checkForGCDOrXPC(Msg, RecContainer, rec, RecRange); + + if (Msg->getMethodFamily() == OMF_release && + isRemovable(RecContainer) && isInAtFinally(RecContainer)) { // Change the -release to "receiver = nil" in a finally to avoid a leak // when an exception is thrown. - Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + Pass.TA.replace(RecContainer->getSourceRange(), RecRange); std::string str = " = "; str += getNilString(Pass.Ctx); - Pass.TA.insertAfterToken(rec->getLocEnd(), str); + Pass.TA.insertAfterToken(RecRange.getEnd(), str); return true; } - if (!hasSideEffects(E, Pass.Ctx)) { - if (tryRemoving(E)) + if (!hasSideEffects(rec, Pass.Ctx)) { + if (tryRemoving(RecContainer)) return true; } - Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + Pass.TA.replace(RecContainer->getSourceRange(), RecRange); return true; } private: + /// \brief Check if the retain/release is due to a GCD/XPC macro that are + /// defined as: + /// + /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; }) + /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; }) + /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; }) + /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; }) + /// + /// and return the top container which is the StmtExpr and the macro argument + /// expression. + void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer, + Expr *&Rec, SourceRange &RecRange) { + SourceLocation Loc = Msg->getExprLoc(); + if (!Loc.isMacroID()) + return; + SourceManager &SM = Pass.Ctx.getSourceManager(); + StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, + Pass.Ctx.getLangOptions()); + bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) + .Case("dispatch_retain", true) + .Case("dispatch_release", true) + .Case("xpc_retain", true) + .Case("xpc_release", true) + .Default(false); + if (!isGCDOrXPC) + return; + + StmtExpr *StmtE = 0; + Stmt *S = Msg; + while (S) { + if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) { + StmtE = SE; + break; + } + S = StmtMap->getParent(S); + } + + if (!StmtE) + return; + + Stmt::child_range StmtExprChild = StmtE->children(); + if (!StmtExprChild) + return; + CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild); + if (!CompS) + return; + + Stmt::child_range CompStmtChild = CompS->children(); + if (!CompStmtChild) + return; + DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild); + if (!DeclS) + return; + if (!DeclS->isSingleDecl()) + return; + VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()); + if (!VD) + return; + Expr *Init = VD->getInit(); + if (!Init) + return; + + RecContainer = StmtE; + Rec = Init->IgnoreParenImpCasts(); + if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec)) + Rec = EWC->getSubExpr()->IgnoreParenImpCasts(); + RecRange = Rec->getSourceRange(); + if (SM.isMacroArgExpansion(RecRange.getBegin())) + RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin())); + if (SM.isMacroArgExpansion(RecRange.getEnd())) + RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd())); + } + void clearDiagnostics(SourceLocation loc) const { Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, diag::err_unavailable, diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index f0e0c9c778..8c33a963c9 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/lib/Basic/DiagnosticIDs.cpp @@ -657,19 +657,6 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const { } } - // If we have any Fix-Its, make sure that all of the Fix-Its point into - // source locations that aren't macro expansions. If any point into macro - // expansions, remove all of the Fix-Its. - for (unsigned I = 0, N = Diag.FixItHints.size(); I != N; ++I) { - const FixItHint &FixIt = Diag.FixItHints[I]; - if (FixIt.RemoveRange.isInvalid() || - FixIt.RemoveRange.getBegin().isMacroID() || - FixIt.RemoveRange.getEnd().isMacroID()) { - Diag.FixItHints.clear(); - break; - } - } - // Finally, report it. Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info); if (Diag.Client->IncludeInDiagnosticCounts()) { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7b46c6040a..54d296c3ab 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(AST) add_subdirectory(Sema) add_subdirectory(CodeGen) add_subdirectory(Analysis) +add_subdirectory(Edit) add_subdirectory(Rewrite) add_subdirectory(ARCMigrate) add_subdirectory(Driver) diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index a9dc17b320..d7b4bc7053 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -27,6 +27,7 @@ const char *Action::getClassName(ActionClass AC) { case PreprocessJobClass: return "preprocessor"; case PrecompileJobClass: return "precompiler"; case AnalyzeJobClass: return "analyzer"; + case MigrateJobClass: return "migrator"; case CompileJobClass: return "compiler"; case AssembleJobClass: return "assembler"; case LinkJobClass: return "linker"; @@ -78,6 +79,12 @@ AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) : JobAction(AnalyzeJobClass, Input, OutputType) { } +void MigrateJobAction::anchor() {} + +MigrateJobAction::MigrateJobAction(Action *Input, types::ID OutputType) + : JobAction(MigrateJobClass, Input, OutputType) { +} + void CompileJobAction::anchor() {} CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index e71f5a6065..30b7eccc8a 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -141,6 +141,7 @@ const { // -{fsyntax-only,-analyze,emit-ast,S} only run up to the compiler. } else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) || + (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || (PhaseArg = DAL.getLastArg(options::OPT__analyze, options::OPT__analyze_auto)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_ast)) || @@ -1140,6 +1141,8 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, return new CompileJobAction(Input, types::TY_RewrittenObjC); } else if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) { return new AnalyzeJobAction(Input, types::TY_Plist); + } else if (Args.hasArg(options::OPT__migrate)) { + return new MigrateJobAction(Input, types::TY_Remap); } else if (Args.hasArg(options::OPT_emit_ast)) { return new CompileJobAction(Input, types::TY_AST); } else if (IsUsingLTO(Args)) { diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index c0c9c504b6..b4f5d0b421 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -54,6 +54,7 @@ void ToolChain::configureObjCRuntime(ObjCRuntime &runtime) const { // Assume a minimal NeXT runtime. runtime.HasARC = false; runtime.HasWeak = false; + runtime.HasSubscripting = false; runtime.HasTerminate = false; return; @@ -61,6 +62,7 @@ void ToolChain::configureObjCRuntime(ObjCRuntime &runtime) const { // Assume a maximal GNU runtime. runtime.HasARC = true; runtime.HasWeak = true; + runtime.HasSubscripting = false; // to be added runtime.HasTerminate = false; // to be added return; } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 5ff82c7600..a45722315e 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -227,6 +227,7 @@ Tool &Darwin::SelectTool(const Compilation &C, const JobAction &JA, case Action::PreprocessJobClass: T = new tools::darwin::Preprocess(*this); break; case Action::AnalyzeJobClass: + case Action::MigrateJobClass: T = new tools::Clang(*this); break; case Action::PrecompileJobClass: case Action::CompileJobClass: @@ -1409,6 +1410,7 @@ Tool &Generic_GCC::SelectTool(const Compilation &C, case Action::PrecompileJobClass: T = new tools::gcc::Precompile(*this); break; case Action::AnalyzeJobClass: + case Action::MigrateJobClass: T = new tools::Clang(*this); break; case Action::CompileJobClass: T = new tools::gcc::Compile(*this); break; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 38f21c0882..076311a78e 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1247,6 +1247,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (isa<AnalyzeJobAction>(JA)) { assert(JA.getType() == types::TY_Plist && "Invalid output type."); CmdArgs.push_back("-analyze"); + } else if (isa<MigrateJobAction>(JA)) { + CmdArgs.push_back("-migrate"); } else if (isa<PreprocessJobAction>(JA)) { if (Output.getType() == types::TY_Dependencies) CmdArgs.push_back("-Eonly"); @@ -1716,10 +1718,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_working_directory); + bool ARCMTEnabled = false; if (!Args.hasArg(options::OPT_fno_objc_arc)) { if (const Arg *A = Args.getLastArg(options::OPT_ccc_arcmt_check, options::OPT_ccc_arcmt_modify, options::OPT_ccc_arcmt_migrate)) { + ARCMTEnabled = true; switch (A->getOption().getID()) { default: llvm_unreachable("missed a case"); @@ -1731,7 +1735,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, break; case options::OPT_ccc_arcmt_migrate: CmdArgs.push_back("-arcmt-migrate"); - CmdArgs.push_back("-arcmt-migrate-directory"); + CmdArgs.push_back("-mt-migrate-directory"); CmdArgs.push_back(A->getValue(Args)); Args.AddLastArg(CmdArgs, options::OPT_arcmt_migrate_report_output); @@ -1741,6 +1745,25 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } } + if (const Arg *A = Args.getLastArg(options::OPT_ccc_objcmt_migrate)) { + if (ARCMTEnabled) { + D.Diag(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "-ccc-arcmt-migrate"; + } + CmdArgs.push_back("-mt-migrate-directory"); + CmdArgs.push_back(A->getValue(Args)); + + if (!Args.hasArg(options::OPT_objcmt_migrate_literals, + options::OPT_objcmt_migrate_subscripting)) { + // None specified, means enable them all. + CmdArgs.push_back("-objcmt-migrate-literals"); + CmdArgs.push_back("-objcmt-migrate-subscripting"); + } else { + Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_literals); + Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_subscripting); + } + } + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // diff --git a/lib/Driver/WindowsToolChain.cpp b/lib/Driver/WindowsToolChain.cpp index 8a32db2fca..70bd4f1fc3 100644 --- a/lib/Driver/WindowsToolChain.cpp +++ b/lib/Driver/WindowsToolChain.cpp @@ -59,6 +59,7 @@ Tool &Windows::SelectTool(const Compilation &C, const JobAction &JA, case Action::PreprocessJobClass: case Action::PrecompileJobClass: case Action::AnalyzeJobClass: + case Action::MigrateJobClass: case Action::CompileJobClass: T = new tools::Clang(*this); break; case Action::AssembleJobClass: diff --git a/lib/Edit/CMakeLists.txt b/lib/Edit/CMakeLists.txt new file mode 100644 index 0000000000..c87478cf7d --- /dev/null +++ b/lib/Edit/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_USED_LIBS clangBasic clangAST clangLex) + +add_clang_library(clangEdit + Commit.cpp + EditedSource.cpp + RewriteObjCFoundationAPI.cpp + ) diff --git a/lib/Edit/Commit.cpp b/lib/Edit/Commit.cpp new file mode 100644 index 0000000000..e0250ccf05 --- /dev/null +++ b/lib/Edit/Commit.cpp @@ -0,0 +1,345 @@ +//===----- Commit.cpp - A unit of edits -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace edit; + +SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { + SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); + Loc = Loc.getLocWithOffset(Offset.getOffset()); + assert(Loc.isFileID()); + return Loc; +} + +CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const { + SourceLocation Loc = getFileLocation(SM); + return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); +} + +CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { + SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); + Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); + assert(Loc.isFileID()); + return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); +} + +Commit::Commit(EditedSource &Editor) + : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOptions()), + PPRec(Editor.getPreprocessingRecord()), + Editor(&Editor), IsCommitable(true) { } + +bool Commit::insert(SourceLocation loc, StringRef text, + bool afterToken, bool beforePreviousInsertions) { + if (text.empty()) + return true; + + FileOffset Offs; + if ((!afterToken && !canInsert(loc, Offs)) || + ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { + IsCommitable = false; + return false; + } + + addInsert(loc, Offs, text, beforePreviousInsertions); + return true; +} + +bool Commit::insertFromRange(SourceLocation loc, + CharSourceRange range, + bool afterToken, bool beforePreviousInsertions) { + FileOffset RangeOffs; + unsigned RangeLen; + if (!canRemoveRange(range, RangeOffs, RangeLen)) { + IsCommitable = false; + return false; + } + + FileOffset Offs; + if ((!afterToken && !canInsert(loc, Offs)) || + ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { + IsCommitable = false; + return false; + } + + if (PPRec && + PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) { + IsCommitable = false; + return false; + } + + addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions); + return true; +} + +bool Commit::remove(CharSourceRange range) { + FileOffset Offs; + unsigned Len; + if (!canRemoveRange(range, Offs, Len)) { |