diff options
64 files changed, 3748 insertions, 99 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 746490be88..fd7a9f3a01 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -3983,6 +3983,20 @@ typedef void *CXRemapping; CINDEX_LINKAGE CXRemapping clang_getRemappings(const char *path); /** + * \brief Retrieve a remapping. + * + * \param filePaths pointer to an array of file paths containing remapping info. + * + * \param numFiles number of file paths. + * + * \returns the requested remapping. This remapping must be freed + * via a call to \c clang_remap_dispose(). Can return NULL if an error occurred. + */ +CINDEX_LINKAGE +CXRemapping clang_getRemappingsFromFileList(const char **filePaths, + unsigned numFiles); + +/** * \brief Determine the number of remappings. */ CINDEX_LINKAGE unsigned clang_remap_getNumFiles(CXRemapping); diff --git a/include/clang/ARCMigrate/ARCMT.h b/include/clang/ARCMigrate/ARCMT.h index 738a00dcd0..86a6cbb22a 100644 --- a/include/clang/ARCMigrate/ARCMT.h +++ b/include/clang/ARCMigrate/ARCMT.h @@ -76,6 +76,15 @@ bool getFileRemappings(std::vector<std::pair<std::string,std::string> > &remap, StringRef outputDir, DiagnosticConsumer *DiagClient); +/// \brief Get the set of file remappings from a list of files with remapping +/// info. +/// +/// \returns false if no error is produced, true otherwise. +bool getFileRemappingsFromFileList( + std::vector<std::pair<std::string,std::string> > &remap, + ArrayRef<StringRef> remapFiles, + DiagnosticConsumer *DiagClient); + typedef void (*TransformFn)(MigrationPass &pass); std::vector<TransformFn> getAllTransformations(LangOptions::GCMode OrigGCMode, diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h index 4eac4facdd..e075252137 100644 --- a/include/clang/ARCMigrate/ARCMTActions.h +++ b/include/clang/ARCMigrate/ARCMTActions.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_ARCMIGRATE_ARCMT_ACTION_H #include "clang/Frontend/FrontendAction.h" +#include "clang/ARCMigrate/FileRemapper.h" #include "llvm/ADT/OwningPtr.h" namespace clang { @@ -32,6 +33,14 @@ public: ModifyAction(FrontendAction *WrappedAction); }; +class MigrateSourceAction : public ASTFrontendAction { + FileRemapper Remapper; +protected: + virtual bool BeginInvocation(CompilerInstance &CI); + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile); +}; + class MigrateAction : public WrapperFrontendAction { std::string MigrateDir; std::string PlistOut; @@ -45,6 +54,23 @@ public: bool emitPremigrationARCErrors); }; +/// \brief Migrates to modern ObjC syntax. +class ObjCMigrateAction : public WrapperFrontendAction { + std::string MigrateDir; + bool MigrateLiterals; + bool MigrateSubscripting; + FileRemapper Remapper; + CompilerInstance *CompInst; +public: + ObjCMigrateAction(FrontendAction *WrappedAction, StringRef migrateDir, + bool migrateLiterals, + bool migrateSubscripting); + +protected: + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,StringRef InFile); + virtual bool BeginInvocation(CompilerInstance &CI); +}; + } } diff --git a/include/clang/ARCMigrate/FileRemapper.h b/include/clang/ARCMigrate/FileRemapper.h index a451988f93..fe7cfadb49 100644 --- a/include/clang/ARCMigrate/FileRemapper.h +++ b/include/clang/ARCMigrate/FileRemapper.h @@ -24,7 +24,7 @@ namespace clang { class FileManager; class FileEntry; class DiagnosticsEngine; - class CompilerInvocation; + class PreprocessorOptions; namespace arcmt { @@ -44,7 +44,10 @@ public: bool initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged); + bool initFromFile(StringRef filePath, DiagnosticsEngine &Diag, + bool ignoreIfFilesChanged); bool flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag); + bool flushToFile(StringRef outputPath, DiagnosticsEngine &Diag); bool overwriteOriginal(DiagnosticsEngine &Diag, StringRef outputDir = StringRef()); @@ -52,9 +55,9 @@ public: void remap(StringRef filePath, llvm::MemoryBuffer *memBuf); void remap(StringRef filePath, StringRef newPath); - void applyMappings(CompilerInvocation &CI) const; + void applyMappings(PreprocessorOptions &PPOpts) const; - void transferMappingsAndClear(CompilerInvocation &CI); + void transferMappingsAndClear(PreprocessorOptions &PPOpts); void clear(StringRef outputDir = StringRef()); diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 3e54b4352b..2b71d445cc 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -50,13 +50,19 @@ public: /// insertion hint. CharSourceRange RemoveRange; + /// \brief Code in the specific range that should be inserted in the insertion + /// location. + CharSourceRange InsertFromRange; + /// \brief The actual code to insert at the insertion location, as a /// string. std::string CodeToInsert; + bool BeforePreviousInsertions; + /// \brief Empty code modification hint, indicating that no code /// modification is known. - FixItHint() : RemoveRange() { } + FixItHint() : BeforePreviousInsertions(false) { } bool isNull() const { return !RemoveRange.isValid(); @@ -65,11 +71,26 @@ public: /// \brief Create a code modification hint that inserts the given /// code string at a specific location. static FixItHint CreateInsertion(SourceLocation InsertionLoc, - StringRef Code) { + StringRef Code, + bool BeforePreviousInsertions = false) { FixItHint Hint; Hint.RemoveRange = CharSourceRange(SourceRange(InsertionLoc, InsertionLoc), false); Hint.CodeToInsert = Code; + Hint.BeforePreviousInsertions = BeforePreviousInsertions; + return Hint; + } + + /// \brief Create a code modification hint that inserts the given + /// code from \arg FromRange at a specific location. + static FixItHint CreateInsertionFromRange(SourceLocation InsertionLoc, + CharSourceRange FromRange, + bool BeforePreviousInsertions = false) { + FixItHint Hint; + Hint.RemoveRange = + CharSourceRange(SourceRange(InsertionLoc, InsertionLoc), false); + Hint.InsertFromRange = FromRange; + Hint.BeforePreviousInsertions = BeforePreviousInsertions; return Hint; } diff --git a/include/clang/Driver/Action.h b/include/clang/Driver/Action.h index 41ce4d92c4..6e317a0726 100644 --- a/include/clang/Driver/Action.h +++ b/include/clang/Driver/Action.h @@ -39,6 +39,7 @@ public: PreprocessJobClass, PrecompileJobClass, AnalyzeJobClass, + MigrateJobClass, CompileJobClass, AssembleJobClass, LinkJobClass, @@ -171,6 +172,17 @@ public: static bool classof(const AnalyzeJobAction *) { return true; } }; +class MigrateJobAction : public JobAction { + virtual void anchor(); +public: + MigrateJobAction(Action *Input, types::ID OutputType); + + static bool classof(const Action *A) { + return A->getKind() == MigrateJobClass; + } + static bool classof(const MigrateJobAction *) { return true; } +}; + class CompileJobAction : public JobAction { virtual void anchor(); public: diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 93e63dee61..2fe4eba205 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -433,21 +433,28 @@ def rewrite_objc : Flag<"-rewrite-objc">, HelpText<"Rewrite ObjC into C (code rewriter example)">; def rewrite_macros : Flag<"-rewrite-macros">, HelpText<"Expand macros without full preprocessing">; +def migrate : Flag<"-migrate">, + HelpText<"Migrate source code">; } +def mt_migrate_directory : Separate<"-mt-migrate-directory">, + HelpText<"Directory for temporary files produced during ARC or ObjC migration">; def arcmt_check : Flag<"-arcmt-check">, HelpText<"Check for ARC migration issues that need manual handling">; def arcmt_modify : Flag<"-arcmt-modify">, HelpText<"Apply modifications to files to conform to ARC">; def arcmt_migrate : Flag<"-arcmt-migrate">, HelpText<"Apply modifications and produces temporary files that conform to ARC">; -def arcmt_migrate_directory : Separate<"-arcmt-migrate-directory">, - HelpText<"Directory for temporary files produced during ARC migration">; def arcmt_migrate_report_output : Separate<"-arcmt-migrate-report-output">, HelpText<"Output path for the plist report">; def arcmt_migrate_emit_arc_errors : Flag<"-arcmt-migrate-emit-errors">, HelpText<"Emit ARC errors even if the migrator can fix them">; +def objcmt_migrate_literals : Flag<"-objcmt-migrate-literals">, + HelpText<"Enable migration to modern ObjC literals">; +def objcmt_migrate_subscripting : Flag<"-objcmt-migrate-subscripting">, + HelpText<"Enable migration to modern ObjC subscripting">; + def working_directory : JoinedOrSeparate<"-working-directory">, HelpText<"Resolve file paths relative to the specified directory">; def working_directory_EQ : Joined<"-working-directory=">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index b269ddb045..51c5e020e1 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -118,13 +118,21 @@ def ccc_arrmt_check : Flag<"-ccc-arrmt-check">, Alias<ccc_arcmt_check>; def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, Alias<ccc_arcmt_modify>; def ccc_arcmt_migrate : Separate<"-ccc-arcmt-migrate">, CCCDriverOpt, HelpText<"Apply modifications and produces temporary files that conform to ARC">; -def ccc_arcmt_migrate_EQ : Joined<"-ccc-arcmt-migrate=">, CCCDriverOpt, - Alias<ccc_arcmt_migrate>; def arcmt_migrate_report_output : Separate<"-arcmt-migrate-report-output">, HelpText<"Output path for the plist report">; def arcmt_migrate_emit_arc_errors : Flag<"-arcmt-migrate-emit-errors">, HelpText<"Emit ARC errors even if the migrator can fix them">; +def _migrate : Flag<"--migrate">, Flags<[DriverOption]>, + HelpText<"Run the migrator">; +def ccc_objcmt_migrate : Separate<"-ccc-objcmt-migrate">, CCCDriverOpt, + HelpText<"Apply modifications and produces temporary files to migrate to " + "modern ObjC syntax">; +def objcmt_migrate_literals : Flag<"-objcmt-migrate-literals">, + HelpText<"Enable migration to modern ObjC literals">; +def objcmt_migrate_subscripting : Flag<"-objcmt-migrate-subscripting">, + HelpText<"Enable migration to modern ObjC subscripting">; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<"-ccc-">, Group<ccc_Group>, Flags<[Unsupported]>; diff --git a/include/clang/Driver/Types.def b/include/clang/Driver/Types.def index 8449d639e6..bba888ca4a 100644 --- a/include/clang/Driver/Types.def +++ b/include/clang/Driver/Types.def @@ -82,6 +82,7 @@ TYPE("lto-bc", LTO_BC, INVALID, "o", "") TYPE("ast", AST, INVALID, "ast", "u") TYPE("plist", Plist, INVALID, "plist", "") TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", "") +TYPE("remap", Remap, INVALID, "remap", "") TYPE("precompiled-header", PCH, INVALID, "gch", "A") TYPE("object", Object, INVALID, "o", "") TYPE("treelang", Treelang, INVALID, 0, "u") diff --git a/include/clang/Edit/Commit.h b/include/clang/Edit/Commit.h new file mode 100644 index 0000000000..aaf6b18384 --- /dev/null +++ b/include/clang/Edit/Commit.h @@ -0,0 +1,140 @@ +//===----- Commit.h - A unit of edits ---------------------------*- 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_EDIT_COMMIT_H +#define LLVM_CLANG_EDIT_COMMIT_H + +#include "clang/Edit/FileOffset.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + class LangOptions; + class PreprocessingRecord; + +namespace edit { + class EditedSource; + +class Commit { +public: + enum EditKind { + Act_Insert, + Act_InsertFromRange, + Act_Remove + }; + + struct Edit { + EditKind Kind; + StringRef Text; + SourceLocation OrigLoc; + FileOffset Offset; + FileOffset InsertFromRangeOffs; + unsigned Length; + bool BeforePrev; + + SourceLocation getFileLocation(SourceManager &SM) const; + CharSourceRange getFileRange(SourceManager &SM) const; + CharSourceRange getInsertFromRange(SourceManager &SM) const; + }; + +private: + const SourceManager &SourceMgr; + const LangOptions &LangOpts; + const PreprocessingRecord *PPRec; + EditedSource *Editor; + + bool IsCommitable; + SmallVector<Edit, 8> CachedEdits; + +public: + explicit Commit(EditedSource &Editor); + Commit(const SourceManager &SM, const LangOptions &LangOpts, + const PreprocessingRecord *PPRec = 0) + : SourceMgr(SM), LangOpts(LangOpts), PPRec(PPRec), Editor(0), + IsCommitable(true) { } + + bool isCommitable() const { return IsCommitable; } + + bool insert(SourceLocation loc, StringRef text, bool afterToken = false, + bool beforePreviousInsertions = false); + bool insertAfterToken(SourceLocation loc, StringRef text, + bool beforePreviousInsertions = false) { + return insert(loc, text, /*afterToken=*/true, beforePreviousInsertions); + } + bool insertBefore(SourceLocation loc, StringRef text) { + return insert(loc, text, /*afterToken=*/false, + /*beforePreviousInsertions=*/true); + } + bool insertFromRange(SourceLocation loc, CharSourceRange range, + bool afterToken = false, + bool beforePreviousInsertions = false); + bool insertWrap(StringRef before, CharSourceRange range, StringRef after); + + bool remove(CharSourceRange range); + + bool replace(CharSourceRange range, StringRef text); + bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange); + bool replaceText(SourceLocation loc, StringRef text, + StringRef replacementText); + + bool insertFromRange(SourceLocation loc, SourceRange TokenRange, + bool afterToken = false, + bool beforePreviousInsertions = false) { + return insertFromRange(loc, CharSourceRange::getTokenRange(TokenRange), + afterToken, beforePreviousInsertions); + } + bool insertWrap(StringRef before, SourceRange TokenRange, StringRef after) { + return insertWrap(before, CharSourceRange::getTokenRange(TokenRange), after); + } + bool remove(SourceRange TokenRange) { + return remove(CharSourceRange::getTokenRange(TokenRange)); + } + bool replace(SourceRange TokenRange, StringRef text) { + return replace(CharSourceRange::getTokenRange(TokenRange), text); + } + bool replaceWithInner(SourceRange TokenRange, SourceRange TokenInnerRange) { + return replaceWithInner(CharSourceRange::getTokenRange(TokenRange), + CharSourceRange::getTokenRange(TokenInnerRange)); + } + + typedef SmallVector<Edit, 8>::const_iterator edit_iterator; + edit_iterator edit_begin() const { return CachedEdits.begin(); } + edit_iterator edit_end() const { return CachedEdits.end(); } + +private: + void addInsert(SourceLocation OrigLoc, + FileOffset Offs, StringRef text, bool beforePreviousInsertions); + void addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs, + FileOffset RangeOffs, unsigned RangeLen, + bool beforePreviousInsertions); + void addRemove(SourceLocation OrigLoc, FileOffset Offs, unsigned Len); + + bool canInsert(SourceLocation loc, FileOffset &Offset); + bool canInsertAfterToken(SourceLocation loc, FileOffset &Offset, + SourceLocation &AfterLoc); + bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs); + bool canRemoveRange(CharSourceRange range, FileOffset &Offs, unsigned &Len); + bool canReplaceText(SourceLocation loc, StringRef text, + FileOffset &Offs, unsigned &Len); + + void commitInsert(FileOffset offset, StringRef text, + bool beforePreviousInsertions); + void commitRemove(FileOffset offset, unsigned length); + + bool isAtStartOfMacroExpansion(SourceLocation loc, + SourceLocation *MacroBegin = 0) const; + bool isAtEndOfMacroExpansion(SourceLocation loc, + SourceLocation *MacroEnd = 0) const; +}; + +} + +} // end namespace clang + +#endif diff --git a/include/clang/Edit/EditedSource.h b/include/clang/Edit/EditedSource.h new file mode 100644 index 0000000000..273921cee5 --- /dev/null +++ b/include/clang/Edit/EditedSource.h @@ -0,0 +1,87 @@ +//===----- EditedSource.h - Collection of source edits ----------*- 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_EDIT_EDITEDSOURCE_H +#define LLVM_CLANG_EDIT_EDITEDSOURCE_H + +#include "clang/Edit/FileOffset.h" +#include "llvm/Support/Allocator.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include <map> + +namespace clang { + class LangOptions; + class PreprocessingRecord; + +namespace edit { + class Commit; + class EditsReceiver; + +class EditedSource { + const SourceManager &SourceMgr; + const LangOptions &LangOpts; + const PreprocessingRecord *PPRec; + + struct FileEdit { + StringRef Text; + unsigned RemoveLen; + + FileEdit() : RemoveLen(0) {} + }; + + typedef std::map<FileOffset, FileEdit> FileEditsTy; + FileEditsTy FileEdits; + + llvm::DenseMap<unsigned, SourceLocation> ExpansionToArgMap; + + llvm::BumpPtrAllocator StrAlloc; + +public: + EditedSource(const SourceManager &SM, const LangOptions &LangOpts, + const PreprocessingRecord *PPRec = 0) + : SourceMgr(SM), LangOpts(LangOpts), PPRec(PPRec), + StrAlloc(/*size=*/512) { } + + const SourceManager &getSourceManager() const { return SourceMgr; } + const LangOptions &getLangOptions() const { return LangOpts; } + const PreprocessingRecord *getPreprocessingRecord() const { return PPRec; } + + bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs); + + bool commit(const Commit &commit); + + void applyRewrites(EditsReceiver &receiver); + void clearRewrites(); + + StringRef copyString(StringRef str) { + char *buf = StrAlloc.Allocate<char>(str.size()); + std::uninitialized_copy(str.begin(), str.end(), buf); + return StringRef(buf, str.size()); + } + StringRef copyString(const Twine &twine); + +private: + bool commitInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text, + bool beforePreviousInsertions); + bool commitInsertFromRange(SourceLocation OrigLoc, FileOffset Offs, + FileOffset InsertFromRangeOffs, unsigned Len, + bool beforePreviousInsertions); + void commitRemove(SourceLocation OrigLoc, FileOffset BeginOffs, unsigned Len); + + StringRef getSourceText(FileOffset BeginOffs, FileOffset EndOffs, + bool &Invalid); + FileEditsTy::iterator getActionForOffset(FileOffset Offs); +}; + +} + +} // end namespace clang + +#endif diff --git a/include/clang/Edit/EditsReceiver.h b/include/clang/Edit/EditsReceiver.h new file mode 100644 index 0000000000..600ac28ea9 --- /dev/null +++ b/include/clang/Edit/EditsReceiver.h @@ -0,0 +1,35 @@ +//===----- EditedSource.h - Collection of source edits ----------*- 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_EDIT_EDITSRECEIVER_H +#define LLVM_CLANG_EDIT_EDITSRECEIVER_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + class SourceLocation; + class CharSourceRange; + +namespace edit { + +class EditsReceiver { +public: + virtual ~EditsReceiver() { } + + virtual void insert(SourceLocation loc, StringRef text) = 0; + virtual void replace(CharSourceRange range, StringRef text) = 0; + /// \brief By default it calls replace with an empty string. + virtual void remove(CharSourceRange range); +}; + +} + +} // end namespace clang + +#endif diff --git a/include/clang/Edit/FileOffset.h b/include/clang/Edit/FileOffset.h new file mode 100644 index 0000000000..675ad18fcd --- /dev/null +++ b/include/clang/Edit/FileOffset.h @@ -0,0 +1,65 @@ +//===----- FileOffset.h - Offset in a file ----------------------*- 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_EDIT_FILEOFFSET_H +#define LLVM_CLANG_EDIT_FILEOFFSET_H + +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +namespace edit { + +class FileOffset { + FileID FID; + unsigned Offs; +public: + FileOffset() : Offs(0) { } + FileOffset(FileID fid, unsigned offs) : FID(fid), Offs(offs) { } + + bool isInvalid() const { return FID.isInvalid(); } + + FileID getFID() const { return FID; } + unsigned getOffset() const { return Offs; } + + FileOffset getWithOffset(unsigned offset) const { + FileOffset NewOffs = *this; + NewOffs.Offs += offset; + return NewOffs; + } + + friend bool operator==(FileOffset LHS, FileOffset RHS) { + return LHS.FID == RHS.FID && LHS.Offs == RHS.Offs; + } + friend bool operator!=(FileOffset LHS, FileOffset RHS) { + return !(LHS == RHS); + } + friend bool operator<(FileOffset LHS, FileOffset RHS) { + if (LHS.FID != RHS.FID) + return LHS.FID < RHS.FID; + return LHS.Offs < RHS.Offs; + } + friend bool operator>(FileOffset LHS, FileOffset RHS) { + if (LHS.FID != RHS.FID) + return LHS.FID > RHS.FID; + return LHS.Offs > RHS.Offs; + } + friend bool operator>=(FileOffset LHS, FileOffset RHS) { + return LHS > RHS || LHS == RHS; + } + friend bool operator<=(FileOffset LHS, FileOffset RHS) { + return LHS < RHS || LHS == RHS; + } +}; + +} + +} // end namespace clang + +#endif diff --git a/include/clang/Edit/Rewriters.h b/include/clang/Edit/Rewriters.h new file mode 100644 index 0000000000..aa7a5b2320 --- /dev/null +++ b/include/clang/Edit/Rewriters.h @@ -0,0 +1,33 @@ +//===--- Rewriters.h - Rewritings ---------------------------*- 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_EDIT_REWRITERS_H +#define LLVM_CLANG_EDIT_REWRITERS_H + +namespace clang { + class ObjCMessageExpr; + class NSAPI; + +namespace edit { + class Commit; + +bool rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); + +bool rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); + +bool rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); + +} + +} // end namespace clang + +#endif diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index f18fdebba7..0ec2f6b1ef 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -46,6 +46,7 @@ namespace frontend { RewriteObjC, ///< ObjC->C Rewriter. RewriteTest, ///< Rewriter playground RunAnalysis, ///< Run one or more source code analyses. + MigrateSource, ///< Run migrator. RunPreprocessorOnly ///< Just lex, no output. }; } @@ -118,7 +119,16 @@ public: ARCMT_Migrate } ARCMTAction; - std::string ARCMTMigrateDir; + enum { + ObjCMT_None = 0, + /// \brief Enable migration to modern ObjC literals. + ObjCMT_Literals = 0x1, + /// \brief Enable migration to modern ObjC subscripting. + ObjCMT_Subscripting = 0x2 + }; + unsigned ObjCMTAction; + + std::string MTMigrateDir; std::string ARCMTMigrateReportOut; /// The input files and their types. @@ -177,6 +187,7 @@ public: ShowVersion = 0; ARCMTAction = ARCMT_None; ARCMTMigrateEmitARCErrors = 0; + ObjCMTAction = ObjCMT_None; } /// getInputKindForExtension - Return the appropriate input kind for a file diff --git a/include/clang/Rewrite/FixItRewriter.h b/include/clang/Rewrite/FixItRewriter.h index 0ebd62c560..44f0611b17 100644 --- a/include/clang/Rewrite/FixItRewriter.h +++ b/include/clang/Rewrite/FixItRewriter.h @@ -18,6 +18,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Rewrite/Rewriter.h" +#include "clang/Edit/EditedSource.h" namespace clang { @@ -56,6 +57,8 @@ class FixItRewriter : public DiagnosticConsumer { /// \brief The diagnostics machinery. DiagnosticsEngine &Diags; + edit::EditedSource Editor; + /// \brief The rewriter used to perform the various code /// modifications. Rewriter Rewrite; 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)) { + IsCommitable = false; + return false; + } + + addRemove(range.getBegin(), Offs, Len); + return true; +} + +bool Commit::insertWrap(StringRef before, CharSourceRange range, + StringRef after) { + bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false, + /*beforePreviousInsertions=*/true); + bool commitableAfter; + if (range.isTokenRange()) + commitableAfter = insertAfterToken(range.getEnd(), after); + else + commitableAfter = insert(range.getEnd(), after); + + return commitableBefore && commitableAfter; +} + +bool Commit::replace(CharSourceRange range, StringRef text) { + if (text.empty()) + return remove(range); + + FileOffset Offs; + unsigned Len; + if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) { + IsCommitable = false; + return false; + } + + addRemove(range.getBegin(), Offs, Len); + addInsert(range.getBegin(), Offs, text, false); + return true; +} + +bool Commit::replaceWithInner(CharSourceRange range, + CharSourceRange replacementRange) { + FileOffset OuterBegin; + unsigned OuterLen; + if (!canRemoveRange(range, OuterBegin, OuterLen)) { + IsCommitable = false; + return false; + } + + FileOffset InnerBegin; + unsigned InnerLen; + if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) { + IsCommitable = false; + return false; + } + + FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen); + FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen); + if (OuterBegin.getFID() != InnerBegin.getFID() || + InnerBegin < OuterBegin || + InnerBegin > OuterEnd || + InnerEnd > OuterEnd) { + IsCommitable = false; + return false; + } + + addRemove(range.getBegin(), + OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset()); + addRemove(replacementRange.getEnd(), + InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset()); + return true; +} + +bool Commit::replaceText(SourceLocation loc, StringRef text, + StringRef replacementText) { + if (text.empty() || replacementText.empty()) + return true; + + FileOffset Offs; + unsigned Len; + if (!canReplaceText(loc, replacementText, Offs, Len)) { + IsCommitable = false; + return false; + } + + addRemove(loc, Offs, Len); + addInsert(loc, Offs, text, false); + return true; +} + +void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text, + bool beforePreviousInsertions) { + if (text.empty()) + return; + + Edit data; + data.Kind = Act_Insert; + data.OrigLoc = OrigLoc; + data.Offset = Offs; + data.Text = text; + data.BeforePrev = beforePreviousInsertions; + CachedEdits.push_back(data); +} + +void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs, + FileOffset RangeOffs, unsigned RangeLen, + bool beforePreviousInsertions) { + if (RangeLen == 0) + return; + + Edit data; + data.Kind = Act_InsertFromRange; + data.OrigLoc = OrigLoc; + data.Offset = Offs; + data.InsertFromRangeOffs = RangeOffs; + data.Length = RangeLen; + data.BeforePrev = beforePreviousInsertions; + CachedEdits.push_back(data); +} + +void Commit::addRemove(SourceLocation OrigLoc, + FileOffset Offs, unsigned Len) { + if (Len == 0) + return; + + Edit data; + data.Kind = Act_Remove; + data.OrigLoc = OrigLoc; + data.Offset = Offs; + data.Length = Len; + CachedEdits.push_back(data); +} + +bool Commit::canInsert(SourceLocation loc, FileOffset &offs) { + if (loc.isInvalid()) + return false; + + if (loc.isMacroID()) + isAtStartOfMacroExpansion(loc, &loc); + + const SourceManager &SM = SourceMgr; + while (SM.isMacroArgExpansion(loc)) + loc = SM.getImmediateSpellingLoc(loc); + + if (loc.isMacroID()) + if (!isAtStartOfMacroExpansion(loc, &loc)) + return false; + + if (SM.isInSystemHeader(loc)) + return false; + + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); + if (locInfo.first.isInvalid()) + return false; + offs = FileOffset(locInfo.first, locInfo.second); + return canInsertInOffset(loc, offs); +} + +bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs, + SourceLocation &AfterLoc) { + if (loc.isInvalid()) + + return false; + + SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc); + unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts); + AfterLoc = loc.getLocWithOffset(tokLen); + + if (loc.isMacroID()) + isAtEndOfMacroExpansion(loc, &loc); + + const SourceManager &SM = SourceMgr; + while (SM.isMacroArgExpansion(loc)) + loc = SM.getImmediateSpellingLoc(loc); + + if (loc.isMacroID()) + if (!isAtEndOfMacroExpansion(loc, &loc)) + return false; + + if (SM.isInSystemHeader(loc)) + return false; + + loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts); + if (loc.isInvalid()) + return false; + + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); + if (locInfo.first.isInvalid()) + return false; + offs = FileOffset(locInfo.first, locInfo.second); + return canInsertInOffset(loc, offs); +} + +bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { + for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) { + Edit &act = CachedEdits[i]; + if (act.Kind == Act_Remove) { + if (act.Offset.getFID() == Offs.getFID() && + Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length)) + return false; // position has been removed. + } + } + + if (!Editor) + return true; + return Editor->canInsertInOffset(OrigLoc, Offs); +} + +bool Commit::canRemoveRange(CharSourceRange range, + FileOffset &Offs, unsigned &Len) { + const SourceManager &SM = SourceMgr; + range = Lexer::makeFileCharRange(range, SM, LangOpts); + if (range.isInvalid()) + return false; + + if (range.getBegin().isMacroID() || range.getEnd().isMacroID()) + return false; + if (SM.isInSystemHeader(range.getBegin()) || + SM.isInSystemHeader(range.getEnd())) + return false; + + if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange())) + return false; + + std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin()); + std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd()); + if (beginInfo.first != endInfo.first || + beginInfo.second > endInfo.second) + return false; + + Offs = FileOffset(beginInfo.first, beginInfo.second); + Len = endInfo.second - beginInfo.second; + return true; +} + +bool Commit::canReplaceText(SourceLocation loc, StringRef text, + FileOffset &Offs, unsigned &Len) { + assert(!text.empty()); + + if (!canInsert(loc, Offs)) + return false; + + // Try to load the file buffer. + bool invalidTemp = false; + StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp); + if (invalidTemp) + return false; + + return file.substr(Offs.getOffset()).startswith(text); +} + +bool Commit::isAtStartOfMacroExpansion(SourceLocation loc, + SourceLocation *MacroBegin) const { + return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin); +} +bool Commit::isAtEndOfMacroExpansion(SourceLocation loc, + SourceLocation *MacroEnd) const { + return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd); +} diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp new file mode 100644 index 0000000000..5b7fa4ad1b --- /dev/null +++ b/lib/Edit/EditedSource.cpp @@ -0,0 +1,329 @@ +//===----- EditedSource.cpp - Collection of source 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/EditedSource.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" + +using namespace clang; +using namespace edit; + +void EditsReceiver::remove(CharSourceRange range) { + replace(range, StringRef()); +} + +StringRef EditedSource::copyString(const Twine &twine) { + llvm::SmallString<128> Data; + return copyString(twine.toStringRef(Data)); +} + +bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { + FileEditsTy::iterator FA = getActionForOffset(Offs); + if (FA != FileEdits.end()) { + if (FA->first != Offs) + return false; // position has been removed. + } + + if (SourceMgr.isMacroArgExpansion(OrigLoc)) { + SourceLocation + DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; + SourceLocation + ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; + llvm::DenseMap<unsigned, SourceLocation>::iterator + I = ExpansionToArgMap.find(ExpLoc.getRawEncoding()); + if (I != ExpansionToArgMap.end() && I->second != DefArgLoc) + return false; // Trying to write in a macro argument input that has + // already been written for another argument of the same macro. + } + + return true; +} + +bool EditedSource::commitInsert(SourceLocation OrigLoc, + FileOffset Offs, StringRef text, + bool beforePreviousInsertions) { + if (!canInsertInOffset(OrigLoc, Offs)) + return false; + if (text.empty()) + return true; + + if (SourceMgr.isMacroArgExpansion(OrigLoc)) { + SourceLocation + DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; + SourceLocation + ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; + ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc; + } + + FileEdit &FA = FileEdits[Offs]; + if (FA.Text.empty()) { + FA.Text = copyString(text); + return true; + } + + Twine concat; + if (beforePreviousInsertions) + concat = Twine(text) + FA.Text; + else + concat = Twine(FA.Text) + text; + + FA.Text = copyString(concat); + return true; +} + +bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc, + FileOffset Offs, + FileOffset InsertFromRangeOffs, unsigned Len, + bool beforePreviousInsertions) { + if (Len == 0) + return true; + + llvm::SmallString<128> StrVec; + FileOffset BeginOffs = InsertFromRangeOffs; + FileOffset EndOffs = BeginOffs.getWithOffset(Len); + FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); + if (I != FileEdits.begin()) + --I; + + for (; I != FileEdits.end(); ++I) { + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + + if (BeginOffs < E) { + if (BeginOffs >= B) { + BeginOffs = E; + ++I; + } + break; + } + } + + for (; I != FileEdits.end() && EndOffs > I->first; ++I) { + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + + if (BeginOffs < B) { + bool Invalid = false; + StringRef text = getSourceText(BeginOffs, B, Invalid); + if (Invalid) + return false; + StrVec += text; + } + StrVec += FA.Text; + BeginOffs = E; + } + + if (BeginOffs < EndOffs) { + bool Invalid = false; + StringRef text = getSourceText(BeginOffs, EndOffs, Invalid); + if (Invalid) + return false; + StrVec += text; + } + + return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions); +} + +void EditedSource::commitRemove(SourceLocation OrigLoc, + FileOffset BeginOffs, unsigned Len) { + if (Len == 0) + return; + + FileOffset EndOffs = BeginOffs.getWithOffset(Len); + FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); + if (I != FileEdits.begin()) + --I; + + for (; I != FileEdits.end(); ++I) { + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + + if (BeginOffs < E) + break; + } + + FileOffset TopBegin, TopEnd; + FileEdit *TopFA = 0; + + if (I == FileEdits.end()) { + FileEditsTy::iterator + NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); + NewI->second.RemoveLen = Len; + return; + } + + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + if (BeginOffs < B) { + FileEditsTy::iterator + NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); + TopBegin = BeginOffs; + TopEnd = EndOffs; + TopFA = &NewI->second; + TopFA->RemoveLen = Len; + } else { + TopBegin = B; + TopEnd = E; + TopFA = &I->second; + if (TopEnd >= EndOffs) + return; + unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); + TopEnd = EndOffs; + TopFA->RemoveLen += diff; + ++I; + } + + while (I != FileEdits.end()) { + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + + if (B >= TopEnd) + break; + + if (E <= TopEnd) { + FileEdits.erase(I++); + continue; + } + + if (B < TopEnd) { + unsigned diff = E.getOffset() - TopEnd.getOffset(); + TopEnd = E; + TopFA->RemoveLen += diff; + FileEdits.erase(I); + } + + break; + } +} + +bool EditedSource::commit(const Commit &commit) { + if (!commit.isCommitable()) + return false; + + for (edit::Commit::edit_iterator + I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) { + const edit::Commit::Edit &edit = *I; + switch (edit.Kind) { + case edit::Commit::Act_Insert: + commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev); + break; + case edit::Commit::Act_InsertFromRange: + commitInsertFromRange(edit.OrigLoc, edit.Offset, + edit.InsertFromRangeOffs, edit.Length, + edit.BeforePrev); + break; + case edit::Commit::Act_Remove: + commitRemove(edit.OrigLoc, edit.Offset, edit.Length); + break; + } + } + + return true; +} + +static void applyRewrite(EditsReceiver &receiver, + StringRef text, FileOffset offs, unsigned len, + const SourceManager &SM) { + assert(!offs.getFID().isInvalid()); + SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID()); + Loc = Loc.getLocWithOffset(offs.getOffset()); + assert(Loc.isFileID()); + CharSourceRange range = CharSourceRange::getCharRange(Loc, + Loc.getLocWithOffset(len)); + + if (text.empty()) { + assert(len); + receiver.remove(range); + return; + } + + if (len) + receiver.replace(range, text); + else + receiver.insert(Loc, text); +} + +void EditedSource::applyRewrites(EditsReceiver &receiver) { + llvm::SmallString<128> StrVec; + FileOffset CurOffs, CurEnd; + unsigned CurLen; + + if (FileEdits.empty()) + return; + + FileEditsTy::iterator I = FileEdits.begin(); + CurOffs = I->first; + StrVec = I->second.Text; + CurLen = I->second.RemoveLen; + CurEnd = CurOffs.getWithOffset(CurLen); + ++I; + + for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) { + FileOffset offs = I->first; + FileEdit act = I->second; + assert(offs >= CurEnd); + + if (offs == CurEnd) { + StrVec += act.Text; + CurLen += act.RemoveLen; + CurEnd.getWithOffset(act.RemoveLen); + continue; + } + + applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); + CurOffs = offs; + StrVec = act.Text; + CurLen = act.RemoveLen; + CurEnd = CurOffs.getWithOffset(CurLen); + } + + applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); +} + +void EditedSource::clearRewrites() { + FileEdits.clear(); + StrAlloc.Reset(); +} + +StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs, + bool &Invalid) { + assert(BeginOffs.getFID() == EndOffs.getFID()); + assert(BeginOffs <= EndOffs); + SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID()); + BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset()); + assert(BLoc.isFileID()); + SourceLocation + ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset()); + return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc), + SourceMgr, LangOpts, &Invalid); +} + +EditedSource::FileEditsTy::iterator +EditedSource::getActionForOffset(FileOffset Offs) { + FileEditsTy::iterator I = FileEdits.upper_bound(Offs); + if (I == FileEdits.begin()) + return FileEdits.end(); + --I; + FileEdit &FA = I->second; + FileOffset B = I->first; + FileOffset E = B.getWithOffset(FA.RemoveLen); + if (Offs >= B && Offs < E) + return I; + + return FileEdits.end(); +} diff --git a/lib/Edit/Makefile b/lib/Edit/Makefile new file mode 100644 index 0000000000..92a67ebc82 --- /dev/null +++ b/lib/Edit/Makefile @@ -0,0 +1,14 @@ +##===- clang/lib/Edit/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangEdit + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp new file mode 100644 index 0000000000..a092768034 --- /dev/null +++ b/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -0,0 +1,589 @@ +//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Rewrites legacy method calls to modern syntax. +// +//===----------------------------------------------------------------------===// + +#include "clang/Edit/Rewriters.h" +#include "clang/Edit/Commit.h" +#include "clang/Lex/Lexer.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/NSAPI.h" + +using namespace clang; +using namespace edit; + +static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, + IdentifierInfo *&ClassId) { + if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) + return false; + + const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); + if (!Receiver) + return false; + ClassId = Receiver->getIdentifier(); + + if (Msg->getReceiverKind() == ObjCMessageExpr::Class) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteObjCRedundantCallWithLiteral. +//===----------------------------------------------------------------------===// + +bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + IdentifierInfo *II = 0; + if (!checkForLiteralCreation(Msg, II)) + return false; + if (Msg->getNumArgs() != 1) + return false; + + const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); + Selector Sel = Msg->getSelector(); + + if ((isa<ObjCStringLiteral>(Arg) && + NS.getNSClassId(NSAPI::ClassId_NSString) == II && + NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) || + + (isa<ObjCArrayLiteral>(Arg) && + NS.getNSClassId(NSAPI::ClassId_NSArray) == II && + NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) || + + (isa<ObjCDictionaryLiteral>(Arg) && + NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && + NS.getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithDictionary) == Sel)) { + + commit.replaceWithInner(Msg->getSourceRange(), + Msg->getArg(0)->getSourceRange()); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToObjCSubscriptSyntax. +//===----------------------------------------------------------------------===// + +static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { + Receiver = Receiver->IgnoreImpCasts(); + if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) { + SourceRange RecRange = Receiver->getSourceRange(); + commit.insertWrap("(", RecRange, ")"); + } +} + +static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) { + if (Msg->getNumArgs() != 1) + return false; + const Expr *Rec = Msg->getInstanceReceiver(); + if (!Rec) + return false; + + SourceRange MsgRange = Msg->getSourceRange(); + SourceRange RecRange = Rec->getSourceRange(); + SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); + + commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), + ArgRange.getBegin()), + CharSourceRange::getTokenRange(RecRange)); + commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), + ArgRange); + commit.insertWrap("[", ArgRange, "]"); + maybePutParensOnReceiver(Rec, commit); + return true; +} + +static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg, + Commit &commit) { + if (Msg->getNumArgs() != 2) + return false; + const Expr *Rec = Msg->getInstanceReceiver(); + if (!Rec) + return false; + + SourceRange MsgRange = Msg->getSourceRange(); + SourceRange RecRange = Rec->getSourceRange(); + SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); + SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); + + commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), + Arg0Range.getBegin()), + CharSourceRange::getTokenRange(RecRange)); + commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), + Arg1Range.getBegin()), + CharSourceRange::getTokenRange(Arg0Range)); + commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), + Arg1Range); + commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), + Arg1Range.getBegin()), + "] = "); + maybePutParensOnReceiver(Rec, commit); + return true; +} + +static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg, + Commit &commit) { + if (Msg->getNumArgs() != 2) + return false; + const Expr *Rec = Msg->getInstanceReceiver(); + if (!Rec) + return false; + + SourceRange MsgRange = Msg->getSourceRange(); + SourceRange RecRange = Rec->getSourceRange(); + SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); + SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); + + SourceLocation LocBeforeVal = Arg0Range.getBegin(); + commit.insertBefore(LocBeforeVal, "] = "); + commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, + /*beforePreviousInsertions=*/true); + commit.insertBefore(LocBeforeVal, "["); + commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), + Arg0Range.getBegin()), + CharSourceRange::getTokenRange(RecRange)); + commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), + Arg0Range); + maybePutParensOnReceiver(Rec, commit); + return true; +} + +bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + if (!Msg || Msg->isImplicit() || + Msg->getReceiverKind() != ObjCMessageExpr::Instance) + return false; + const ObjCMethodDecl *Method = Msg->getMethodDecl(); + if (!Method) + return false; + + const ObjCInterfaceDecl * + IFace = NS.getASTContext().getObjContainingInterface( + const_cast<ObjCMethodDecl *>(Method)); + if (!IFace) + return false; + IdentifierInfo *II = IFace->getIdentifier(); + Selector Sel = Msg->getSelector(); + + if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) && + Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) || + (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) && + Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))) + return rewriteToSubscriptGet(Msg, commit); + + if (Msg->getNumArgs() != 2) + return false; + + if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) && + Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) + return rewriteToArraySubscriptSet(Msg, commit); + + if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) && + Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) + return rewriteToDictionarySubscriptSet(Msg, commit); + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToObjCLiteralSyntax. +//===----------------------------------------------------------------------===// + +static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); +static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); +static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); + +bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + IdentifierInfo *II = 0; + if (!checkForLiteralCreation(Msg, II)) + return false; + + if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) + return rewriteToArrayLiteral(Msg, NS, commit); + if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) + return rewriteToDictionaryLiteral(Msg, NS, commit); + if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) + return rewriteToNumberLiteral(Msg, NS, commit); + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToArrayLiteral. +//===----------------------------------------------------------------------===// + +static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + Selector Sel = Msg->getSelector(); + SourceRange MsgRange = Msg->getSourceRange(); + + if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { + if (Msg->getNumArgs() != 0) + return false; + commit.replace(MsgRange, "@[]"); + return true; + } + + if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { + if (Msg->getNumArgs() != 1) + return false; + SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); + commit.replaceWithInner(MsgRange, ArgRange); + commit.insertWrap("@[", ArgRange, "]"); + return true; + } + + if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) { + if (Msg->getNumArgs() == 0) + return false; + const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); + if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) + return false; + + if (Msg->getNumArgs() == 1) { + commit.replace(MsgRange, "@[]"); + return true; + } + SourceRange ArgRange(Msg->getArg(0)->getLocStart(), + Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); + commit.replaceWithInner(MsgRange, ArgRange); + commit.insertWrap("@[", ArgRange, "]"); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToDictionaryLiteral. +//===----------------------------------------------------------------------===// + +static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + Selector Sel = Msg->getSelector(); + SourceRange MsgRange = Msg->getSourceRange(); + + if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { + if (Msg->getNumArgs() != 0) + return false; + commit.replace(MsgRange, "@{}"); + return true; + } + + if (Sel == NS.getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithObjectForKey)) { + if (Msg->getNumArgs() != 2) + return false; + SourceRange ValRange = Msg->getArg(0)->getSourceRange(); + SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); + // Insert key before the value. + commit.insertBefore(ValRange.getBegin(), ": "); + commit.insertFromRange(ValRange.getBegin(), + CharSourceRange::getTokenRange(KeyRange), + /*afterToken=*/false, /*beforePreviousInsertions=*/true); + commit.insertBefore(ValRange.getBegin(), "@{"); + commit.insertAfterToken(ValRange.getEnd(), "}"); + commit.replaceWithInner(MsgRange, ValRange); + return true; + } + + if (Sel == NS.getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithObjectsAndKeys)) { + if (Msg->getNumArgs() % 2 != 1) + return false; + unsigned SentinelIdx = Msg->getNumArgs() - 1; + const Expr *SentinelExpr = Msg->getArg(SentinelIdx); + if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) + return false; + + if (Msg->getNumArgs() == 1) { + commit.replace(MsgRange, "@{}"); + return true; + } + + for (unsigned i = 0; i < SentinelIdx; i += 2) { + SourceRange ValRange = Msg->getArg(i)->getSourceRange(); + SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); + // Insert value after key. + commit.insertAfterToken(KeyRange.getEnd(), ": "); + commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); + commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), + KeyRange.getBegin())); + } + // Range of arguments up until and including the last key. + // The sentinel and first value are cut off, the value will move after the + // key. + SourceRange ArgRange(Msg->getArg(1)->getLocStart(), + Msg->getArg(SentinelIdx-1)->getLocEnd()); + commit.insertWrap("@{", ArgRange, "}"); + commit.replaceWithInner(MsgRange, ArgRange); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToNumberLiteral. +//===----------------------------------------------------------------------===// + +static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, + const CharacterLiteral *Arg, + const NSAPI &NS, Commit &commit) { + if (Arg->getKind() != CharacterLiteral::Ascii) + return false; + if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, + Msg->getSelector())) { + SourceRange ArgRange = Arg->getSourceRange(); + commit.replaceWithInner(Msg->getSourceRange(), ArgRange); + commit.insert(ArgRange.getBegin(), "@"); + return true; + } + + return false; +} + +static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, + const Expr *Arg, + const NSAPI &NS, Commit &commit) { + if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, + Msg->getSelector())) { + SourceRange ArgRange = Arg->getSourceRange(); + commit.replaceWithInner(Msg->getSourceRange(), ArgRange); + commit.insert(ArgRange.getBegin(), "@"); + return true; + } + + return false; +} + +namespace { + +struct LiteralInfo { + bool Hex, Octal; + StringRef U, F, L, LL; + CharSourceRange WithoutSuffRange; +}; + +} + +static bool getLiteralInfo(SourceRange literalRange, + bool isFloat, bool isIntZero, + ASTContext &Ctx, LiteralInfo &Info) { + if (literalRange.getBegin().isMacroID() || + literalRange.getEnd().isMacroID()) + return false; + StringRef text = Lexer::getSourceText( + CharSourceRange::getTokenRange(literalRange), + Ctx.getSourceManager(), Ctx.getLangOptions()); + if (text.empty()) + return false; + + llvm::Optional<bool> UpperU, UpperL; + bool UpperF = false; + + struct Suff { + static bool has(StringRef suff, StringRef &text) { + if (text.endswith(suff)) { + text = text.substr(0, text.size()-suff.size()); + return true; + } + return false; + } + }; + + while (1) { + if (Suff::has("u", text)) { + UpperU = false; + } else if (Suff::has("U", text)) { + UpperU = true; + } else if (Suff::has("ll", text)) { + UpperL = false; + } else if (Suff::has("LL", text)) { + UpperL = true; + } else if (Suff::has("l", text)) { + UpperL = false; + } else if (Suff::has("L", text)) { + UpperL = true; + } else if (isFloat && Suff::has("f", text)) { + UpperF = false; + } else if (isFloat && Suff::has("F", text)) { + UpperF = true; + } else + break; + } + + if (!UpperU.hasValue() && !UpperL.hasValue()) + UpperU = UpperL = true; + else if (UpperU.hasValue() && !UpperL.hasValue()) + UpperL = UpperU; + else if (UpperL.hasValue() && !UpperU.hasValue()) + UpperU = UpperL; + + Info.U = *UpperU ? "U" : "u"; + Info.L = *UpperL ? "L" : "l"; + Info.LL = *UpperL ? "LL" : "ll"; + Info.F = UpperF ? "F" : "f"; + + Info.Hex = Info.Octal = false; + if (text.startswith("0x")) + Info.Hex = true; + else if (!isFloat && !isIntZero && text.startswith("0")) + Info.Octal = true; + + SourceLocation B = literalRange.getBegin(); + Info.WithoutSuffRange = + CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); + return true; +} + +static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + if (Msg->getNumArgs() != 1) + return false; + + const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); + if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) + return rewriteToCharLiteral(Msg, CharE, NS, commit); + if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) + return rewriteToBoolLiteral(Msg, BE, NS, commit); + if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) + return rewriteToBoolLiteral(Msg, BE, NS, commit); + + const Expr *literalE = Arg; + if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { + if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) + literalE = UOE->getSubExpr(); + } + + // Only integer and floating literals; non-literals or imaginary literal + // cannot be rewritten. + if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) + return false; + + ASTContext &Ctx = NS.getASTContext(); + Selector Sel = Msg->getSelector(); + llvm::Optional<NSAPI::NSNumberLiteralMethodKind> + MKOpt = NS.getNSNumberLiteralMethodKind(Sel); + if (!MKOpt) + return false; + NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; + + bool CallIsInteger = false, CallIsUnsigned = false; + bool CallIsLong = false, CallIsLongLong = false; + bool CallIsFloating = false, CallIsDouble = false; + + switch (MK) { + // We cannot have these calls with int/float literals. + case NSAPI::NSNumberWithChar: + case NSAPI::NSNumberWithUnsignedChar: + case NSAPI::NSNumberWithShort: + case NSAPI::NSNumberWithUnsignedShort: + case NSAPI::NSNumberWithBool: + return false; + + case NSAPI::NSNumberWithUnsignedInt: + case NSAPI::NSNumberWithUnsignedInteger: + CallIsUnsigned = true; + case NSAPI::NSNumberWithInt: + case NSAPI::NSNumberWithInteger: + CallIsInteger = true; + break; + + case NSAPI::NSNumberWithUnsignedLong: + CallIsUnsigned = true; + case NSAPI::NSNumberWithLong: + CallIsInteger = true; CallIsLong = true; + break; + + case NSAPI::NSNumberWithUnsignedLongLong: + CallIsUnsigned = true; + case NSAPI::NSNumberWithLongLong: + CallIsInteger = true; CallIsLongLong = true; + break; + + case NSAPI::NSNumberWithDouble: + CallIsDouble = true; + case NSAPI::NSNumberWithFloat: + CallIsFloating = true; + break; + } + + SourceRange ArgRange = Arg->getSourceRange(); + QualType ArgTy = Arg->getType(); + QualType CallTy = Msg->getArg(0)->getType(); + + // Check for the easy case, the literal maps directly to the call. + if (Ctx.hasSameType(ArgTy, CallTy)) { + commit.replaceWithInner(Msg->getSourceRange(), ArgRange); + commit.insert(ArgRange.getBegin(), "@"); + return true; + } + + // We will need to modify the literal suffix to get the same type as the call. + // Don't even try if it came from a macro. + if (ArgRange.getBegin().isMacroID()) + return false; + + bool LitIsFloat = ArgTy->isFloatingType(); + // For a float passed to integer call, don't try rewriting. It is difficult + // and a very uncommon case anyway. + if (LitIsFloat && !CallIsFloating) + return false; + + // Try to modify the literal make it the same type as the method call. + // -Modify the suffix, and/or + // -Change integer to float + + LiteralInfo LitInfo; + bool isIntZero = false; + if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) + isIntZero = !IntE->getValue().getBoolValue(); + if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) + return false; + + // Not easy to do int -> float with hex/octal and uncommon anyway. + if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) + return false; + + SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); + SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); + + commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), + LitInfo.WithoutSuffRange); + commit.insert(LitB, "@"); + + if (!LitIsFloat && CallIsFloating) + commit.insert(LitE, ".0"); + + if (CallIsFloating) { + if (!CallIsDouble) + commit.insert(LitE, LitInfo.F); + } else { + if (CallIsUnsigned) + commit.insert(LitE, LitInfo.U); + + if (CallIsLong) + commit.insert(LitE, LitInfo.L); + else if (CallIsLongLong) + commit.insert(LitE, LitInfo.LL); + } + return true; +} diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 43ac475211..9391eea32d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -428,6 +428,7 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::RewriteObjC: return "-rewrite-objc"; case frontend::RewriteTest: return "-rewrite-test"; case frontend::RunAnalysis: return "-analyze"; + case frontend::MigrateSource: return "-migrate"; case frontend::RunPreprocessorOnly: return "-Eonly"; } @@ -483,9 +484,9 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, Res.push_back("-arcmt-migrate"); break; } - if (!Opts.ARCMTMigrateDir.empty()) { - Res.push_back("-arcmt-migrate-directory"); - Res.push_back(Opts.ARCMTMigrateDir); + if (!Opts.MTMigrateDir.empty()) { + Res.push_back("-mt-migrate-directory"); + Res.push_back(Opts.MTMigrateDir); } if (!Opts.ARCMTMigrateReportOut.empty()) { Res.push_back("-arcmt-migrate-report-output"); @@ -494,6 +495,11 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, if (Opts.ARCMTMigrateEmitARCErrors) Res.push_back("-arcmt-migrate-emit-errors"); + if (Opts.ObjCMTAction & ~FrontendOptions::ObjCMT_Literals) + Res.push_back("-objcmt-migrate-literals"); + if (Opts.ObjCMTAction & ~FrontendOptions::ObjCMT_Subscripting) + Res.push_back("-objcmt-migrate-subscripting"); + bool NeedLang = false; for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) if (FrontendOptions::getInputKindForExtension(Opts.Inputs[i].File) != @@ -828,6 +834,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fdebugger-support"); if (Opts.DebuggerCastResultToId) Res.push_back("-fdebugger-cast-result-to-id"); + if (Opts.DebuggerObjCLiteral) + Res.push_back("-fdebugger-objc-literal"); if (Opts.DelayedTemplateParsing) Res.push_back("-fdelayed-template-parsing"); if (Opts.Deprecated) @@ -1376,6 +1384,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::RewriteTest; break; case OPT_analyze: Opts.ProgramAction = frontend::RunAnalysis; break; + case OPT_migrate: + Opts.ProgramAction = frontend::MigrateSource; break; case OPT_Eonly: Opts.ProgramAction = frontend::RunPreprocessorOnly; break; } @@ -1432,7 +1442,6 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); Opts.OverrideRecordLayoutsFile = Args.getLastArgValue(OPT_foverride_record_layout_EQ); - Opts.ARCMTAction = FrontendOptions::ARCMT_None; if (const Arg *A = Args.getLastArg(OPT_arcmt_check, OPT_arcmt_modify, OPT_arcmt_migrate)) { @@ -1450,12 +1459,23 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, break; } } - Opts.ARCMTMigrateDir = Args.getLastArgValue(OPT_arcmt_migrate_directory); + Opts.MTMigrateDir = Args.getLastArgValue(OPT_mt_migrate_directory); Opts.ARCMTMigrateReportOut = Args.getLastArgValue(OPT_arcmt_migrate_report_output); Opts.ARCMTMigrateEmitARCErrors = Args.hasArg(OPT_arcmt_migrate_emit_arc_errors); + if (Args.hasArg(OPT_objcmt_migrate_literals)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Literals; + if (Args.hasArg(OPT_objcmt_migrate_subscripting)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Subscripting; + + if (Opts.ARCMTAction != FrontendOptions::ARCMT_None && + Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "ARC migration" << "ObjC migration"; + } + InputKind DashX = IK_None; if (const Arg *A = Args.getLastArg(OPT_x)) { DashX = llvm::StringSwitch<InputKind>(A->getValue(Args)) @@ -1899,6 +1919,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype); Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support); Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id); + Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal); Opts.AddressSanitizer = Args.hasArg(OPT_faddress_sanitizer); Opts.ThreadSanitizer = Args.hasArg(OPT_fthread_sanitizer); Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack); diff --git a/lib/Frontend/DiagnosticRenderer.cpp b/lib/Frontend/DiagnosticRenderer.cpp index 29f9ed5a7d..6c3bb1d695 100644 --- a/lib/Frontend/DiagnosticRenderer.cpp +++ b/lib/Frontend/DiagnosticRenderer.cpp @@ -12,6 +12,9 @@ #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DiagnosticOptions.h" #include "clang/Lex/Lexer.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" @@ -127,6 +130,54 @@ DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM, DiagnosticRenderer::~DiagnosticRenderer() {} +namespace { + +class FixitReceiver : public edit::EditsReceiver { + SmallVectorImpl<FixItHint> &MergedFixits; + +public: + FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) + : MergedFixits(MergedFixits) { } + virtual void insert(SourceLocation loc, StringRef text) { + MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); + } + virtual void replace(CharSourceRange range, StringRef text) { + MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); + } +}; + +} + +static void mergeFixits(ArrayRef<FixItHint> FixItHints, + const SourceManager &SM, const LangOptions &LangOpts, + SmallVectorImpl<FixItHint> &MergedFixits) { + edit::Commit commit(SM, LangOpts); + for (ArrayRef<FixItHint>::const_iterator + I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) { + const FixItHint &Hint = *I; + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + + edit::EditedSource Editor(SM, LangOpts); + if (Editor.commit(commit)) { + FixitReceiver Rec(MergedFixits); + Editor.applyRewrites(Rec); + } +} void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, DiagnosticsEngine::Level Level, @@ -152,6 +203,12 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), Ranges.end()); + llvm::SmallVector<FixItHint, 8> MergedFixits; + if (!FixItHints.empty()) { + mergeFixits(FixItHints, SM, LangOpts, MergedFixits); + FixItHints = MergedFixits; + } + for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 0841b2cef8..07d2b8d19f 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -76,6 +76,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case RewriteObjC: return new RewriteObjCAction(); case RewriteTest: return new RewriteTestAction(); case RunAnalysis: return new ento::AnalysisAction(); + case MigrateSource: return new arcmt::MigrateSourceAction(); case RunPreprocessorOnly: return new PreprocessOnlyAction(); } llvm_unreachable("Invalid program action!"); @@ -105,12 +106,18 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { break; case FrontendOptions::ARCMT_Migrate: Act = new arcmt::MigrateAction(Act, - FEOpts.ARCMTMigrateDir, + FEOpts.MTMigrateDir, FEOpts.ARCMTMigrateReportOut, FEOpts.ARCMTMigrateEmitARCErrors); break; } + if (FEOpts.ObjCMTAction != FrontendOptions::ObjCMT_None) { + Act = new arcmt::ObjCMigrateAction(Act, FEOpts.MTMigrateDir, + FEOpts.ObjCMTAction & ~FrontendOptions::ObjCMT_Literals, + FEOpts.ObjCMTAction & ~FrontendOptions::ObjCMT_Subscripting); + } + // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!FEOpts.ASTMergeFiles.empty()) diff --git a/lib/Makefile b/lib/Makefile index 74df7abcef..69c5cd5d83 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ CLANG_LEVEL := .. PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ - StaticAnalyzer Rewrite ARCMigrate Serialization Frontend \ + StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \ FrontendTool Index Driver include $(CLANG_LEVEL)/Makefile diff --git a/lib/Rewrite/FixItRewriter.cpp b/lib/Rewrite/FixItRewriter.cpp index caa7d06ac2..3863adb4f1 100644 --- a/lib/Rewrite/FixItRewriter.cpp +++ b/lib/Rewrite/FixItRewriter.cpp @@ -14,6 +14,8 @@ //===----------------------------------------------------------------------===// #include "clang/Rewrite/FixItRewriter.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" @@ -29,6 +31,7 @@ FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const LangOptions &LangOpts, FixItOptions *FixItOpts) : Diags(Diags), + Editor(SourceMgr, LangOpts), Rewrite(SourceMgr, LangOpts), FixItOpts(FixItOpts), NumFailures(0), @@ -51,6 +54,24 @@ bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { return false; } +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); + } +}; + +} + bool FixItRewriter::WriteFixedFiles( std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { @@ -58,6 +79,9 @@ bool FixItRewriter::WriteFixedFiles( return true; } + RewritesReceiver Rec(Rewrite); + Editor.applyRewrites(Rec); + for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); int fd; @@ -116,16 +140,28 @@ void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, // Make sure that we can perform all of the modifications we // in this diagnostic. - bool CanRewrite = Info.getNumFixItHints() > 0; + edit::Commit commit(Editor); for (unsigned Idx = 0, Last = Info.getNumFixItHints(); Idx < Last; ++Idx) { const FixItHint &Hint = Info.getFixItHint(Idx); - if (Hint.RemoveRange.isValid() && - Rewrite.getRangeSize(Hint.RemoveRange) == -1) { - CanRewrite = false; - break; + + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); } } + bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); if (!CanRewrite) { if (Info.getNumFixItHints() > 0) @@ -138,27 +174,8 @@ void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, } return; } - - bool Failed = false; - for (unsigned Idx = 0, Last = Info.getNumFixItHints(); - Idx < Last; ++Idx) { - const FixItHint &Hint = Info.getFixItHint(Idx); - - if (Hint.CodeToInsert.empty()) { - // We're removing code. - if (Rewrite.RemoveText(Hint.RemoveRange)) - Failed = true; - continue; - } - - // We're replacing code. - if (Rewrite.ReplaceText(Hint.RemoveRange.getBegin(), - Rewrite.getRangeSize(Hint.RemoveRange), - Hint.CodeToInsert)) - Failed = true; - } - - if (Failed) { + + if (!Editor.commit(commit)) { ++NumFailures; Diag(Info.getLocation(), diag::note_fixit_failed); return; diff --git a/test/ARCMT/dispatch.m b/test/ARCMT/dispatch.m new file mode 100644 index 0000000000..75c4a83459 --- /dev/null +++ b/test/ARCMT/dispatch.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fblocks -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +#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]; }) + +typedef id dispatch_object_t; +typedef id xpc_object_t; + +void _dispatch_object_validate(dispatch_object_t object); +void _xpc_object_validate(xpc_object_t object); + +dispatch_object_t getme(void); + +void func(dispatch_object_t o) { + dispatch_retain(o); + dispatch_release(o); + dispatch_retain(getme()); +} + +void func2(xpc_object_t o) { + xpc_retain(o); + xpc_release(o); +} diff --git a/test/ARCMT/dispatch.m.result b/test/ARCMT/dispatch.m.result new file mode 100644 index 0000000000..e897672a26 --- /dev/null +++ b/test/ARCMT/dispatch.m.result @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fblocks -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +#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]; }) + +typedef id dispatch_object_t; +typedef id xpc_object_t; + +void _dispatch_object_validate(dispatch_object_t object); +void _xpc_object_validate(xpc_object_t object); + +dispatch_object_t getme(void); + +void func(dispatch_object_t o) { + getme(); +} + +void func2(xpc_object_t o) { +} diff --git a/test/ARCMT/driver-migrate.m b/test/ARCMT/driver-migrate.m index a2b1a7dc27..a912ad95b1 100644 --- a/test/ARCMT/driver-migrate.m +++ b/test/ARCMT/driver-migrate.m @@ -1,6 +1,6 @@ // RUN: %clang -### -ccc-arcmt-migrate /foo/bar -fsyntax-only %s 2>&1 | FileCheck %s -// CHECK: "-arcmt-migrate" "-arcmt-migrate-directory" "{{[^"]*}}/foo/bar" +// CHECK: "-arcmt-migrate" "-mt-migrate-directory" "{{[^"]*}}/foo/bar" // RUN: touch %t.o // RUN: %clang -ccc-arcmt-check -target i386-apple-darwin9 -### %t.o 2> %t.log diff --git a/test/ARCMT/migrate-emit-errors.m b/test/ARCMT/migrate-emit-errors.m index 6a4a3963ca..95c0d2f8f0 100644 --- a/test/ARCMT/migrate-emit-errors.m +++ b/test/ARCMT/migrate-emit-errors.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t -arcmt-migrate-emit-errors %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t -arcmt-migrate-emit-errors %s 2>&1 | FileCheck %s // RUN: rm -rf %t @protocol NSObject diff --git a/test/ARCMT/migrate-plist-output.m b/test/ARCMT/migrate-plist-output.m index b9e3ceff86..12efa93f07 100644 --- a/test/ARCMT/migrate-plist-output.m +++ b/test/ARCMT/migrate-plist-output.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t.dir -arcmt-migrate-report-output %t.plist %s +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t.dir -arcmt-migrate-report-output %t.plist %s // RUN: FileCheck %s -input-file=%t.plist // RUN: rm -rf %t.dir diff --git a/test/ARCMT/migrate-space-in-path.m b/test/ARCMT/migrate-space-in-path.m index e454fae642..89dfe1475c 100644 --- a/test/ARCMT/migrate-space-in-path.m +++ b/test/ARCMT/migrate-space-in-path.m @@ -1,6 +1,6 @@ // RUN: rm -rf %t.migrate -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t.migrate %S/"with space"/test1.m.in -x objective-c -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t.migrate %S/"with space"/test2.m.in -x objective-c -// RUN: c-arcmt-test -arcmt-migrate-directory %t.migrate | arcmt-test -verify-transformed-files %S/"with space"/test1.m.in.result %S/"with space"/test2.m.in.result %S/"with space"/test.h.result +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t.migrate %S/"with space"/test1.m.in -x objective-c +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t.migrate %S/"with space"/test2.m.in -x objective-c +// RUN: c-arcmt-test -mt-migrate-directory %t.migrate | arcmt-test -verify-transformed-files %S/"with space"/test1.m.in.result %S/"with space"/test2.m.in.result %S/"with space"/test.h.result // RUN: rm -rf %t.migrate // DISABLE: mingw32 diff --git a/test/ARCMT/migrate.m b/test/ARCMT/migrate.m index beaf8e5b15..6f41258e59 100644 --- a/test/ARCMT/migrate.m +++ b/test/ARCMT/migrate.m @@ -1,6 +1,6 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test1.m.in -x objective-c -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test2.m.in -x objective-c -// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %S/Inputs/test1.m.in.result %S/Inputs/test2.m.in.result %S/Inputs/test.h.result +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t %S/Inputs/test1.m.in -x objective-c +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t %S/Inputs/test2.m.in -x objective-c +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %S/Inputs/test1.m.in.result %S/Inputs/test2.m.in.result %S/Inputs/test.h.result // RUN: rm -rf %t // DISABLE: mingw32 diff --git a/test/ARCMT/objcmt-numeric-literals.m b/test/ARCMT/objcmt-numeric-literals.m new file mode 100644 index 0000000000..b86af4d056 --- /dev/null +++ b/test/ARCMT/objcmt-numeric-literals.m @@ -0,0 +1,501 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result + +#define YES __objc_yes +#define NO __objc_no + +typedef long NSInteger; +typedef unsigned long NSUInteger; +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) +- (id)initWithChar:(char)value; +- (id)initWithUnsignedChar:(unsigned char)value; +- (id)initWithShort:(short)value; +- (id)initWithUnsignedShort:(unsigned short)value; +- (id)initWithInt:(int)value; +- (id)initWithUnsignedInt:(unsigned int)value; +- (id)initWithLong:(long)value; +- (id)initWithUnsignedLong:(unsigned long)value; +- (id)initWithLongLong:(long long)value; +- (id)initWithUnsignedLongLong:(unsigned long long)value; +- (id)initWithFloat:(float)value; +- (id)initWithDouble:(double)value; +- (id)initWithBool:(BOOL)value; +- (id)initWithInteger:(NSInteger)value; +- (id)initWithUnsignedInteger:(NSUInteger)value; + ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; ++ (NSNumber *)numberWithInteger:(NSInteger)value; ++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value; +@end + +#define VAL_INT 2 +#define VAL_UINT 2U +#define VAL_CHAR 'a' + +void foo() { + [NSNumber numberWithChar:'a']; + [NSNumber numberWithChar:L'a']; + [NSNumber numberWithChar:2]; + [NSNumber numberWithChar:2U]; + [NSNumber numberWithChar:2u]; + [NSNumber numberWithChar:2L]; + [NSNumber numberWithChar:2l]; + [NSNumber numberWithChar:2LL]; + [NSNumber numberWithChar:2ll]; + [NSNumber numberWithChar:2ul]; + [NSNumber numberWithChar:2lu]; + [NSNumber numberWithChar:2ull]; + [NSNumber numberWithChar:2llu]; + [NSNumber numberWithChar:2.0]; + [NSNumber numberWithChar:2.0f]; + [NSNumber numberWithChar:2.0F]; + [NSNumber numberWithChar:2.0l]; + [NSNumber numberWithChar:2.0L]; + [NSNumber numberWithChar:0x2f]; + [NSNumber numberWithChar:04]; + [NSNumber numberWithChar:0]; + [NSNumber numberWithChar:0.0]; + [NSNumber numberWithChar:YES]; + [NSNumber numberWithChar:NO]; + [NSNumber numberWithChar:true]; + [NSNumber numberWithChar:false]; + [NSNumber numberWithChar:VAL_INT]; + [NSNumber numberWithChar:VAL_UINT]; + [NSNumber numberWithChar:VAL_CHAR]; + + [NSNumber numberWithUnsignedChar:'a']; + [NSNumber numberWithUnsignedChar:L'a']; + [NSNumber numberWithUnsignedChar:2]; + [NSNumber numberWithUnsignedChar:2U]; + [NSNumber numberWithUnsignedChar:2u]; + [NSNumber numberWithUnsignedChar:2L]; + [NSNumber numberWithUnsignedChar:2l]; + [NSNumber numberWithUnsignedChar:2LL]; + [NSNumber numberWithUnsignedChar:2ll]; + [NSNumber numberWithUnsignedChar:2ul]; + [NSNumber numberWithUnsignedChar:2lu]; + [NSNumber numberWithUnsignedChar:2ull]; + [NSNumber numberWithUnsignedChar:2llu]; + [NSNumber numberWithUnsignedChar:2.0]; + [NSNumber numberWithUnsignedChar:2.0f]; + [NSNumber numberWithUnsignedChar:2.0F]; + [NSNumber numberWithUnsignedChar:2.0l]; + [NSNumber numberWithUnsignedChar:2.0L]; + [NSNumber numberWithUnsignedChar:0x2f]; + [NSNumber numberWithUnsignedChar:04]; + [NSNumber numberWithUnsignedChar:0]; + [NSNumber numberWithUnsignedChar:0.0]; + [NSNumber numberWithUnsignedChar:YES]; + [NSNumber numberWithUnsignedChar:NO]; + [NSNumber numberWithUnsignedChar:true]; + [NSNumber numberWithUnsignedChar:false]; + [NSNumber numberWithUnsignedChar:VAL_INT]; + [NSNumber numberWithUnsignedChar:VAL_UINT]; + [NSNumber numberWithUnsignedChar:VAL_CHAR]; + + [NSNumber numberWithShort:'a']; + [NSNumber numberWithShort:L'a']; + [NSNumber numberWithShort:2]; + [NSNumber numberWithShort:2U]; + [NSNumber numberWithShort:2u]; + [NSNumber numberWithShort:2L]; + [NSNumber numberWithShort:2l]; + [NSNumber numberWithShort:2LL]; + [NSNumber numberWithShort:2ll]; + [NSNumber numberWithShort:2ul]; + [NSNumber numberWithShort:2lu]; + [NSNumber numberWithShort:2ull]; + [NSNumber numberWithShort:2llu]; + [NSNumber numberWithShort:2.0]; + [NSNumber numberWithShort:2.0f]; + [NSNumber numberWithShort:2.0F]; + [NSNumber numberWithShort:2.0l]; + [NSNumber numberWithShort:2.0L]; + [NSNumber numberWithShort:0x2f]; + [NSNumber numberWithShort:04]; + [NSNumber numberWithShort:0]; + [NSNumber numberWithShort:0.0]; + [NSNumber numberWithShort:YES]; + [NSNumber numberWithShort:NO]; + [NSNumber numberWithShort:true]; + [NSNumber numberWithShort:false]; + [NSNumber numberWithShort:VAL_INT]; + [NSNumber numberWithShort:VAL_UINT]; + + [NSNumber numberWithUnsignedShort:'a']; + [NSNumber numberWithUnsignedShort:L'a']; + [NSNumber numberWithUnsignedShort:2]; + [NSNumber numberWithUnsignedShort:2U]; + [NSNumber numberWithUnsignedShort:2u]; + [NSNumber numberWithUnsignedShort:2L]; + [NSNumber numberWithUnsignedShort:2l]; + [NSNumber numberWithUnsignedShort:2LL]; + [NSNumber numberWithUnsignedShort:2ll]; + [NSNumber numberWithUnsignedShort:2ul]; + [NSNumber numberWithUnsignedShort:2lu]; + [NSNumber numberWithUnsignedShort:2ull]; + [NSNumber numberWithUnsignedShort:2llu]; + [NSNumber numberWithUnsignedShort:2.0]; + [NSNumber numberWithUnsignedShort:2.0f]; + [NSNumber numberWithUnsignedShort:2.0F]; + [NSNumber numberWithUnsignedShort:2.0l]; + [NSNumber numberWithUnsignedShort:2.0L]; + [NSNumber numberWithUnsignedShort:0x2f]; + [NSNumber numberWithUnsignedShort:04]; + [NSNumber numberWithUnsignedShort:0]; + [NSNumber numberWithUnsignedShort:0.0]; + [NSNumber numberWithUnsignedShort:YES]; + [NSNumber numberWithUnsignedShort:NO]; + [NSNumber numberWithUnsignedShort:true]; + [NSNumber numberWithUnsignedShort:false]; + [NSNumber numberWithUnsignedShort:VAL_INT]; + [NSNumber numberWithUnsignedShort:VAL_UINT]; + + [NSNumber numberWithInt:'a']; + [NSNumber numberWithInt:L'a']; + [NSNumber numberWithInt:2]; + [NSNumber numberWithInt:2U]; + [NSNumber numberWithInt:2u]; + [NSNumber numberWithInt:2L]; + [NSNumber numberWithInt:2l]; + [NSNumber numberWithInt:2LL]; + [NSNumber numberWithInt:2ll]; + [NSNumber numberWithInt:2ul]; + [NSNumber numberWithInt:2lu]; + [NSNumber numberWithInt:2ull]; + [NSNumber numberWithInt:2llu]; + [NSNumber numberWithInt:2.0]; + [NSNumber numberWithInt:2.0f]; + [NSNumber numberWithInt:2.0F]; + [NSNumber numberWithInt:2.0l]; + [NSNumber numberWithInt:2.0L]; + [NSNumber numberWithInt:0x2f]; + [NSNumber numberWithInt:04]; + [NSNumber numberWithInt:0]; + [NSNumber numberWithInt:0.0]; + [NSNumber numberWithInt:YES]; + [NSNumber numberWithInt:NO]; + [NSNumber numberWithInt:true]; + [NSNumber numberWithInt:false]; + [NSNumber numberWithInt:VAL_INT]; + [NSNumber numberWithInt:VAL_UINT]; + + (void)[[NSNumber alloc] initWithInt:2]; + (void)[[NSNumber alloc] initWithInt:2U]; + + [NSNumber numberWithInt:+2]; + [NSNumber numberWithInt:-2]; + + [NSNumber numberWithUnsignedInt:'a']; + [NSNumber numberWithUnsignedInt:L'a']; + [NSNumber numberWithUnsignedInt:2]; + [NSNumber numberWithUnsignedInt:2U]; + [NSNumber numberWithUnsignedInt:2u]; + [NSNumber numberWithUnsignedInt:2L]; + [NSNumber numberWithUnsignedInt:2l]; + [NSNumber numberWithUnsignedInt:2LL]; + [NSNumber numberWithUnsignedInt:2ll]; + [NSNumber numberWithUnsignedInt:2ul]; + [NSNumber numberWithUnsignedInt:2lu]; + [NSNumber numberWithUnsignedInt:2ull]; + [NSNumber numberWithUnsignedInt:2llu]; + [NSNumber numberWithUnsignedInt:2.0]; + [NSNumber numberWithUnsignedInt:2.0f]; + [NSNumber numberWithUnsignedInt:2.0F]; + [NSNumber numberWithUnsignedInt:2.0l]; + [NSNumber numberWithUnsignedInt:2.0L]; + [NSNumber numberWithUnsignedInt:0x2f]; + [NSNumber numberWithUnsignedInt:04]; + [NSNumber numberWithUnsignedInt:0]; + [NSNumber numberWithUnsignedInt:0.0]; + [NSNumber numberWithUnsignedInt:YES]; + [NSNumber numberWithUnsignedInt:NO]; + [NSNumber numberWithUnsignedInt:true]; + [NSNumber numberWithUnsignedInt:false]; + [NSNumber numberWithUnsignedInt:VAL_INT]; + [NSNumber numberWithUnsignedInt:VAL_UINT]; + + [NSNumber numberWithLong:'a']; + [NSNumber numberWithLong:L'a']; + [NSNumber numberWithLong:2]; + [NSNumber numberWithLong:2U]; + [NSNumber numberWithLong:2u]; + [NSNumber numberWithLong:2L]; + [NSNumber numberWithLong:2l]; + [NSNumber numberWithLong:2LL]; + [NSNumber numberWithLong:2ll]; + [NSNumber numberWithLong:2ul]; + [NSNumber numberWithLong:2lu]; + [NSNumber numberWithLong:2ull]; + [NSNumber numberWithLong:2llu]; + [NSNumber numberWithLong:2.0]; + [NSNumber numberWithLong:2.0f]; + [NSNumber numberWithLong:2.0F]; + [NSNumber numberWithLong:2.0l]; + [NSNumber numberWithLong:2.0L]; + [NSNumber numberWithLong:0x2f]; + [NSNumber numberWithLong:04]; + [NSNumber numberWithLong:0]; + [NSNumber numberWithLong:0.0]; + [NSNumber numberWithLong:YES]; + [NSNumber numberWithLong:NO]; + [NSNumber numberWithLong:true]; + [NSNumber numberWithLong:false]; + [NSNumber numberWithLong:VAL_INT]; + [NSNumber numberWithLong:VAL_UINT]; + + [NSNumber numberWithUnsignedLong:'a']; + [NSNumber numberWithUnsignedLong:L'a']; + [NSNumber numberWithUnsignedLong:2]; + [NSNumber numberWithUnsignedLong:2U]; + [NSNumber numberWithUnsignedLong:2u]; + [NSNumber numberWithUnsignedLong:2L]; + [NSNumber numberWithUnsignedLong:2l]; + [NSNumber numberWithUnsignedLong:2LL]; + [NSNumber numberWithUnsignedLong:2ll]; + [NSNumber numberWithUnsignedLong:2ul]; + [NSNumber numberWithUnsignedLong:2lu]; + [NSNumber numberWithUnsignedLong:2ull]; + [NSNumber numberWithUnsignedLong:2llu]; + [NSNumber numberWithUnsignedLong:2.0]; + [NSNumber numberWithUnsignedLong:2.0f]; + [NSNumber numberWithUnsignedLong:2.0F]; + [NSNumber numberWithUnsignedLong:2.0l]; + [NSNumber numberWithUnsignedLong:2.0L]; + [NSNumber numberWithUnsignedLong:0x2f]; + [NSNumber numberWithUnsignedLong:04]; + [NSNumber numberWithUnsignedLong:0]; + [NSNumber numberWithUnsignedLong:0.0]; + [NSNumber numberWithUnsignedLong:YES]; + [NSNumber numberWithUnsignedLong:NO]; + [NSNumber numberWithUnsignedLong:true]; + [NSNumber numberWithUnsignedLong:false]; + [NSNumber numberWithUnsignedLong:VAL_INT]; + [NSNumber numberWithUnsignedLong:VAL_UINT]; + + [NSNumber numberWithLongLong:'a']; + [NSNumber numberWithLongLong:L'a']; + [NSNumber numberWithLongLong:2]; + [NSNumber numberWithLongLong:2U]; + [NSNumber numberWithLongLong:2u]; + [NSNumber numberWithLongLong:2L]; + [NSNumber numberWithLongLong:2l]; + [NSNumber numberWithLongLong:2LL]; + [NSNumber numberWithLongLong:2ll]; + [NSNumber numberWithLongLong:2ul]; + [NSNumber numberWithLongLong:2lu]; + [NSNumber numberWithLongLong:2ull]; + [NSNumber numberWithLongLong:2llu]; + [NSNumber numberWithLongLong:2.0]; + [NSNumber numberWithLongLong:2.0f]; + [NSNumber numberWithLongLong:2.0F]; + [NSNumber numberWithLongLong:2.0l]; + [NSNumber numberWithLongLong:2.0L]; + [NSNumber numberWithLongLong:0x2f]; + [NSNumber numberWithLongLong:04]; + [NSNumber numberWithLongLong:0]; + [NSNumber numberWithLongLong:0.0]; + [NSNumber numberWithLongLong:YES]; + [NSNumber numberWithLongLong:NO]; + [NSNumber numberWithLongLong:true]; + [NSNumber numberWithLongLong:false]; + [NSNumber numberWithLongLong:VAL_INT]; + [NSNumber numberWithLongLong:VAL_UINT]; + + [NSNumber numberWithUnsignedLongLong:'a']; + [NSNumber numberWithUnsignedLongLong:L'a']; + [NSNumber numberWithUnsignedLongLong:2]; + [NSNumber numberWithUnsignedLongLong:2U]; + [NSNumber numberWithUnsignedLongLong:2u]; + [NSNumber numberWithUnsignedLongLong:2L]; + [NSNumber numberWithUnsignedLongLong:2l]; + [NSNumber numberWithUnsignedLongLong:2LL]; + [NSNumber numberWithUnsignedLongLong:2ll]; + [NSNumber numberWithUnsignedLongLong:2ul]; + [NSNumber numberWithUnsignedLongLong:2lu]; + [NSNumber numberWithUnsignedLongLong:2ull]; + [NSNumber numberWithUnsignedLongLong:2llu]; + [NSNumber numberWithUnsignedLongLong:2.0]; + [NSNumber numberWithUnsignedLongLong:2.0f]; + [NSNumber numberWithUnsignedLongLong:2.0F]; + [NSNumber numberWithUnsignedLongLong:2.0l]; + [NSNumber numberWithUnsignedLongLong:2.0L]; + [NSNumber numberWithUnsignedLongLong:0x2f]; + [NSNumber numberWithUnsignedLongLong:04]; + [NSNumber numberWithUnsignedLongLong:0]; + [NSNumber numberWithUnsignedLongLong:0.0]; + [NSNumber numberWithUnsignedLongLong:YES]; + [NSNumber numberWithUnsignedLongLong:NO]; + [NSNumber numberWithUnsignedLongLong:true]; + [NSNumber numberWithUnsignedLongLong:false]; + [NSNumber numberWithUnsignedLongLong:VAL_INT]; + [NSNumber numberWithUnsignedLongLong:VAL_UINT]; + + [NSNumber numberWithFloat:'a']; + [NSNumber numberWithFloat:L'a']; + [NSNumber numberWithFloat:2]; + [NSNumber numberWithFloat:2U]; + [NSNumber numberWithFloat:2u]; + [NSNumber numberWithFloat:2L]; + [NSNumber numberWithFloat:2l]; + [NSNumber numberWithFloat:2LL]; + [NSNumber numberWithFloat:2ll]; + [NSNumber numberWithFloat:2ul]; + [NSNumber numberWithFloat:2lu]; + [NSNumber numberWithFloat:2ull]; + [NSNumber numberWithFloat:2llu]; + [NSNumber numberWithFloat:2.0]; + [NSNumber numberWithFloat:2.0f]; + [NSNumber numberWithFloat:2.0F]; + [NSNumber numberWithFloat:2.0l]; + [NSNumber numberWithFloat:2.0L]; + [NSNumber numberWithFloat:0x2f]; + [NSNumber numberWithFloat:04]; + [NSNumber numberWithFloat:0]; + [NSNumber numberWithFloat:0.0]; + [NSNumber numberWithFloat:YES]; + [NSNumber numberWithFloat:NO]; + [NSNumber numberWithFloat:true]; + [NSNumber numberWithFloat:false]; + [NSNumber numberWithFloat:VAL_INT]; + [NSNumber numberWithFloat:VAL_UINT]; + + [NSNumber numberWithDouble:'a']; + [NSNumber numberWithDouble:L'a']; + [NSNumber numberWithDouble:2]; + [NSNumber numberWithDouble:2U]; + [NSNumber numberWithDouble:2u]; + [NSNumber numberWithDouble:2L]; + [NSNumber numberWithDouble:2l]; + [NSNumber numberWithDouble:2LL]; + [NSNumber numberWithDouble:2ll]; + [NSNumber numberWithDouble:2ul]; + [NSNumber numberWithDouble:2lu]; + [NSNumber numberWithDouble:2ull]; + [NSNumber numberWithDouble:2llu]; + [NSNumber numberWithDouble:2.0]; + [NSNumber numberWithDouble:2.0f]; + [NSNumber numberWithDouble:2.0F]; + [NSNumber numberWithDouble:2.0l]; + [NSNumber numberWithDouble:2.0L]; + [NSNumber numberWithDouble:0x2f]; + [NSNumber numberWithDouble:04]; + [NSNumber numberWithDouble:0]; + [NSNumber numberWithDouble:0.0]; + [NSNumber numberWithDouble:YES]; + [NSNumber numberWithDouble:NO]; + [NSNumber numberWithDouble:true]; + [NSNumber numberWithDouble:false]; + [NSNumber numberWithDouble:VAL_INT]; + [NSNumber numberWithDouble:VAL_UINT]; + + [NSNumber numberWithBool:'a']; + [NSNumber numberWithBool:L'a']; + [NSNumber numberWithBool:2]; + [NSNumber numberWithBool:2U]; + [NSNumber numberWithBool:2u]; + [NSNumber numberWithBool:2L]; + [NSNumber numberWithBool:2l]; + [NSNumber numberWithBool:2LL]; + [NSNumber numberWithBool:2ll]; + [NSNumber numberWithBool:2ul]; + [NSNumber numberWithBool:2lu]; + [NSNumber numberWithBool:2ull]; + [NSNumber numberWithBool:2llu]; + [NSNumber numberWithBool:2.0]; + [NSNumber numberWithBool:2.0f]; + [NSNumber numberWithBool:2.0F]; + [NSNumber numberWithBool:2.0l]; + [NSNumber numberWithBool:2.0L]; + [NSNumber numberWithBool:0x2f]; + [NSNumber numberWithBool:04]; + [NSNumber numberWithBool:0]; + [NSNumber numberWithBool:0.0]; + [NSNumber numberWithBool:YES]; + [NSNumber numberWithBool:NO]; + [NSNumber numberWithBool:true]; + [NSNumber numberWithBool:false]; + [NSNumber numberWithBool:VAL_INT]; + [NSNumber numberWithBool:VAL_UINT]; + + [NSNumber numberWithInteger:'a']; + [NSNumber numberWithInteger:L'a']; + [NSNumber numberWithInteger:2]; + [NSNumber numberWithInteger:2U]; + [NSNumber numberWithInteger:2u]; + [NSNumber numberWithInteger:2L]; + [NSNumber numberWithInteger:2l]; + [NSNumber numberWithInteger:2LL]; + [NSNumber numberWithInteger:2ll]; + [NSNumber numberWithInteger:2ul]; + [NSNumber numberWithInteger:2lu]; + [NSNumber numberWithInteger:2ull]; + [NSNumber numberWithInteger:2llu]; + [NSNumber numberWithInteger:2.0]; + [NSNumber numberWithInteger:2.0f]; + [NSNumber numberWithInteger:2.0F]; + [NSNumber numberWithInteger:2.0l]; + [NSNumber numberWithInteger:2.0L]; + [NSNumber numberWithInteger:0x2f]; + [NSNumber numberWithInteger:04]; + [NSNumber numberWithInteger:0]; + [NSNumber numberWithInteger:0.0]; + [NSNumber numberWithInteger:YES]; + [NSNumber numberWithInteger:NO]; + [NSNumber numberWithInteger:true]; + [NSNumber numberWithInteger:false]; + [NSNumber numberWithInteger:VAL_INT]; + [NSNumber numberWithInteger:VAL_UINT]; + + [NSNumber numberWithUnsignedInteger:'a']; + [NSNumber numberWithUnsignedInteger:L'a']; + [NSNumber numberWithUnsignedInteger:2]; + [NSNumber numberWithUnsignedInteger:2U]; + [NSNumber numberWithUnsignedInteger:2u]; + [NSNumber numberWithUnsignedInteger:2L]; + [NSNumber numberWithUnsignedInteger:2l]; + [NSNumber numberWithUnsignedInteger:2LL]; + [NSNumber numberWithUnsignedInteger:2ll]; + [NSNumber numberWithUnsignedInteger:2ul]; + [NSNumber numberWithUnsignedInteger:2lu]; + [NSNumber numberWithUnsignedInteger:2ull]; + [NSNumber numberWithUnsignedInteger:2llu]; + [NSNumber numberWithUnsignedInteger:2.0]; + [NSNumber numberWithUnsignedInteger:2.0f]; + [NSNumber numberWithUnsignedInteger:2.0F]; + [NSNumber numberWithUnsignedInteger:2.0l]; + [NSNumber numberWithUnsignedInteger:2.0L]; + [NSNumber numberWithUnsignedInteger:0x2f]; + [NSNumber numberWithUnsignedInteger:04]; + [NSNumber numberWithUnsignedInteger:0]; + [NSNumber numberWithUnsignedInteger:0.0]; + [NSNumber numberWithUnsignedInteger:YES]; + [NSNumber numberWithUnsignedInteger:NO]; + [NSNumber numberWithUnsignedInteger:true]; + [NSNumber numberWithUnsignedInteger:false]; + [NSNumber numberWithUnsignedInteger:VAL_INT]; + [NSNumber numberWithUnsignedInteger:VAL_UINT]; +} diff --git a/test/ARCMT/objcmt-numeric-literals.m.result b/test/ARCMT/objcmt-numeric-literals.m.result new file mode 100644 index 0000000000..1c4187aaef --- /dev/null +++ b/test/ARCMT/objcmt-numeric-literals.m.result @@ -0,0 +1,501 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result + +#define YES __objc_yes +#define NO __objc_no + +typedef long NSInteger; +typedef unsigned long NSUInteger; +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) +- (id)initWithChar:(char)value; +- (id)initWithUnsignedChar:(unsigned char)value; +- (id)initWithShort:(short)value; +- (id)initWithUnsignedShort:(unsigned short)value; +- (id)initWithInt:(int)value; +- (id)initWithUnsignedInt:(unsigned int)value; +- (id)initWithLong:(long)value; +- (id)initWithUnsignedLong:(unsigned long)value; +- (id)initWithLongLong:(long long)value; +- (id)initWithUnsignedLongLong:(unsigned long long)value; +- (id)initWithFloat:(float)value; +- (id)initWithDouble:(double)value; +- (id)initWithBool:(BOOL)value; +- (id)initWithInteger:(NSInteger)value; +- (id)initWithUnsignedInteger:(NSUInteger)value; + ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; ++ (NSNumber *)numberWithInteger:(NSInteger)value; ++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value; +@end + +#define VAL_INT 2 +#define VAL_UINT 2U +#define VAL_CHAR 'a' + +void foo() { + @'a'; + [NSNumber numberWithChar:L'a']; + [NSNumber numberWithChar:2]; + [NSNumber numberWithChar:2U]; + [NSNumber numberWithChar:2u]; + [NSNumber numberWithChar:2L]; + [NSNumber numberWithChar:2l]; + [NSNumber numberWithChar:2LL]; + [NSNumber numberWithChar:2ll]; + [NSNumber numberWithChar:2ul]; + [NSNumber numberWithChar:2lu]; + [NSNumber numberWithChar:2ull]; + [NSNumber numberWithChar:2llu]; + [NSNumber numberWithChar:2.0]; + [NSNumber numberWithChar:2.0f]; + [NSNumber numberWithChar:2.0F]; + [NSNumber numberWithChar:2.0l]; + [NSNumber numberWithChar:2.0L]; + [NSNumber numberWithChar:0x2f]; + [NSNumber numberWithChar:04]; + [NSNumber numberWithChar:0]; + [NSNumber numberWithChar:0.0]; + [NSNumber numberWithChar:YES]; + [NSNumber numberWithChar:NO]; + [NSNumber numberWithChar:true]; + [NSNumber numberWithChar:false]; + [NSNumber numberWithChar:VAL_INT]; + [NSNumber numberWithChar:VAL_UINT]; + @VAL_CHAR; + + [NSNumber numberWithUnsignedChar:'a']; + [NSNumber numberWithUnsignedChar:L'a']; + [NSNumber numberWithUnsignedChar:2]; + [NSNumber numberWithUnsignedChar:2U]; + [NSNumber numberWithUnsignedChar:2u]; + [NSNumber numberWithUnsignedChar:2L]; + [NSNumber numberWithUnsignedChar:2l]; + [NSNumber numberWithUnsignedChar:2LL]; + [NSNumber numberWithUnsignedChar:2ll]; + [NSNumber numberWithUnsignedChar:2ul]; + [NSNumber numberWithUnsignedChar:2lu]; + [NSNumber numberWithUnsignedChar:2ull]; + [NSNumber numberWithUnsignedChar:2llu]; + [NSNumber numberWithUnsignedChar:2.0]; + [NSNumber numberWithUnsignedChar:2.0f]; + [NSNumber numberWithUnsignedChar:2.0F]; + [NSNumber numberWithUnsignedChar:2.0l]; + [NSNumber numberWithUnsignedChar:2.0L]; + [NSNumber numberWithUnsignedChar:0x2f]; + [NSNumber numberWithUnsignedChar:04]; + [NSNumber numberWithUnsignedChar:0]; + [NSNumber numberWithUnsignedChar:0.0]; + [NSNumber numberWithUnsignedChar:YES]; + [NSNumber numberWithUnsignedChar:NO]; + [NSNumber numberWithUnsignedChar:true]; + [NSNumber numberWithUnsignedChar:false]; + [NSNumber numberWithUnsignedChar:VAL_INT]; + [NSNumber numberWithUnsignedChar:VAL_UINT]; + [NSNumber numberWithUnsignedChar:VAL_CHAR]; + + [NSNumber numberWithShort:'a']; + [NSNumber numberWithShort:L'a']; + [NSNumber numberWithShort:2]; + [NSNumber numberWithShort:2U]; + [NSNumber numberWithShort:2u]; + [NSNumber numberWithShort:2L]; + [NSNumber numberWithShort:2l]; + [NSNumber numberWithShort:2LL]; + [NSNumber numberWithShort:2ll]; + [NSNumber numberWithShort:2ul]; + [NSNumber numberWithShort:2lu]; + [NSNumber numberWithShort:2ull]; + [NSNumber numberWithShort:2llu]; + [NSNumber numberWithShort:2.0]; + [NSNumber numberWithShort:2.0f]; + [NSNumber numberWithShort:2.0F]; + [NSNumber numberWithShort:2.0l]; + [NSNumber numberWithShort:2.0L]; + [NSNumber numberWithShort:0x2f]; + [NSNumber numberWithShort:04]; + [NSNumber numberWithShort:0]; + [NSNumber numberWithShort:0.0]; + [NSNumber numberWithShort:YES]; + [NSNumber numberWithShort:NO]; + [NSNumber numberWithShort:true]; + [NSNumber numberWithShort:false]; + [NSNumber numberWithShort:VAL_INT]; + [NSNumber numberWithShort:VAL_UINT]; + + [NSNumber numberWithUnsignedShort:'a']; + [NSNumber numberWithUnsignedShort:L'a']; + [NSNumber numberWithUnsignedShort:2]; + [NSNumber numberWithUnsignedShort:2U]; + [NSNumber numberWithUnsignedShort:2u]; + [NSNumber numberWithUnsignedShort:2L]; + [NSNumber numberWithUnsignedShort:2l]; + [NSNumber numberWithUnsignedShort:2LL]; + [NSNumber numberWithUnsignedShort:2ll]; + [NSNumber numberWithUnsignedShort:2ul]; + [NSNumber numberWithUnsignedShort:2lu]; + [NSNumber numberWithUnsignedShort:2ull]; + [NSNumber numberWithUnsignedShort:2llu]; + [NSNumber numberWithUnsignedShort:2.0]; + [NSNumber numberWithUnsignedShort:2.0f]; + [NSNumber numberWithUnsignedShort:2.0F]; + [NSNumber numberWithUnsignedShort:2.0l]; + [NSNumber numberWithUnsignedShort:2.0L]; + [NSNumber numberWithUnsignedShort:0x2f]; + [NSNumber numberWithUnsignedShort:04]; + [NSNumber numberWithUnsignedShort:0]; + [NSNumber numberWithUnsignedShort:0.0]; + [NSNumber numberWithUnsignedShort:YES]; + [NSNumber numberWithUnsignedShort:NO]; + [NSNumber numberWithUnsignedShort:true]; + [NSNumber numberWithUnsignedShort:false]; + [NSNumber numberWithUnsignedShort:VAL_INT]; + [NSNumber numberWithUnsignedShort:VAL_UINT]; + + [NSNumber numberWithInt:'a']; + [NSNumber numberWithInt:L'a']; + @2; + @2; + @2; + @2; + @2; + @2; + @2; + @2; + @2; + @2; + @2; + [NSNumber numberWithInt:2.0]; + [NSNumber numberWithInt:2.0f]; + [NSNumber numberWithInt:2.0F]; + [NSNumber numberWithInt:2.0l]; + [NSNumber numberWithInt:2.0L]; + @0x2f; + @04; + @0; + [NSNumber numberWithInt:0.0]; + [NSNumber numberWithInt:YES]; + [NSNumber numberWithInt:NO]; + [NSNumber numberWithInt:true]; + [NSNumber numberWithInt:false]; + @VAL_INT; + [NSNumber numberWithInt:VAL_UINT]; + + (void)[[NSNumber alloc] initWithInt:2]; + (void)[[NSNumber alloc] initWithInt:2U]; + + @+2; + @-2; + + [NSNumber numberWithUnsignedInt:'a']; + [NSNumber numberWithUnsignedInt:L'a']; + @2U; + @2U; + @2u; + @2U; + @2u; + @2U; + @2u; + @2u; + @2u; + @2u; + @2u; + [NSNumber numberWithUnsignedInt:2.0]; + [NSNumber numberWithUnsignedInt:2.0f]; + [NSNumber numberWithUnsignedInt:2.0F]; + [NSNumber numberWithUnsignedInt:2.0l]; + [NSNumber numberWithUnsignedInt:2.0L]; + @0x2fU; + @04U; + @0U; + [NSNumber numberWithUnsignedInt:0.0]; + [NSNumber numberWithUnsignedInt:YES]; + [NSNumber numberWithUnsignedInt:NO]; + [NSNumber numberWithUnsignedInt:true]; + [NSNumber numberWithUnsignedInt:false]; + [NSNumber numberWithUnsignedInt:VAL_INT]; + @VAL_UINT; + + [NSNumber numberWithLong:'a']; + [NSNumber numberWithLong:L'a']; + @2L; + @2L; + @2l; + @2L; + @2l; + @2L; + @2l; + @2l; + @2l; + @2l; + @2l; + [NSNumber numberWithLong:2.0]; + [NSNumber numberWithLong:2.0f]; + [NSNumber numberWithLong:2.0F]; + [NSNumber numberWithLong:2.0l]; + [NSNumber numberWithLong:2.0L]; + @0x2fL; + @04L; + @0L; + [NSNumber numberWithLong:0.0]; + [NSNumber numberWithLong:YES]; + [NSNumber numberWithLong:NO]; + [NSNumber numberWithLong:true]; + [NSNumber numberWithLong:false]; + [NSNumber numberWithLong:VAL_INT]; + [NSNumber numberWithLong:VAL_UINT]; + + [NSNumber numberWithUnsignedLong:'a']; + [NSNumber numberWithUnsignedLong:L'a']; + @2UL; + @2UL; + @2ul; + @2UL; + @2ul; + @2UL; + @2ul; + @2ul; + @2lu; + @2ul; + @2ul; + [NSNumber numberWithUnsignedLong:2.0]; + [NSNumber numberWithUnsignedLong:2.0f]; + [NSNumber numberWithUnsignedLong:2.0F]; + [NSNumber numberWithUnsignedLong:2.0l]; + [NSNumber numberWithUnsignedLong:2.0L]; + @0x2fUL; + @04UL; + @0UL; + [NSNumber numberWithUnsignedLong:0.0]; + [NSNumber numberWithUnsignedLong:YES]; + [NSNumber numberWithUnsignedLong:NO]; + [NSNumber numberWithUnsignedLong:true]; + [NSNumber numberWithUnsignedLong:false]; + [NSNumber numberWithUnsignedLong:VAL_INT]; + [NSNumber numberWithUnsignedLong:VAL_UINT]; + + [NSNumber numberWithLongLong:'a']; + [NSNumber numberWithLongLong:L'a']; + @2LL; + @2LL; + @2ll; + @2LL; + @2ll; + @2LL; + @2ll; + @2ll; + @2ll; + @2ll; + @2ll; + [NSNumber numberWithLongLong:2.0]; + [NSNumber numberWithLongLong:2.0f]; + [NSNumber numberWithLongLong:2.0F]; + [NSNumber numberWithLongLong:2.0l]; + [NSNumber numberWithLongLong:2.0L]; + @0x2fLL; + @04LL; + @0LL; + [NSNumber numberWithLongLong:0.0]; + [NSNumber numberWithLongLong:YES]; + [NSNumber numberWithLongLong:NO]; + [NSNumber numberWithLongLong:true]; + [NSNumber numberWithLongLong:false]; + [NSNumber numberWithLongLong:VAL_INT]; + [NSNumber numberWithLongLong:VAL_UINT]; + + [NSNumber numberWithUnsignedLongLong:'a']; + [NSNumber numberWithUnsignedLongLong:L'a']; + @2ULL; + @2ULL; + @2ull; + @2ULL; + @2ull; + @2ULL; + @2ull; + @2ull; + @2ull; + @2ull; + @2llu; + [NSNumber numberWithUnsignedLongLong:2.0]; + [NSNumber numberWithUnsignedLongLong:2.0f]; + [NSNumber numberWithUnsignedLongLong:2.0F]; + [NSNumber numberWithUnsignedLongLong:2.0l]; + [NSNumber numberWithUnsignedLongLong:2.0L]; + @0x2fULL; + @04ULL; + @0ULL; + [NSNumber numberWithUnsignedLongLong:0.0]; + [NSNumber numberWithUnsignedLongLong:YES]; + [NSNumber numberWithUnsignedLongLong:NO]; + [NSNumber numberWithUnsignedLongLong:true]; + [NSNumber numberWithUnsignedLongLong:false]; + [NSNumber numberWithUnsignedLongLong:VAL_INT]; + [NSNumber numberWithUnsignedLongLong:VAL_UINT]; + + [NSNumber numberWithFloat:'a']; + [NSNumber numberWithFloat:L'a']; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0f; + @2.0F; + @2.0f; + @2.0f; + [NSNumber numberWithFloat:0x2f]; + [NSNumber numberWithFloat:04]; + @0.0f; + @0.0f; + [NSNumber numberWithFloat:YES]; + [NSNumber numberWithFloat:NO]; + [NSNumber numberWithFloat:true]; + [NSNumber numberWithFloat:false]; + [NSNumber numberWithFloat:VAL_INT]; + [NSNumber numberWithFloat:VAL_UINT]; + + [NSNumber numberWithDouble:'a']; + [NSNumber numberWithDouble:L'a']; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + @2.0; + [NSNumber numberWithDouble:0x2f]; + [NSNumber numberWithDouble:04]; + @0.0; + @0.0; + [NSNumber numberWithDouble:YES]; + [NSNumber numberWithDouble:NO]; + [NSNumber numberWithDouble:true]; + [NSNumber numberWithDouble:false]; + [NSNumber numberWithDouble:VAL_INT]; + [NSNumber numberWithDouble:VAL_UINT]; + + [NSNumber numberWithBool:'a']; + [NSNumber numberWithBool:L'a']; + [NSNumber numberWithBool:2]; + [NSNumber numberWithBool:2U]; + [NSNumber numberWithBool:2u]; + [NSNumber numberWithBool:2L]; + [NSNumber numberWithBool:2l]; + [NSNumber numberWithBool:2LL]; + [NSNumber numberWithBool:2ll]; + [NSNumber numberWithBool:2ul]; + [NSNumber numberWithBool:2lu]; + [NSNumber numberWithBool:2ull]; + [NSNumber numberWithBool:2llu]; + [NSNumber numberWithBool:2.0]; + [NSNumber numberWithBool:2.0f]; + [NSNumber numberWithBool:2.0F]; + [NSNumber numberWithBool:2.0l]; + [NSNumber numberWithBool:2.0L]; + [NSNumber numberWithBool:0x2f]; + [NSNumber numberWithBool:04]; + [NSNumber numberWithBool:0]; + [NSNumber numberWithBool:0.0]; + @YES; + @NO; + @true; + @false; + [NSNumber numberWithBool:VAL_INT]; + [NSNumber numberWithBool:VAL_UINT]; + + [NSNumber numberWithInteger:'a']; + [NSNumber numberWithInteger:L'a']; + @2; + @2; + @2; + @2L; + @2l; + @2; + @2; + @2; + @2; + @2; + @2; + [NSNumber numberWithInteger:2.0]; + [NSNumber numberWithInteger:2.0f]; + [NSNumber numberWithInteger:2.0F]; + [NSNumber numberWithInteger:2.0l]; + [NSNumber numberWithInteger:2.0L]; + @0x2f; + @04; + @0; + [NSNumber numberWithInteger:0.0]; + [NSNumber numberWithInteger:YES]; + [NSNumber numberWithInteger:NO]; + [NSNumber numberWithInteger:true]; + [NSNumber numberWithInteger:false]; + [NSNumber numberWithInteger:VAL_INT]; + [NSNumber numberWithInteger:VAL_UINT]; + + [NSNumber numberWithUnsignedInteger:'a']; + [NSNumber numberWithUnsignedInteger:L'a']; + @2U; + @2U; + @2u; + @2U; + @2u; + @2U; + @2u; + @2ul; + @2lu; + @2u; + @2u; + [NSNumber numberWithUnsignedInteger:2.0]; + [NSNumber numberWithUnsignedInteger:2.0f]; + [NSNumber numberWithUnsignedInteger:2.0F]; + [NSNumber numberWithUnsignedInteger:2.0l]; + [NSNumber numberWithUnsignedInteger:2.0L]; + @0x2fU; + @04U; + @0U; + [NSNumber numberWithUnsignedInteger:0.0]; + [NSNumber numberWithUnsignedInteger:YES]; + [NSNumber numberWithUnsignedInteger:NO]; + [NSNumber numberWithUnsignedInteger:true]; + [NSNumber numberWithUnsignedInteger:false]; + [NSNumber numberWithUnsignedInteger:VAL_INT]; + [NSNumber numberWithUnsignedInteger:VAL_UINT]; +} diff --git a/test/ARCMT/objcmt-subscripting-literals.m b/test/ARCMT/objcmt-subscripting-literals.m new file mode 100644 index 0000000000..1632bf91ad --- /dev/null +++ b/test/ARCMT/objcmt-subscripting-literals.m @@ -0,0 +1,137 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result + +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSString : NSObject ++ (id)stringWithString:(NSString *)string; +- (id)initWithString:(NSString *)aString; +@end + +@interface NSArray : NSObject +- (id)objectAtIndex:(unsigned long)index; +- (id)objectAtIndexedSubscript:(int)index; +@end + +@interface NSArray (NSArrayCreation) ++ (id)array; ++ (id)arrayWithObject:(id)anObject; ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; ++ (id)arrayWithObjects:(id)firstObj, ...; ++ (id)arrayWithArray:(NSArray *)array; + +- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt; +- (id)initWithObjects:(id)firstObj, ...; +- (id)initWithArray:(NSArray *)array; + +- (id)objectAtIndex:(unsigned long)index; +@end + +@interface NSMutableArray : NSArray +- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +@interface NSDictionary : NSObject +- (id)objectForKeyedSubscript:(id)key; +@end + +@interface NSDictionary (NSDictionaryCreation) ++ (id)dictionary; ++ (id)dictionaryWithObject:(id)object forKey:(id)key; ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; ++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; ++ (id)dictionaryWithDictionary:(NSDictionary *)dict; ++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +- (id)initWithObjectsAndKeys:(id)firstObject, ...; +- (id)initWithDictionary:(NSDictionary *)otherDictionary; +- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)objectForKey:(id)aKey; +@end + +@interface NSMutableDictionary : NSDictionary +- (void)setObject:(id)anObject forKey:(id)aKey; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithInt:(int)value; +@end + +#define M(x) (x) +#define PAIR(x) @#x, [NSNumber numberWithInt:(x)] +#define TWO(x) ((x), (x)) + +@interface I +@end +@implementation I +-(void) foo { + NSString *str; + NSArray *arr; + NSDictionary *dict; + + arr = [NSArray array]; + arr = [NSArray arrayWithObject:str]; + arr = [NSArray arrayWithObjects:str, str, nil]; + dict = [NSDictionary dictionary]; + dict = [NSDictionary dictionaryWithObject:arr forKey:str]; + dict = [NSDictionary dictionaryWithObjectsAndKeys: @"value1", @"key1", @"value2", @"key2", nil]; + dict = [NSDictionary dictionaryWithObjectsAndKeys: PAIR(1), PAIR(2), nil]; + dict = [NSDictionary dictionaryWithObjectsAndKeys: + @"value1", @"key1", +#ifdef BLAH + @"value2", @"key2", +#else + @"value3", @"key3", +#endif + nil ]; + + id o = [arr objectAtIndex:2]; + o = [dict objectForKey:@"key"]; + o = TWO([dict objectForKey:@"key"]); + o = [NSDictionary dictionaryWithObject:[NSDictionary dictionary] forKey:@"key"]; + NSMutableArray *marr = 0; + NSMutableDictionary *mdict = 0; + [marr replaceObjectAtIndex:2 withObject:@"val"]; + [mdict setObject:@"value" forKey:@"key"]; + [marr replaceObjectAtIndex:2 withObject:[arr objectAtIndex:4]]; + [mdict setObject:[dict objectForKey:@"key2"] forKey:@"key"]; + [mdict setObject:[dict objectForKey:@"key2"] forKey: +#if 1 + @"key1" +#else + @"key2" +#endif + ]; + [mdict setObject:[dict objectForKey: +#if 2 + @"key3" +#else + @"key4" +#endif + ] forKey:@"key"]; + [mdict setObject:@"value" forKey:[dict objectForKey: +#if 3 + @"key5" +#else + @"key6" +#endif + ] ]; + [mdict setObject:@"val" forKey:[dict objectForKey:@"key2"]]; + [mdict setObject:[dict objectForKey:@"key1"] forKey:[dict objectForKey:[NSArray arrayWithObject:@"arrkey"]]]; + __strong NSArray **parr = 0; + o = [*parr objectAtIndex:2]; +} +@end diff --git a/test/ARCMT/objcmt-subscripting-literals.m.result b/test/ARCMT/objcmt-subscripting-literals.m.result new file mode 100644 index 0000000000..cc93cb4906 --- /dev/null +++ b/test/ARCMT/objcmt-subscripting-literals.m.result @@ -0,0 +1,137 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result + +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSString : NSObject ++ (id)stringWithString:(NSString *)string; +- (id)initWithString:(NSString *)aString; +@end + +@interface NSArray : NSObject +- (id)objectAtIndex:(unsigned long)index; +- (id)objectAtIndexedSubscript:(int)index; +@end + +@interface NSArray (NSArrayCreation) ++ (id)array; ++ (id)arrayWithObject:(id)anObject; ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; ++ (id)arrayWithObjects:(id)firstObj, ...; ++ (id)arrayWithArray:(NSArray *)array; + +- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt; +- (id)initWithObjects:(id)firstObj, ...; +- (id)initWithArray:(NSArray *)array; + +- (id)objectAtIndex:(unsigned long)index; +@end + +@interface NSMutableArray : NSArray +- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +@interface NSDictionary : NSObject +- (id)objectForKeyedSubscript:(id)key; +@end + +@interface NSDictionary (NSDictionaryCreation) ++ (id)dictionary; ++ (id)dictionaryWithObject:(id)object forKey:(id)key; ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; ++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; ++ (id)dictionaryWithDictionary:(NSDictionary *)dict; ++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +- (id)initWithObjectsAndKeys:(id)firstObject, ...; +- (id)initWithDictionary:(NSDictionary *)otherDictionary; +- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)objectForKey:(id)aKey; +@end + +@interface NSMutableDictionary : NSDictionary +- (void)setObject:(id)anObject forKey:(id)aKey; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithInt:(int)value; +@end + +#define M(x) (x) +#define PAIR(x) @#x, [NSNumber numberWithInt:(x)] +#define TWO(x) ((x), (x)) + +@interface I +@end +@implementation I +-(void) foo { + NSString *str; + NSArray *arr; + NSDictionary *dict; + + arr = @[]; + arr = @[str]; + arr = @[str, str]; + dict = @{}; + dict = @{str: arr}; + dict = @{@"key1": @"value1", @"key2": @"value2"}; + dict = [NSDictionary dictionaryWithObjectsAndKeys: PAIR(1), PAIR(2), nil]; + dict = [NSDictionary dictionaryWithObjectsAndKeys: + @"value1", @"key1", +#ifdef BLAH + @"value2", @"key2", +#else + @"value3", @"key3", +#endif + nil ]; + + id o = arr[2]; + o = dict[@"key"]; + o = TWO(dict[@"key"]); + o = @{@"key": @{}}; + NSMutableArray *marr = 0; + NSMutableDictionary *mdict = 0; + marr[2] = @"val"; + mdict[@"key"] = @"value"; + marr[2] = arr[4]; + mdict[@"key"] = dict[@"key2"]; + [mdict setObject:dict[@"key2"] forKey: +#if 1 + @"key1" +#else + @"key2" +#endif + ]; + mdict[@"key"] = [dict objectForKey: +#if 2 + @"key3" +#else + @"key4" +#endif + ]; + mdict[[dict objectForKey: +#if 3 + @"key5" +#else + @"key6" +#endif + ]] = @"value"; + mdict[dict[@"key2"]] = @"val"; + mdict[dict[@[@"arrkey"]]] = dict[@"key1"]; + __strong NSArray **parr = 0; + o = (*parr)[2]; +} +@end diff --git a/test/ARCMT/with-arc-mode-migrate.m b/test/ARCMT/with-arc-mode-migrate.m index 32bcad1950..468859478e 100644 --- a/test/ARCMT/with-arc-mode-migrate.m +++ b/test/ARCMT/with-arc-mode-migrate.m @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fsyntax-only -fobjc-arc -x objective-c %s.result -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t -fsyntax-only -fobjc-arc %s -// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t -fsyntax-only -fobjc-arc %s +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result // RUN: rm -rf %t @protocol NSObject diff --git a/test/ARCMT/with-arc-mode-migrate.m.result b/test/ARCMT/with-arc-mode-migrate.m.result index f060793f6e..dd34b99908 100644 --- a/test/ARCMT/with-arc-mode-migrate.m.result +++ b/test/ARCMT/with-arc-mode-migrate.m.result @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fsyntax-only -fobjc-arc -x objective-c %s.result -// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t -fsyntax-only -fobjc-arc %s -// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result +// RUN: %clang_cc1 -arcmt-migrate -mt-migrate-directory %t -fsyntax-only -fobjc-arc %s +// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result // RUN: rm -rf %t @protocol NSObject diff --git a/tools/arcmt-test/CMakeLists.txt b/tools/arcmt-test/CMakeLists.txt index 9227f8ee63..a0029b416f 100644 --- a/tools/arcmt-test/CMakeLists.txt +++ b/tools/arcmt-test/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_USED_LIBS clangARCMigrate + clangEdit clangRewrite ) diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile index c143e27f33..f5ca81a6d1 100644 --- a/tools/arcmt-test/Makefile +++ b/tools/arcmt-test/Makefile @@ -19,6 +19,6 @@ NO_INSTALL = 1 LINK_COMPONENTS := support mc USEDLIBS = clangIndex.a clangARCMigrate.a clangRewrite.a \ clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ - clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a + clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/arcmt-test.cpp b/tools/arcmt-test/arcmt-test.cpp index 881a058c2c..2dc8034e32 100644 --- a/tools/arcmt-test/arcmt-test.cpp +++ b/tools/arcmt-test/arcmt-test.cpp @@ -135,9 +135,8 @@ static bool checkForMigration(StringRef resourcesPath, } static void printResult(FileRemapper &remapper, raw_ostream &OS) { - CompilerInvocation CI; - remapper.applyMappings(CI); - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); // The changed files will be in memory buffers, print them. for (unsigned i = 0, e = PPOpts.RemappedFileBuffers.size(); i != e; ++i) { const llvm::MemoryBuffer *mem = PPOpts.RemappedFileBuffers[i].second; diff --git a/tools/c-arcmt-test/c-arcmt-test.c b/tools/c-arcmt-test/c-arcmt-test.c index 5522b33e23..b91d3e1b98 100644 --- a/tools/c-arcmt-test/c-arcmt-test.c +++ b/tools/c-arcmt-test/c-arcmt-test.c @@ -34,22 +34,51 @@ static int print_remappings(const char *path) { return 0; } +static int print_remappings_filelist(const char **files, unsigned numFiles) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappingsFromFileList(files, numFiles); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ static void print_usage(void) { fprintf(stderr, - "usage: c-arcmt-test -arcmt-migrate-directory <path>\n\n\n"); + "usage: c-arcmt-test -mt-migrate-directory <path>\n" + " c-arcmt-test <remap-file-path1> <remap-file-path2> ...\n\n\n"); } /***/ int carcmttest_main(int argc, const char **argv) { clang_enableStackTraces(); - if (argc == 3 && strncmp(argv[1], "-arcmt-migrate-directory", 24) == 0) + if (argc == 3 && strncmp(argv[1], "-mt-migrate-directory", 21) == 0) return print_remappings(argv[2]); + if (argc > 1) + return print_remappings_filelist(argv+1, argc-1); + print_usage(); return 1; } diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 539ae80058..c4c864bdbf 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -5,10 +5,12 @@ set( LLVM_USED_LIBS clangBasic clangCodeGen clangDriver + clangEdit clangFrontend clangIndex clangLex clangParse + clangEdit clangARCMigrate clangRewrite clangSema diff --git a/tools/driver/Makefile b/tools/driver/Makefile index c7d91a728d..d828f67868 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -36,7 +36,7 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ clangStaticAnalyzerCore.a \ clangAnalysis.a clangIndex.a clangARCMigrate.a clangRewrite.a \ - clangAST.a clangLex.a clangBasic.a + clangEdit.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/libclang/ARCMigrate.cpp b/tools/libclang/ARCMigrate.cpp index 58f8ba454d..5ee5cf6e4e 100644 --- a/tools/libclang/ARCMigrate.cpp +++ b/tools/libclang/ARCMigrate.cpp @@ -74,6 +74,47 @@ CXRemapping clang_getRemappings(const char *migrate_dir_path) { return remap.take(); } +CXRemapping clang_getRemappingsFromFileList(const char **filePaths, + unsigned numFiles) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + OwningPtr<Remap> remap(new Remap()); + + if (numFiles == 0) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "numFiles=0\n"; + return remap.take(); + } + + if (!filePaths) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "NULL filePaths\n"; + return 0; + } + + TextDiagnosticBuffer diagBuffer; + SmallVector<StringRef, 32> Files; + for (unsigned i = 0; i != numFiles; ++i) + Files.push_back(filePaths[i]); + + bool err = arcmt::getFileRemappingsFromFileList(remap->Vec, Files, + &diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappingsFromFileList\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return remap.take(); + } + + return remap.take(); +} + unsigned clang_remap_getNumFiles(CXRemapping map) { return static_cast<Remap *>(map)->Vec.size(); diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 742448d2ea..66a1710bac 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_USED_LIBS clangSerialization clangIndex clangSema + clangEdit clangAST clangLex clangBasic) diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile index 375f7f20fe..1fff166bbf 100644 --- a/tools/libclang/Makefile +++ b/tools/libclang/Makefile @@ -18,7 +18,8 @@ SHARED_LIBRARY = 1 LINK_COMPONENTS := support mc USEDLIBS = clangARCMigrate.a clangRewrite.a clangFrontend.a clangDriver.a \ clangSerialization.a \ - clangParse.a clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a + clangParse.a clangSema.a clangEdit.a clangAnalysis.a \ + clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 8645b15f7b..1900ac86c0 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -132,6 +132,7 @@ clang_getRange clang_getRangeEnd clang_getRangeStart clang_getRemappings +clang_getRemappingsFromFileList clang_getResultType clang_getSpecializedCursorTemplate clang_getSpellingLocation diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile index 7ec2b894a0..7c282d6b47 100644 --- a/unittests/Frontend/Makefile +++ b/unittests/Frontend/Makefile @@ -13,7 +13,7 @@ LINK_COMPONENTS := support mc USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ - clangIndex.a clangARCMigrate.a clangRewrite.a \ + clangIndex.a clangARCMigrate.a clangRewrite.a clangEdit.a \ clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp index 584ca10365..56581fcc63 100644 --- a/unittests/Lex/LexerTest.cpp +++ b/unittests/Lex/LexerTest.cpp @@ -66,10 +66,10 @@ TEST_F(LexerTest, LexAPI) { "N(INN(val)) N(NOF1) N(NOF2) N(val)"; MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); - SourceMgr.createMainFileIDForMemBuffer(buf); + FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(buf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); Preprocessor PP(Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, |