ioeric updated this revision to Diff 71845.
ioeric marked 4 inline comments as done.
ioeric added a comment.
- Added MigrationEnvironment class and a dummy migrate-tool binary.


https://reviews.llvm.org/D24380

Files:
  CMakeLists.txt
  migrate-tool/AffectedFilesFinder.h
  migrate-tool/BuildManager.h
  migrate-tool/CMakeLists.txt
  migrate-tool/DummyMigrationEnvironment.cpp
  migrate-tool/DummyMigrationEnvironment.h
  migrate-tool/HeaderGenerator.cpp
  migrate-tool/HeaderGenerator.h
  migrate-tool/MigrateTool.cpp
  migrate-tool/MigrateTool.h
  migrate-tool/MigrationEnvironment.h
  migrate-tool/RefactoringManager.h
  migrate-tool/tool/CMakeLists.txt
  migrate-tool/tool/ClangMigrateTool.cpp
  test/CMakeLists.txt
  test/migrate-tool/Inputs/old.h
  test/migrate-tool/old.cpp
  unittests/CMakeLists.txt
  unittests/migrate-tool/CMakeLists.txt
  unittests/migrate-tool/HeaderBuildTest.cpp

Index: unittests/migrate-tool/HeaderBuildTest.cpp
===================================================================
--- /dev/null
+++ unittests/migrate-tool/HeaderBuildTest.cpp
@@ -0,0 +1,100 @@
+//===-- HeaderGeneratorTest.cpp - HeaderGenerator unit tests ----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGenerator.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace migrate_tool {
+
+TEST(HeaderGenerator, Empty) {
+  HeaderGenerator Hdr("a.h");
+  std::string Expected = "#ifndef A_H\n"
+                         "#define A_H\n"
+                         "\n"
+                         "#endif // A_H";
+  EXPECT_EQ(Expected, Hdr.generateContent());
+}
+
+TEST(HeaderGenerator, SingleAlias) {
+  HeaderGenerator Hdr("a/b/c.h");
+  Hdr.addAlias("na::nb::C", "x::y::Z");
+  std::string Expected = "#ifndef A_B_C_H\n"
+                         "#define A_B_C_H\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "using C = ::x::y::Z;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "#endif // A_B_C_H";
+  EXPECT_EQ(Expected, Hdr.generateContent());
+}
+
+TEST(HeaderGenerator, SingleAliasWithIncludes) {
+  HeaderGenerator Hdr("a/b/c.h");
+  Hdr.addInclude("x/y/z.h");
+  Hdr.addInclude("x/y/zz.h");
+  Hdr.addAlias("na::nb::C", "x::y::Z");
+  std::string Expected = "#ifndef A_B_C_H\n"
+                         "#define A_B_C_H\n"
+                         "#include \"x/y/z.h\"\n"
+                         "#include \"x/y/zz.h\"\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "using C = ::x::y::Z;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "#endif // A_B_C_H";
+  EXPECT_EQ(Expected, Hdr.generateContent());
+}
+
+TEST(HeaderGenerator, MultipleAliasInOneNamespace) {
+  HeaderGenerator Hdr("a/b/c.h");
+  Hdr.addAlias("na::nb::C", "x::y::Z");
+  Hdr.addAlias("na::nb::D", "x::y::D");
+  Hdr.addAlias("na::nb::Q", "x::y::Q");
+  std::string Expected = "#ifndef A_B_C_H\n"
+                         "#define A_B_C_H\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "using C = ::x::y::Z;\n"
+                         "using D = ::x::y::D;\n"
+                         "using Q = ::x::y::Q;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "#endif // A_B_C_H";
+  EXPECT_EQ(Expected, Hdr.generateContent());
+}
+
+TEST(HeaderGenerator, AliasesInMultipleNamespace) {
+  HeaderGenerator Hdr("a/b/c.h");
+  Hdr.addAlias("nb::Q", "x::Q");
+  Hdr.addAlias("na::nb::C", "x::y::Z");
+  Hdr.addAlias("na::nc::D", "x::y::D");
+  Hdr.addAlias("na::nb::Q", "x::y::Q");
+  std::string Expected = "#ifndef A_B_C_H\n"
+                         "#define A_B_C_H\n"
+                         "namespace nb {\n"
+                         "using Q = ::x::Q;\n"
+                         "} // namespace nb\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "using C = ::x::y::Z;\n"
+                         "using Q = ::x::y::Q;\n"
+                         "} // namespace nb\n"
+                         "namespace nc {\n"
+                         "using D = ::x::y::D;\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n"
+                         "#endif // A_B_C_H";
+  EXPECT_EQ(Expected, Hdr.generateContent());
+}
+
+} // namespace migrate_tool
+} // namespace clang
Index: unittests/migrate-tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/migrate-tool/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(MIGRATE_TOOL_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../migrate-tool REALPATH)
+include_directories(
+  ${MIGRATE_TOOL_SOURCE_DIR}
+  )
+
+# We'd like to clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(MigrateToolTests
+  HeaderBuildTest.cpp
+  )
+
+target_link_libraries(MigrateToolTests
+  migrateTool
+  clangBasic
+  clangFormat
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -9,3 +9,4 @@
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
 add_subdirectory(include-fixer)
+add_subdirectory(migrate-tool)
Index: test/migrate-tool/old.cpp
===================================================================
--- /dev/null
+++ test/migrate-tool/old.cpp
@@ -0,0 +1,27 @@
+// RUN: migrate-tool -old_header=%S/Inputs/old.h -new_header=%T/new.h -old_name=old::Old -new_name=new::New | FileCheck %s
+// RUN: cat %T/new.h | FileCheck %s -check-prefix=CHECK-HEADER
+// CHECK-HEADER: #ifndef {{.*}}_NEW_H
+// CHECK-HEADER-NEXT: #define {{.*}}_NEW_H
+// CHECK-HEADER-NEXT: #include "{{.*}}/old.h"
+// CHECK-HEADER-NEXT: namespace new {
+// CHECK-HEADER-NEXT:   using New = ::old::Old;
+// CHECK-HEADER-NEXT: } // namespace new
+// CHECK-HEADER-NEXT: #endif // {{.*}}_NEW_H
+
+// CHECK: Preparation phase...
+// CHECK-NEXT: Adding library "{{.*}}/new" with soucee files [{{.*}}/new.h]
+// CHECK-NEXT: Rename phase...
+// CHECK-NEXT: Getting affected files for symbol "old::Old": [dummy.h,dummy.cpp]
+// CHECK-NEXT: Renaming:
+// CHECK-NEXT:   old::Old to new::New
+// CHECK-NEXT: in files [dummy.h,dummy.cpp]
+// CHECK-NEXT: Adding includes [{{.*}}/new.h] to files [dummy.h,dummy.cpp]
+// CHECK-NEXT: Adding dependency "{{.*}}/new" to ""
+// CHECK-NEXT: Adding dependency "{{.*}}/new" to ""
+// CHECK-NEXT: Migrate phase...
+// CHECK-NEXT: Moving symbols [old::Old] from "{{.*}}/old.h" to "{{.*}}/new.h"
+// CHECK-NEXT: Renaming:
+// CHECK-NEXT:   old::Old to new::New
+// CHECK-NEXT: in files [{{.*}}/new.h]
+// CHECK-NEXT: Changing namespace from "old" to "new" in filles matching "{{.*}}/new.h"
+// CHECK-NEXT: Running change-namespace tool on files [{{.*}}/new.h]
Index: test/migrate-tool/Inputs/old.h
===================================================================
--- /dev/null
+++ test/migrate-tool/Inputs/old.h
@@ -0,0 +1,3 @@
+namespace old {
+class Old {};
+}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -48,6 +48,7 @@
   clang-reorder-fields
   clang-tidy
   find-all-symbols
+  migrate-tool
   modularize
   pp-trace
 
Index: migrate-tool/tool/ClangMigrateTool.cpp
===================================================================
--- /dev/null
+++ migrate-tool/tool/ClangMigrateTool.cpp
@@ -0,0 +1,86 @@
+//===-- ClangMigrateTool.cpp - Standalone migration tool ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DummyMigrationEnvironment.h"
+#include "MigrateTool.h"
+#include "clang/Basic/Version.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Signals.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+cl::OptionCategory MigrateToolCategory("Migrate tool options");
+
+enum CodebaseTy {
+  dummy, ///< Dummy codebase.
+};
+
+cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
+
+cl::opt<CodebaseTy> Codebase("codebase", cl::desc("Name of the codebase"),
+                             cl::values(clEnumVal(dummy, "Dummy codebase."),
+                                        clEnumValEnd),
+                             cl::init(dummy), cl::cat(MigrateToolCategory));
+
+cl::opt<std::string> OldName("old_name", cl::desc("The old symbol name."),
+                             cl::cat(MigrateToolCategory));
+
+cl::opt<std::string> NewName("new_name", cl::desc("The new symbol name."),
+                             cl::cat(MigrateToolCategory));
+
+cl::opt<std::string> OldHeader("old_header", cl::desc("The old header name."),
+                               cl::cat(MigrateToolCategory));
+
+cl::opt<std::string> NewHeader("new_header", cl::desc("The new header name."),
+                               cl::cat(MigrateToolCategory));
+
+static void PrintVersion() {
+  raw_ostream &OS = outs();
+  OS << clang::getClangToolFullVersion("migrate-tool") << '\n';
+}
+
+int migrateToolMain(int argc, const char **argv) {
+  sys::PrintStackTraceOnErrorSignal(argv[0]);
+  cl::HideUnrelatedOptions(MigrateToolCategory);
+  cl::SetVersionPrinter(PrintVersion);
+  cl::ParseCommandLineOptions(
+      argc, argv,
+      "A tool to migrate a C++ symbol (class, function etc).\n\n"
+      "This tool automates the entire proces of migrating a C++\n"
+      "symbol (class, function, enum etc.) including renaming a \n"
+      "symbol and/or moving a symbol across namespace and/or \n"
+      "files. Besides moving the symbol definition, it also takes \n"
+      "care of users of the symbol (i.e. update all references of \n"
+      "old symbol to new symbol) as well as build rules.\n");
+  if (Help)
+    cl::PrintHelpMessage();
+
+  migrate_tool::MigrateTool::MigrateSpec Spec(OldHeader, NewHeader, OldName,
+                                              NewName);
+  std::unique_ptr<migrate_tool::MigrationEnvironment> Env;
+  switch (Codebase) {
+  case dummy:
+    Env = migrate_tool::createDummyMigrationEnvironment();
+    break;
+  }
+  migrate_tool::MigrateTool Tool(Spec, *Env);
+  if (llvm::Error Err = Tool.run()) {
+    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+    return 1;
+  }
+  return 0;
+}
+
+} // namespace
+
+int main(int argc, const char **argv) {
+  return migrateToolMain(argc, argv);
+}
Index: migrate-tool/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ migrate-tool/tool/CMakeLists.txt
@@ -0,0 +1,13 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(migrate-tool
+  ClangMigrateTool.cpp
+  )
+
+target_link_libraries(migrate-tool
+  clangBasic
+  migrateTool
+  )
+
+install(TARGETS migrate-tool
+  RUNTIME DESTINATION bin)
Index: migrate-tool/RefactoringManager.h
===================================================================
--- /dev/null
+++ migrate-tool/RefactoringManager.h
@@ -0,0 +1,54 @@
+//===-- RefactoringManager.h - Perform refactoring on files. ------*- 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_TOOLS_EXTRA_MIGRATE_TOOL_REFACTORINGMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_REFACTORINGMANAGER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <set>
+
+namespace clang {
+namespace migrate_tool {
+
+// This defines interfaces for a codebase-dependent `RefactoringManager` that
+// performs some refactoring operations.
+class RefactoringManager {
+public:
+  // Adds #include directives in \p Includes to each file in \p Files.
+  virtual llvm::Error
+  addIncludesToFiles(const std::set<llvm::StringRef> &Includes,
+                     llvm::ArrayRef<std::string> Files) = 0;
+
+  // Applies all rename rules in \p Renames on all \p Files.
+  // Rename rules are a set of <Old Name, New Name> pair.
+  virtual llvm::Error renameSymbolsInFiles(
+      llvm::ArrayRef<std::pair<llvm::StringRef, llvm::StringRef>> Renames,
+      llvm::ArrayRef<std::string> Files) = 0;
+
+  // Changes namespace \p OldNamespace to \p NewNamespace in \p Files and
+  // the corresponding files in translation units that match the given \p
+  // FilePattern.
+  virtual llvm::Error changeNamespaceInFiles(
+      llvm::StringRef OldNamespace, llvm::StringRef NewNamespace,
+      llvm::StringRef FilePattern, llvm::ArrayRef<llvm::StringRef> Files) = 0;
+
+  // Moves definitions of \p Symbols from \p OldHeader and the corresponding
+  // source file to \p NewHeader and the new source file.
+  virtual llvm::Error
+  moveSymbolsAcrossFiles(llvm::ArrayRef<llvm::StringRef> Symbols,
+                         llvm::StringRef OldHeader,
+                         llvm::StringRef NewHeader) = 0;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_REFACTORINGMANAGER_H
Index: migrate-tool/MigrationEnvironment.h
===================================================================
--- /dev/null
+++ migrate-tool/MigrationEnvironment.h
@@ -0,0 +1,52 @@
+//===-- MigrationEnvironment.h - Codebase-dependent environment. --*- 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_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATIONENVIRONMENT_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATIONENVIRONMENT_H
+
+#include "BuildManager.h"
+#include "RefactoringManager.h"
+#include "AffectedFilesFinder.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// This contains codebase-dependent components including BuildManager,
+// RefactoringManager, and AffectedFilesFinder.
+class MigrationEnvironment {
+public:
+  MigrationEnvironment() = delete;
+  // Creates a migration environement containing codebase-dependent component.
+  // This also takes ownership of all objects passed in.
+
+  MigrationEnvironment(BuildManager *BuildMgr, RefactoringManager *RefactorMgr,
+                       AffectedFilesFinder *Finder)
+      : BuildMgr(BuildMgr), RefactorMgr(RefactorMgr), Finder(Finder) {}
+
+  BuildManager &getBuildManager() { return *BuildMgr; }
+
+  RefactoringManager &getRefactoringManager() { return *RefactorMgr; }
+
+  AffectedFilesFinder &getAffectedFilesFinder() { return *Finder; }
+
+private:
+  // A codebase-dependent `BuildManager` that takes care of build rules during
+  // the migration.
+  std::unique_ptr<BuildManager> BuildMgr;
+  // A codebase-dependent `RefactoringManager` that performs some refactoring
+  // operations.
+  std::unique_ptr<RefactoringManager> RefactorMgr;
+  // Finds files that are affected by refactoring actions.
+  std::unique_ptr<AffectedFilesFinder> Finder;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATIONENVIRONMENT_H
Index: migrate-tool/MigrateTool.h
===================================================================
--- /dev/null
+++ migrate-tool/MigrateTool.h
@@ -0,0 +1,92 @@
+//===-- MigrateTool.h - Migrate class, function etc. ------------*- 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_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATETOOL_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATETOOL_H
+
+#include "MigrationEnvironment.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// This tool automates the entire process of migrating a C++ symbol (class,
+// function, enum etc.) including renaming a symbol and/or moving a symbol
+// across namespace and/or files. Besides moving the symbol definition, it also
+// takes care of users of the symbol (i.e. update all references of old symbol
+// to new symbol) as well as build rules.
+// The migration path is divided into 3 phases:
+//   1. Preparation phase.
+//      Create an alias from the old symbol name to to new symbol name. If the
+//      symbol is to be moved acrossed files, a new temporary header containing
+//      type alias is created.
+//   2. Renaming phase.
+//      Update all affected projects (i.e. users of the old symbol) to use the
+//      new symbol name via the alias above.
+//   3. Migration phase.
+//      Actual migration happens at this phase after all affected projects are
+//      updated.
+//      Move the symbol definition to the new file if the symbol is to be moved
+//      to a different file.
+//      Rename the symbol (i.e. the definition) if the symbol is to be renamed.
+//      Change the namespace in which the symbol is defined if the symbol is to
+//      be moved to a different namespace.
+// With this migration path, we can divide the three phases into three separate
+// patches. For large scale changes where many projects are affected, the
+// renaming phase can be splitted into multiple smaller patches to make the
+// both refactoring and review process easier.
+class MigrateTool {
+public:
+  class MigrateSpec {
+  public:
+    MigrateSpec(llvm::StringRef OldHeader, llvm::StringRef NewHeader,
+                llvm::StringRef OldName, llvm::StringRef NewName)
+        : OldHeader(OldHeader), NewHeader(NewHeader), OldName(OldName),
+          NewName(NewName) {}
+
+    llvm::StringRef getOldHeader() const { return OldHeader; }
+    llvm::StringRef getNewHeader() const { return NewHeader; }
+    llvm::StringRef getOldName() const { return OldName; }
+    llvm::StringRef getNewName() const { return NewName; }
+
+  private:
+    std::string OldHeader;
+    std::string NewHeader;
+    std::string OldName;
+    std::string NewName;
+  };
+
+  // FIXME: support handling multiple `MigrateSpec`s in one run.
+  MigrateTool(const MigrateSpec &Spec, MigrationEnvironment &Env);
+
+  llvm::Error run();
+
+private:
+  // Preparation phase. See comment for the class above.
+  llvm::Error prepare();
+
+  // Renaming phase. See comment for the class above.
+  llvm::Error rename();
+
+  // Migration phase. See comment for the class above.
+  llvm::Error migrate();
+
+  // Create a new file with the given `Content`.
+  llvm::Error createFile(llvm::StringRef FilePath, llvm::StringRef Content);
+
+  MigrateSpec Spec;
+  // The environment contains codebase-dependent components like BuildManager,
+  // RefactoringManager, AffectedFilesFinder etc.
+  MigrationEnvironment &Env;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_MIGRATETOOL_H
Index: migrate-tool/MigrateTool.cpp
===================================================================
--- /dev/null
+++ migrate-tool/MigrateTool.cpp
@@ -0,0 +1,137 @@
+//===-- MigrateTool.cpp - Migrate class, function etc. ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MigrateTool.h"
+#include "HeaderGenerator.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+#include <fstream>
+#include <set>
+
+namespace clang {
+namespace migrate_tool {
+
+MigrateTool::MigrateTool(const MigrateSpec &Spec, MigrationEnvironment &Env)
+    : Spec(Spec), Env(Env) {}
+
+llvm::Error MigrateTool::createFile(llvm::StringRef FilePath,
+                                    llvm::StringRef Content) {
+  std::error_code EC;
+  llvm::raw_fd_ostream File(FilePath.str(), EC,
+                            llvm::sys::fs::OpenFlags::F_None);
+  if (EC)
+    return llvm::errorCodeToError(EC);
+  File << Content.str();
+  File.close();
+  return llvm::Error::success();
+}
+
+llvm::Error MigrateTool::prepare() {
+  llvm::outs() << "Preparation phase...\n";
+  HeaderGenerator NewHeader(Spec.getNewHeader());
+  if (Spec.getNewHeader() == Spec.getOldHeader()) {
+    // FIXME: rename class within the same file. No need for new header. Just
+    // insert the alias in the old header.
+    return llvm::make_error<llvm::StringError>(
+        "Inplace renaming is not supported yet.",
+        llvm::inconvertibleErrorCode());
+  }
+  // Construct a new header for the symbol alias.
+  NewHeader.addInclude(Spec.getOldHeader());
+  if (Spec.getNewName() != Spec.getOldName())
+    NewHeader.addAlias(Spec.getNewName(), Spec.getOldName());
+
+  // Create new header and build target.
+  std::string Code = NewHeader.generateContent();
+  if (auto Err = createFile(NewHeader.getName(), Code))
+    return Err;
+  Env.getBuildManager().addLibrary({NewHeader.getName()});
+
+  return llvm::Error::success();
+}
+
+llvm::Error MigrateTool::rename() {
+  llvm::outs() << "Rename phase...\n";
+  // Get all files that are affected by the migration, i.e. users of the symbol.
+  auto Files = Env.getAffectedFilesFinder().getAffectedFiles(Spec.getOldName());
+  if (!Files)
+    return Files.takeError();
+  llvm::SmallVector<std::pair<llvm::StringRef, llvm::StringRef>, 4> Renames;
+  std::set<llvm::StringRef> NewIncludes;
+  if (Spec.getOldName() != Spec.getNewName())
+    Renames.emplace_back(Spec.getOldName(), Spec.getNewName());
+  if (Spec.getOldHeader() != Spec.getNewHeader())
+    NewIncludes.insert(Spec.getNewHeader());
+  if (auto Err =
+          Env.getRefactoringManager().renameSymbolsInFiles(Renames, *Files))
+    return Err;
+  if (auto Err =
+          Env.getRefactoringManager().addIncludesToFiles(NewIncludes, *Files))
+    return Err;
+  // Add dependency to affected target.
+  std::string NewTarget =
+      Env.getBuildManager().getBuildTargetForFile(Spec.getNewHeader());
+  for (llvm::StringRef File : *Files)
+    Env.getBuildManager().addDependency(
+        Env.getBuildManager().getBuildTargetForFile(File), NewTarget);
+  return llvm::Error::success();
+}
+
+static inline llvm::StringRef
+extractNamespaceFromQualifiedName(llvm::StringRef QualifiedName) {
+  auto splitted = QualifiedName.rsplit(':');
+  return splitted.second.empty() ? "" : splitted.first.drop_back(1);
+}
+
+llvm::Error MigrateTool::migrate() {
+  llvm::outs() << "Migrate phase...\n";
+  // Move symbol definitions, rename symbol definitions, change
+  // surroudning namespaces, and update build rules if necessary.
+  llvm::SmallVector<std::pair<llvm::StringRef, llvm::StringRef>, 4> Renames;
+  if (Spec.getOldName() != Spec.getNewName())
+    Renames.emplace_back(Spec.getOldName(), Spec.getNewName());
+  llvm::SmallVector<llvm::StringRef, 4> MovedSymbols;
+  MovedSymbols.push_back(Spec.getOldName());
+  // FIXME: consider source files. Need to determine whether source files end
+  // with ".cpp" or ".cc" etc.
+  // FIXME: copy dependencies from old target to new target.
+  if (auto Err = Env.getRefactoringManager().moveSymbolsAcrossFiles(
+          MovedSymbols, Spec.getOldHeader(), Spec.getNewHeader()))
+    return Err;
+  if (auto Err = Env.getRefactoringManager().renameSymbolsInFiles(
+          Renames, {Spec.getNewHeader()}))
+    return Err;
+
+  llvm::StringRef OldNamespace =
+      extractNamespaceFromQualifiedName(Spec.getOldName());
+  llvm::StringRef NewNamespace =
+      extractNamespaceFromQualifiedName(Spec.getNewName());
+
+  llvm::SmallVector<llvm::StringRef, 4> Files;
+  Files.push_back(Spec.getNewHeader());
+  if (auto Err = Env.getRefactoringManager().changeNamespaceInFiles(
+          OldNamespace, NewNamespace, Spec.getNewHeader(), Files))
+    return Err;
+
+  return llvm::Error::success();
+}
+
+llvm::Error MigrateTool::run() {
+  if (auto Err = prepare())
+    return Err;
+  if (auto Err = rename())
+    return Err;
+  if (auto Err = migrate())
+    return Err;
+  return llvm::Error::success();
+}
+
+} // namespace migrate_tool
+} // namespace clang
Index: migrate-tool/HeaderGenerator.h
===================================================================
--- /dev/null
+++ migrate-tool/HeaderGenerator.h
@@ -0,0 +1,58 @@
+//===-- HeaderGenerator.h - Generate new headers. ----------------*- 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_TOOLS_EXTRA_MIGRATE_TOOL_HEADERGENERATOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_HEADERGENERATOR_H
+
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace clang {
+namespace migrate_tool {
+
+// This constructs a C++ header containing type aliases.
+class HeaderGenerator {
+public:
+  explicit HeaderGenerator(llvm::StringRef HeaderName);
+
+  // Returns the name of the header.
+  llvm::StringRef getName() const { return HeaderName; }
+
+  /// \brief Adds an `using` shadow declaration from \p TypeName to \p NewName.
+  /// Both names should be fully-qualified.
+  /// For example, if \p NewName is `a::b::New` and TypeName is `x::y::Old`.
+  /// Then, the following code will be added into the header:
+  /// \code
+  ///   namespace a {
+  ///   namespace b {
+  ///   using New = ::x::y::Old;
+  ///   } // namespace b
+  ///   } // namespace a
+  /// \endcode
+  void addAlias(llvm::StringRef NewName, llvm::StringRef TypeName);
+
+  // Adds an #include into the header.
+  void addInclude(llvm::StringRef IncludeHeader);
+
+  // Generates the header content containing given aliases and #include's.
+  // FIXME: consider code style.
+  std::string generateContent() const;
+
+private:
+  std::string HeaderName;
+  std::string HeaderGuard;
+  // Old symbol name, New symbol name pairs.
+  std::vector<std::pair<std::string, std::string>> Aliases;
+  std::vector<std::string> Includes;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_HEADERGENERATOR_H
Index: migrate-tool/HeaderGenerator.cpp
===================================================================
--- /dev/null
+++ migrate-tool/HeaderGenerator.cpp
@@ -0,0 +1,112 @@
+//===-- HeaderGenerator.cpp - Generate new headers. -------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGenerator.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <sstream>
+
+namespace clang {
+namespace migrate_tool {
+
+namespace {
+
+class Namespace {
+public:
+  Namespace(llvm::StringRef Name, bool IsTopLevel = false)
+      : Name(Name), IsTopLevel(IsTopLevel) {
+    assert(!(!Name.empty() && IsTopLevel) &&
+           "A namespace must be either a top-level namespace with no name or "
+           "not a top-level namespace with name.");
+  }
+
+  void addAlias(llvm::StringRef NewName, llvm::StringRef TypeName) {
+    std::string OS;
+    llvm::raw_string_ostream SS(OS);
+    SS << "using " << NewName.str() << " = ::" << TypeName.str() << ";";
+    SS.flush();
+    CodeBlocks.push_back(OS);
+  }
+
+  Namespace *addNestedNamespace(llvm::StringRef Name) {
+    auto Pair = NestedNamespaces.try_emplace(Name, Name);
+    return &Pair.first->getValue();
+  }
+
+  std::string GenerateNamespaceCodeBlock() const {
+    std::vector<std::string> Lines;
+    if (!IsTopLevel)
+      Lines.push_back("namespace " + Name + " {");
+    // Add code blocks.
+    Lines.insert(Lines.end(), CodeBlocks.begin(), CodeBlocks.end());
+    // Add nested namespaces.
+    // If there are multiple nested namespaces, add newlines around each
+    // namespace.
+    for (const auto &Entry : NestedNamespaces)
+      Lines.push_back(Entry.second.GenerateNamespaceCodeBlock());
+    if (!IsTopLevel)
+      Lines.push_back("} // namespace " + Name);
+    return llvm::join(Lines.begin(), Lines.end(), "\n");
+  }
+
+private:
+  std::string Name;
+  bool IsTopLevel;
+  std::vector<std::string> CodeBlocks;
+  llvm::StringMap<Namespace> NestedNamespaces;
+};
+
+} // namespace
+
+HeaderGenerator::HeaderGenerator(llvm::StringRef HeaderName)
+    : HeaderName(HeaderName), HeaderGuard(HeaderName) {
+  // FIXME: use clang-tidy to generate the header guard.
+  std::replace(HeaderGuard.begin(), HeaderGuard.end(), '/', '_');
+  std::replace(HeaderGuard.begin(), HeaderGuard.end(), '.', '_');
+  HeaderGuard = llvm::StringRef(HeaderGuard).upper();
+}
+
+void HeaderGenerator::addAlias(llvm::StringRef NewName, llvm::StringRef TypeName) {
+  Aliases.emplace_back(NewName, TypeName);
+}
+
+void HeaderGenerator::addInclude(llvm::StringRef IncludeHeader) {
+  Includes.push_back(IncludeHeader);
+}
+
+// FIXME: format generated code.
+std::string HeaderGenerator::generateContent() const {
+  std::vector<std::string> Lines;
+  Lines.push_back("#ifndef " + HeaderGuard);
+  Lines.push_back("#define " + HeaderGuard);
+  for (const auto &Include : Includes)
+    Lines.push_back("#include \"" + Include + "\"");
+
+  Namespace TopNs("", /*IsTopLevel=*/true);
+  // Generate namespces containing aliases.
+  for (const auto &Entry : Aliases) {
+    llvm::StringRef NewName = Entry.first;
+    llvm::SmallVector<llvm::StringRef, 4> NewNameSplitted;
+    NewName.split(NewNameSplitted, "::");
+    auto *CurNs = &TopNs;
+    for (auto I = NewNameSplitted.begin(), E = NewNameSplitted.end() - 1;
+         I != E; ++I)
+      CurNs = CurNs->addNestedNamespace(*I);
+    CurNs->addAlias(NewNameSplitted.back(), Entry.second);
+  }
+
+  Lines.push_back(TopNs.GenerateNamespaceCodeBlock());
+  Lines.push_back("#endif // " + HeaderGuard);
+  return llvm::join(Lines.begin(), Lines.end(), "\n");
+}
+
+} // namespace migrate_tool
+} // namespace clang
Index: migrate-tool/DummyMigrationEnvironment.h
===================================================================
--- /dev/null
+++ migrate-tool/DummyMigrationEnvironment.h
@@ -0,0 +1,65 @@
+//===-- DummyMigrationEnvironment.h - Dummy environment. --------*- 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_TOOLS_EXTRA_MIGRATE_TOOL_DUMMYMIGRATIONENVIRONMENT_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_DUMMYMIGRATIONENVIRONMENT_H
+
+#include "MigrationEnvironment.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// Creates a dummy migration environment whose components do not perform actual
+// work and only print messages.
+std::unique_ptr<MigrationEnvironment> createDummyMigrationEnvironment();
+
+class DummyBuildManager : public BuildManager {
+public:
+  bool addLibrary(llvm::ArrayRef<std::string> Sources,
+                  llvm::StringRef Name = llvm::StringRef()) override;
+
+  bool addDependency(llvm::StringRef BuildTarget,
+                     llvm::StringRef Dependency) override;
+
+  std::string getBuildTargetForFile(llvm::StringRef File) override;
+
+private:
+  llvm::StringMap<std::string> FileToBuildTarget;
+};
+
+class DummyRefactoringManager : public RefactoringManager {
+public:
+  llvm::Error addIncludesToFiles(const std::set<llvm::StringRef> &Includes,
+                                 llvm::ArrayRef<std::string> Files) override;
+
+  llvm::Error renameSymbolsInFiles(
+      llvm::ArrayRef<std::pair<llvm::StringRef, llvm::StringRef>> Renames,
+      llvm::ArrayRef<std::string> Files) override;
+
+  llvm::Error
+  changeNamespaceInFiles(llvm::StringRef OldNamespace,
+                         llvm::StringRef NewNamespace,
+                         llvm::StringRef FilePattern,
+                         llvm::ArrayRef<llvm::StringRef> Files) override;
+
+  llvm::Error moveSymbolsAcrossFiles(llvm::ArrayRef<llvm::StringRef> Symbols,
+                                     llvm::StringRef OldHeader,
+                                     llvm::StringRef NewHeader) override;
+};
+
+class DummyAffectedFilesFinder : public AffectedFilesFinder {
+public:
+  llvm::Expected<std::vector<std::string>>
+  getAffectedFiles(llvm::StringRef Symbol);
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_DUMMYMIGRATIONENVIRONMENT_H
Index: migrate-tool/DummyMigrationEnvironment.cpp
===================================================================
--- /dev/null
+++ migrate-tool/DummyMigrationEnvironment.cpp
@@ -0,0 +1,101 @@
+//===-- DummyMigrationEnvironment.cpp - Dummy environment. ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DummyMigrationEnvironment.h"
+
+namespace clang {
+namespace migrate_tool {
+
+bool DummyBuildManager::addLibrary(llvm::ArrayRef<std::string> Sources,
+                                   llvm::StringRef Name) {
+  if (Sources.empty())
+    return false;
+  // If `Name` is not specified, use the name of the first file as the library
+  // name.
+  if (Name.empty()) {
+    Name = Sources[0];
+    Name = Name.substr(0, Name.find_last_of('.'));
+  }
+  llvm::outs() << "Adding library \"" << Name << "\" with soucee files ["
+               << llvm::join(Sources.begin(), Sources.end(), ",") << "]\n";
+  for (auto Source : Sources)
+    FileToBuildTarget[Source] = Name;
+  return true;
+}
+
+bool DummyBuildManager::addDependency(llvm::StringRef BuildTarget,
+                                      llvm::StringRef Dependency) {
+  llvm::outs() << "Adding dependency \"" << Dependency << "\" to \""
+               << BuildTarget << "\"\n";
+  return true;
+}
+
+std::string DummyBuildManager::getBuildTargetForFile(llvm::StringRef File) {
+  return FileToBuildTarget[File];
+}
+
+llvm::Error DummyRefactoringManager::addIncludesToFiles(
+    const std::set<llvm::StringRef> &Includes,
+    llvm::ArrayRef<std::string> Files) {
+  llvm::outs() << "Adding includes ["
+               << llvm::join(Includes.begin(), Includes.end(), ",")
+               << "] to files [" << llvm::join(Files.begin(), Files.end(), ",")
+               << "]\n";
+  return llvm::Error::success();
+}
+
+llvm::Error DummyRefactoringManager::renameSymbolsInFiles(
+    llvm::ArrayRef<std::pair<llvm::StringRef, llvm::StringRef>> Renames,
+    llvm::ArrayRef<std::string> Files) {
+  llvm::outs() << "Renaming:\n";
+  for (auto Rename : Renames)
+    llvm::outs() << "  " << Rename.first << " to " << Rename.second << "\n";
+  llvm::outs() << "in files [" << llvm::join(Files.begin(), Files.end(), ",")
+               << "]\n";
+  return llvm::Error::success();
+}
+
+llvm::Error DummyRefactoringManager::changeNamespaceInFiles(
+    llvm::StringRef OldNamespace, llvm::StringRef NewNamespace,
+    llvm::StringRef FilePattern, llvm::ArrayRef<llvm::StringRef> Files) {
+  llvm::outs() << "Changing namespace from \"" << OldNamespace << "\" to \""
+               << NewNamespace << "\" in filles matching \"" << FilePattern
+               << "\"\n"
+               << "Running change-namespace tool on files ["
+               << llvm::join(Files.begin(), Files.end(), ",") << "]\n";
+  return llvm::Error::success();
+}
+
+llvm::Error DummyRefactoringManager::moveSymbolsAcrossFiles(
+    llvm::ArrayRef<llvm::StringRef> Symbols, llvm::StringRef OldHeader,
+    llvm::StringRef NewHeader) {
+  llvm::outs() << "Moving symbols ["
+               << llvm::join(Symbols.begin(), Symbols.end(), ",") << "] from \""
+               << OldHeader << "\" to \"" << NewHeader << "\"\n";
+  return llvm::Error::success();
+}
+
+llvm::Expected<std::vector<std::string>>
+DummyAffectedFilesFinder::getAffectedFiles(llvm::StringRef Symbol) {
+  std::vector<std::string> DummyFiles = {"dummy.h", "dummy.cpp"};
+  llvm::outs() << "Getting affected files for symbol \"" << Symbol << "\": ["
+               << llvm::join(DummyFiles.begin(), DummyFiles.end(), ",")
+               << "]\n";
+  return DummyFiles;
+}
+
+std::unique_ptr<MigrationEnvironment> createDummyMigrationEnvironment() {
+  return llvm::make_unique<MigrationEnvironment>(
+      new DummyBuildManager(), new DummyRefactoringManager(),
+      new DummyAffectedFilesFinder());
+}
+
+
+} // namespace migrate_tool
+} // namespace clang
Index: migrate-tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ migrate-tool/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(migrateTool
+  DummyMigrationEnvironment.cpp
+  HeaderGenerator.cpp
+  MigrateTool.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFormat
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
Index: migrate-tool/BuildManager.h
===================================================================
--- /dev/null
+++ migrate-tool/BuildManager.h
@@ -0,0 +1,39 @@
+//===-- BuildManager.h - Manages build targets ------------------*- 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_TOOLS_EXTRA_MIGRATE_TOOL_BUILDMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_BUILDMANAGER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// This defines interfaces for a codebase-dependent build manager.
+class BuildManager {
+public:
+  // Adds a new library build target with \p Sources as source files. The name
+  // of the new library will be \p Name if it is provided; otherwise, the name
+  // will be auto-generated.
+  virtual bool addLibrary(llvm::ArrayRef<std::string> Sources,
+                          llvm::StringRef Name = llvm::StringRef()) = 0;
+
+  // Adds a new dependency to the build target.
+  virtual bool addDependency(llvm::StringRef BuildTarget,
+                             llvm::StringRef Dependency) = 0;
+
+  // Returns the name of the build target containing \p File.
+  virtual std::string getBuildTargetForFile(llvm::StringRef File) = 0;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_BUILDMANAGER_H
Index: migrate-tool/AffectedFilesFinder.h
===================================================================
--- /dev/null
+++ migrate-tool/AffectedFilesFinder.h
@@ -0,0 +1,33 @@
+//===-- AffectedFilesFinder.h - Finds files affected by a refactor.-*- 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_TOOLS_EXTRA_MIGRATE_TOOL_AFFECTEDFILESINGFINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_AFFECTEDFILESINGFINDER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace clang {
+namespace migrate_tool {
+
+// This defines interface for finding files that are affected by a refactoring
+// action, e.g. renaming a symbol.
+class AffectedFilesFinder {
+public:
+  // Get all files that need to be updated when a symbol is renamed and/or
+  // moved.
+  virtual llvm::Expected<std::vector<std::string>>
+  getAffectedFiles(llvm::StringRef Symbol) = 0;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_AFFECTEDFILESINGFINDER_H
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -1,6 +1,7 @@
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-rename)
 add_subdirectory(clang-reorder-fields)
+add_subdirectory(migrate-tool)
 add_subdirectory(modularize)
 if(CLANG_ENABLE_STATIC_ANALYZER)
 add_subdirectory(clang-tidy)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to