1 //===-- ClangMoveTests.cpp - clang-move unit tests ------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #include "unittests/Tooling/RewriterTestContext.h"
11 #include "clang/Format/Format.h"
12 #include "clang/Frontend/FrontendActions.h"
13 #include "clang/Frontend/TextDiagnosticPrinter.h"
14 #include "clang/Rewrite/Core/Rewriter.h"
15 #include "clang/Tooling/Refactoring.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "gmock/gmock-matchers.h"
19 #include "gtest/gtest.h"
27 const char TestHeader
[] = "namespace a {\n"
29 "template <typename T> class C2;\n"
31 "// This is a Foo class\n"
32 "// which is used in\n"
50 const char TestCC
[] = "#include \"foo.h\"\n"
57 "int kConstInt1 = 0;\n"
61 "static int kConstInt2 = 1;\n"
65 "static int help() {\n"
93 const char ExpectedTestHeader
[] = "namespace a {\n"
95 "template <typename T> class C2;\n"
103 "} // namespace a\n";
105 const char ExpectedTestCC
[] = "#include \"foo.h\"\n"
112 "int kConstInt1 = 0;\n"
116 "static int kConstInt2 = 1;\n"
120 "static int help() {\n"
133 "} // namespace a\n";
135 const char ExpectedNewHeader
[] = "#ifndef NEW_FOO_H\n"
136 "#define NEW_FOO_H\n"
139 "class C1; // test\n"
141 "template <typename T> class C2;\n"
143 "// This is a Foo class\n"
144 "// which is used in\n"
157 "#endif // NEW_FOO_H\n";
159 const char ExpectedNewCC
[] = "namespace a {\n"
166 "int kConstInt1 = 0;\n"
170 "static int kConstInt2 = 1;\n"
174 "static int help() {\n"
193 "} // namespace a\n";
196 const char WorkingDir
[] = "C:\\test";
198 const char WorkingDir
[] = "/test";
201 const char TestHeaderName
[] = "foo.h";
202 const char TestCCName
[] = "foo.cc";
204 std::map
<std::string
, std::string
>
205 runClangMoveOnCode(const move::MoveDefinitionSpec
&Spec
,
206 const char *const Header
= TestHeader
,
207 const char *const CC
= TestCC
,
208 DeclarationReporter
*const Reporter
= nullptr) {
209 clang::RewriterTestContext Context
;
211 llvm::SmallString
<16> Dir(WorkingDir
);
212 llvm::sys::path::native(Dir
);
213 Context
.InMemoryFileSystem
->setCurrentWorkingDirectory(Dir
);
215 std::map
<llvm::StringRef
, clang::FileID
> FileToFileID
;
217 auto CreateFiles
= [&Context
, &FileToFileID
](llvm::StringRef Name
,
218 llvm::StringRef Code
) {
220 FileToFileID
[Name
] = Context
.createInMemoryFile(Name
, Code
);
223 CreateFiles(Spec
.NewCC
, "");
224 CreateFiles(Spec
.NewHeader
, "");
225 CreateFiles(TestHeaderName
, Header
);
226 CreateFiles(TestCCName
, CC
);
228 std::map
<std::string
, tooling::Replacements
> FileToReplacements
;
229 ClangMoveContext MoveContext
= {Spec
, FileToReplacements
, Dir
.c_str(), "LLVM",
230 Reporter
!= nullptr};
232 auto Factory
= std::make_unique
<clang::move::ClangMoveActionFactory
>(
233 &MoveContext
, Reporter
);
235 tooling::runToolOnCodeWithArgs(
236 Factory
->create(), CC
, Context
.InMemoryFileSystem
,
237 {"-std=c++11", "-fparse-all-comments", "-I."}, TestCCName
, "clang-move",
238 std::make_shared
<PCHContainerOperations
>());
239 formatAndApplyAllReplacements(FileToReplacements
, Context
.Rewrite
, "llvm");
240 // The Key is file name, value is the new code after moving the class.
241 std::map
<std::string
, std::string
> Results
;
242 for (const auto &It
: FileToReplacements
) {
243 // The path may come out as "./foo.h", normalize to "foo.h".
244 SmallString
<32> FilePath (It
.first
);
245 llvm::sys::path::remove_dots(FilePath
);
246 Results
[FilePath
.str().str()] = Context
.getRewrittenText(FileToFileID
[FilePath
]);
251 TEST(ClangMove
, MoveHeaderAndCC
) {
252 move::MoveDefinitionSpec Spec
;
253 Spec
.Names
= {std::string("a::b::Foo")};
254 Spec
.OldHeader
= "foo.h";
255 Spec
.OldCC
= "foo.cc";
256 Spec
.NewHeader
= "new_foo.h";
257 Spec
.NewCC
= "new_foo.cc";
258 std::string ExpectedHeader
= "#include \"" + Spec
.NewHeader
+ "\"\n\n";
259 auto Results
= runClangMoveOnCode(Spec
);
260 EXPECT_EQ(ExpectedTestHeader
, Results
[Spec
.OldHeader
]);
261 EXPECT_EQ(ExpectedTestCC
, Results
[Spec
.OldCC
]);
262 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
263 EXPECT_EQ(ExpectedHeader
+ ExpectedNewCC
, Results
[Spec
.NewCC
]);
266 TEST(ClangMove
, MoveHeaderOnly
) {
267 move::MoveDefinitionSpec Spec
;
268 Spec
.Names
= {std::string("a::b::Foo")};
269 Spec
.OldHeader
= "foo.h";
270 Spec
.NewHeader
= "new_foo.h";
271 auto Results
= runClangMoveOnCode(Spec
);
272 EXPECT_EQ(2u, Results
.size());
273 EXPECT_EQ(ExpectedTestHeader
, Results
[Spec
.OldHeader
]);
274 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
277 TEST(ClangMove
, MoveCCOnly
) {
278 move::MoveDefinitionSpec Spec
;
279 Spec
.Names
= {std::string("a::b::Foo")};
280 Spec
.OldCC
= "foo.cc";
281 Spec
.NewCC
= "new_foo.cc";
282 std::string ExpectedHeader
= "#include \"foo.h\"\n\n";
283 auto Results
= runClangMoveOnCode(Spec
);
284 EXPECT_EQ(2u, Results
.size());
285 EXPECT_EQ(ExpectedTestCC
, Results
[Spec
.OldCC
]);
286 EXPECT_EQ(ExpectedHeader
+ ExpectedNewCC
, Results
[Spec
.NewCC
]);
289 TEST(ClangMove
, MoveNonExistClass
) {
290 move::MoveDefinitionSpec Spec
;
291 Spec
.Names
= {std::string("NonExistFoo")};
292 Spec
.OldHeader
= "foo.h";
293 Spec
.OldCC
= "foo.cc";
294 Spec
.NewHeader
= "new_foo.h";
295 Spec
.NewCC
= "new_foo.cc";
296 auto Results
= runClangMoveOnCode(Spec
);
297 EXPECT_EQ(0u, Results
.size());
300 TEST(ClangMove
, HeaderIncludeSelf
) {
301 move::MoveDefinitionSpec Spec
;
302 Spec
.Names
= {std::string("Foo")};
303 Spec
.OldHeader
= "foo.h";
304 Spec
.OldCC
= "foo.cc";
305 Spec
.NewHeader
= "new_foo.h";
306 Spec
.NewCC
= "new_foo.cc";
308 const char TestHeader
[] = "#ifndef FOO_H\n"
310 "#include \"foo.h\"\n"
313 const char TestCode
[] = "#include \"foo.h\"";
314 const char ExpectedNewHeader
[] = "#ifndef FOO_H\n"
316 "#include \"new_foo.h\"\n"
319 const char ExpectedNewCC
[] = "#include \"new_foo.h\"";
320 auto Results
= runClangMoveOnCode(Spec
, TestHeader
, TestCode
);
321 EXPECT_EQ("", Results
[Spec
.OldHeader
]);
322 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
323 EXPECT_EQ(ExpectedNewCC
, Results
[Spec
.NewCC
]);
326 TEST(ClangMove
, MoveAll
) {
327 std::vector
<std::string
> TestHeaders
= {
328 "class A {\npublic:\n int f();\n};",
329 // forward declaration.
330 "class B;\nclass A {\npublic:\n int f();\n};",
331 // template forward declaration.
332 "template <typename T> class B;\nclass A {\npublic:\n int f();\n};",
333 "namespace a {}\nclass A {\npublic:\n int f();\n};",
334 "namespace a {}\nusing namespace a;\nclass A {\npublic:\n int f();\n};",
336 const char Code
[] = "#include \"foo.h\"\nint A::f() { return 0; }";
337 move::MoveDefinitionSpec Spec
;
338 Spec
.Names
.push_back("A");
339 Spec
.OldHeader
= "foo.h";
340 Spec
.OldCC
= "foo.cc";
341 Spec
.NewHeader
= "new_foo.h";
342 Spec
.NewCC
= "new_foo.cc";
343 for (const auto& Header
: TestHeaders
) {
344 auto Results
= runClangMoveOnCode(Spec
, Header
.c_str(), Code
);
345 EXPECT_EQ(Header
, Results
[Spec
.NewHeader
]);
346 EXPECT_EQ("", Results
[Spec
.OldHeader
]);
347 EXPECT_EQ("", Results
[Spec
.OldCC
]);
351 TEST(ClangMove
, MoveAllMultipleClasses
) {
352 move::MoveDefinitionSpec Spec
;
353 std::vector
<std::string
> TestHeaders
= {
354 "class C;\nclass A {\npublic:\n int f();\n};\nclass B {};",
355 "class C;\nclass B;\nclass A {\npublic:\n int f();\n};\nclass B {};",
357 const char Code
[] = "#include \"foo.h\"\nint A::f() { return 0; }";
358 Spec
.Names
= {std::string("A"), std::string("B")};
359 Spec
.OldHeader
= "foo.h";
360 Spec
.OldCC
= "foo.cc";
361 Spec
.NewHeader
= "new_foo.h";
362 Spec
.NewCC
= "new_foo.cc";
363 for (const auto& Header
: TestHeaders
) {
364 auto Results
= runClangMoveOnCode(Spec
, Header
.c_str(), Code
);
365 EXPECT_EQ(Header
, Results
[Spec
.NewHeader
]);
366 EXPECT_EQ("", Results
[Spec
.OldHeader
]);
367 EXPECT_EQ("", Results
[Spec
.OldCC
]);
371 TEST(ClangMove
, DontMoveAll
) {
372 const char ExpectedHeader
[] = "#ifndef NEW_FOO_H\n"
373 "#define NEW_FOO_H\n"
375 "class A {\npublic:\n int f();\n};\n"
377 "#endif // NEW_FOO_H\n";
378 const char Code
[] = "#include \"foo.h\"\nint A::f() { return 0; }";
379 std::vector
<std::string
> TestHeaders
= {
380 "class B {};\nclass A {\npublic:\n int f();\n};\n",
381 "void f() {};\nclass A {\npublic:\n int f();\n};\n",
383 move::MoveDefinitionSpec Spec
;
384 Spec
.Names
.push_back("A");
385 Spec
.OldHeader
= "foo.h";
386 Spec
.OldCC
= "foo.cc";
387 Spec
.NewHeader
= "new_foo.h";
388 Spec
.NewCC
= "new_foo.cc";
389 for (const auto& Header
: TestHeaders
) {
390 auto Results
= runClangMoveOnCode(Spec
, Header
.c_str(), Code
);
391 EXPECT_EQ(ExpectedHeader
, Results
[Spec
.NewHeader
]);
392 // The expected old header should not contain class A definition.
393 std::string ExpectedOldHeader
= Header
.substr(0, Header
.size() - 32);
394 EXPECT_EQ(ExpectedOldHeader
, Results
[Spec
.OldHeader
]);
398 TEST(ClangMove
, IgnoreMacroSymbolsAndMoveAll
) {
399 const char TestCode
[] = "#include \"foo.h\"";
400 std::vector
<std::string
> TestHeaders
= {
401 "#define DEFINE_Foo int Foo = 1;\nDEFINE_Foo;\nclass Bar {};\n",
402 "#define DEFINE(x) int var_##x = 1;\nDEFINE(foo);\nclass Bar {};\n",
404 move::MoveDefinitionSpec Spec
;
405 Spec
.Names
.push_back("Bar");
406 Spec
.OldHeader
= "foo.h";
407 Spec
.OldCC
= "foo.cc";
408 Spec
.NewHeader
= "new_foo.h";
409 Spec
.NewCC
= "new_foo.cc";
411 for (const auto& Header
: TestHeaders
) {
412 auto Results
= runClangMoveOnCode(Spec
, Header
.c_str(), TestCode
);
413 EXPECT_EQ("", Results
[Spec
.OldHeader
]);
414 EXPECT_EQ(Header
, Results
[Spec
.NewHeader
]);
418 TEST(ClangMove
, MacroInFunction
) {
419 const char TestHeader
[] = "#define INT int\n"
420 "class A {\npublic:\n int f();\n};\n"
422 const char TestCode
[] = "#include \"foo.h\"\n"
423 "INT A::f() { return 0; }\n";
424 const char ExpectedNewCode
[] = "#include \"new_foo.h\"\n\n"
425 "INT A::f() { return 0; }\n";
426 move::MoveDefinitionSpec Spec
;
427 Spec
.Names
.push_back("A");
428 Spec
.OldHeader
= "foo.h";
429 Spec
.OldCC
= "foo.cc";
430 Spec
.NewHeader
= "new_foo.h";
431 Spec
.NewCC
= "new_foo.cc";
432 auto Results
= runClangMoveOnCode(Spec
, TestHeader
, TestCode
);
433 EXPECT_EQ(ExpectedNewCode
, Results
[Spec
.NewCC
]);
436 TEST(ClangMove
, DefinitionInMacro
) {
437 const char TestHeader
[] = "#define DEF(CLASS) void CLASS##_::f() {}\n"
438 "#define DEF2(CLASS, ...) void CLASS##_::f2() {}\n"
439 "class A_ {\nvoid f();\nvoid f2();\n};\n"
441 const char TestCode
[] = "#include \"foo.h\"\n"
445 const char ExpectedNewCode
[] = "#include \"new_foo.h\"\n\n"
448 move::MoveDefinitionSpec Spec
;
449 Spec
.Names
.push_back("A_");
450 Spec
.OldHeader
= "foo.h";
451 Spec
.OldCC
= "foo.cc";
452 Spec
.NewHeader
= "new_foo.h";
453 Spec
.NewCC
= "new_foo.cc";
454 auto Results
= runClangMoveOnCode(Spec
, TestHeader
, TestCode
);
455 EXPECT_EQ(ExpectedNewCode
, Results
[Spec
.NewCC
]);
458 TEST(ClangMove
, WellFormattedCode
) {
459 const std::string CommonHeader
=
465 "class A {\npublic:\n void f();\n void f2();\n};\n"
471 "class B {\npublic:\n void f();\n};\n"
474 "} // namespace a\n";
475 const std::string CommonCode
= "\n"
490 "} // namespace a\n";
491 // Add dummy class to prevent behavior of moving all declarations from header.
492 const std::string TestHeader
= CommonHeader
+ "class D {};\n";
493 const std::string TestCode
= "#include \"foo.h\"\n" + CommonCode
;
494 const std::string ExpectedNewHeader
= "#ifndef NEW_FOO_H\n"
495 "#define NEW_FOO_H\n"
499 "#endif // NEW_FOO_H\n";
500 const std::string ExpectedNewCC
= "#include \"new_foo.h\"\n" + CommonCode
;
501 move::MoveDefinitionSpec Spec
;
502 Spec
.Names
.push_back("a::b::c::A");
503 Spec
.Names
.push_back("a::d::e::B");
504 Spec
.OldHeader
= "foo.h";
505 Spec
.OldCC
= "foo.cc";
506 Spec
.NewHeader
= "new_foo.h";
507 Spec
.NewCC
= "new_foo.cc";
508 auto Results
= runClangMoveOnCode(Spec
, TestHeader
.c_str(), TestCode
.c_str());
509 EXPECT_EQ(ExpectedNewCC
, Results
[Spec
.NewCC
]);
510 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
513 TEST(ClangMove
, AddDependentNewHeader
) {
514 const char TestHeader
[] = "class A {};\n"
516 const char TestCode
[] = "#include \"foo.h\"\n";
517 const char ExpectedOldHeader
[] = "#include \"new_foo.h\"\nclass B {};\n";
518 const char ExpectedNewHeader
[] = "#ifndef NEW_FOO_H\n"
519 "#define NEW_FOO_H\n"
523 "#endif // NEW_FOO_H\n";
524 move::MoveDefinitionSpec Spec
;
525 Spec
.Names
.push_back("A");
526 Spec
.OldHeader
= "foo.h";
527 Spec
.OldCC
= "foo.cc";
528 Spec
.NewHeader
= "new_foo.h";
529 Spec
.NewCC
= "new_foo.cc";
530 Spec
.OldDependOnNew
= true;
531 auto Results
= runClangMoveOnCode(Spec
, TestHeader
, TestCode
);
532 EXPECT_EQ(ExpectedOldHeader
, Results
[Spec
.OldHeader
]);
533 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
536 TEST(ClangMove
, AddDependentOldHeader
) {
537 const char TestHeader
[] = "class A {};\n"
539 const char TestCode
[] = "#include \"foo.h\"\n";
540 const char ExpectedNewHeader
[] = "#ifndef NEW_FOO_H\n"
541 "#define NEW_FOO_H\n"
543 "#include \"foo.h\"\n"
547 "#endif // NEW_FOO_H\n";
548 const char ExpectedOldHeader
[] = "class A {};\n";
549 move::MoveDefinitionSpec Spec
;
550 Spec
.Names
.push_back("B");
551 Spec
.OldHeader
= "foo.h";
552 Spec
.OldCC
= "foo.cc";
553 Spec
.NewHeader
= "new_foo.h";
554 Spec
.NewCC
= "new_foo.cc";
555 Spec
.NewDependOnOld
= true;
556 auto Results
= runClangMoveOnCode(Spec
, TestHeader
, TestCode
);
557 EXPECT_EQ(ExpectedNewHeader
, Results
[Spec
.NewHeader
]);
558 EXPECT_EQ(ExpectedOldHeader
, Results
[Spec
.OldHeader
]);
561 TEST(ClangMove
, DumpDecls
) {
562 const char TestHeader
[] = "template <typename T>\n"
566 " template <typename U> void h();\n"
570 "template <typename T> void A<T>::f() {}\n"
572 "template <typename T>\n"
573 "template <typename U>\n"
574 "void A<T>::h() {}\n"
576 "template <typename T> int A<T>::b = 2;\n"
578 "template <> class A<int> {};\n"
585 "template <typename T>"
589 "class ForwardClass;\n"
592 "class Move1 { public : void f(); };\n"
594 "enum E1 { Green };\n"
595 "enum class E2 { Red };\n"
596 "typedef int Int2;\n"
597 "typedef A<double> A_d;"
599 "template <typename T>\n"
601 "extern int kGlobalInt;\n"
602 "extern const char* const kGlobalStr;\n"
605 "#define DEFINE_FOO class Foo {};\n"
607 const char TestCode
[] = "#include \"foo.h\"\n";
608 move::MoveDefinitionSpec Spec
;
609 Spec
.Names
.push_back("B");
610 Spec
.OldHeader
= "foo.h";
611 Spec
.OldCC
= "foo.cc";
612 Spec
.NewHeader
= "new_foo.h";
613 Spec
.NewCC
= "new_foo.cc";
614 DeclarationReporter Reporter
;
615 std::vector
<DeclarationReporter::Declaration
> ExpectedDeclarations
= {
616 {"A", "Class", true},
617 {"B", "Class", false},
618 {"a::Move1", "Class", false},
619 {"a::f1", "Function", false},
620 {"a::f2", "Function", true},
621 {"a::b::Move1", "Class", false},
622 {"a::b::f", "Function", false},
623 {"a::b::E1", "Enum", false},
624 {"a::b::E2", "Enum", false},
625 {"a::b::Int2", "TypeAlias", false},
626 {"a::b::A_d", "TypeAlias", false},
627 {"a::b::Int", "TypeAlias", false},
628 {"a::b::AA", "TypeAlias", true},
629 {"a::b::kGlobalInt", "Variable", false},
630 {"a::b::kGlobalStr", "Variable", false}};
631 runClangMoveOnCode(Spec
, TestHeader
, TestCode
, &Reporter
);
632 std::vector
<DeclarationReporter::Declaration
> Results
;
633 for (const auto &DelPair
: Reporter
.getDeclarationList())
634 Results
.push_back(DelPair
);
635 EXPECT_THAT(ExpectedDeclarations
,
636 testing::UnorderedElementsAreArray(Results
));