diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:33 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:33 +0000 |
commit | 30660a898545416f0fea2d717f16f75640001e38 (patch) | |
tree | a8408d8c12d992b4110fca1bfe78f337b48f5d11 /lib | |
parent | 2a253960f0312c9759965a1defb9326d6b2764a4 (diff) |
Add new code migrator support for migrating existing Objective-C code to use
the new Objective-C NSArray/NSDictionary/NSNumber literal syntax.
This introduces a new library, libEdit, which provides a new way to support
migration of code that improves on the original ARC migrator. We now believe
that most of its functionality can be refactored into the existing libraries,
and thus this new library may shortly disappear.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152141 91177308-0d34-0410-b5e6-96231b3b80d8
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; |