ioeric updated this revision to Diff 73446.
ioeric added a comment.
Herald added a subscriber: modocache.
- Add SymbolRenameSpec; replace addIncludesToFiles and renameSymbolsInFiles
with renameSymbolsInAffectedFiles.
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,154 @@
+//===-- 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 renameSymbolsInAffectedFiles(
+ AffectedFilesFinder &Finder, BuildManager &BuildMgr,
+ const RefactoringManager::SymbolRenameSpec &RenameSpec) 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,247 @@
+//===-- 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::renameSymbolsInAffectedFiles(
+ AffectedFilesFinder &Finder, BuildManager &BuildMgr,
+ const RefactoringManager::SymbolRenameSpec &RenameSpec) {
+ llvm::Expected<std::vector<std::string>> Files =
+ Finder.getAffectedFiles(RenameSpec.OldSymbol);
+ if (!Files)
+ return Files.takeError();
+
+ llvm::outs() << "Renaming:\n";
+ llvm::outs() << " " << RenameSpec.OldSymbol << " to " << RenameSpec.NewSymbol
+ << "\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 (auto &Definition : ParsedFile->SymbolDefinitions)
+ if (Definition == RenameSpec.OldSymbol)
+ Definition = RenameSpec.NewSymbol;
+
+ for (auto &Reference : ParsedFile->SymbolReferences)
+ if (Reference == RenameSpec.OldSymbol)
+ Reference = RenameSpec.NewSymbol;
+
+ if (!RenameSpec.NewHeader.empty())
+ ParsedFile->Includes.push_back(RenameSpec.NewHeader);
+
+ if (!RenameSpec.NewDependency.empty())
+ BuildMgr.addDependency(BuildMgr.getBuildTargetForFile(File),
+ RenameSpec.NewDependency);
+
+ 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
@@ -11,3 +11,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,67 @@
+//===-- 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 "AffectedFilesFinder.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() {}
+
+ // FIXME: when we support removing old header and old target, add OldHeader
+ // and OldDependency so that they can be removed from users.
+ struct SymbolRenameSpec {
+ SymbolRenameSpec(llvm::StringRef OldSymbol, llvm::StringRef NewSymbol)
+ : OldSymbol(OldSymbol), NewSymbol(NewSymbol) {}
+
+ std::string OldSymbol;
+ std::string NewSymbol;
+
+ // If specified, NewHeader #include is added to files that use OldSymbol.
+ std::string NewHeader = "";
+
+ // If specified, add the following dependencies to targets that reference
+ // the OldSymol.
+ std::string NewDependency = "";
+ };
+
+ virtual llvm::Error
+ renameSymbolsInAffectedFiles(AffectedFilesFinder &Finder,
+ BuildManager &BuildManager,
+ const SymbolRenameSpec &RenameSpec) = 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,114 @@
+//===-- 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";
+ RefactoringManager::SymbolRenameSpec RenameSpec(Spec.getOldName(),
+ Spec.getNewName());
+ if (Spec.getOldHeader() != Spec.getNewHeader()) {
+ // FIXME: if all symbols in the old header has been moved to the new header,
+ // we should also delete all #include's of the old header.
+ RenameSpec.NewHeader = Spec.getNewHeader();
+ RenameSpec.NewDependency =
+ Env.getBuildManager().getBuildTargetForFile(Spec.getNewHeader());
+ }
+
+ return Env.getRefactoringManager().renameSymbolsInAffectedFiles(
+ Env.getAffectedFilesFinder(), Env.getBuildManager(), RenameSpec);
+}
+
+// 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();
+ RefactoringManager::SymbolRenameSpec RenameSpec(Spec.getOldName(),
+ NewNameInOldNamespace);
+ IdentityFilesFinder IdentityFiles({Spec.getNewHeader()});
+ // 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().renameSymbolsInAffectedFiles(
+ IdentityFiles, Env.getBuildManager(), RenameSpec))
+ 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,113 @@
+//===-- 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,53 @@
+//===-- 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/ArrayRef.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;
+};
+
+// This simply returns a given set of files as the affected files.
+class IdentityFilesFinder : public AffectedFilesFinder {
+public:
+ IdentityFilesFinder(llvm::ArrayRef<std::string> Files) : Files(Files) {}
+
+ virtual llvm::Expected<std::vector<std::string>>
+ getAffectedFiles(llvm::StringRef Symbol) {
+ return Files;
+ }
+
+private:
+ std::vector<std::string> Files;
+};
+
+} // 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits