From: Argyrios Kyrtzidis Date: Wed, 11 Dec 2013 21:39:06 +0000 (+0000) Subject: [objcmt] When emitting a remap file, use a json format with the edit entries, instead... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4e3d3d342b21045bbcc18d50e345c72b438c711e;p=clang [objcmt] When emitting a remap file, use a json format with the edit entries, instead of applying the changes to a temp file directly. This allows to combine the edits when they can be different based on whether you saw the implementation or not, e.g. with the designated initializer migration. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@197076 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 3e429beded..b099f08ce9 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -416,44 +416,6 @@ bool arcmt::getFileRemappings(std::vector > & return false; } -bool arcmt::getFileRemappingsFromFileList( - std::vector > &remap, - ArrayRef remapFiles, - DiagnosticConsumer *DiagClient) { - bool hasErrorOccurred = false; - llvm::StringMap Uniquer; - - IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); - IntrusiveRefCntPtr Diags( - new DiagnosticsEngine(DiagID, new DiagnosticOptions, - DiagClient, /*ShouldOwnClient=*/false)); - - for (ArrayRef::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. diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index b2dcd4f9c7..5a140dba55 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Transforms.h" +#include "clang/ARCMigrate/ARCMT.h" #include "clang/ARCMigrate/ARCMTActions.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -29,6 +30,8 @@ #include "clang/AST/Attr.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" using namespace clang; using namespace arcmt; @@ -1631,6 +1634,84 @@ public: } }; +class JSONEditWriter : public edit::EditsReceiver { + SourceManager &SourceMgr; + llvm::raw_ostream &OS; + +public: + JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS) + : SourceMgr(SM), OS(OS) { + OS << "[\n"; + } + ~JSONEditWriter() { + OS << "]\n"; + } + +private: + struct EntryWriter { + SourceManager &SourceMgr; + llvm::raw_ostream &OS; + + EntryWriter(SourceManager &SM, llvm::raw_ostream &OS) + : SourceMgr(SM), OS(OS) { + OS << " {\n"; + } + ~EntryWriter() { + OS << " },\n"; + } + + void writeLoc(SourceLocation Loc) { + FileID FID; + unsigned Offset; + llvm::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc); + assert(!FID.isInvalid()); + SmallString<200> Path = + StringRef(SourceMgr.getFileEntryForID(FID)->getName()); + llvm::sys::fs::make_absolute(Path); + OS << " \"file\": \""; + OS.write_escaped(Path.str()) << "\",\n"; + OS << " \"offset\": " << Offset << ",\n"; + } + + void writeRemove(CharSourceRange Range) { + assert(Range.isCharRange()); + std::pair Begin = + SourceMgr.getDecomposedLoc(Range.getBegin()); + std::pair End = + SourceMgr.getDecomposedLoc(Range.getEnd()); + assert(Begin.first == End.first); + assert(Begin.second <= End.second); + unsigned Length = End.second - Begin.second; + + OS << " \"remove\": " << Length << ",\n"; + } + + void writeText(StringRef Text) { + OS << " \"text\": \""; + OS.write_escaped(Text) << "\",\n"; + } + }; + + virtual void insert(SourceLocation Loc, StringRef Text) { + EntryWriter Writer(SourceMgr, OS); + Writer.writeLoc(Loc); + Writer.writeText(Text); + } + + virtual void replace(CharSourceRange Range, StringRef Text) { + EntryWriter Writer(SourceMgr, OS); + Writer.writeLoc(Range.getBegin()); + Writer.writeRemove(Range); + Writer.writeText(Text); + } + + virtual void remove(CharSourceRange Range) { + EntryWriter Writer(SourceMgr, OS); + Writer.writeLoc(Range.getBegin()); + Writer.writeRemove(Range); + } +}; + } static bool @@ -1757,6 +1838,21 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { AnnotateImplicitBridging(Ctx); } + if (IsOutputFile) { + std::string Error; + llvm::raw_fd_ostream OS(MigrateDir.c_str(), Error, llvm::sys::fs::F_Binary); + if (!Error.empty()) { + unsigned ID = Ctx.getDiagnostics().getDiagnosticIDs()-> + getCustomDiagID(DiagnosticIDs::Error, Error); + Ctx.getDiagnostics().Report(ID); + return; + } + + JSONEditWriter Writer(Ctx.getSourceManager(), OS); + Editor->applyRewrites(Writer); + return; + } + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); RewritesReceiver Rec(rewriter); Editor->applyRewrites(Rec); @@ -1838,3 +1934,247 @@ ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, /*isOutputFile=*/true, WhiteList); } + +namespace { +struct EditEntry { + const FileEntry *File; + unsigned Offset; + unsigned RemoveLen; + std::string Text; + + EditEntry() : File(), Offset(), RemoveLen() {} +}; +} + +namespace llvm { +template<> struct llvm::DenseMapInfo { + static inline EditEntry getEmptyKey() { + EditEntry Entry; + Entry.Offset = unsigned(-1); + return Entry; + } + static inline EditEntry getTombstoneKey() { + EditEntry Entry; + Entry.Offset = unsigned(-2); + return Entry; + } + static unsigned getHashValue(const EditEntry& Val) { + llvm::FoldingSetNodeID ID; + ID.AddPointer(Val.File); + ID.AddInteger(Val.Offset); + ID.AddInteger(Val.RemoveLen); + ID.AddString(Val.Text); + return ID.ComputeHash(); + } + static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) { + return LHS.File == RHS.File && + LHS.Offset == RHS.Offset && + LHS.RemoveLen == RHS.RemoveLen && + LHS.Text == RHS.Text; + } +}; +} + +namespace { +class RemapFileParser { + FileManager &FileMgr; + +public: + RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { } + + bool parse(StringRef File, SmallVectorImpl &Entries) { + using namespace llvm::yaml; + + OwningPtr FileBuf; + if (llvm::MemoryBuffer::getFile(File, FileBuf)) + return true; + + llvm::SourceMgr SM; + Stream YAMLStream(FileBuf.take(), SM); + document_iterator I = YAMLStream.begin(); + if (I == YAMLStream.end()) + return true; + Node *Root = I->getRoot(); + if (!Root) + return true; + + SequenceNode *SeqNode = dyn_cast(Root); + if (!SeqNode) + return true; + + for (SequenceNode::iterator + AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) { + MappingNode *MapNode = dyn_cast(&*AI); + if (!MapNode) + continue; + parseEdit(MapNode, Entries); + } + + return false; + } + +private: + void parseEdit(llvm::yaml::MappingNode *Node, + SmallVectorImpl &Entries) { + using namespace llvm::yaml; + EditEntry Entry; + bool Ignore = false; + + for (MappingNode::iterator + KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) { + ScalarNode *KeyString = dyn_cast((*KVI).getKey()); + if (!KeyString) + continue; + SmallString<10> KeyStorage; + StringRef Key = KeyString->getValue(KeyStorage); + + ScalarNode *ValueString = dyn_cast((*KVI).getValue()); + if (!ValueString) + continue; + SmallString<64> ValueStorage; + StringRef Val = ValueString->getValue(ValueStorage); + + if (Key == "file") { + const FileEntry *FE = FileMgr.getFile(Val); + if (!FE) + Ignore = true; + Entry.File = FE; + } else if (Key == "offset") { + if (Val.getAsInteger(10, Entry.Offset)) + Ignore = true; + } else if (Key == "remove") { + if (Val.getAsInteger(10, Entry.RemoveLen)) + Ignore = true; + } else if (Key == "text") { + Entry.Text = Val; + } + } + + if (!Ignore) + Entries.push_back(Entry); + } +}; +} + +static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) { + SmallString<128> Buf; + unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, + Err.toStringRef(Buf)); + Diag.Report(ID); + return true; +} + +static std::string applyEditsToTemp(const FileEntry *FE, + ArrayRef Edits, + FileManager &FileMgr, + DiagnosticsEngine &Diag) { + using namespace llvm::sys; + + SourceManager SM(Diag, FileMgr); + FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); + LangOptions LangOpts; + edit::EditedSource Editor(SM, LangOpts); + for (ArrayRef::iterator + I = Edits.begin(), E = Edits.end(); I != E; ++I) { + const EditEntry &Entry = *I; + assert(Entry.File == FE); + SourceLocation Loc = + SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset); + CharSourceRange Range; + if (Entry.RemoveLen != 0) { + Range = CharSourceRange::getCharRange(Loc, + Loc.getLocWithOffset(Entry.RemoveLen)); + } + + edit::Commit commit(Editor); + if (Range.isInvalid()) { + commit.insert(Loc, Entry.Text); + } else if (Entry.Text.empty()) { + commit.remove(Range); + } else { + commit.replace(Range, Entry.Text); + } + Editor.commit(commit); + } + + Rewriter rewriter(SM, LangOpts); + RewritesReceiver Rec(rewriter); + Editor.applyRewrites(Rec); + + const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID); + SmallString<512> NewText; + llvm::raw_svector_ostream OS(NewText); + Buf->write(OS); + OS.flush(); + + SmallString<64> TempPath; + int FD; + if (fs::createTemporaryFile(path::filename(FE->getName()), + path::extension(FE->getName()), FD, + TempPath)) { + reportDiag("Could not create file: " + TempPath.str(), Diag); + return std::string(); + } + + llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true); + TmpOut.write(NewText.data(), NewText.size()); + TmpOut.close(); + + return TempPath.str(); +} + +bool arcmt::getFileRemappingsFromFileList( + std::vector > &remap, + ArrayRef remapFiles, + DiagnosticConsumer *DiagClient) { + bool hasErrorOccurred = false; + + FileSystemOptions FSOpts; + FileManager FileMgr(FSOpts); + RemapFileParser Parser(FileMgr); + + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr Diags( + new DiagnosticsEngine(DiagID, new DiagnosticOptions, + DiagClient, /*ShouldOwnClient=*/false)); + + typedef llvm::DenseMap > + FileEditEntriesTy; + FileEditEntriesTy FileEditEntries; + + llvm::DenseSet EntriesSet; + + for (ArrayRef::iterator + I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { + SmallVector Entries; + if (Parser.parse(*I, Entries)) + continue; + + for (SmallVectorImpl::iterator + EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) { + EditEntry &Entry = *EI; + if (!Entry.File) + continue; + std::pair::iterator, bool> + Insert = EntriesSet.insert(Entry); + if (!Insert.second) + continue; + + FileEditEntries[Entry.File].push_back(Entry); + } + } + + for (FileEditEntriesTy::iterator + I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) { + std::string TempFile = applyEditsToTemp(I->first, I->second, + FileMgr, *Diags); + if (TempFile.empty()) { + hasErrorOccurred = true; + continue; + } + + remap.push_back(std::make_pair(I->first->getName(), TempFile)); + } + + return hasErrorOccurred; +} diff --git a/test/ARCMT/designated-init-in-header/designated-init-in-header.m b/test/ARCMT/designated-init-in-header/designated-init-in-header.m new file mode 100644 index 0000000000..8286583b3c --- /dev/null +++ b/test/ARCMT/designated-init-in-header/designated-init-in-header.m @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -objcmt-migrate-designated-init -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -x objective-c %S/file1.m.in -triple x86_64-apple-darwin11 -fobjc-arc -migrate -o %t1.remap +// RUN: %clang_cc1 -objcmt-migrate-designated-init -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -x objective-c %S/file2.m.in -triple x86_64-apple-darwin11 -fobjc-arc -migrate -o %t2.remap +// RUN: c-arcmt-test %t1.remap %t2.remap | arcmt-test -verify-transformed-files %S/header1.h.result %S/file2.m.in.result diff --git a/test/ARCMT/designated-init-in-header/file1.m.in b/test/ARCMT/designated-init-in-header/file1.m.in new file mode 100644 index 0000000000..0201b32abd --- /dev/null +++ b/test/ARCMT/designated-init-in-header/file1.m.in @@ -0,0 +1,2 @@ +#include "header1.h" + diff --git a/test/ARCMT/designated-init-in-header/file2.m.in b/test/ARCMT/designated-init-in-header/file2.m.in new file mode 100644 index 0000000000..258159735a --- /dev/null +++ b/test/ARCMT/designated-init-in-header/file2.m.in @@ -0,0 +1,14 @@ +#include "header1.h" + +@implementation S1 +-(int)prop { return 0; } +-(void)setProp:(int)p {} ++(id)s1 { return 0; } +-(id)initWithFoo:(NSString*)foo +{ + self = [super init]; + if (self) { + } + return self; +} +@end diff --git a/test/ARCMT/designated-init-in-header/file2.m.in.result b/test/ARCMT/designated-init-in-header/file2.m.in.result new file mode 100644 index 0000000000..7465ed576f --- /dev/null +++ b/test/ARCMT/designated-init-in-header/file2.m.in.result @@ -0,0 +1,14 @@ +#include "header1.h" + +@implementation S1 +-(int)prop { return 0; } +-(void)setProp:(int)p {} ++(instancetype)s1 { return 0; } +-(instancetype)initWithFoo:(NSString*)foo +{ + self = [super init]; + if (self) { + } + return self; +} +@end diff --git a/test/ARCMT/designated-init-in-header/header1.h b/test/ARCMT/designated-init-in-header/header1.h new file mode 100644 index 0000000000..c5668cc460 --- /dev/null +++ b/test/ARCMT/designated-init-in-header/header1.h @@ -0,0 +1,14 @@ +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) + +@class NSString; + +@interface B1 +-(id)init; +@end + +@interface S1 : B1 +-(int)prop; +-(void)setProp:(int)p; ++(id)s1; +-(id)initWithFoo:(NSString*)foo; +@end diff --git a/test/ARCMT/designated-init-in-header/header1.h.result b/test/ARCMT/designated-init-in-header/header1.h.result new file mode 100644 index 0000000000..974175b1c3 --- /dev/null +++ b/test/ARCMT/designated-init-in-header/header1.h.result @@ -0,0 +1,13 @@ +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) + +@class NSString; + +@interface B1 +-(instancetype)init; +@end + +@interface S1 : B1 +@property (nonatomic) int prop; ++(instancetype)s1; +-(instancetype)initWithFoo:(NSString*)foo NS_DESIGNATED_INITIALIZER; +@end