Hi tareqsiraj, arielbernal, Sarcasm, klimek,

Introducing new tool 'migmerge' to merge and apply changes to headers as
found in header change description files generated by the C++11 Migrator.

CMake files updated to build new tool.

Includes a conflict test case.

http://llvm-reviews.chandlerc.com/D1382

Files:
  cpp11-migrate/Core/ApplyChangeDescriptions.cpp
  cpp11-migrate/Core/ApplyChangeDescriptions.h
  cpp11-migrate/Core/CMakeLists.txt
  cpp11-migrate/tool/CMakeLists.txt
  cpp11-migrate/tool/MergeMain.cpp
  test/CMakeLists.txt
  test/migmerge/conflict.cpp
  test/migmerge/conflict/common.h
  test/migmerge/conflict/expected.txt
  test/migmerge/conflict/file1.yaml
  test/migmerge/conflict/file2.yaml
  test/migmerge/conflict/file3.yaml
Index: cpp11-migrate/Core/ApplyChangeDescriptions.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/Core/ApplyChangeDescriptions.cpp
@@ -0,0 +1,172 @@
+#include "Core/ReplacementsYaml.h"
+
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+
+using namespace llvm;
+using namespace clang;
+
+typedef std::vector<HeaderChangeDocument> HeaderChangeDocs;
+typedef std::set<StringRef> UniqueFiles;
+
+void eatDiagnostics(const SMDiagnostic &, void *) {}
+
+error_code collectHeaderChangeDocs(const StringRef Directory,
+                                   HeaderChangeDocs &ChangeDocs,
+                                   UniqueFiles &FoundFiles) {
+  using namespace llvm::sys::fs;
+  using namespace llvm::sys::path;
+
+  error_code ErrorCode;
+
+  for (recursive_directory_iterator I(Directory, ErrorCode), E;
+       I != E && !ErrorCode; I.increment(ErrorCode)) {
+    StringRef Filename(filename(I->path()));
+    if (Filename == ".git") {
+      I.no_push();
+      continue;
+    }
+
+    if (extension(I->path()) != ".yaml")
+      continue;
+
+    OwningPtr<MemoryBuffer> Out;
+    error_code BufferError= MemoryBuffer::getFile(I->path(), Out);
+    if (BufferError) {
+      errs() << "Error reading " << I->path() << ": " << BufferError.message()
+             << "\n";
+      continue;
+    }
+
+    yaml::Input yin(Out->getBuffer());
+    yin.setDiagHandler(&eatDiagnostics);
+    HeaderChangeDocument Doc;
+    yin >> Doc;
+    if (yin.error()) {
+      // File doesn't appear to be a header change description. Ignore it.
+      continue;
+    }
+
+    // Only keep files that properly parse.
+    ChangeDocs.push_back(Doc);
+
+    FoundFiles.insert(Doc.HeaderFileName);
+  }
+
+  return ErrorCode;
+}
+
+bool applyChangeDescriptions(const StringRef Directory,
+                             DiagnosticsEngine &Diagnostics) {
+
+  // FIXME: Use Diagnostics for output
+
+  HeaderChangeDocs Docs;
+  UniqueFiles FoundFiles;
+
+  error_code ErrorCode;
+  ErrorCode = collectHeaderChangeDocs(Directory, Docs, FoundFiles);
+
+  if (ErrorCode) {
+    errs() << "Trouble iterating over directory '" << Directory
+           << "': " << ErrorCode.message() << "\n";
+    return false;
+  }
+
+  // FIXME: For multi-threading, use contents of FoundFiles to divvy up work
+  // among threads.
+
+  typedef StringMap<std::vector<clang::tooling::Replacement> >
+  GroupedReplacementsTy;
+
+  GroupedReplacementsTy GroupedReplacements(FoundFiles.size());
+
+  // Group all replacements for a single file together.
+  for (HeaderChangeDocs::const_iterator I = Docs.begin(), E = Docs.end();
+       I != E; ++I) {
+    GroupedReplacements[I->HeaderFileName]
+        .insert(GroupedReplacements[I->HeaderFileName].end(),
+                I->Replacements.begin(), I->Replacements.end());
+  }
+
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+
+  bool conflictsFound = false;
+
+  // Ask clang to deduplicate and report conflicts.
+  for (GroupedReplacementsTy::iterator I = GroupedReplacements.begin(),
+       E = GroupedReplacements.end(); I != E; ++I) {
+
+    const FileEntry *Entry = SM.getFileManager().getFile(I->getKey());
+    if (!Entry) {
+      errs() << "Described file '" << I->getKey()
+             << "' doesn't exist. Ignoring...\n";
+      continue;
+    }
+
+    std::vector<tooling::Range> Conflicts;
+    deduplicate(I->getValue(), Conflicts);
+
+    if (Conflicts.empty())
+      continue;
+
+    conflictsFound = true;
+
+    // Report conflicts
+
+    FileID FID = SM.translateFile(Entry);
+    if (FID.isInvalid())
+      FID = SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+
+    errs() << "There are conflicting changes to " << I->getKey() << ":\n";
+
+    // FIXME: Output something a little more user-friendly (e.g. unified diff?)
+    unsigned conflictCount = 1;
+    for (std::vector<tooling::Range>::const_iterator
+             ConflictI = Conflicts.begin(),
+             ConflictE = Conflicts.end();
+         ConflictI != ConflictE; ++ConflictI, ++conflictCount) {
+      errs() << "Conflict #" << conflictCount << "\n";
+      for (unsigned i = ConflictI->getOffset(),
+                    e = ConflictI->getOffset() + ConflictI->getLength();
+           i < e; ++i) {
+        const tooling::Replacement &R = I->getValue()[i];
+        if (R.getLength() == 0) {
+          errs() << "  Insert at " << SM.getLineNumber(FID, R.getOffset())
+                 << ":" << SM.getColumnNumber(FID, R.getOffset())
+                 << " " << R.getReplacementText() << "\n";
+        } else {
+          if (R.getReplacementText().empty())
+            errs() << "  Remove ";
+          else
+            errs() << "  Replace ";
+
+          errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
+                 << SM.getColumnNumber(FID, R.getOffset()) << "-"
+                 << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1)
+                 << ":"
+                 << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
+
+          if (R.getReplacementText().empty())
+            errs() << "\n";
+          else
+            errs() << " with \"" << R.getReplacementText() << "\"\n";
+        }
+      }
+    }
+  }
+
+  if (conflictsFound)
+    return false;
+
+  // FIXME: Time to apply 
+
+  return true;
+}
Index: cpp11-migrate/Core/ApplyChangeDescriptions.h
===================================================================
--- /dev/null
+++ cpp11-migrate/Core/ApplyChangeDescriptions.h
@@ -0,0 +1,30 @@
+//===-- Core/ApplyChangeDescriptions.h --------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the declaration for the base Transform class from
+/// which all transforms must subclass.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
+#define CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
+
+#include "llvm/ADT/StringRef.h"
+
+// Forward declarations
+
+namespace clang {
+class DiagnosticsEngine;
+} // namespace clang
+
+bool applyChangeDescriptions(const llvm::StringRef Directory,
+                             clang::DiagnosticsEngine &Diagnostics);
+
+#endif // CPP11_MIGRATE_APPLYCHANGEDESCRIPTIONS_H
Index: cpp11-migrate/Core/CMakeLists.txt
===================================================================
--- cpp11-migrate/Core/CMakeLists.txt
+++ cpp11-migrate/Core/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(migrateCore
+  ApplyChangeDescriptions.cpp
   FileOverrides.cpp
   SyntaxCheck.cpp
   Transforms.cpp
Index: cpp11-migrate/tool/CMakeLists.txt
===================================================================
--- cpp11-migrate/tool/CMakeLists.txt
+++ cpp11-migrate/tool/CMakeLists.txt
@@ -4,6 +4,10 @@
   Cpp11Migrate.cpp
   )
 
