r197076 - [objcmt] When emitting a remap file, use a json format with the edit entries, instead of applying the changes

Argyrios Kyrtzidis akyrtzi at gmail.com
Wed Dec 11 13:39:06 PST 2013


Author: akirtzidis
Date: Wed Dec 11 15:39:06 2013
New Revision: 197076

URL: http://llvm.org/viewvc/llvm-project?rev=197076&view=rev
Log:
[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.

Added:
    cfe/trunk/test/ARCMT/designated-init-in-header/
    cfe/trunk/test/ARCMT/designated-init-in-header/designated-init-in-header.m
    cfe/trunk/test/ARCMT/designated-init-in-header/file1.m.in
    cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in
    cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in.result
    cfe/trunk/test/ARCMT/designated-init-in-header/header1.h
    cfe/trunk/test/ARCMT/designated-init-in-header/header1.h.result
Modified:
    cfe/trunk/lib/ARCMigrate/ARCMT.cpp
    cfe/trunk/lib/ARCMigrate/ObjCMT.cpp

Modified: cfe/trunk/lib/ARCMigrate/ARCMT.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ARCMigrate/ARCMT.cpp?rev=197076&r1=197075&r2=197076&view=diff
==============================================================================
--- cfe/trunk/lib/ARCMigrate/ARCMT.cpp (original)
+++ cfe/trunk/lib/ARCMigrate/ARCMT.cpp Wed Dec 11 15:39:06 2013
@@ -416,44 +416,6 @@ bool arcmt::getFileRemappings(std::vecto
   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;
-
-  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
-  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
-      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
-                            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.

Modified: cfe/trunk/lib/ARCMigrate/ObjCMT.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ARCMigrate/ObjCMT.cpp?rev=197076&r1=197075&r2=197076&view=diff
==============================================================================
--- cfe/trunk/lib/ARCMigrate/ObjCMT.cpp (original)
+++ cfe/trunk/lib/ARCMigrate/ObjCMT.cpp Wed Dec 11 15:39:06 2013
@@ -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<FileID, unsigned> Begin =
+          SourceMgr.getDecomposedLoc(Range.getBegin());
+      std::pair<FileID, unsigned> 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::HandleTrans
       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::Create
                                     /*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<EditEntry> {
+  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<EditEntry> &Entries) {
+    using namespace llvm::yaml;
+
+    OwningPtr<llvm::MemoryBuffer> 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<SequenceNode>(Root);
+    if (!SeqNode)
+      return true;
+
+    for (SequenceNode::iterator
+           AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
+      MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
+      if (!MapNode)
+        continue;
+      parseEdit(MapNode, Entries);
+    }
+
+    return false;
+  }
+
+private:
+  void parseEdit(llvm::yaml::MappingNode *Node,
+                 SmallVectorImpl<EditEntry> &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<ScalarNode>((*KVI).getKey());
+      if (!KeyString)
+        continue;
+      SmallString<10> KeyStorage;
+      StringRef Key = KeyString->getValue(KeyStorage);
+
+      ScalarNode *ValueString = dyn_cast<ScalarNode>((*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<EditEntry> 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<EditEntry>::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<std::pair<std::string,std::string> > &remap,
+                        ArrayRef<StringRef> remapFiles,
+                        DiagnosticConsumer *DiagClient) {
+  bool hasErrorOccurred = false;
+
+  FileSystemOptions FSOpts;
+  FileManager FileMgr(FSOpts);
+  RemapFileParser Parser(FileMgr);
+
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+      new DiagnosticsEngine(DiagID, new DiagnosticOptions,
+                            DiagClient, /*ShouldOwnClient=*/false));
+
+  typedef llvm::DenseMap<const FileEntry *, std::vector<EditEntry> >
+      FileEditEntriesTy;
+  FileEditEntriesTy FileEditEntries;
+
+  llvm::DenseSet<EditEntry> EntriesSet;
+
+  for (ArrayRef<StringRef>::iterator
+         I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
+    SmallVector<EditEntry, 16> Entries;
+    if (Parser.parse(*I, Entries))
+      continue;
+
+    for (SmallVectorImpl<EditEntry>::iterator
+           EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
+      EditEntry &Entry = *EI;
+      if (!Entry.File)
+        continue;
+      std::pair<llvm::DenseSet<EditEntry>::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;
+}

Added: cfe/trunk/test/ARCMT/designated-init-in-header/designated-init-in-header.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/designated-init-in-header.m?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/designated-init-in-header.m (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/designated-init-in-header.m Wed Dec 11 15:39:06 2013
@@ -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

Added: cfe/trunk/test/ARCMT/designated-init-in-header/file1.m.in
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/file1.m.in?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/file1.m.in (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/file1.m.in Wed Dec 11 15:39:06 2013
@@ -0,0 +1,2 @@
+#include "header1.h"
+

Added: cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in Wed Dec 11 15:39:06 2013
@@ -0,0 +1,14 @@
+#include "header1.h"
+
+ at 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;
+}
+ at end

Added: cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in.result
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in.result?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in.result (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/file2.m.in.result Wed Dec 11 15:39:06 2013
@@ -0,0 +1,14 @@
+#include "header1.h"
+
+ at 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;
+}
+ at end

Added: cfe/trunk/test/ARCMT/designated-init-in-header/header1.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/header1.h?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/header1.h (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/header1.h Wed Dec 11 15:39:06 2013
@@ -0,0 +1,14 @@
+#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+
+ at class NSString;
+
+ at interface B1
+-(id)init;
+ at end
+
+ at interface S1 : B1
+-(int)prop;
+-(void)setProp:(int)p;
++(id)s1;
+-(id)initWithFoo:(NSString*)foo;
+ at end

Added: cfe/trunk/test/ARCMT/designated-init-in-header/header1.h.result
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/ARCMT/designated-init-in-header/header1.h.result?rev=197076&view=auto
==============================================================================
--- cfe/trunk/test/ARCMT/designated-init-in-header/header1.h.result (added)
+++ cfe/trunk/test/ARCMT/designated-init-in-header/header1.h.result Wed Dec 11 15:39:06 2013
@@ -0,0 +1,13 @@
+#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
+
+ at class NSString;
+
+ at interface B1
+-(instancetype)init;
+ at end
+
+ at interface S1 : B1
+ at property (nonatomic) int prop;
++(instancetype)s1;
+-(instancetype)initWithFoo:(NSString*)foo NS_DESIGNATED_INITIALIZER;
+ at end





More information about the cfe-commits mailing list