aboutsummaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-07-01 13:24:05 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-07-01 13:24:05 +0000
commitcf1b401909b5e54edfd80656b1a18eaa31f9f6f1 (patch)
treeedb0ffff2a43d84ba9b4c862b394cfeeebb36ddc /unittests
parentef915aab0ac566c55bfb0d7a9f6635bb5d94d4af (diff)
Vendor import of clang trunk r306956:vendor/clang/clang-trunk-r306956
Notes
Notes: svn path=/vendor/clang/dist/; revision=320535 svn path=/vendor/clang/clang-trunk-r306956/; revision=320536; tag=vendor/clang/clang-trunk-r306956
Diffstat (limited to 'unittests')
-rw-r--r--unittests/CMakeLists.txt1
-rw-r--r--unittests/Driver/ToolChainTest.cpp11
-rw-r--r--unittests/Format/FormatTest.cpp204
-rw-r--r--unittests/Format/FormatTestJava.cpp5
-rw-r--r--unittests/Format/FormatTestProto.cpp172
-rw-r--r--unittests/Format/SortIncludesTest.cpp23
-rw-r--r--unittests/Rename/CMakeLists.txt22
-rw-r--r--unittests/Rename/ClangRenameTest.h112
-rw-r--r--unittests/Rename/RenameClassTest.cpp684
-rw-r--r--unittests/Tooling/CMakeLists.txt1
-rw-r--r--unittests/Tooling/CastExprTest.cpp38
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp21
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp86
-rw-r--r--unittests/Tooling/RefactoringTest.cpp12
-rw-r--r--unittests/Tooling/TestVisitor.h5
15 files changed, 1384 insertions, 13 deletions
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 7d407ce3f649..b622d66af4ed 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -29,3 +29,4 @@ add_subdirectory(CodeGen)
if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD)
add_subdirectory(libclang)
endif()
+add_subdirectory(Rename)
diff --git a/unittests/Driver/ToolChainTest.cpp b/unittests/Driver/ToolChainTest.cpp
index bce2748aa21b..ec50560b202b 100644
--- a/unittests/Driver/ToolChainTest.cpp
+++ b/unittests/Driver/ToolChainTest.cpp
@@ -152,5 +152,16 @@ TEST(ToolChainTest, DefaultDriverMode) {
EXPECT_TRUE(CXXDriver.CCCIsCXX());
EXPECT_TRUE(CLDriver.IsCLMode());
}
+TEST(ToolChainTest, InvalidArgument) {
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ struct TestDiagnosticConsumer : public DiagnosticConsumer {};
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
+ Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags);
+ std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(
+ {"-fsyntax-only", "-fan-unknown-option", "foo.cpp"}));
+ EXPECT_TRUE(C);
+ EXPECT_TRUE(C->containsError());
+}
} // end anonymous namespace.
diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp
index 64bb28e0be23..b5f959f9c1f7 100644
--- a/unittests/Format/FormatTest.cpp
+++ b/unittests/Format/FormatTest.cpp
@@ -6004,7 +6004,10 @@ TEST_F(FormatTest, LayoutBraceInitializersInReturnStatement) {
TEST_F(FormatTest, LayoutCxx11BraceInitializers) {
verifyFormat("vector<int> x{1, 2, 3, 4};");
verifyFormat("vector<int> x{\n"
- " 1, 2, 3, 4,\n"
+ " 1,\n"
+ " 2,\n"
+ " 3,\n"
+ " 4,\n"
"};");
verifyFormat("vector<T> x{{}, {}, {}, {}};");
verifyFormat("f({1, 2});");
@@ -6049,6 +6052,17 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) {
"};");
verifyFormat("#define A {a, a},");
+ // Binpacking only if there is no trailing comma
+ verifyFormat("const Aaaaaa aaaaa = {aaaaaaaaaa, bbbbbbbbbb,\n"
+ " cccccccccc, dddddddddd};",
+ getLLVMStyleWithColumns(50));
+ verifyFormat("const Aaaaaa aaaaa = {\n"
+ " aaaaaaaaaaa,\n"
+ " bbbbbbbbbbb,\n"
+ " ccccccccccc,\n"
+ " ddddddddddd,\n"
+ "};", getLLVMStyleWithColumns(50));
+
// Cases where distinguising braced lists and blocks is hard.
verifyFormat("vector<int> v{12} GUARDED_BY(mutex);");
verifyFormat("void f() {\n"
@@ -6128,10 +6142,12 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) {
" // Second element:\n"
" 2};",
getLLVMStyleWithColumns(30)));
- // A trailing comma should still lead to an enforced line break.
+ // A trailing comma should still lead to an enforced line break and no
+ // binpacking.
EXPECT_EQ("vector<int> SomeVector = {\n"
" // aaa\n"
- " 1, 2,\n"
+ " 1,\n"
+ " 2,\n"
"};",
format("vector<int> SomeVector = { // aaa\n"
" 1, 2, };"));
@@ -6297,7 +6313,7 @@ TEST_F(FormatTest, FormatsBracedListsInColumnLayout) {
" aaaaaaaaaaaa, a, aaaaaaaaaa, aaaaaaaaa, aaa}};");
// No column layout should be used here.
- verifyFormat("aaaaaaaaaaaaaaa = {aaaaaaaaaaaaaaaaaaaaaaaaaaa, 0, 0,\n"
+ verifyFormat("aaaaaaaaaaaaaaa = {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 0, 0,\n"
" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb};");
verifyNoCrash("a<,");
@@ -6555,12 +6571,12 @@ TEST_F(FormatTest, PullInlineOnlyFunctionDefinitionsIntoSingleLine) {
MergeInlineOnly);
}
-TEST_F(FormatTest, SplitEmptyFunctionBody) {
+TEST_F(FormatTest, SplitEmptyFunction) {
FormatStyle Style = getLLVMStyle();
Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
Style.BraceWrapping.AfterFunction = true;
- Style.BraceWrapping.SplitEmptyFunctionBody = false;
+ Style.BraceWrapping.SplitEmptyFunction = false;
Style.ColumnLimit = 40;
verifyFormat("int f()\n"
@@ -6623,6 +6639,178 @@ TEST_F(FormatTest, SplitEmptyFunctionBody) {
Style);
}
+TEST_F(FormatTest, SplitEmptyClass) {
+ FormatStyle Style = getLLVMStyle();
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.AfterClass = true;
+ Style.BraceWrapping.SplitEmptyRecord = false;
+
+ verifyFormat("class Foo\n"
+ "{};",
+ Style);
+ verifyFormat("/* something */ class Foo\n"
+ "{};",
+ Style);
+ verifyFormat("template <typename X> class Foo\n"
+ "{};",
+ Style);
+ verifyFormat("class Foo\n"
+ "{\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef class Foo\n"
+ "{\n"
+ "} Foo_t;",
+ Style);
+}
+
+TEST_F(FormatTest, SplitEmptyStruct) {
+ FormatStyle Style = getLLVMStyle();
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.AfterStruct = true;
+ Style.BraceWrapping.SplitEmptyRecord = false;
+
+ verifyFormat("struct Foo\n"
+ "{};",
+ Style);
+ verifyFormat("/* something */ struct Foo\n"
+ "{};",
+ Style);
+ verifyFormat("template <typename X> struct Foo\n"
+ "{};",
+ Style);
+ verifyFormat("struct Foo\n"
+ "{\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef struct Foo\n"
+ "{\n"
+ "} Foo_t;",
+ Style);
+ //typedef struct Bar {} Bar_t;
+}
+
+TEST_F(FormatTest, SplitEmptyUnion) {
+ FormatStyle Style = getLLVMStyle();
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.AfterUnion = true;
+ Style.BraceWrapping.SplitEmptyRecord = false;
+
+ verifyFormat("union Foo\n"
+ "{};",
+ Style);
+ verifyFormat("/* something */ union Foo\n"
+ "{};",
+ Style);
+ verifyFormat("union Foo\n"
+ "{\n"
+ " A,\n"
+ "};",
+ Style);
+ verifyFormat("typedef union Foo\n"
+ "{\n"
+ "} Foo_t;",
+ Style);
+}
+
+TEST_F(FormatTest, SplitEmptyNamespace) {
+ FormatStyle Style = getLLVMStyle();
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.AfterNamespace = true;
+ Style.BraceWrapping.SplitEmptyNamespace = false;
+
+ verifyFormat("namespace Foo\n"
+ "{};",
+ Style);
+ verifyFormat("/* something */ namespace Foo\n"
+ "{};",
+ Style);
+ verifyFormat("inline namespace Foo\n"
+ "{};",
+ Style);
+ verifyFormat("namespace Foo\n"
+ "{\n"
+ "void Bar();\n"
+ "};",
+ Style);
+}
+
+TEST_F(FormatTest, NeverMergeShortRecords) {
+ FormatStyle Style = getLLVMStyle();
+
+ verifyFormat("class Foo {\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef class Foo {\n"
+ " Foo();\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("struct Foo {\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef struct Foo {\n"
+ " Foo();\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("union Foo {\n"
+ " A,\n"
+ "};",
+ Style);
+ verifyFormat("typedef union Foo {\n"
+ " A,\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("namespace Foo {\n"
+ "void Bar();\n"
+ "};",
+ Style);
+
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.AfterClass = true;
+ Style.BraceWrapping.AfterStruct = true;
+ Style.BraceWrapping.AfterUnion = true;
+ Style.BraceWrapping.AfterNamespace = true;
+ verifyFormat("class Foo\n"
+ "{\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef class Foo\n"
+ "{\n"
+ " Foo();\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("struct Foo\n"
+ "{\n"
+ " Foo();\n"
+ "};",
+ Style);
+ verifyFormat("typedef struct Foo\n"
+ "{\n"
+ " Foo();\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("union Foo\n"
+ "{\n"
+ " A,\n"
+ "};",
+ Style);
+ verifyFormat("typedef union Foo\n"
+ "{\n"
+ " A,\n"
+ "} Foo_t;",
+ Style);
+ verifyFormat("namespace Foo\n"
+ "{\n"
+ "void Bar();\n"
+ "};",
+ Style);
+}
+
TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) {
// Elaborate type variable declarations.
verifyFormat("struct foo a = {bar};\nint n;");
@@ -9355,7 +9543,9 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeCatch);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeElse);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces);
- CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunctionBody);
+ CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction);
+ CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord);
+ CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace);
}
#undef CHECK_PARSE_BOOL
diff --git a/unittests/Format/FormatTestJava.cpp b/unittests/Format/FormatTestJava.cpp
index 6e685f6703e1..b9cfaffb0181 100644
--- a/unittests/Format/FormatTestJava.cpp
+++ b/unittests/Format/FormatTestJava.cpp
@@ -237,7 +237,10 @@ TEST_F(FormatTestJava, EnumDeclarations) {
TEST_F(FormatTestJava, ArrayInitializers) {
verifyFormat("new int[] {1, 2, 3, 4};");
verifyFormat("new int[] {\n"
- " 1, 2, 3, 4,\n"
+ " 1,\n"
+ " 2,\n"
+ " 3,\n"
+ " 4,\n"
"};");
FormatStyle Style = getStyleWithColumns(65);
diff --git a/unittests/Format/FormatTestProto.cpp b/unittests/Format/FormatTestProto.cpp
index d174c65a1f32..2e3b9311d12c 100644
--- a/unittests/Format/FormatTestProto.cpp
+++ b/unittests/Format/FormatTestProto.cpp
@@ -61,6 +61,29 @@ TEST_F(FormatTestProto, FormatsMessages) {
" really.really.long.qualified.type.aaa.aaaaaaa.aaaaaaaa\n"
" another_fiiiiiiiiiiiiiiiiiiiiield = 2;\n"
"}");
+ verifyFormat("message SomeMessage {\n"
+ " map<string, Project> projects = 1;\n"
+ " optional map<string, int32> size_projects = 2;\n"
+ " map<int, really.really.really.long.qualified.type.nameeee>\n"
+ " projects = 3;\n"
+ " map<int, really.really.really.really.long.qualified.type\n"
+ " .nameeee> projects = 4;\n"
+ " map<int,\n"
+ " reallyreallyreallyreallyreallyreallyreallylongname>\n"
+ " projects = 5;\n"
+ " map<int, Project>\n"
+ " longlonglonglonglonglonglonglonglonglongonglon = 6;\n"
+ " map<releleallyreallyreallyreallyreallyreallyreallylongname,\n"
+ " int> projects = 7;\n"
+ " map<releleallyreallyreallyreallyreallyreallyreallylongname,\n"
+ " releleallyreallyreallyreallyreallyreallyreallylongname>\n"
+ " releleallyreallyreallyreallyreallyreallyreallylongnam =\n"
+ " 8;\n"
+ " map<relele.llyreal.yreallyr.allyreally.eallyreal\n"
+ " .sauenirylongname,\n"
+ " really.really.really.really.long.qualified.type\n"
+ " .nameeee> projects = 9;\n"
+ "}");
}
TEST_F(FormatTestProto, KeywordsInOtherLanguages) {
@@ -178,12 +201,161 @@ TEST_F(FormatTestProto, FormatsOptions) {
" field_c: \"OK\"\n"
" msg_field{field_d: 123}\n"
"};");
+ verifyFormat("option (MyProto.options) = {\n"
+ " field_a: OK\n"
+ " field_b{field_c: OK}\n"
+ " field_d: OKOKOK\n"
+ " field_e: OK\n"
+ "}");
// Support syntax with <> instead of {}.
verifyFormat("option (MyProto.options) = {\n"
" field_c: \"OK\",\n"
" msg_field: <field_d: 123>\n"
"};");
+
+ verifyFormat("option (MyProto.options) = {\n"
+ " field_a: OK\n"
+ " field_b<field_c: OK>\n"
+ " field_d: OKOKOK\n"
+ " field_e: OK\n"
+ "}");
+
+ verifyFormat("option (MyProto.options) = {\n"
+ " msg_field: <>\n"
+ " field_c: \"OK\",\n"
+ " msg_field: <field_d: 123>\n"
+ " field_e: OK\n"
+ " msg_field: <field_d: 12>\n"
+ "};");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: OK\n"
+ " field_b: \"OK\"\n"
+ " field_c: 1\n"
+ " field_d: 12.5\n"
+ " field_e: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: OK,\n"
+ " field_b: \"OK\",\n"
+ " field_c: 1,\n"
+ " field_d: 12.5,\n"
+ " field_e: OK,\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field: {field_b: OK}\n"
+ " field_g: OK\n"
+ " field_g: OK\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field<\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " >\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field<\n"
+ " field_b: OK,\n"
+ " field_c: OK,\n"
+ " field_d: OK,\n"
+ " field_e: OK,\n"
+ " field_f: OK\n"
+ " >\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field: <\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " >\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field: {\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " }\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field{\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " }\n"
+ " field_g: OK\n"
+ ">;");
+
+ verifyFormat("option (MyProto.options) = {\n"
+ " field_a: \"OK\"\n"
+ " msg_field<\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " >\n"
+ " field_g: OK\n"
+ "};");
+
+ verifyFormat("option (MyProto.options) = {\n"
+ " field_a: \"OK\"\n"
+ " msg_field: <\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " >\n"
+ " field_g: OK\n"
+ "};");
+
+ verifyFormat("option (MyProto.options) = <\n"
+ " field_a: \"OK\"\n"
+ " msg_field{\n"
+ " field_b: OK\n"
+ " field_c: OK\n"
+ " field_d: OK\n"
+ " msg_field<\n"
+ " field_A: 1\n"
+ " field_B: 2\n"
+ " field_C: 3\n"
+ " field_D: 4\n"
+ " field_E: 5\n"
+ " >\n"
+ " msg_field<field_A: 1 field_B: 2 field_C: 3 field_D: 4>\n"
+ " field_e: OK\n"
+ " field_f: OK\n"
+ " }\n"
+ " field_g: OK\n"
+ ">;");
}
TEST_F(FormatTestProto, FormatsService) {
diff --git a/unittests/Format/SortIncludesTest.cpp b/unittests/Format/SortIncludesTest.cpp
index c3c56a813041..1128ed829ff2 100644
--- a/unittests/Format/SortIncludesTest.cpp
+++ b/unittests/Format/SortIncludesTest.cpp
@@ -266,6 +266,29 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) {
"a.cc"));
}
+TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) {
+ // Setup an regex for main includes so we can cover those as well.
+ Style.IncludeIsMainRegex = "([-_](test|unittest))?$";
+
+ // Ensure both main header detection and grouping work in a case insensitive
+ // manner.
+ EXPECT_EQ("#include \"llvm/A.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "#include \"LLVM/z.h\"\n"
+ "#include \"llvm/X.h\"\n"
+ "#include \"GTest/GTest.h\"\n"
+ "#include \"gmock/gmock.h\"\n",
+ sort("#include \"c.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"GTest/GTest.h\"\n"
+ "#include \"llvm/A.h\"\n"
+ "#include \"gmock/gmock.h\"\n"
+ "#include \"llvm/X.h\"\n"
+ "#include \"LLVM/z.h\"\n",
+ "a_TEST.cc"));
+}
+
TEST_F(SortIncludesTest, NegativePriorities) {
Style.IncludeCategories = {{".*important_os_header.*", -1}, {".*", 1}};
EXPECT_EQ("#include \"important_os_header.h\"\n"
diff --git a/unittests/Rename/CMakeLists.txt b/unittests/Rename/CMakeLists.txt
new file mode 100644
index 000000000000..aa7609260cc0
--- /dev/null
+++ b/unittests/Rename/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_clang_unittest(ClangRenameTests
+ RenameClassTest.cpp
+ )
+
+target_link_libraries(ClangRenameTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
diff --git a/unittests/Rename/ClangRenameTest.h b/unittests/Rename/ClangRenameTest.h
new file mode 100644
index 000000000000..0933dd5aaf4f
--- /dev/null
+++ b/unittests/Rename/ClangRenameTest.h
@@ -0,0 +1,112 @@
+//===-- ClangRenameTests.cpp - clang-rename unit tests --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+
+struct Case {
+ std::string Before;
+ std::string After;
+ std::string OldName;
+ std::string NewName;
+};
+
+class ClangRenameTest : public testing::Test,
+ public testing::WithParamInterface<Case> {
+protected:
+ void AppendToHeader(StringRef Code) { HeaderContent += Code.str(); }
+
+ std::string runClangRenameOnCode(llvm::StringRef Code,
+ llvm::StringRef OldName,
+ llvm::StringRef NewName) {
+ std::string NewCode;
+ llvm::raw_string_ostream(NewCode) << llvm::format(
+ "#include \"%s\"\n%s", HeaderName.c_str(), Code.str().c_str());
+ tooling::FileContentMappings FileContents = {{HeaderName, HeaderContent},
+ {CCName, NewCode}};
+ clang::RewriterTestContext Context;
+ Context.createInMemoryFile(HeaderName, HeaderContent);
+ clang::FileID InputFileID = Context.createInMemoryFile(CCName, NewCode);
+
+ tooling::USRFindingAction FindingAction({}, {OldName}, false);
+ std::unique_ptr<tooling::FrontendActionFactory> USRFindingActionFactory =
+ tooling::newFrontendActionFactory(&FindingAction);
+
+ if (!tooling::runToolOnCodeWithArgs(
+ USRFindingActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
+ "clang-rename", std::make_shared<PCHContainerOperations>(),
+ FileContents))
+ return "";
+
+ const std::vector<std::vector<std::string>> &USRList =
+ FindingAction.getUSRList();
+ std::vector<std::string> NewNames = {NewName};
+ std::map<std::string, tooling::Replacements> FileToReplacements;
+ tooling::QualifiedRenamingAction RenameAction(NewNames, USRList,
+ FileToReplacements);
+ auto RenameActionFactory = tooling::newFrontendActionFactory(&RenameAction);
+ if (!tooling::runToolOnCodeWithArgs(
+ RenameActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
+ "clang-rename", std::make_shared<PCHContainerOperations>(),
+ FileContents))
+ return "";
+
+ formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm");
+ return Context.getRewrittenText(InputFileID);
+ }
+
+ void CompareSnippets(StringRef Expected, StringRef Actual) {
+ std::string ExpectedCode;
+ llvm::raw_string_ostream(ExpectedCode) << llvm::format(
+ "#include \"%s\"\n%s", HeaderName.c_str(), Expected.str().c_str());
+ EXPECT_EQ(format(ExpectedCode), format(Actual));
+ }
+
+ std::string format(llvm::StringRef Code) {
+ tooling::Replacements Replaces = format::reformat(
+ format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())});
+ auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(ChangedCode));
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError());
+ return "";
+ }
+ return *ChangedCode;
+ }
+
+ std::string HeaderContent;
+ std::string HeaderName = "header.h";
+ std::string CCName = "input.cc";
+};
+
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
diff --git a/unittests/Rename/RenameClassTest.cpp b/unittests/Rename/RenameClassTest.cpp
new file mode 100644
index 000000000000..29b4594fb0a4
--- /dev/null
+++ b/unittests/Rename/RenameClassTest.cpp
@@ -0,0 +1,684 @@
+//===-- RenameClassTest.cpp - unit tests for renaming classes -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangRenameTest.h"
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+namespace {
+
+class RenameClassTest : public ClangRenameTest {
+public:
+ RenameClassTest() {
+ AppendToHeader(R"(
+ namespace a {
+ class Foo {
+ public:
+ struct Nested {
+ enum NestedEnum {E1, E2};
+ };
+ void func() {}
+ static int Constant;
+ };
+ class Goo {
+ public:
+ struct Nested {
+ enum NestedEnum {E1, E2};
+ };
+ };
+ int Foo::Constant = 1;
+ } // namespace a
+ namespace b {
+ class Foo {};
+ } // namespace b
+
+ #define MACRO(x) x
+
+ template<typename T> class ptr {};
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTests, RenameClassTest,
+ testing::ValuesIn(std::vector<Case>({
+ // basic classes
+ {"a::Foo f;", "b::Bar f;", "", ""},
+ {"void f(a::Foo f) {}", "void f(b::Bar f) {}", "", ""},
+ {"void f(a::Foo *f) {}", "void f(b::Bar *f) {}", "", ""},
+ {"a::Foo f() { return a::Foo(); }", "b::Bar f() { return b::Bar(); }",
+ "", ""},
+ {"namespace a {a::Foo f() { return Foo(); }}",
+ "namespace a {b::Bar f() { return b::Bar(); }}", "", ""},
+ {"void f(const a::Foo& a1) {}", "void f(const b::Bar& a1) {}", "", ""},
+ {"void f(const a::Foo* a1) {}", "void f(const b::Bar* a1) {}", "", ""},
+ {"namespace a { void f(Foo a1) {} }",
+ "namespace a { void f(b::Bar a1) {} }", "", ""},
+ {"void f(MACRO(a::Foo) a1) {}", "void f(MACRO(b::Bar) a1) {}", "", ""},
+ {"void f(MACRO(a::Foo a1)) {}", "void f(MACRO(b::Bar a1)) {}", "", ""},
+ {"a::Foo::Nested ns;", "b::Bar::Nested ns;", "", ""},
+ {"auto t = a::Foo::Constant;", "auto t = b::Bar::Constant;", "", ""},
+ {"a::Foo::Nested ns;", "a::Foo::Nested2 ns;", "a::Foo::Nested",
+ "a::Foo::Nested2"},
+
+ // use namespace and typedefs
+ {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;", "", ""},
+ {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}",
+ "", ""},
+ {"using a::Foo; namespace x { Foo gA; }",
+ "using b::Bar; namespace x { Bar gA; }", "", ""},
+ {"struct S { using T = a::Foo; T a_; };",
+ "struct S { using T = b::Bar; T a_; };", "", ""},
+ {"using T = a::Foo; T gA;", "using T = b::Bar; T gA;", "", ""},
+ {"typedef a::Foo T; T gA;", "typedef b::Bar T; T gA;", "", ""},
+ {"typedef MACRO(a::Foo) T; T gA;", "typedef MACRO(b::Bar) T; T gA;", "",
+ ""},
+
+ // struct members and other oddities
+ {"struct S : public a::Foo {};", "struct S : public b::Bar {};", "",
+ ""},
+ {"struct F { void f(a::Foo a1) {} };",
+ "struct F { void f(b::Bar a1) {} };", "", ""},
+ {"struct F { a::Foo a_; };", "struct F { b::Bar a_; };", "", ""},
+ {"struct F { ptr<a::Foo> a_; };", "struct F { ptr<b::Bar> a_; };", "",
+ ""},
+
+ {"void f() { a::Foo::Nested ne; }", "void f() { b::Bar::Nested ne; }",
+ "", ""},
+ {"void f() { a::Goo::Nested ne; }", "void f() { a::Goo::Nested ne; }",
+ "", ""},
+ {"void f() { a::Foo::Nested::NestedEnum e; }",
+ "void f() { b::Bar::Nested::NestedEnum e; }", "", ""},
+ {"void f() { auto e = a::Foo::Nested::NestedEnum::E1; }",
+ "void f() { auto e = b::Bar::Nested::NestedEnum::E1; }", "", ""},
+ {"void f() { auto e = a::Foo::Nested::E1; }",
+ "void f() { auto e = b::Bar::Nested::E1; }", "", ""},
+
+ // templates
+ {"template <typename T> struct Foo { T t; };\n"
+ "void f() { Foo<a::Foo> foo; }",
+ "template <typename T> struct Foo { T t; };\n"
+ "void f() { Foo<b::Bar> foo; }",
+ "", ""},
+ {"template <typename T> struct Foo { a::Foo a; };",
+ "template <typename T> struct Foo { b::Bar a; };", "", ""},
+ {"template <typename T> void f(T t) {}\n"
+ "void g() { f<a::Foo>(a::Foo()); }",
+ "template <typename T> void f(T t) {}\n"
+ "void g() { f<b::Bar>(b::Bar()); }",
+ "", ""},
+ {"template <typename T> int f() { return 1; }\n"
+ "template <> int f<a::Foo>() { return 2; }\n"
+ "int g() { return f<a::Foo>(); }",
+ "template <typename T> int f() { return 1; }\n"
+ "template <> int f<b::Bar>() { return 2; }\n"
+ "int g() { return f<b::Bar>(); }",
+ "", ""},
+ {"struct Foo { template <typename T> T foo(); };\n"
+ "void g() { Foo f; auto a = f.template foo<a::Foo>(); }",
+ "struct Foo { template <typename T> T foo(); };\n"
+ "void g() { Foo f; auto a = f.template foo<b::Bar>(); }",
+ "", ""},
+
+ // The following two templates are distilled from regressions found in
+ // unique_ptr<> and type_traits.h
+ {"template <typename T> struct outer {\n"
+ " typedef T type;\n"
+ " type Baz();\n"
+ " };\n"
+ " outer<a::Foo> g_A;",
+ "template <typename T> struct outer {\n"
+ " typedef T type;\n"
+ " type Baz();\n"
+ " };\n"
+ " outer<b::Bar> g_A;",
+ "", ""},
+ {"template <typename T> struct nested { typedef T type; };\n"
+ "template <typename T> struct outer { typename nested<T>::type Foo(); "
+ "};\n"
+ "outer<a::Foo> g_A;",
+ "template <typename T> struct nested { typedef T type; };\n"
+ "template <typename T> struct outer { typename nested<T>::type Foo(); "
+ "};\n"
+ "outer<b::Bar> g_A;",
+ "", ""},
+
+ // macros
+ {"#define FOO(T, t) T t\n"
+ "void f() { FOO(a::Foo, a1); FOO(a::Foo, a2); }",
+ "#define FOO(T, t) T t\n"
+ "void f() { FOO(b::Bar, a1); FOO(b::Bar, a2); }",
+ "", ""},
+ {"#define FOO(n) a::Foo n\n"
+ " void f() { FOO(a1); FOO(a2); }",
+ "#define FOO(n) b::Bar n\n"
+ " void f() { FOO(a1); FOO(a2); }",
+ "", ""},
+
+ // Pointer to member functions
+ {"auto gA = &a::Foo::func;", "auto gA = &b::Bar::func;", "", ""},
+ {"using a::Foo; auto gA = &Foo::func;",
+ "using b::Bar; auto gA = &b::Bar::func;", "", ""},
+ {"using a::Foo; namespace x { auto gA = &Foo::func; }",
+ "using b::Bar; namespace x { auto gA = &Bar::func; }", "", ""},
+ {"typedef a::Foo T; auto gA = &T::func;",
+ "typedef b::Bar T; auto gA = &T::func;", "", ""},
+ {"auto gA = &MACRO(a::Foo)::func;", "auto gA = &MACRO(b::Bar)::func;",
+ "", ""},
+
+ // Short match inside a namespace
+ {"namespace a { void f(Foo a1) {} }",
+ "namespace a { void f(b::Bar a1) {} }", "", ""},
+
+ // Correct match.
+ {"using a::Foo; struct F { ptr<Foo> a_; };",
+ "using b::Bar; struct F { ptr<Bar> a_; };", "", ""},
+
+ // avoid false positives
+ {"void f(b::Foo a) {}", "void f(b::Foo a) {}", "", ""},
+ {"namespace b { void f(Foo a) {} }", "namespace b { void f(Foo a) {} }",
+ "", ""},
+
+ // friends, everyone needs friends.
+ {"class Foo { int i; friend class a::Foo; };",
+ "class Foo { int i; friend class b::Bar; };", "", ""},
+ })), );
+
+TEST_P(RenameClassTest, RenameClasses) {
+ auto Param = GetParam();
+ std::string OldName = Param.OldName.empty() ? "a::Foo" : Param.OldName;
+ std::string NewName = Param.NewName.empty() ? "b::Bar" : Param.NewName;
+ std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+class NamespaceDetectionTest : public ClangRenameTest {
+protected:
+ NamespaceDetectionTest() {
+ AppendToHeader(R"(
+ class Old {};
+ namespace o1 {
+ class Old {};
+ namespace o2 {
+ class Old {};
+ namespace o3 {
+ class Old {};
+ } // namespace o3
+ } // namespace o2
+ } // namespace o1
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTest, NamespaceDetectionTest,
+ ::testing::ValuesIn(std::vector<Case>({
+ // Test old and new namespace overlap.
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::o3::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { n3::New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::n3::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { n2::n3::New moo; } } }",
+ "o1::o2::o3::Old", "o1::n2::n3::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old",
+ "::o1::o2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { n2::New moo; } }", "::o1::o2::Old",
+ "::o1::n2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { ::n1::n2::New moo; } }",
+ "::o1::o2::Old", "::n1::n2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { n1::n2::New moo; } }", "::o1::o2::Old",
+ "n1::n2::New"},
+
+ // Test old and new namespace with differing depths.
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "::o1::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "::o1::o2::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::New"},
+ {"Old moo;", "o1::New moo;", "::Old", "o1::New"},
+ {"Old moo;", "o1::New moo;", "Old", "o1::New"},
+ {"namespace o1 { ::Old moo; }", "namespace o1 { New moo; }", "Old",
+ "o1::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { ::New moo; } }", "::o1::o2::Old",
+ "::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old", "New"},
+
+ // Test moving into the new namespace at different levels.
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+ "::n1::n2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+ "n1::n2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+ "::n1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+ "n1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { ::o1::o2::New moo; } }",
+ "::o1::o2::Old", "::o1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o1::o2::New moo; } }", "::o1::o2::Old",
+ "o1::o2::New"},
+
+ // Test friends declarations.
+ {"class Foo { friend class o1::Old; };",
+ "class Foo { friend class o1::New; };", "o1::Old", "o1::New"},
+ {"class Foo { int i; friend class o1::Old; };",
+ "class Foo { int i; friend class ::o1::New; };", "::o1::Old",
+ "::o1::New"},
+ {"namespace o1 { class Foo { int i; friend class Old; }; }",
+ "namespace o1 { class Foo { int i; friend class New; }; }", "o1::Old",
+ "o1::New"},
+ {"namespace o1 { class Foo { int i; friend class Old; }; }",
+ "namespace o1 { class Foo { int i; friend class New; }; }",
+ "::o1::Old", "::o1::New"},
+ })), );
+
+TEST_P(NamespaceDetectionTest, RenameClasses) {
+ auto Param = GetParam();
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+class TemplatedClassRenameTest : public ClangRenameTest {
+protected:
+ TemplatedClassRenameTest() {
+ AppendToHeader(R"(
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ namespace ns {
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ } // namespace ns
+
+ namespace o1 {
+ namespace o2 {
+ namespace o3 {
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ } // namespace o3
+ } // namespace o2
+ } // namespace o1
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTests, TemplatedClassRenameTest,
+ ::testing::ValuesIn(std::vector<Case>({
+ {"Old<int> gI; Old<bool> gB;", "New<int> gI; New<bool> gB;", "Old",
+ "New"},
+ {"ns::Old<int> gI; ns::Old<bool> gB;",
+ "ns::New<int> gI; ns::New<bool> gB;", "ns::Old", "ns::New"},
+ {"auto gI = &Old<int>::f; auto gB = &Old<bool>::f;",
+ "auto gI = &New<int>::f; auto gB = &New<bool>::f;", "Old", "New"},
+ {"auto gI = &ns::Old<int>::f;", "auto gI = &ns::New<int>::f;",
+ "ns::Old", "ns::New"},
+
+ {"int gI = Old<int>::s(0); bool gB = Old<bool>::s(false);",
+ "int gI = New<int>::s(0); bool gB = New<bool>::s(false);", "Old",
+ "New"},
+ {"int gI = ns::Old<int>::s(0); bool gB = ns::Old<bool>::s(false);",
+ "int gI = ns::New<int>::s(0); bool gB = ns::New<bool>::s(false);",
+ "ns::Old", "ns::New"},
+
+ {"struct S { Old<int*> o_; };", "struct S { New<int*> o_; };", "Old",
+ "New"},
+ {"struct S { ns::Old<int*> o_; };", "struct S { ns::New<int*> o_; };",
+ "ns::Old", "ns::New"},
+
+ {"auto a = reinterpret_cast<Old<int>*>(new Old<int>);",
+ "auto a = reinterpret_cast<New<int>*>(new New<int>);", "Old", "New"},
+ {"auto a = reinterpret_cast<ns::Old<int>*>(new ns::Old<int>);",
+ "auto a = reinterpret_cast<ns::New<int>*>(new ns::New<int>);",
+ "ns::Old", "ns::New"},
+ {"auto a = reinterpret_cast<const Old<int>*>(new Old<int>);",
+ "auto a = reinterpret_cast<const New<int>*>(new New<int>);", "Old",
+ "New"},
+ {"auto a = reinterpret_cast<const ns::Old<int>*>(new ns::Old<int>);",
+ "auto a = reinterpret_cast<const ns::New<int>*>(new ns::New<int>);",
+ "ns::Old", "ns::New"},
+
+ {"Old<bool>& foo();", "New<bool>& foo();", "Old", "New"},
+ {"ns::Old<bool>& foo();", "ns::New<bool>& foo();", "ns::Old",
+ "ns::New"},
+ {"o1::o2::o3::Old<bool>& foo();", "o1::o2::o3::New<bool>& foo();",
+ "o1::o2::o3::Old", "o1::o2::o3::New"},
+ {"namespace ns { Old<bool>& foo(); }",
+ "namespace ns { New<bool>& foo(); }", "ns::Old", "ns::New"},
+ {"const Old<bool>& foo();", "const New<bool>& foo();", "Old", "New"},
+ {"const ns::Old<bool>& foo();", "const ns::New<bool>& foo();",
+ "ns::Old", "ns::New"},
+
+ // FIXME: figure out why this only works when Moo gets
+ // specialized at some point.
+ {"template <typename T> struct Moo { Old<T> o_; }; Moo<int> m;",
+ "template <typename T> struct Moo { New<T> o_; }; Moo<int> m;", "Old",
+ "New"},
+ {"template <typename T> struct Moo { ns::Old<T> o_; }; Moo<int> m;",
+ "template <typename T> struct Moo { ns::New<T> o_; }; Moo<int> m;",
+ "ns::Old", "ns::New"},
+ })), );
+
+TEST_P(TemplatedClassRenameTest, RenameTemplateClasses) {
+ auto Param = GetParam();
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithOutOfLineMembers) {
+ std::string Before = R"(
+ class Old {
+ public:
+ Old();
+ ~Old();
+
+ Old* next();
+
+ private:
+ Old* next_;
+ };
+
+ Old::Old() {}
+ Old::~Old() {}
+ Old* Old::next() { return next_; }
+ )";
+ std::string Expected = R"(
+ class New {
+ public:
+ New();
+ ~New();
+
+ New* next();
+
+ private:
+ New* next_;
+ };
+
+ New::New() {}
+ New::~New() {}
+ New* New::next() { return next_; }
+ )";
+ std::string After = runClangRenameOnCode(Before, "Old", "New");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithInlineMembers) {
+ std::string Before = R"(
+ class Old {
+ public:
+ Old() {}
+ ~Old() {}
+
+ Old* next() { return next_; }
+
+ private:
+ Old* next_;
+ };
+ )";
+ std::string Expected = R"(
+ class New {
+ public:
+ New() {}
+ ~New() {}
+
+ New* next() { return next_; }
+
+ private:
+ New* next_;
+ };
+ )";
+ std::string After = runClangRenameOnCode(Before, "Old", "New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ public:
+ Old() {}
+ ~Old() {}
+
+ Old* next() { return next_; }
+
+ private:
+ Old* next_;
+ };
+ } // namespace ns
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ public:
+ ns::New() {}
+ ~New() {}
+
+ New* next() { return next_; }
+
+ private:
+ New* next_;
+ };
+ } // namespace ns
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ public:
+ Old();
+ ~Old();
+
+ Old* next();
+
+ private:
+ Old* next_;
+ };
+
+ Old::Old() {}
+ Old::~Old() {}
+ Old* Old::next() { return next_; }
+ } // namespace ns
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ public:
+ ns::New();
+ ~New();
+
+ New* next();
+
+ private:
+ New* next_;
+ };
+
+ New::ns::New() {}
+ New::~New() {}
+ New* New::next() { return next_; }
+ } // namespace ns
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the definition.
+TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) {
+ // `using Base::Base;` will generate an implicit constructor containing usage
+ // of `::ns::Old` which should not be matched.
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ int x;
+ };
+ class Base {
+ protected:
+ Old *moo_;
+ public:
+ Base(Old *moo) : moo_(moo) {}
+ };
+ class Derived : public Base {
+ public:
+ using Base::Base;
+ };
+ } // namespace ns
+ int main() {
+ ::ns::Old foo;
+ ::ns::Derived d(&foo);
+ return 0;
+ })";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ int x;
+ };
+ class Base {
+ protected:
+ New *moo_;
+ public:
+ Base(New *moo) : moo_(moo) {}
+ };
+ class Derived : public Base {
+ public:
+ using Base::Base;
+ };
+ } // namespace ns
+ int main() {
+ ::ns::New foo;
+ ::ns::Derived d(&foo);
+ return 0;
+ })";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ };
+ } // namespace ns
+ struct S {
+ int y;
+ ns::Old old;
+ };
+ void f() {
+ S s1, s2, s3;
+ // This causes an implicit assignment operator to be created.
+ s1 = s2 = s3;
+ }
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ::new_ns::New {
+ };
+ } // namespace ns
+ struct S {
+ int y;
+ ::new_ns::New old;
+ };
+ void f() {
+ S s1, s2, s3;
+ // This causes an implicit assignment operator to be created.
+ s1 = s2 = s3;
+ }
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being adding to the definition.
+TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
+ std::string Before = R"(
+ template <class T>
+ class function;
+ template <class R, class... ArgTypes>
+ class function<R(ArgTypes...)> {
+ public:
+ template <typename Functor>
+ function(Functor f) {}
+
+ function() {}
+
+ R operator()(ArgTypes...) const {}
+ };
+
+ namespace ns {
+ class Old {};
+ void f() {
+ function<void(Old)> func;
+ }
+ } // namespace ns)";
+ std::string Expected = R"(
+ template <class T>
+ class function;
+ template <class R, class... ArgTypes>
+ class function<R(ArgTypes...)> {
+ public:
+ template <typename Functor>
+ function(Functor f) {}
+
+ function() {}
+
+ R operator()(ArgTypes...) const {}
+ };
+
+ namespace ns {
+ class ::new_ns::New {};
+ void f() {
+ function<void(::new_ns::New)> func;
+ }
+ } // namespace ns)";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
index b5af99bdfd6d..8ed35480cb88 100644
--- a/unittests/Tooling/CMakeLists.txt
+++ b/unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@ if (MSVC)
endif()
add_clang_unittest(ToolingTests
+ CastExprTest.cpp
CommentHandlerTest.cpp
CompilationDatabaseTest.cpp
FixItTest.cpp
diff --git a/unittests/Tooling/CastExprTest.cpp b/unittests/Tooling/CastExprTest.cpp
new file mode 100644
index 000000000000..5310c2125472
--- /dev/null
+++ b/unittests/Tooling/CastExprTest.cpp
@@ -0,0 +1,38 @@
+//===- unittest/Tooling/CastExprTest.cpp ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+struct CastExprVisitor : TestVisitor<CastExprVisitor> {
+ std::function<void(ExplicitCastExpr *)> OnExplicitCast;
+
+ bool VisitExplicitCastExpr(ExplicitCastExpr *Expr) {
+ if (OnExplicitCast)
+ OnExplicitCast(Expr);
+ return true;
+ }
+};
+
+TEST(CastExprTest, GetSubExprAsWrittenThroughMaterializedTemporary) {
+ CastExprVisitor Visitor;
+ Visitor.OnExplicitCast = [](ExplicitCastExpr *Expr) {
+ auto Sub = Expr->getSubExprAsWritten();
+ EXPECT_TRUE(isa<DeclRefExpr>(Sub))
+ << "Expected DeclRefExpr, but saw " << Sub->getStmtClassName();
+ };
+ Visitor.runOver("struct S1 {};\n"
+ "struct S2 { operator S1(); };\n"
+ "S1 f(S2 s) { return static_cast<S1>(s); }\n");
+}
+
+}
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 5a6693eb4dbb..fd8afe6b7976 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -586,6 +586,27 @@ TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
EXPECT_EQ(2, Argc);
}
+TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
+ // Adjust the given command line arguments to ensure that any positional
+ // arguments in them are stripped.
+ const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
+ int Argc = llvm::array_lengthof(Argv);
+ std::string ErrorMessage;
+ std::unique_ptr<CompilationDatabase> Database =
+ FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMessage);
+ ASSERT_TRUE((bool)Database);
+ ASSERT_TRUE(ErrorMessage.empty());
+ std::vector<CompileCommand> Result = Database->getCompileCommands("source");
+ ASSERT_EQ(1ul, Result.size());
+ ASSERT_EQ(".", Result[0].Directory);
+ std::vector<std::string> Expected;
+ Expected.push_back("clang-tool");
+ Expected.push_back("-fsyntax-only");
+ Expected.push_back("-DDEF3");
+ Expected.push_back("source");
+ ASSERT_EQ(Expected, Result[0].CommandLine);
+}
+
TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
int Argc = sizeof(Argv) / sizeof(char*);
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
index 269bdbb34ab1..74c9c3db34d8 100644
--- a/unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -158,4 +158,90 @@ TEST(RecursiveASTVisitor, DefaultArgumentsAreVisited) {
"static int k = f();\n"));
}
+// Check to ensure that InitListExpr is visited twice, once each for the
+// syntactic and semantic form.
+class InitListExprPreOrderVisitor
+ : public ExpectedLocationVisitor<InitListExprPreOrderVisitor> {
+public:
+ bool VisitInitListExpr(InitListExpr *ILE) {
+ Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
+ return true;
+ }
+};
+
+class InitListExprPostOrderVisitor
+ : public ExpectedLocationVisitor<InitListExprPostOrderVisitor> {
+public:
+ bool shouldTraversePostOrder() const { return true; }
+
+ bool VisitInitListExpr(InitListExpr *ILE) {
+ Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
+ return true;
+ }
+};
+
+class InitListExprPreOrderNoQueueVisitor
+ : public ExpectedLocationVisitor<InitListExprPreOrderNoQueueVisitor> {
+public:
+ bool TraverseInitListExpr(InitListExpr *ILE) {
+ return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
+ }
+
+ bool VisitInitListExpr(InitListExpr *ILE) {
+ Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
+ return true;
+ }
+};
+
+class InitListExprPostOrderNoQueueVisitor
+ : public ExpectedLocationVisitor<InitListExprPostOrderNoQueueVisitor> {
+public:
+ bool shouldTraversePostOrder() const { return true; }
+
+ bool TraverseInitListExpr(InitListExpr *ILE) {
+ return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
+ }
+
+ bool VisitInitListExpr(InitListExpr *ILE) {
+ Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, InitListExprIsPreOrderVisitedTwice) {
+ InitListExprPreOrderVisitor Visitor;
+ Visitor.ExpectMatch("syntactic", 2, 21);
+ Visitor.ExpectMatch("semantic", 2, 21);
+ EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+ "static struct S s = {.x = 0};\n",
+ InitListExprPreOrderVisitor::Lang_C));
+}
+
+TEST(RecursiveASTVisitor, InitListExprIsPostOrderVisitedTwice) {
+ InitListExprPostOrderVisitor Visitor;
+ Visitor.ExpectMatch("syntactic", 2, 21);
+ Visitor.ExpectMatch("semantic", 2, 21);
+ EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+ "static struct S s = {.x = 0};\n",
+ InitListExprPostOrderVisitor::Lang_C));
+}
+
+TEST(RecursiveASTVisitor, InitListExprIsPreOrderNoQueueVisitedTwice) {
+ InitListExprPreOrderNoQueueVisitor Visitor;
+ Visitor.ExpectMatch("syntactic", 2, 21);
+ Visitor.ExpectMatch("semantic", 2, 21);
+ EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+ "static struct S s = {.x = 0};\n",
+ InitListExprPreOrderNoQueueVisitor::Lang_C));
+}
+
+TEST(RecursiveASTVisitor, InitListExprIsPostOrderNoQueueVisitedTwice) {
+ InitListExprPostOrderNoQueueVisitor Visitor;
+ Visitor.ExpectMatch("syntactic", 2, 21);
+ Visitor.ExpectMatch("semantic", 2, 21);
+ EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+ "static struct S s = {.x = 0};\n",
+ InitListExprPostOrderNoQueueVisitor::Lang_C));
+}
+
} // end anonymous namespace
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
index 495ac755b39d..15900940c887 100644
--- a/unittests/Tooling/RefactoringTest.cpp
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -1123,8 +1123,10 @@ TEST_F(AtomicChangeTest, AtomicChangeToYAML) {
"Key: 'input.cpp:20'\n"
"FilePath: input.cpp\n"
"Error: ''\n"
- "InsertedHeaders: [ a.h ]\n"
- "RemovedHeaders: [ b.h ]\n"
+ "InsertedHeaders: \n" // Extra whitespace here!
+ " - a.h\n"
+ "RemovedHeaders: \n" // Extra whitespace here!
+ " - b.h\n"
"Replacements: \n" // Extra whitespace here!
" - FilePath: input.cpp\n"
" Offset: 20\n"
@@ -1143,8 +1145,10 @@ TEST_F(AtomicChangeTest, YAMLToAtomicChange) {
"Key: 'input.cpp:20'\n"
"FilePath: input.cpp\n"
"Error: 'ok'\n"
- "InsertedHeaders: [ a.h ]\n"
- "RemovedHeaders: [ b.h ]\n"
+ "InsertedHeaders: \n" // Extra whitespace here!
+ " - a.h\n"
+ "RemovedHeaders: \n" // Extra whitespace here!
+ " - b.h\n"
"Replacements: \n" // Extra whitespace here!
" - FilePath: input.cpp\n"
" Offset: 20\n"
diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h
index a762ec8b1453..adfd3ef60f50 100644
--- a/unittests/Tooling/TestVisitor.h
+++ b/unittests/Tooling/TestVisitor.h
@@ -53,7 +53,10 @@ public:
bool runOver(StringRef Code, Language L = Lang_CXX) {
std::vector<std::string> Args;
switch (L) {
- case Lang_C: Args.push_back("-std=c99"); break;
+ case Lang_C:
+ Args.push_back("-x");
+ Args.push_back("c");
+ break;
case Lang_CXX98: Args.push_back("-std=c++98"); break;
case Lang_CXX11: Args.push_back("-std=c++11"); break;
case Lang_CXX14: Args.push_back("-std=c++14"); break;