Index: clang-move/ClangMove.h =================================================================== --- clang-move/ClangMove.h +++ clang-move/ClangMove.h @@ -37,8 +37,9 @@ }; struct MoveDefinitionSpec { - // A fully qualified name, e.g. "X", "a::X". - std::string Name; + // A semicolon-separated list of fully qualified names, e.g. "Foo", + // "a::Foo;b::Foo". + std::string Names; // The file path of old header, can be relative path and absolute path. std::string OldHeader; // The file path of old cc, can be relative path and absolute path. Index: clang-move/ClangMove.cpp =================================================================== --- clang-move/ClangMove.cpp +++ clang-move/ClangMove.cpp @@ -287,31 +287,44 @@ : Spec(MoveSpec), FileToReplacements(FileToReplacements), OriginalRunningDirectory(OriginalRunningDirectory), FallbackStyle(FallbackStyle) { - Spec.Name = llvm::StringRef(Spec.Name).ltrim(':'); if (!Spec.NewHeader.empty()) CCIncludes.push_back("#include \"" + Spec.NewHeader + "\"\n"); } void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) { - std::string FullyQualifiedName = "::" + Spec.Name; + auto ParseNames = [](llvm::StringRef Names) { + SmallVector ClassNames; + Names.split(ClassNames, ';'); + return ClassNames; + }; + auto ClassNames = ParseNames(Spec.Names); + Optional> InMovedClassNames; + for (StringRef ClassName : ClassNames) { + const auto HasName = hasName(("::" + ClassName.ltrim(":")).str()); + if (InMovedClassNames) + InMovedClassNames = anyOf(*InMovedClassNames, HasName); + else + InMovedClassNames = HasName; + } + auto InOldHeader = isExpansionInFile( MakeAbsolutePath(OriginalRunningDirectory, Spec.OldHeader)); auto InOldCC = isExpansionInFile( MakeAbsolutePath(OriginalRunningDirectory, Spec.OldCC)); auto InOldFiles = anyOf(InOldHeader, InOldCC); auto InMovedClass = - hasDeclContext(cxxRecordDecl(hasName(FullyQualifiedName))); + hasDeclContext(cxxRecordDecl(*InMovedClassNames)); // Match moved class declarations. auto MovedClass = cxxRecordDecl( - InOldFiles, hasName(FullyQualifiedName), isDefinition(), + InOldFiles, *InMovedClassNames, isDefinition(), hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()))); Finder->addMatcher(MovedClass.bind("moved_class"), this); // Match moved class methods (static methods included) which are defined // outside moved class declaration. Finder->addMatcher(cxxMethodDecl(InOldFiles, - ofClass(hasName(FullyQualifiedName)), + ofClass(*InMovedClassNames), isDefinition()) .bind("class_method"), this); Index: clang-move/tool/ClangMoveMain.cpp =================================================================== --- clang-move/tool/ClangMoveMain.cpp +++ clang-move/tool/ClangMoveMain.cpp @@ -37,8 +37,10 @@ cl::OptionCategory ClangMoveCategory("clang-move options"); -cl::opt Name("name", cl::desc("The name of class being moved."), - cl::cat(ClangMoveCategory)); +cl::opt + Name("name", cl::desc("A semicolon-separated list of the names of classes " + "being moved, e.g. \"Foo\", \"a::Foo;b::Foo\"."), + cl::cat(ClangMoveCategory)); cl::opt OldHeader("old_header", @@ -86,7 +88,7 @@ tooling::RefactoringTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); move::ClangMoveTool::MoveDefinitionSpec Spec; - Spec.Name = Name; + Spec.Names = Name; Spec.OldHeader = OldHeader; Spec.NewHeader = NewHeader; Spec.OldCC = OldCC; Index: test/clang-move/Inputs/database_template.json =================================================================== --- test/clang-move/Inputs/database_template.json +++ test/clang-move/Inputs/database_template.json @@ -3,5 +3,10 @@ "directory": "$test_dir/build", "command": "clang++ -o test.o $test_dir/test.cpp", "file": "$test_dir/test.cpp" +}, +{ + "directory": "$test_dir/build", + "command": "clang++ -o test.o $test_dir/multiple_class_test.cpp", + "file": "$test_dir/multiple_class_test.cpp" } ] Index: test/clang-move/Inputs/multiple_class_test.h =================================================================== --- /dev/null +++ test/clang-move/Inputs/multiple_class_test.h @@ -0,0 +1,20 @@ +namespace a { +class Foo { +public: + int f(); +}; +} // namespace a + +namespace b { +class Foo2 { +public: + int f(); +}; +} // namespace b + +namespace c { +class Bar { +public: + int f(); +}; +} // namespace c Index: test/clang-move/Inputs/multiple_class_test.cpp =================================================================== --- /dev/null +++ test/clang-move/Inputs/multiple_class_test.cpp @@ -0,0 +1,19 @@ +#include "multiple_class_test.h" + +namespace a { +int Foo::f() { + return 0; +} +} // namespace a + +namespace b { +int Foo2::f() { + return 0; +} +} // namespace b + +namespace c { +int Bar::f() { + return 0; +} +} // namespace c Index: test/clang-move/move-multiple-classes.cpp =================================================================== --- /dev/null +++ test/clang-move/move-multiple-classes.cpp @@ -0,0 +1,44 @@ +// RUN: mkdir -p %T/clang-move/build +// RUN: sed 's|$test_dir|%/T/clang-move|g' %S/Inputs/database_template.json > %T/clang-move/compile_commands.json +// RUN: cp %S/Inputs/multiple_class_test* %T/clang-move/ +// RUN: cd %T/clang-move +// RUN: clang-move -name="a::Foo;b::Foo2" -new_cc=%T/clang-move/new_multiple_class_test.cpp -new_header=%T/clang-move/new_multiple_class_test.h -old_cc=%T/clang-move/multiple_class_test.cpp -old_header=../clang-move/multiple_class_test.h %T/clang-move/multiple_class_test.cpp +// RUN: FileCheck -input-file=%T/clang-move/new_multiple_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s +// RUN: FileCheck -input-file=%T/clang-move/new_multiple_class_test.h -check-prefix=CHECK-NEW-TEST-H %s +// RUN: FileCheck -input-file=%T/clang-move/multiple_class_test.cpp -check-prefix=CHECK-OLD-TEST-CPP %s +// RUN: FileCheck -input-file=%T/clang-move/multiple_class_test.h -check-prefix=CHECK-OLD-TEST-H %s +// +// CHECK-OLD-TEST-H: namespace c { +// CHECK-OLD-TEST-H: class Bar { +// CHECK-OLD-TEST-H: public: +// CHECK-OLD-TEST-H: int f(); +// CHECK-OLD-TEST-H: }; +// CHECK-OLD-TEST-H: } // namespace c + +// CHECK-OLD-TEST-CPP: #include "{{.*}}multiple_class_test.h" +// CHECK-OLD-TEST-CPP: namespace c { +// CHECK-OLD-TEST-CPP: int Bar::f() { +// CHECK-OLD-TEST-CPP: return 0; +// CHECK-OLD-TEST-CPP: } +// CHECK-OLD-TEST-CPP: } // namespace c + +// CHECK-NEW-TEST-H: namespace a { +// CHECK-NEW-TEST-H: class Foo { +// CHECK-NEW-TEST-H: public: +// CHECK-NEW-TEST-H: int f(); +// CHECK-NEW-TEST-H: }; +// CHECK-NEW-TEST-H: } // namespace a +// CHECK-NEW-TEST-H: namespace b { +// CHECK-NEW-TEST-H: class Foo2 { +// CHECK-NEW-TEST-H: public: +// CHECK-NEW-TEST-H: int f(); +// CHECK-NEW-TEST-H: }; +// CHECK-NEW-TEST-H: } // namespace b + +// CHECK-NEW-TEST-CPP: #include "{{.*}}new_multiple_class_test.h" +// CHECK-NEW-TEST-CPP: namespace a { +// CHECK-NEW-TEST-CPP: int Foo::f() { return 0; } +// CHECK-NEW-TEST-CPP: } // namespace a +// CHECK-NEW-TEST-CPP: namespace b { +// CHECK-NEW-TEST-CPP: int Foo2::f() { return 0; } +// CHECK-NEW-TEST-CPP: } // namespace b Index: unittests/clang-move/ClangMoveTests.cpp =================================================================== --- unittests/clang-move/ClangMoveTests.cpp +++ unittests/clang-move/ClangMoveTests.cpp @@ -210,7 +210,7 @@ TEST(ClangMove, MoveHeaderAndCC) { move::ClangMoveTool::MoveDefinitionSpec Spec; - Spec.Name = "a::b::Foo"; + Spec.Names = "a::b::Foo"; Spec.OldHeader = "foo.h"; Spec.OldCC = "foo.cc"; Spec.NewHeader = "new_foo.h"; @@ -225,7 +225,7 @@ TEST(ClangMove, MoveHeaderOnly) { move::ClangMoveTool::MoveDefinitionSpec Spec; - Spec.Name = "a::b::Foo"; + Spec.Names = "a::b::Foo"; Spec.OldHeader = "foo.h"; Spec.NewHeader = "new_foo.h"; auto Results = runClangMoveOnCode(Spec); @@ -236,7 +236,7 @@ TEST(ClangMove, MoveCCOnly) { move::ClangMoveTool::MoveDefinitionSpec Spec; - Spec.Name = "a::b::Foo"; + Spec.Names = "a::b::Foo"; Spec.OldCC = "foo.cc"; Spec.NewCC = "new_foo.cc"; std::string ExpectedHeader = "#include \"foo.h\"\n\n"; @@ -248,7 +248,7 @@ TEST(ClangMove, MoveNonExistClass) { move::ClangMoveTool::MoveDefinitionSpec Spec; - Spec.Name = "NonExistFoo"; + Spec.Names = "NonExistFoo"; Spec.OldHeader = "foo.h"; Spec.OldCC = "foo.cc"; Spec.NewHeader = "new_foo.h";