+set(LLVM_OPTIONAL_SOURCES
+  MergeMain.cpp
+  )
+
 # FIXME: Lib-ify the transforms to simplify the build rules.
 
 # For each transform subdirectory.
@@ -36,3 +40,22 @@
 
 install(TARGETS cpp11-migrate
   RUNTIME DESTINATION bin)
+
+################################################################################
+
+set(LLVM_OPTIONAL_SOURCES
+  Cpp11Migrate.cpp
+  )
+
+add_clang_executable(migmerge
+  MergeMain.cpp
+  )
+
+add_dependencies(migmerge
+  clang-headers
+  )
+
+target_link_libraries(migmerge
+  migrateCore
+  )
+
Index: cpp11-migrate/tool/MergeMain.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/tool/MergeMain.cpp
@@ -0,0 +1,26 @@
+#include "Core/ApplyChangeDescriptions.h"
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace clang;
+
+static cl::opt<std::string>
+Directory(cl::Positional, cl::Required,
+          cl::desc("<Search Root Directory>"));
+
+int main(int argc, char **argv) {
+  cl::ParseCommandLineOptions(argc, argv);
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+      DiagOpts.getPtr());
+
+  if (applyChangeDescriptions(Directory, Diagnostics))
+    return 0;
+  return 1;
+}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -27,7 +27,7 @@
   clang clang-headers FileCheck count not
 
   # Individual tools we test.
-  remove-cstr-calls cpp11-migrate modularize clang-tidy
+  remove-cstr-calls migmerge cpp11-migrate modularize clang-tidy
 
   # Unit tests
   ExtraToolsUnitTests
Index: test/migmerge/conflict.cpp
===================================================================
--- /dev/null
+++ test/migmerge/conflict.cpp
@@ -0,0 +1,5 @@
+// RUN: mkdir -p %T/conflict
+// RUN: for f in %S/conflict/*.yaml; do sed "s#\$(path)#%S/conflict#" $f > %T/conflict/`basename $f`; done
+// RUN: sed "s#\$(path)#%S/conflict#" %S/conflict/expected.txt > %T/conflict/expected.txt
+// RUN: not migmerge %T/conflict > %T/conflict/output.txt 2>&1
+// RUN: cmp %T/conflict/output.txt %T/conflict/expected.txt
Index: test/migmerge/conflict/common.h
===================================================================
--- /dev/null
+++ test/migmerge/conflict/common.h
@@ -0,0 +1,17 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+extern void ext(int (&)[5]);
+
+void func(int t) {
+  int ints[5];
+  for (unsigned i = 0; i < 5; ++i) {
+    ints[i] = t;
+  }
+
+  int *i = 0;
+
+  ext(ints);
+}
+
+#endif // COMMON_H
Index: test/migmerge/conflict/expected.txt
===================================================================
--- /dev/null
+++ test/migmerge/conflict/expected.txt
@@ -0,0 +1,11 @@
+There are conflicting changes to $(path)/common.h:
+Conflict #1
+  Replace 8:8-8:33 with "auto & i : ints"
+  Replace 8:8-8:33 with "int & elem : ints"
+Conflict #2
+  Replace 9:5-9:11 with "elem"
+  Replace 9:5-9:11 with "i"
+Conflict #3
+  Remove 12:3-12:14
+  Insert at 12:12 (int*)
+  Replace 12:12-12:12 with "nullptr"
Index: test/migmerge/conflict/file1.yaml
===================================================================
--- /dev/null
+++ test/migmerge/conflict/file1.yaml
@@ -0,0 +1,15 @@
+---
+TransformID:     "Blah"
+Replacements:
+  - Offset:          106
+    Length:          26
+    ReplacementText: "auto & i : ints"
+  - Offset:          140
+    Length:          7
+    ReplacementText: "i"
+  - Offset:          160
+    Length:          12
+    ReplacementText: ""
+HeaderFileName: "$(path)/common.h"
+SourceFileName: "source1.cpp"
+...
Index: test/migmerge/conflict/file2.yaml
===================================================================
--- /dev/null
+++ test/migmerge/conflict/file2.yaml
@@ -0,0 +1,15 @@
+---
+TransformID:     "Blah"
+Replacements:
+  - Offset:          106
+    Length:          26
+    ReplacementText: "int & elem : ints"
+  - Offset:          140
+    Length:          7
+    ReplacementText: "elem"
+  - Offset:          169
+    Length:          1
+    ReplacementText: "nullptr"
+HeaderFileName: "$(path)/common.h"
+SourceFileName: "source2.cpp"
+...
Index: test/migmerge/conflict/file3.yaml
===================================================================
--- /dev/null
+++ test/migmerge/conflict/file3.yaml
@@ -0,0 +1,9 @@
+---
+TransformID:     "Blah"
+Replacements:
+  - Offset:          169
+    Length:          0
+    ReplacementText: "(int*)"
+HeaderFileName: "$(path)/common.h"
+SourceFileName: "source3.cpp"
+...
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to