ioeric updated this revision to Diff 72635.
ioeric marked 2 inline comments as done.
ioeric added a comment.
- Replace dummy binary with unit test with dummy environemnt + fake codebase.


https://reviews.llvm.org/D24380

Files:
  CMakeLists.txt
  migrate-tool/AffectedFilesFinder.h
  migrate-tool/BuildManager.h
  migrate-tool/CMakeLists.txt
  migrate-tool/FileSystemManager.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
  unittests/CMakeLists.txt
  unittests/migrate-tool/CMakeLists.txt
  unittests/migrate-tool/DummyMigrateToolTest.cpp
  unittests/migrate-tool/DummyMigrationEnvironment.cpp
  unittests/migrate-tool/DummyMigrationEnvironment.h
  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/DummyMigrationEnvironment.h
===================================================================
--- /dev/null
+++ unittests/migrate-tool/DummyMigrationEnvironment.h
@@ -0,0 +1,157 @@
+//===-- 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"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// A representation of a file in the dummy codebase. It can be converted to
+// string in YAML format.
+class DummyFile {
+public:
+  DummyFile() {}
+
+  DummyFile(llvm::ArrayRef<std::string> Includes,
+            llvm::ArrayRef<std::string> SymbolDefinitions,
+            llvm::ArrayRef<std::string> SymbolReferences)
+      : Includes(Includes), SymbolDefinitions(SymbolDefinitions),
+        SymbolReferences(SymbolReferences) {}
+
+  std::string toYamlString();
+
+  // #include headers in this file.
+  std::vector<std::string> Includes;
+  // Fully qualified names of symbols that are defined in this file.
+  std::vector<std::string> SymbolDefinitions;
+  // Fully qualified names of symbols that are referenced in this file.
+  std::vector<std::string> SymbolReferences;
+};
+
+// A dummy `BuildManager` for the dummy codebase. The build rules of the
+// codebase is stored in a map from target names to build rules.
+class DummyBuildManager : public BuildManager {
+public:
+  // A build rule in the dummy build system.
+  class BuildRule {
+  public:
+    BuildRule() {}
+
+    BuildRule(llvm::StringRef TargetName, llvm::ArrayRef<std::string> Sources,
+              llvm::ArrayRef<std::string> Dependencies)
+        : TargetName(TargetName), Sources(Sources), Dependencies(Dependencies) {
+    }
+
+    bool operator==(const BuildRule &RHS) const {
+      return TargetName == RHS.TargetName && Sources == RHS.Sources &&
+             Dependencies == RHS.Dependencies;
+    }
+
+    std::string TargetName;
+    std::vector<std::string> Sources;
+    std::vector<std::string> Dependencies;
+  };
+
+  DummyBuildManager(llvm::StringMap<BuildRule> *BuildRules);
+
+  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:
+  // A reference to all build rules in the codebase.
+  llvm::StringMap<BuildRule> *BuildRules;
+  // A (cached) mapping from file paths to the target that contains it.
+  llvm::StringMap<std::string> FileToBuildTarget;
+};
+
+// This performas refactoring actions on dummy files in the codebase.
+class DummyRefactoringManager : public RefactoringManager {
+public:
+  DummyRefactoringManager(FileSystemManager *Files) : FileSystemMgr(Files) {}
+
+  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;
+
+private:
+  FileSystemManager *FileSystemMgr;
+};
+
+// This stores a set of pre-computed affected files, which is provided during
+// instantiation.
+class DummyAffectedFilesFinder : public AffectedFilesFinder {
+public:
+  DummyAffectedFilesFinder(llvm::ArrayRef<std::string> &AffectedFiles)
+      : AffectedFiles(AffectedFiles) {}
+
+  llvm::Expected<std::vector<std::string>>
+  getAffectedFiles(llvm::StringRef Symbol);
+
+private:
+  std::vector<std::string> AffectedFiles;
+};
+
+// This provides interfaces to access/modify the dummy file system, which is
+// just a mapping from file names to file content.
+class DummyFileSystemManager : public FileSystemManager {
+public:
+  DummyFileSystemManager(llvm::StringMap<std::string> *DummyFS)
+      : DummyFS(DummyFS) {}
+
+  bool exists(llvm::StringRef FilePath) override;
+
+  llvm::Expected<std::string> readFile(llvm::StringRef FilePath) override;
+
+  llvm::Error createFile(llvm::StringRef FilePath,
+                         llvm::StringRef Code) override;
+
+  void deleteFile(llvm::StringRef FilePath) override;
+
+private:
+  llvm::StringMap<std::string> *DummyFS;
+};
+
+// Creates a dummy migration environment with the dummy file system and dummy
+// build rules.
+// \param DummyFS A dummy file system where all files in the codebase live.
+// \param BuildRules A reference to all build rules in the codebase.
+// \param AffectedFiles A set of pre-computed affected files for a migration
+// action.
+std::unique_ptr<MigrationEnvironment> createDummyMigrationEnvironment(
+    llvm::StringMap<std::string> *DummyFS,
+    llvm::StringMap<DummyBuildManager::BuildRule> *BuildRules,
+    llvm::ArrayRef<std::string> AffectedFiles);
+
+} // namespace migrate_tool
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_DUMMYMIGRATIONENVIRONMENT_H
Index: unittests/migrate-tool/DummyMigrationEnvironment.cpp
===================================================================
--- /dev/null
+++ unittests/migrate-tool/DummyMigrationEnvironment.cpp
@@ -0,0 +1,256 @@
+//===-- 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"
+#include "llvm/Support/YAMLTraits.h"
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<clang::migrate_tool::DummyFile> {
+  static void mapping(IO &io, clang::migrate_tool::DummyFile &File) {
+    io.mapOptional("Includes", File.Includes);
+    io.mapOptional("SymbolDefinitions", File.SymbolDefinitions);
+    io.mapOptional("SymbolReferences", File.SymbolReferences);
+  }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace migrate_tool {
+
+std::string DummyFile::toYamlString() {
+  std::string Content;
+  llvm::raw_string_ostream OS(Content);
+  llvm::yaml::Output YOut(OS);
+  YOut << *this;
+  OS.flush();
+  return Content;
+}
+
+DummyBuildManager::DummyBuildManager(llvm::StringMap<BuildRule> *BuildRules)
+    : BuildRules(BuildRules) {
+  for (const auto &Rule : *BuildRules)
+    for (const auto &File : Rule.second.Sources)
+      FileToBuildTarget[File] = Rule.first();
+}
+
+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('.'));
+  }
+
+  auto Rule = BuildRules->find(Name);
+  if (Rule != BuildRules->end()) {
+    // Update the cached mapping from files to build targets.
+    for (const auto &Source : Rule->second.Sources) {
+      auto I = FileToBuildTarget.find(Source);
+      if (I != FileToBuildTarget.end())
+        FileToBuildTarget.erase(I);
+    }
+    // Override the existing rule.
+    Rule->second.Sources = Sources;
+    Rule->second.Dependencies.clear();
+  } else {
+    // Create a new rule.
+    (*BuildRules)[Name] = BuildRule(Name, Sources, {});
+  }
+  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) {
+  auto I = BuildRules->find(BuildTarget);
+  if (I == BuildRules->end())
+    return false;
+  I->second.Dependencies.push_back(Dependency);
+  return true;
+}
+
+std::string DummyBuildManager::getBuildTargetForFile(llvm::StringRef File) {
+  return FileToBuildTarget[File];
+}
+
+static llvm::Expected<DummyFile>
+readFileAndParse(llvm::StringRef FileName, FileSystemManager *FileSystemMgr) {
+  llvm::Expected<std::string> Content = FileSystemMgr->readFile(FileName);
+  if (!Content)
+    return Content.takeError();
+  llvm::yaml::Input YIn(*Content);
+  DummyFile ParsedFile;
+  YIn >> ParsedFile;
+  if (YIn.error())
+    return llvm::errorCodeToError(YIn.error());
+  return ParsedFile;
+}
+
+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";
+  for (const auto &File : Files) {
+    llvm::Expected<DummyFile> ParsedFile =
+        readFileAndParse(File, FileSystemMgr);
+    if (!ParsedFile)
+      return ParsedFile.takeError();
+    for (auto Include : Includes)
+      ParsedFile->Includes.push_back(Include);
+    FileSystemMgr->createFile(File, ParsedFile->toYamlString());
+  }
+  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";
+  }
+  for (const auto &File : Files) {
+    llvm::Expected<DummyFile> ParsedFile =
+        readFileAndParse(File, FileSystemMgr);
+    if (!ParsedFile)
+      return ParsedFile.takeError();
+    // Rename all definitions/references of OldSymbol to NewSymbol.
+    for (const auto &Rename : Renames) {
+      for (auto &Definition : ParsedFile->SymbolDefinitions)
+        if (Definition == Rename.first)
+          Definition = Rename.second;
+      for (auto &Reference : ParsedFile->SymbolReferences)
+        if (Reference == Rename.first)
+          Reference = Rename.second;
+    }
+    // Rename all references to OldSymbol to NewSymbol.
+    FileSystemMgr->createFile(File, ParsedFile->toYamlString());
+  }
+  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";
+  llvm::Regex RE(FilePattern);
+  for (auto File : Files) {
+    if (!RE.match(File)) continue;
+    llvm::Expected<DummyFile> ParsedFile =
+        readFileAndParse(File, FileSystemMgr);
+    if (!ParsedFile)
+      return ParsedFile.takeError();
+    for (auto &Symbol : ParsedFile->SymbolDefinitions) {
+      llvm::StringRef SymbolRef = Symbol;
+      if (!SymbolRef.consume_front(OldNamespace))
+        continue;
+      Symbol = (NewNamespace + SymbolRef).str();
+    }
+    FileSystemMgr->createFile(File, ParsedFile->toYamlString());
+  }
+  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";
+  llvm::Expected<DummyFile> OldHeaderFile =
+      readFileAndParse(OldHeader, FileSystemMgr);
+  if (!OldHeaderFile)
+    return OldHeaderFile.takeError();
+
+  DummyFile NewHeaderFile;
+  if (FileSystemMgr->exists(NewHeader)) {
+    llvm::Expected<DummyFile> ParsedNewHeader =
+        readFileAndParse(NewHeader, FileSystemMgr);
+    if (!ParsedNewHeader)
+      return ParsedNewHeader.takeError();
+    NewHeaderFile = *ParsedNewHeader;
+  }
+
+  std::vector<std::string> RemainingSymbols;
+  for (auto Symbol : Symbols) {
+    for (const auto &Definition : OldHeaderFile->SymbolDefinitions) {
+      if (Symbol == Definition)
+        NewHeaderFile.SymbolDefinitions.push_back(Symbol);
+      else
+        RemainingSymbols.push_back(Symbol);
+    }
+  }
+  OldHeaderFile->SymbolDefinitions = RemainingSymbols;
+  FileSystemMgr->createFile(OldHeader, OldHeaderFile->toYamlString());
+  FileSystemMgr->createFile(NewHeader, NewHeaderFile.toYamlString());
+  return llvm::Error::success();
+}
+
+llvm::Expected<std::vector<std::string>>
+DummyAffectedFilesFinder::getAffectedFiles(llvm::StringRef Symbol) {
+  return AffectedFiles;
+}
+
+bool DummyFileSystemManager::exists(llvm::StringRef FilePath) {
+  return DummyFS->find(FilePath) != DummyFS->end();
+}
+
+llvm::Expected<std::string>
+DummyFileSystemManager::readFile(llvm::StringRef FilePath) {
+  auto I = DummyFS->find(FilePath);
+  if (I != DummyFS->end())
+    return I->second;
+  return llvm::errorCodeToError(
+      std::make_error_code(std::errc::no_such_file_or_directory));
+}
+
+llvm::Error DummyFileSystemManager::createFile(llvm::StringRef FilePath,
+                                               llvm::StringRef Code) {
+  (*DummyFS)[FilePath] = Code;
+  return llvm::Error::success();
+}
+
+void DummyFileSystemManager::deleteFile(llvm::StringRef FilePath) {
+  auto I = DummyFS->find(FilePath);
+  if (I != DummyFS->end())
+    DummyFS->erase(I);
+}
+
+std::unique_ptr<MigrationEnvironment> createDummyMigrationEnvironment(
+    llvm::StringMap<std::string> *DummyFS,
+    llvm::StringMap<DummyBuildManager::BuildRule> *BuildRules,
+    llvm::ArrayRef<std::string> AffectedFiles) {
+  auto *Files = new DummyFileSystemManager(DummyFS);
+  return llvm::make_unique<MigrationEnvironment>(
+      new DummyBuildManager(BuildRules), new DummyRefactoringManager(Files),
+      new DummyAffectedFilesFinder(AffectedFiles), Files);
+}
+
+} // namespace migrate_tool
+} // namespace clang
Index: unittests/migrate-tool/DummyMigrateToolTest.cpp
===================================================================
--- /dev/null
+++ unittests/migrate-tool/DummyMigrateToolTest.cpp
@@ -0,0 +1,216 @@
+//===-- DummyMigrateToolTest.cpp - Dummy migrate tool unit tests ----------===//
+//
+//                     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 "HeaderGenerator.h"
+#include "MigrateTool.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace migrate_tool {
+namespace {
+
+class DummyMigrateToolTest : public ::testing::Test {
+ protected:
+  void setupMigrateTool(const MigrateTool::MigrateSpec &Spec,
+                        llvm::ArrayRef<std::string> AffectedFiles) {
+    Env = createDummyMigrationEnvironment(&DummyFS, &BuildRules, AffectedFiles);
+    Tool = llvm::make_unique<MigrateTool>(Spec, *Env);
+    EXPECT_NE(nullptr, Tool.get());
+  }
+
+  bool prepare() {
+    if (auto Err = Tool->prepare()) {
+      llvm::errs() << "Failed preparation phase: "
+                   << llvm::toString(std::move(Err)) << "\n";
+      return false;
+    }
+    return true;
+  }
+
+  bool rename() {
+    if (auto Err = Tool->rename()) {
+      llvm::errs() << "Failed preparation phase: "
+                   << llvm::toString(std::move(Err)) << "\n";
+      return false;
+    }
+    return true;
+  }
+
+  bool migrate() {
+    if (auto Err = Tool->migrate()) {
+      llvm::errs() << "Failed migration phase: "
+                   << llvm::toString(std::move(Err)) << "\n";
+      return false;
+    }
+    return true;
+  }
+
+  llvm::StringRef readFile(llvm::StringRef FileName) {
+    return DummyFS[FileName];
+  }
+
+  std::string createSingleAliasHeader(llvm::StringRef HeaderName,
+                                      llvm::StringRef OldSymbol,
+                                      llvm::StringRef NewSymbol,
+                                      llvm::StringRef Include) {
+    HeaderGenerator Hdr(HeaderName);
+    Hdr.addAlias(NewSymbol, OldSymbol);
+    Hdr.addInclude(Include);
+    return Hdr.generateContent();
+  }
+
+  llvm::StringMap<std::string> DummyFS;
+  llvm::StringMap<DummyBuildManager::BuildRule> BuildRules;
+
+private:
+  std::unique_ptr<MigrationEnvironment> Env;
+  std::unique_ptr<MigrateTool> Tool;
+};
+
+TEST_F(DummyMigrateToolTest, MoveWithoutAffectedFiles) {
+  llvm::StringRef OldSymbol = "x::y::Old";
+  llvm::StringRef NewSymbol = "a::b::New";
+  llvm::StringRef OldHeader = "old.h";
+  llvm::StringRef NewHeader = "new.h";
+  llvm::StringRef OldTarget = "old";
+  llvm::StringRef NewTarget = "new";
+
+  std::string OldHeaderContent =
+      DummyFile(/*Includes=*/{}, /*SymbolDefinitions=*/{OldSymbol},
+                /*SymbolReferences=*/{})
+          .toYamlString();
+  DummyFS[OldHeader] = OldHeaderContent;
+
+  std::string OldHeaderContentAfterMigration = DummyFile().toYamlString();
+
+  std::string NewHeaderContentAfterMigration =
+      DummyFile(/*Includes=*/{}, /*SymbolDefinitions=*/{NewSymbol},
+                /*SymbolReferences=*/{})
+          .toYamlString();
+
+  auto OldRule =
+      DummyBuildManager::BuildRule(OldTarget, /*Sources=*/{OldHeader},
+                                   /*Dependencies=*/{});
+  auto NewRule =
+      DummyBuildManager::BuildRule(NewTarget, /*Sources=*/{NewHeader},
+                                   /*Dependencies=*/{});
+  BuildRules[OldTarget] = OldRule;
+
+  setupMigrateTool(
+      MigrateTool::MigrateSpec(OldHeader, NewHeader, OldSymbol, NewSymbol),
+      /*AffectedFiles=*/{});
+
+  EXPECT_TRUE(prepare());
+  std::string AliasHeaderContent =
+      createSingleAliasHeader(NewHeader, OldSymbol, NewSymbol, OldHeader);
+  EXPECT_EQ(OldHeaderContent, DummyFS[OldHeader]);
+  EXPECT_EQ(AliasHeaderContent, DummyFS[NewHeader]);
+  EXPECT_EQ(OldRule, BuildRules[OldTarget]);
+  EXPECT_EQ(NewRule, BuildRules[NewTarget]);
+
+  EXPECT_TRUE(rename());
+  // No affected file, so everything should remain unchanged.
+  EXPECT_EQ(2u, DummyFS.size());
+  EXPECT_EQ(OldHeaderContent, DummyFS[OldHeader]);
+  EXPECT_EQ(AliasHeaderContent, DummyFS[NewHeader]);
+  EXPECT_EQ(OldRule, BuildRules[OldTarget]);
+  EXPECT_EQ(NewRule, BuildRules[NewTarget]);
+
+  EXPECT_TRUE(migrate());
+  EXPECT_EQ(2u, DummyFS.size());
+  EXPECT_EQ(OldHeaderContentAfterMigration, DummyFS[OldHeader]);
+  EXPECT_EQ(NewHeaderContentAfterMigration, DummyFS[NewHeader]);
+}
+
+TEST_F(DummyMigrateToolTest, MoveWithAffectedFiles) {
+  llvm::StringRef OldSymbol = "x::y::Old";
+  llvm::StringRef NewSymbol = "a::b::New";
+  llvm::StringRef OldHeader = "old.h";
+  llvm::StringRef NewHeader = "new.h";
+  llvm::StringRef OldTarget = "old";
+  llvm::StringRef NewTarget = "new";
+  llvm::StringRef AffectedFile = "user.h";
+  llvm::StringRef AffectedTarget = "user";
+
+  std::string OldHeaderContent =
+      DummyFile(/*Includes=*/{}, /*SymbolDefinitions=*/{OldSymbol},
+                /*SymbolReferences=*/{})
+          .toYamlString();
+  DummyFS[OldHeader] = OldHeaderContent;
+
+  std::string OldHeaderContentAfterMigration = DummyFile().toYamlString();
+
+  std::string NewHeaderContentAfterMigration =
+      DummyFile(/*Includes=*/{}, /*SymbolDefinitions=*/{NewSymbol},
+                /*SymbolReferences=*/{})
+          .toYamlString();
+
+  std::string OldAffectedFileContent =
+      DummyFile(/*Includes=*/{OldHeader}, /*SymbolDefinitions=*/{"x::y::Z"},
+                /*SymbolReferences=*/{OldSymbol})
+          .toYamlString();
+
+  std::string NewAffectedFileContent =
+      DummyFile(/*Includes=*/{OldHeader, NewHeader},
+                /*SymbolDefinitions=*/{"x::y::Z"},
+                /*SymbolReferences=*/{NewSymbol})
+          .toYamlString();
+  DummyFS[AffectedFile] = OldAffectedFileContent;
+
+  auto OldRule =
+      DummyBuildManager::BuildRule(OldTarget, /*Sources=*/{OldHeader},
+                                   /*Dependencies=*/{});
+  auto NewRule =
+      DummyBuildManager::BuildRule(NewTarget, /*Sources=*/{NewHeader},
+                                   /*Dependencies=*/{});
+  BuildRules[OldTarget] = OldRule;
+
+  auto OldAffectedRule =
+      DummyBuildManager::BuildRule(AffectedTarget, /*Sources=*/{AffectedFile},
+                                   /*Dependencies=*/{OldTarget});
+  auto NewAffectedRule =
+      DummyBuildManager::BuildRule(AffectedTarget, /*Sources=*/{AffectedFile},
+                                   /*Dependencies=*/{OldTarget, NewTarget});
+  BuildRules[AffectedTarget] = OldAffectedRule;
+
+  setupMigrateTool(
+      MigrateTool::MigrateSpec(OldHeader, NewHeader, OldSymbol, NewSymbol),
+      /*AffectedFiles=*/{AffectedFile});
+
+  EXPECT_TRUE(prepare());
+  std::string AliasHeaderContent =
+      createSingleAliasHeader(NewHeader, OldSymbol, NewSymbol, OldHeader);
+  EXPECT_EQ(OldHeaderContent, DummyFS[OldHeader]);
+  EXPECT_EQ(AliasHeaderContent, DummyFS[NewHeader]);
+  EXPECT_EQ(OldRule, BuildRules[OldTarget]);
+  EXPECT_EQ(NewRule, BuildRules[NewTarget]);
+
+  EXPECT_EQ(OldAffectedFileContent, DummyFS[AffectedFile]);
+  EXPECT_EQ(OldAffectedRule, BuildRules[AffectedTarget]);
+
+  EXPECT_TRUE(rename());
+  EXPECT_EQ(OldHeaderContent, DummyFS[OldHeader]);
+  EXPECT_EQ(AliasHeaderContent, DummyFS[NewHeader]);
+  EXPECT_EQ(OldRule, BuildRules[OldTarget]);
+  EXPECT_EQ(NewRule, BuildRules[NewTarget]);
+
+  EXPECT_EQ(NewAffectedFileContent, DummyFS[AffectedFile]);
+  EXPECT_EQ(NewAffectedRule, BuildRules[AffectedTarget]);
+
+  EXPECT_TRUE(migrate());
+  EXPECT_EQ(3u, DummyFS.size());
+  EXPECT_EQ(OldHeaderContentAfterMigration, DummyFS[OldHeader]);
+  EXPECT_EQ(NewHeaderContentAfterMigration, DummyFS[NewHeader]);
+  EXPECT_EQ(NewAffectedFileContent, DummyFS[AffectedFile]);
+}
+
+} // anonymous namespace
+} // namespace migrate_tool
+} // namespace clang
Index: unittests/migrate-tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/migrate-tool/CMakeLists.txt
@@ -0,0 +1,27 @@
+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
+  DummyMigrateToolTest.cpp
+  DummyMigrationEnvironment.cpp
+  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: migrate-tool/RefactoringManager.h
===================================================================
--- /dev/null
+++ migrate-tool/RefactoringManager.h
@@ -0,0 +1,55 @@
+//===-- 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:
+  virtual ~RefactoringManager() {}
+
+  // 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 files that matches
+  // \p FilePattern in translation units \p TUs .
+  virtual llvm::Error changeNamespaceInFiles(
+      llvm::StringRef OldNamespace, llvm::StringRef NewNamespace,
+      llvm::StringRef FilePattern, llvm::ArrayRef<llvm::StringRef> TUs) = 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,63 @@
+//===-- 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 "AffectedFilesFinder.h"
+#include "BuildManager.h"
+#include "FileSystemManager.h"
+#include "RefactoringManager.h"
+#include "clang/Basic/VirtualFileSystem.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, FileSystemManager *Files)
+      : BuildMgr(BuildMgr), RefactorMgr(RefactorMgr), Finder(Finder),
+        Files(Files) {}
+
+  BuildManager &getBuildManager() { return *BuildMgr; }
+
+  RefactoringManager &getRefactoringManager() { return *RefactorMgr; }
+
+  AffectedFilesFinder &getAffectedFilesFinder() { return *Finder; }
+
+  FileSystemManager &getFileSystemManager() { return *Files; }
+
+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;
+
+  // A file system dependent `FileSystemManager` that provides interfaces for
+  // accessing and modifying files in a file system.
+  std::unique_ptr<FileSystemManager> Files;
+};
+
+} // 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,88 @@
+//===-- 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);
+
+  // 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();
+
+private:
+  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,125 @@
+//===-- 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::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 =
+          Env.getFileSystemManager().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();
+}
+
+// Splits a qualified symbol name into a pair of namespace and unqialified name.
+static std::pair<llvm::StringRef, llvm::StringRef>
+splitQualifiedName(llvm::StringRef QualifiedName) {
+  auto splitted = QualifiedName.rsplit(':');
+  return splitted.second.empty()
+             ? std::make_pair("", QualifiedName)
+             : std::make_pair(splitted.first.drop_back(1), splitted.second);
+}
+
+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<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.
+  // Delete the alias header.
+  Env.getFileSystemManager().deleteFile(Spec.getNewHeader());
+  if (auto Err = Env.getRefactoringManager().moveSymbolsAcrossFiles(
+          MovedSymbols, Spec.getOldHeader(), Spec.getNewHeader()))
+    return Err;
+
+  auto SplittedOldName = splitQualifiedName(Spec.getOldName());
+  auto SplittedNewName = splitQualifiedName(Spec.getNewName());
+  std::string NewNameInOldNamespace =
+      (SplittedOldName.first + "::" + SplittedNewName.second).str();
+  llvm::SmallVector<std::pair<llvm::StringRef, llvm::StringRef>, 4> Renames;
+  Renames.emplace_back(Spec.getOldName(), NewNameInOldNamespace);
+
+  // FIXME: if the moved symbol are referenced in old header/sources, we also
+  // need to rename references of OldSymbol in those files after the definition
+  // of OldSymol is moved to new files.
+  if (auto Err = Env.getRefactoringManager().renameSymbolsInFiles(
+          Renames, {Spec.getNewHeader()}))
+    return Err;
+
+  llvm::SmallVector<llvm::StringRef, 4> Files;
+  Files.push_back(Spec.getNewHeader());
+  if (auto Err = Env.getRefactoringManager().changeNamespaceInFiles(
+          SplittedOldName.first, SplittedNewName.first, Spec.getNewHeader(),
+          Files))
+    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/FileSystemManager.h
===================================================================
--- /dev/null
+++ migrate-tool/FileSystemManager.h
@@ -0,0 +1,41 @@
+//===-- FileSystemManager.h - Reading/writing file systems. --------*- 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_FILESYSTEMMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_FILESYSTEMMANAGER_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace migrate_tool {
+
+// This provides interfaces for reading and writing files in a specific file
+// system.
+class FileSystemManager {
+public:
+  virtual ~FileSystemManager() {}
+
+  // Returns true a file with \p FilePath exists.
+  virtual bool exists(llvm::StringRef FilePath) = 0;
+
+  // Reads content of a file from the underlying file system.
+  virtual llvm::Expected<std::string> readFile(llvm::StringRef FilePath) = 0;
+
+  // Creates a file at \p FilePath with \p Content. If the file already exists,
+  // it will be overwritten.
+  virtual llvm::Error createFile(llvm::StringRef FilePath,
+                                 llvm::StringRef Code) = 0;
+
+  virtual void deleteFile(llvm::StringRef FilePath) = 0;
+};
+
+} // namespace migrate_tool
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_MIGRATE_TOOL_FILESYSTEMMANAGER_H
Index: migrate-tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ migrate-tool/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(migrateTool
+  HeaderGenerator.cpp
+  MigrateTool.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFormat
+  clangTooling
+  clangToolingCore
+  )
Index: migrate-tool/BuildManager.h
===================================================================
--- /dev/null
+++ migrate-tool/BuildManager.h
@@ -0,0 +1,51 @@
+//===-- 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, which
+// provides interfaces to create/modify build rules in the codebase and query
+// existing build rules.
+// Build rules are rules for generating build targets, which can be programs,
+// libraries, and tests.
+// Each build rule/target in the codebase is uniquely identified a string name.
+class BuildManager {
+public:
+  virtual ~BuildManager() {}
+
+  // Adds build rule with \p Sources as source files for a new library build
+  // target. The name of the new library will be \p Name if it is provided;
+  // otherwise, the name will be auto-generated.
+  // If there is existing target with name \p Name, the old rule will be
+  // overridden.
+  // \returns True if the rule is successfully added.
+  virtual bool addLibrary(llvm::ArrayRef<std::string> Sources,
+                          llvm::StringRef Name = llvm::StringRef()) = 0;
+
+  // Adds a new dependency to the build target.
+  // A dependency is a build target that \p BuildTarget depends on and needs in
+  // order to be genertaed.
+  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,38 @@
+//===-- 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:
+  virtual ~AffectedFilesFinder() {}
+
+  // Get all files that need to be updated when a symbol is renamed and/or
+  // moved.
+  // \param Symbol A fully qualified symbol name.
+  // \returns Absolute paths of all affected files if the symbol is found;
+  // otherwise, this returns an llvm::StringError.
+  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