summaryrefslogtreecommitdiff
path: root/unittests/Format
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-07-23 20:44:14 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-07-23 20:44:14 +0000
commit2b6b257f4e5503a7a2675bdb8735693db769f75c (patch)
treee85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /unittests/Format
parentb4348ed0b7e90c0831b925fbee00b5f179a99796 (diff)
Notes
Diffstat (limited to 'unittests/Format')
-rw-r--r--unittests/Format/CMakeLists.txt5
-rw-r--r--unittests/Format/CleanupTest.cpp726
-rw-r--r--unittests/Format/FormatTest.cpp508
-rw-r--r--unittests/Format/FormatTestJS.cpp453
-rw-r--r--unittests/Format/FormatTestJava.cpp11
-rw-r--r--unittests/Format/FormatTestProto.cpp39
-rw-r--r--unittests/Format/FormatTestSelective.cpp37
-rw-r--r--unittests/Format/Makefile19
-rw-r--r--unittests/Format/SortImportsTestJS.cpp269
-rw-r--r--unittests/Format/SortIncludesTest.cpp52
10 files changed, 1981 insertions, 138 deletions
diff --git a/unittests/Format/CMakeLists.txt b/unittests/Format/CMakeLists.txt
index 01af435fffce..240be6ead2a5 100644
--- a/unittests/Format/CMakeLists.txt
+++ b/unittests/Format/CMakeLists.txt
@@ -3,15 +3,20 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_unittest(FormatTests
+ CleanupTest.cpp
FormatTest.cpp
FormatTestJava.cpp
FormatTestJS.cpp
FormatTestProto.cpp
FormatTestSelective.cpp
+ SortImportsTestJS.cpp
SortIncludesTest.cpp
)
target_link_libraries(FormatTests
+ clangBasic
clangFormat
+ clangFrontend
+ clangRewrite
clangToolingCore
)
diff --git a/unittests/Format/CleanupTest.cpp b/unittests/Format/CleanupTest.cpp
new file mode 100644
index 000000000000..5f85c53b780a
--- /dev/null
+++ b/unittests/Format/CleanupTest.cpp
@@ -0,0 +1,726 @@
+//===- unittest/Format/CleanupTest.cpp - Code cleanup unit tests ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Format/Format.h"
+
+#include "../Tooling/RewriterTestContext.h"
+#include "clang/Tooling/Core/Replacement.h"
+
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace format {
+namespace {
+
+class CleanupTest : public ::testing::Test {
+protected:
+ std::string cleanup(llvm::StringRef Code,
+ const std::vector<tooling::Range> &Ranges,
+ const FormatStyle &Style = getLLVMStyle()) {
+ tooling::Replacements Replaces = format::cleanup(Style, Code, Ranges);
+
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ return *Result;
+ }
+};
+
+TEST_F(CleanupTest, DeleteEmptyNamespaces) {
+ std::string Code = "namespace A {\n"
+ "namespace B {\n"
+ "} // namespace B\n"
+ "} // namespace A\n\n"
+ "namespace C {\n"
+ "namespace D { int i; }\n"
+ "inline namespace E { namespace { } }\n"
+ "}";
+ std::string Expected = "\n\n\n\n\nnamespace C {\n"
+ "namespace D { int i; }\n \n"
+ "}";
+ std::vector<tooling::Range> Ranges;
+ Ranges.push_back(tooling::Range(28, 0));
+ Ranges.push_back(tooling::Range(91, 6));
+ Ranges.push_back(tooling::Range(132, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, NamespaceWithSyntaxError) {
+ std::string Code = "namespace A {\n"
+ "namespace B {\n" // missing r_brace
+ "} // namespace A\n\n"
+ "namespace C {\n"
+ "namespace D int i; }\n"
+ "inline namespace E { namespace { } }\n"
+ "}";
+ std::string Expected = "namespace A {\n"
+ "\n\n\nnamespace C {\n"
+ "namespace D int i; }\n \n"
+ "}";
+ std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, EmptyNamespaceNotAffected) {
+ std::string Code = "namespace A {\n\n"
+ "namespace {\n\n}}";
+ // Even though the namespaces are empty, but the inner most empty namespace
+ // block is not affected by the changed ranges.
+ std::string Expected = "namespace A {\n\n"
+ "namespace {\n\n}}";
+ // Set the changed range to be the second "\n".
+ std::vector<tooling::Range> Ranges(1, tooling::Range(14, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, EmptyNamespaceWithCommentsNoBreakBeforeBrace) {
+ std::string Code = "namespace A {\n"
+ "namespace B {\n"
+ "// Yo\n"
+ "} // namespace B\n"
+ "} // namespace A\n"
+ "namespace C { // Yo\n"
+ "}";
+ std::string Expected = "\n\n\n\n\n\n";
+ std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) {
+ std::string Code = "namespace A\n"
+ "/* Yo */ {\n"
+ "namespace B\n"
+ "{\n"
+ "// Yo\n"
+ "} // namespace B\n"
+ "} // namespace A\n"
+ "namespace C\n"
+ "{ // Yo\n"
+ "}\n";
+ std::string Expected = "\n\n\n\n\n\n\n\n\n\n";
+ std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+ FormatStyle Style = getLLVMStyle();
+ Style.BraceWrapping.AfterNamespace = true;
+ std::string Result = cleanup(Code, Ranges, Style);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
+ std::string Code = "class A {\nA() : , {} };";
+ std::string Expected = "class A {\nA() {} };";
+ std::vector<tooling::Range> Ranges;
+ Ranges.push_back(tooling::Range(17, 0));
+ Ranges.push_back(tooling::Range(19, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ Code = "class A {\nA() : x(1), {} };";
+ Expected = "class A {\nA() : x(1) {} };";
+ Ranges.clear();
+ Ranges.push_back(tooling::Range(23, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ Code = "class A {\nA() :,,,,{} };";
+ Expected = "class A {\nA() {} };";
+ Ranges.clear();
+ Ranges.push_back(tooling::Range(15, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, ListSimpleRedundantComma) {
+ std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }";
+ std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }";
+ std::vector<tooling::Range> Ranges;
+ Ranges.push_back(tooling::Range(40, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ Code = "int main() { f(1,,2,3,,4);}";
+ Expected = "int main() { f(1,2,3,4);}";
+ Ranges.clear();
+ Ranges.push_back(tooling::Range(17, 0));
+ Ranges.push_back(tooling::Range(22, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, CtorInitializationBracesInParens) {
+ std::string Code = "class A {\nA() : x({1}),, {} };";
+ std::string Expected = "class A {\nA() : x({1}) {} };";
+ std::vector<tooling::Range> Ranges;
+ Ranges.push_back(tooling::Range(24, 0));
+ Ranges.push_back(tooling::Range(26, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
+ std::string Code =
+ "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
+ std::string Expected =
+ "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
+ // Set the affected range to be "int x = 0", which does not intercept the
+ // constructor initialization list.
+ std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ Code = "class A {\nA() : x(1), {} };";
+ Expected = "class A {\nA() : x(1), {} };";
+ // No range. Fixer should do nothing.
+ Ranges.clear();
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+// FIXME: delete comments too.
+TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) {
+ // Remove redundant commas around comment.
+ std::string Code = "class A {\nA() : x({1}), /* comment */, {} };";
+ std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };";
+ std::vector<tooling::Range> Ranges;
+ Ranges.push_back(tooling::Range(25, 0));
+ Ranges.push_back(tooling::Range(40, 0));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ // Remove trailing comma and ignore comment.
+ Code = "class A {\nA() : x({1}), // comment\n{} };";
+ Expected = "class A {\nA() : x({1}) // comment\n{} };";
+ Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ // Remove trailing comma and ignore comment.
+ Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
+ Expected = "class A {\nA() : x({1}), // comment\n y(1){} };";
+ Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ // Remove trailing comma and ignore comment.
+ Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
+ Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };";
+ Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+
+ // Remove trailing comma and ignore comment.
+ Code = "class A {\nA() : , // comment\n y(1),{} };";
+ Expected = "class A {\nA() : // comment\n y(1){} };";
+ Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0));
+ Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, CtorInitializerInNamespace) {
+ std::string Code = "namespace A {\n"
+ "namespace B {\n" // missing r_brace
+ "} // namespace A\n\n"
+ "namespace C {\n"
+ "class A { A() : x(0),, {} };\n"
+ "inline namespace E { namespace { } }\n"
+ "}";
+ std::string Expected = "namespace A {\n"
+ "\n\n\nnamespace C {\n"
+ "class A { A() : x(0) {} };\n \n"
+ "}";
+ std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+ std::string Result = cleanup(Code, Ranges);
+ EXPECT_EQ(Expected, Result);
+}
+
+class CleanUpReplacementsTest : public ::testing::Test {
+protected:
+ tooling::Replacement createReplacement(unsigned Offset, unsigned Length,
+ StringRef Text) {
+ return tooling::Replacement(FileName, Offset, Length, Text);
+ }
+
+ tooling::Replacement createInsertion(StringRef HeaderName) {
+ return createReplacement(UINT_MAX, 0, HeaderName);
+ }
+
+ inline std::string apply(StringRef Code,
+ const tooling::Replacements Replaces) {
+ auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_TRUE(static_cast<bool>(CleanReplaces))
+ << llvm::toString(CleanReplaces.takeError()) << "\n";
+ auto Result = applyAllReplacements(Code, *CleanReplaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ return *Result;
+ }
+
+ inline std::string formatAndApply(StringRef Code,
+ const tooling::Replacements Replaces) {
+
+ auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_TRUE(static_cast<bool>(CleanReplaces))
+ << llvm::toString(CleanReplaces.takeError()) << "\n";
+ auto FormattedReplaces = formatReplacements(Code, *CleanReplaces, Style);
+ EXPECT_TRUE(static_cast<bool>(FormattedReplaces))
+ << llvm::toString(FormattedReplaces.takeError()) << "\n";
+ auto Result = applyAllReplacements(Code, *FormattedReplaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ return *Result;
+ }
+
+ int getOffset(StringRef Code, int Line, int Column) {
+ RewriterTestContext Context;
+ FileID ID = Context.createInMemoryFile(FileName, Code);
+ auto DecomposedLocation =
+ Context.Sources.getDecomposedLoc(Context.getLocation(ID, Line, Column));
+ return DecomposedLocation.second;
+ }
+
+ const std::string FileName = "fix.cpp";
+ FormatStyle Style = getLLVMStyle();
+};
+
+TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) {
+ std::string Code = "namespace A {\n"
+ "namespace B {\n"
+ " int x;\n"
+ "} // namespace B\n"
+ "} // namespace A\n"
+ "\n"
+ "namespace C {\n"
+ "namespace D { int i; }\n"
+ "inline namespace E { namespace { int y; } }\n"
+ "int x= 0;"
+ "}";
+ std::string Expected = "\n\nnamespace C {\n"
+ "namespace D { int i; }\n\n"
+ "int x= 0;"
+ "}";
+ tooling::Replacements Replaces = {
+ createReplacement(getOffset(Code, 3, 3), 6, ""),
+ createReplacement(getOffset(Code, 9, 34), 6, "")};
+
+ EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithoutDefine) {
+ std::string Code = "int main() {}";
+ std::string Expected = "#include \"a.h\"\n"
+ "int main() {}";
+ tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithDefine) {
+ std::string Code = "#ifndef A_H\n"
+ "#define A_H\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+ std::string Expected = "#ifndef A_H\n"
+ "#define A_H\n"
+ "#include \"b.h\"\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+
+ tooling::Replacements Replaces = {createInsertion("#include \"b.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertBeforeCategoryWithLowerPriority) {
+ std::string Code = "#ifndef A_H\n"
+ "#define A_H\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include <vector>\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+ std::string Expected = "#ifndef A_H\n"
+ "#define A_H\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include \"a.h\"\n"
+ "#include <vector>\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+
+ tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertAfterMainHeader) {
+ std::string Code = "#include \"fix.h\"\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include \"fix.h\"\n"
+ "#include <a>\n"
+ "\n"
+ "int main() {}";
+ tooling::Replacements Replaces = {createInsertion("#include <a>")};
+ Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertBeforeSystemHeaderLLVM) {
+ std::string Code = "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include \"z.h\"\n"
+ "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertAfterSystemHeaderGoogle) {
+ std::string Code = "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include <memory>\n"
+ "#include \"z.h\"\n"
+ "\n"
+ "int main() {}";
+ tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")};
+ Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertOneIncludeLLVMStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"d.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include \"llvm/x/y.h\"\n"
+ "#include <memory>\n";
+ tooling::Replacements Replaces = {createInsertion("#include \"d.h\""),
+ createInsertion("#include \"llvm/x/y.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"new/new.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n"
+ "#include <list>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <list>"),
+ createInsertion("#include \"new/new.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertNewSystemIncludeGoogleStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ // FIXME: inserting after the empty line following the main header might be
+ // prefered.
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include <vector>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include <vector>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include <vector>\n"
+ "#include <list>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n"
+ "#include \"x/x.h\"\n";
+ tooling::Replacements Replaces = {createInsertion("#include <list>"),
+ createInsertion("#include \"x/x.h\"")};
+ Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
+ std::string Code = "\nint x;";
+ std::string Expected = "\n#include \"fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "int x;";
+ tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
+ createInsertion("#include \"c.h\""),
+ createInsertion("#include \"b.h\""),
+ createInsertion("#include <vector>"),
+ createInsertion("#include <list>"),
+ createInsertion("#include \"fix.h\"")};
+ EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
+ std::string Code = "\nint x;";
+ std::string Expected = "\n#include \"fix.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "int x;";
+ tooling::Replacements Replaces = {createInsertion("#include \"a.h\""),
+ createInsertion("#include \"c.h\""),
+ createInsertion("#include \"b.h\""),
+ createInsertion("#include <vector>"),
+ createInsertion("#include <list>"),
+ createInsertion("#include \"fix.h\"")};
+ Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
+ std::string Code = "\n"
+ "int x;\n"
+ "int a;\n"
+ "int a;\n"
+ "int a;";
+
+ std::string Expected = "\n#include \"x.h\"\n"
+ "#include \"y.h\"\n"
+ "#include \"clang/x/x.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "int x;\n"
+ "int a;\n"
+ "int b;\n"
+ "int a;";
+ tooling::Replacements Replaces = {
+ createReplacement(getOffset(Code, 4, 8), 1, "b"),
+ createInsertion("#include <vector>"),
+ createInsertion("#include <list>"),
+ createInsertion("#include \"clang/x/x.h\""),
+ createInsertion("#include \"y.h\""),
+ createInsertion("#include \"x.h\"")};
+ EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, NotConfusedByDefine) {
+ std::string Code = "void f() {}\n"
+ "#define A \\\n"
+ " int i;";
+ std::string Expected = "#include <vector>\n"
+ "void f() {}\n"
+ "#define A \\\n"
+ " int i;";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, formatAndApply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, SkippedTopComment) {
+ std::string Code = "// comment\n"
+ "\n"
+ " // comment\n";
+ std::string Expected = "// comment\n"
+ "\n"
+ " // comment\n"
+ "#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, SkippedMixedComments) {
+ std::string Code = "// comment\n"
+ "// comment \\\n"
+ " comment continued\n"
+ "/*\n"
+ "* comment\n"
+ "*/\n";
+ std::string Expected = "// comment\n"
+ "// comment \\\n"
+ " comment continued\n"
+ "/*\n"
+ "* comment\n"
+ "*/\n"
+ "#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, MultipleBlockCommentsInOneLine) {
+ std::string Code = "/*\n"
+ "* comment\n"
+ "*/ /* comment\n"
+ "*/\n"
+ "\n\n"
+ "/* c1 */ /*c2 */\n";
+ std::string Expected = "/*\n"
+ "* comment\n"
+ "*/ /* comment\n"
+ "*/\n"
+ "\n\n"
+ "/* c1 */ /*c2 */\n"
+ "#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, CodeAfterComments) {
+ std::string Code = "/*\n"
+ "* comment\n"
+ "*/ /* comment\n"
+ "*/\n"
+ "\n\n"
+ "/* c1 */ /*c2 */\n"
+ "\n"
+ "int x;\n";
+ std::string Expected = "/*\n"
+ "* comment\n"
+ "*/ /* comment\n"
+ "*/\n"
+ "\n\n"
+ "/* c1 */ /*c2 */\n"
+ "\n"
+ "#include <vector>\n"
+ "int x;\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, FakeHeaderGuardIfDef) {
+ std::string Code = "// comment \n"
+ "#ifdef X\n"
+ "#define X\n";
+ std::string Expected = "// comment \n"
+ "#include <vector>\n"
+ "#ifdef X\n"
+ "#define X\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, RealHeaderGuardAfterComments) {
+ std::string Code = "// comment \n"
+ "#ifndef X\n"
+ "#define X\n"
+ "int x;\n"
+ "#define Y 1\n";
+ std::string Expected = "// comment \n"
+ "#ifndef X\n"
+ "#define X\n"
+ "#include <vector>\n"
+ "int x;\n"
+ "#define Y 1\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, IfNDefWithNoDefine) {
+ std::string Code = "// comment \n"
+ "#ifndef X\n"
+ "int x;\n"
+ "#define Y 1\n";
+ std::string Expected = "// comment \n"
+ "#include <vector>\n"
+ "#ifndef X\n"
+ "int x;\n"
+ "#define Y 1\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, HeaderGuardWithComment) {
+ std::string Code = "// comment \n"
+ "#ifndef X // comment\n"
+ "// comment\n"
+ "/* comment\n"
+ "*/\n"
+ "/* comment */ #define X\n"
+ "int x;\n"
+ "#define Y 1\n";
+ std::string Expected = "// comment \n"
+ "#ifndef X // comment\n"
+ "// comment\n"
+ "/* comment\n"
+ "*/\n"
+ "/* comment */ #define X\n"
+ "#include <vector>\n"
+ "int x;\n"
+ "#define Y 1\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, EmptyCode) {
+ std::string Code = "";
+ std::string Expected = "#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+// FIXME: although this case does not crash, the insertion is wrong. A '\n'
+// should be inserted between the two #includes.
+TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCode) {
+ std::string Code = "#include <map>";
+ std::string Expected = "#include <map>#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, SkipExistingHeaders) {
+ std::string Code = "#include \"a.h\"\n"
+ "#include <vector>\n";
+ std::string Expected = "#include \"a.h\"\n"
+ "#include <vector>\n";
+ tooling::Replacements Replaces = {createInsertion("#include <vector>"),
+ createInsertion("#include \"a.h\"")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, AddIncludesWithDifferentForms) {
+ std::string Code = "#include \"a.h\"\n"
+ "#include <vector>\n";
+ // FIXME: this might not be the best behavior.
+ std::string Expected = "#include \"a.h\"\n"
+ "#include \"vector\"\n"
+ "#include <vector>\n"
+ "#include <a.h>\n";
+ tooling::Replacements Replaces = {createInsertion("#include \"vector\""),
+ createInsertion("#include <a.h>")};
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+} // end namespace
+} // end namespace format
+} // end namespace clang
diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp
index d9a16db74c88..8d46ba6efcfe 100644
--- a/unittests/Format/FormatTest.cpp
+++ b/unittests/Format/FormatTest.cpp
@@ -7,9 +7,14 @@
//
//===----------------------------------------------------------------------===//
-#include "FormatTestUtils.h"
#include "clang/Format/Format.h"
+
+#include "../Tooling/RewriterTestContext.h"
+#include "FormatTestUtils.h"
+
+#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
#define DEBUG_TYPE "format-test"
@@ -42,10 +47,10 @@ protected:
EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n";
}
ReplacementCount = Replaces.size();
- std::string Result = applyAllReplacements(Code, Replaces);
- EXPECT_NE("", Result);
- DEBUG(llvm::errs() << "\n" << Result << "\n\n");
- return Result;
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+ return *Result;
}
FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) {
@@ -291,11 +296,23 @@ TEST_F(FormatTest, FormatIfWithoutCompoundStatement) {
verifyFormat("if (a)\n if (b) {\n f();\n }\ng();");
FormatStyle AllowsMergedIf = getLLVMStyle();
+ AllowsMergedIf.AlignEscapedNewlinesLeft = true;
AllowsMergedIf.AllowShortIfStatementsOnASingleLine = true;
verifyFormat("if (a)\n"
" // comment\n"
" f();",
AllowsMergedIf);
+ verifyFormat("{\n"
+ " if (a)\n"
+ " label:\n"
+ " f();\n"
+ "}",
+ AllowsMergedIf);
+ verifyFormat("#define A \\\n"
+ " if (a) \\\n"
+ " label: \\\n"
+ " f()",
+ AllowsMergedIf);
verifyFormat("if (a)\n"
" ;",
AllowsMergedIf);
@@ -738,6 +755,7 @@ TEST_F(FormatTest, CaseRanges) {
verifyFormat("switch (x) {\n"
"case 'A' ... 'Z':\n"
"case 1 ... 5:\n"
+ "case a ... b:\n"
" break;\n"
"}");
}
@@ -962,6 +980,14 @@ TEST_F(FormatTest, UnderstandsSingleLineComments) {
"// at start\n"
"otherLine();"));
EXPECT_EQ("lineWith(); // comment\n"
+ "/*\n"
+ " * at start */\n"
+ "otherLine();",
+ format("lineWith(); // comment\n"
+ "/*\n"
+ " * at start */\n"
+ "otherLine();"));
+ EXPECT_EQ("lineWith(); // comment\n"
" // at start\n"
"otherLine();",
format("lineWith(); // comment\n"
@@ -1097,7 +1123,7 @@ TEST_F(FormatTest, RemovesTrailingWhitespaceOfComments) {
TEST_F(FormatTest, UnderstandsBlockComments) {
verifyFormat("f(/*noSpaceAfterParameterNamingComment=*/true);");
- verifyFormat("void f() { g(/*aaa=*/x, /*bbb=*/!y); }");
+ verifyFormat("void f() { g(/*aaa=*/x, /*bbb=*/!y, /*c=*/::c); }");
EXPECT_EQ("f(aaaaaaaaaaaaaaaaaaaaaaaaa, /* Trailing comment for aa... */\n"
" bbbbbbbbbbbbbbbbbbbbbbbbb);",
format("f(aaaaaaaaaaaaaaaaaaaaaaaaa , \\\n"
@@ -1117,6 +1143,8 @@ TEST_F(FormatTest, UnderstandsBlockComments) {
" aaaaaaaaaaaaaaaaaa ,\n"
" aaaaaaaaaaaaaaaaaa) { /*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/\n"
"}"));
+ verifyFormat("f(/* aaaaaaaaaaaaaaaaaa = */\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);");
FormatStyle NoBinPacking = getLLVMStyle();
NoBinPacking.BinPackParameters = false;
@@ -2741,6 +2769,12 @@ TEST_F(FormatTest, MacroDefinitionsWithIncompleteCode) {
" case 1: \\\n"
" case 2\n",
getLLVMStyleWithColumns(20));
+ verifyFormat("#define MACRO(a) \\\n"
+ " if (a) \\\n"
+ " f(); \\\n"
+ " else \\\n"
+ " g()",
+ getLLVMStyleWithColumns(18));
verifyFormat("#define A template <typename T>");
verifyIncompleteFormat("#define STR(x) #x\n"
"f(STR(this_is_a_string_literal{));");
@@ -3842,6 +3876,11 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) {
"typename aaaaaaaaaa<aaaaaa>::aaaaaaaaaaa\n"
"aaaaaaaaaa<aaaaaa>::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
" bool *aaaaaaaaaaaaaaaaaa, bool *aa) {}");
+ verifyGoogleFormat(
+ "template <typename T>\n"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ "aaaaaaaaaaaaaaaaaaaaaaa<T>::aaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa);");
FormatStyle Style = getLLVMStyle();
Style.PointerAlignment = FormatStyle::PAS_Left;
@@ -3979,6 +4018,12 @@ TEST_F(FormatTest, FunctionAnnotations) {
" << bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
verifyFormat("TEST_F(ThisIsATestFixtureeeeeeeeeeeee,\n"
" ThisIsATestWithAReallyReallyReallyReallyLongName) {}");
+ verifyFormat("MACRO(abc).function() // wrap\n"
+ " << abc;");
+ verifyFormat("MACRO(abc)->function() // wrap\n"
+ " << abc;");
+ verifyFormat("MACRO(abc)::function() // wrap\n"
+ " << abc;");
}
TEST_F(FormatTest, BreaksDesireably) {
@@ -4426,6 +4471,11 @@ TEST_F(FormatTest, AlignsAfterOpenBracket) {
" aaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa));",
Style);
+ verifyFormat("bbbbbbbbbbbb(aaaaaaaaaaaaaaaaaaaaaaaa, //\n"
+ " ccccccc(aaaaaaaaaaaaaaaaa, //\n"
+ " b));",
+ Style);
+
Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
Style.BinPackArguments = false;
Style.BinPackParameters = false;
@@ -4439,12 +4489,31 @@ TEST_F(FormatTest, AlignsAfterOpenBracket) {
" aaaaaaaaaaa aaaaaaaaa,\n"
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);",
Style);
- verifyFormat("SomeLongVariableName->someFunction(\n"
- " foooooooo(\n"
- " aaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaaaaaaaa,\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa));",
+ verifyFormat("SomeLongVariableName->someFunction(foooooooo(\n"
+ " aaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaaaaaaaa,\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa));",
Style);
+ verifyFormat(
+ "aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)));",
+ Style);
+ verifyFormat(
+ "aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaa.aaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)));",
+ Style);
+ verifyFormat(
+ "aaaaaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)),\n"
+ " aaaaaaaaaaaaaaaa);",
+ Style);
+ verifyFormat(
+ "aaaaaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)) &&\n"
+ " aaaaaaaaaaaaaaaa);",
+ Style);
}
TEST_F(FormatTest, ParenthesesAndOperandAlignment) {
@@ -4686,6 +4755,14 @@ TEST_F(FormatTest, BreaksConditionalExpressionsAfterOperator) {
" ccccccccccccccc :\n"
" ddddddddddddddd);",
Style);
+ verifyFormat("int i = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ?\n"
+ " /*bbbbbbbbbbbbbbb=*/bbbbbbbbbbbbbbbbbbbbbbbbb :\n"
+ " ccccccccccccccccccccccccccc;",
+ Style);
+ verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ?\n"
+ " aaaaa :\n"
+ " bbbbbbbbbbbbbbb + cccccccccccccccc;",
+ Style);
}
TEST_F(FormatTest, DeclarationsOfMultipleVariables) {
@@ -5083,6 +5160,13 @@ TEST_F(FormatTest, AlignsPipes) {
" << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;");
verifyFormat("SemaRef.Diag(Loc, diag::note_for_range_begin_end)\n"
" << BEF << IsTemplate << Description << E->getType();");
+ verifyFormat("Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa)\n"
+ " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);");
+ verifyFormat("Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa)\n"
+ " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n"
+ " << aaa;");
verifyFormat(
"llvm::errs() << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
@@ -5306,6 +5390,10 @@ TEST_F(FormatTest, WrapsTemplateDeclarations) {
verifyFormat("template <typename T> // T can be A, B or C.\n"
"struct C {};",
AlwaysBreak);
+ verifyFormat("template <enum E> class A {\n"
+ "public:\n"
+ " E *f();\n"
+ "};");
}
TEST_F(FormatTest, WrapsAtNestedNameSpecifiers) {
@@ -5382,6 +5470,10 @@ TEST_F(FormatTest, UnderstandsTemplateParameters) {
verifyFormat("struct A<std::enable_if<sizeof(T2) ? sizeof(int32) : "
"sizeof(char)>::type>;");
verifyFormat("template <class T> struct S<std::is_arithmetic<T>{}> {};");
+ verifyFormat("f(a.operator()<A>());");
+ verifyFormat("f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ " .template operator()<A>());",
+ getLLVMStyleWithColumns(35));
// Not template parameters.
verifyFormat("return a < b && c > d;");
@@ -5402,6 +5494,7 @@ TEST_F(FormatTest, UnderstandsTemplateParameters) {
TEST_F(FormatTest, UnderstandsBinaryOperators) {
verifyFormat("COMPARE(a, ==, b);");
+ verifyFormat("auto s = sizeof...(Ts) - 1;");
}
TEST_F(FormatTest, UnderstandsPointersToMembers) {
@@ -5534,14 +5627,21 @@ TEST_F(FormatTest, UnderstandsFunctionRefQualification) {
verifyFormat("SomeType MemberFunction(const Deleted &) && {}");
verifyFormat("SomeType MemberFunction(const Deleted &) && final {}");
verifyFormat("SomeType MemberFunction(const Deleted &) && override {}");
+ verifyFormat("SomeType MemberFunction(const Deleted &) const &;");
FormatStyle AlignLeft = getLLVMStyle();
AlignLeft.PointerAlignment = FormatStyle::PAS_Left;
+ verifyFormat("void A::b() && {}", AlignLeft);
verifyFormat("Deleted& operator=(const Deleted&) & = default;", AlignLeft);
verifyFormat("SomeType MemberFunction(const Deleted&) & = delete;",
AlignLeft);
verifyFormat("Deleted& operator=(const Deleted&) &;", AlignLeft);
verifyFormat("SomeType MemberFunction(const Deleted&) &;", AlignLeft);
+ verifyFormat("auto Function(T t) & -> void {}", AlignLeft);
+ verifyFormat("auto Function(T... t) & -> void {}", AlignLeft);
+ verifyFormat("auto Function(T) & -> void {}", AlignLeft);
+ verifyFormat("auto Function(T) & -> void;", AlignLeft);
+ verifyFormat("SomeType MemberFunction(const Deleted&) const &;", AlignLeft);
FormatStyle Spaces = getLLVMStyle();
Spaces.SpacesInCStyleCastParentheses = true;
@@ -5626,6 +5726,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) {
verifyFormat("[](const decltype(*a) &value) {}");
verifyFormat("decltype(a * b) F();");
verifyFormat("#define MACRO() [](A *a) { return 1; }");
+ verifyFormat("Constructor() : member([](A *a, B *b) {}) {}");
verifyIndependentOfContext("typedef void (*f)(int *a);");
verifyIndependentOfContext("int i{a * b};");
verifyIndependentOfContext("aaa && aaa->f();");
@@ -5654,6 +5755,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) {
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
" aaaaaaaaaaaaaaaaaaaaaaaaaaaa, *aaaaaaaaaaaaaaaaaaaaaaaaaaaaa);");
+ verifyGoogleFormat("int const* a = &b;");
verifyGoogleFormat("**outparam = 1;");
verifyGoogleFormat("*outparam = a * b;");
verifyGoogleFormat("int main(int argc, char** argv) {}");
@@ -5922,6 +6024,7 @@ TEST_F(FormatTest, FormatsCasts) {
verifyFormat("my_int a = (my_int)(my_int)-1;");
verifyFormat("my_int a = (ns::my_int)-2;");
verifyFormat("case (my_int)ONE:");
+ verifyFormat("auto x = (X)this;");
// FIXME: single value wrapped with paren will be treated as cast.
verifyFormat("void f(int i = (kValue)*kMask) {}");
@@ -5995,6 +6098,7 @@ TEST_F(FormatTest, FormatsFunctionTypes) {
verifyFormat("some_var = function(*some_pointer_var)[0];");
verifyFormat("void f() { function(*some_pointer_var)[0] = 10; }");
verifyFormat("int x = f(&h)();");
+ verifyFormat("returnsFunction(&param1, &param2)(param);");
}
TEST_F(FormatTest, FormatsPointersToArrayTypes) {
@@ -6038,6 +6142,10 @@ TEST_F(FormatTest, BreaksLongDeclarations) {
"LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}");
verifyFormat("decltype(LoooooooooooooooooooooooooooooooooooooooongName)\n"
"LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}");
+ verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n"
+ "LooooooooooooooooooooooooooongFunctionDeclaration(T... t);");
+ verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n"
+ "LooooooooooooooooooooooooooongFunctionDeclaration(T /*t*/) {}");
FormatStyle Indented = getLLVMStyle();
Indented.IndentWrappedFunctionNames = true;
verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n"
@@ -6126,6 +6234,8 @@ TEST_F(FormatTest, FormatsArrays) {
"llvm::outs() << \"aaaaaaaaaaaa: \"\n"
" << (*aaaaaaaiaaaaaaa)[aaaaaaaaaaaaaaaaaaaaaaaaa]\n"
" [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];");
+ verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[aaaaaaaaaaaaaaaaa][a]\n"
+ " .aaaaaaaaaaaaaaaaaaaaaa();");
verifyGoogleFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<int>\n"
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[aaaaaaaaaaaa];");
@@ -6133,6 +6243,7 @@ TEST_F(FormatTest, FormatsArrays) {
"aaaaaaaaaaa aaaaaaaaaaaaaaa = aaaaaaaaaaaaaaaaaaaaaaaaaa->aaaaaaaaa[0]\n"
" .aaaaaaa[0]\n"
" .aaaaaaaaaaaaaaaaaaaaaa();");
+ verifyFormat("a[::b::c];");
verifyNoCrash("a[,Y?)]", getLLVMStyleWithColumns(10));
@@ -6607,7 +6718,7 @@ TEST_F(FormatTest, FormatsBracedListsInColumnLayout) {
" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb};");
verifyNoCrash("a<,");
-
+
// No braced initializer here.
verifyFormat("void f() {\n"
" struct Dummy {};\n"
@@ -6918,6 +7029,14 @@ TEST_F(FormatTest, BlockComments) {
"* aaaaaa aaaaaa\n"
"*/",
getLLVMStyleWithColumns(10)));
+ EXPECT_EQ("int aaaaaaaaaaaaaaaaaaaaaaaaaaaa =\n"
+ " /* line 1\n"
+ " bbbbbbbbbbbb */\n"
+ " bbbbbbbbbbbbbbbbbbbbbbbbbbbb;",
+ format("int aaaaaaaaaaaaaaaaaaaaaaaaaaaa =\n"
+ " /* line 1\n"
+ " bbbbbbbbbbbb */ bbbbbbbbbbbbbbbbbbbbbbbbbbbb;",
+ getLLVMStyleWithColumns(50)));
FormatStyle NoBinPacking = getLLVMStyle();
NoBinPacking.BinPackParameters = false;
@@ -7026,10 +7145,9 @@ TEST_F(FormatTest, BlockCommentsAtEndOfLine) {
}
TEST_F(FormatTest, IndentLineCommentsInStartOfBlockAtEndOfFile) {
- // FIXME: This is not what we want...
verifyFormat("{\n"
- "// a"
- "// b");
+ " // a\n"
+ " // b");
}
TEST_F(FormatTest, FormatStarDependingOnContext) {
@@ -7949,6 +8067,10 @@ TEST_F(FormatTest, BreaksStringLiterals) {
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"});",
getGoogleStyle()));
+ FormatStyle Style = getLLVMStyleWithColumns(12);
+ Style.BreakStringLiterals = false;
+ EXPECT_EQ("\"some text other\";", format("\"some text other\";", Style));
+
FormatStyle AlignLeft = getLLVMStyleWithColumns(12);
AlignLeft.AlignEscapedNewlinesLeft = true;
EXPECT_EQ("#define A \\\n"
@@ -8484,6 +8606,230 @@ TEST_F(FormatTest, ConfigurableUseOfTab) {
"\t */\n"
"\t int i;\n"
"}"));
+
+ Tab.UseTab = FormatStyle::UT_ForContinuationAndIndentation;
+ Tab.TabWidth = 8;
+ Tab.IndentWidth = 8;
+ EXPECT_EQ("if (aaaaaaaa && // q\n"
+ " bb) // w\n"
+ "\t;",
+ format("if (aaaaaaaa &&// q\n"
+ "bb)// w\n"
+ ";",
+ Tab));
+ EXPECT_EQ("if (aaa && bbb) // w\n"
+ "\t;",
+ format("if(aaa&&bbb)// w\n"
+ ";",
+ Tab));
+ verifyFormat("class X {\n"
+ "\tvoid f() {\n"
+ "\t\tsomeFunction(parameter1,\n"
+ "\t\t\t parameter2);\n"
+ "\t}\n"
+ "};",
+ Tab);
+ verifyFormat("#define A \\\n"
+ "\tvoid f() { \\\n"
+ "\t\tsomeFunction( \\\n"
+ "\t\t parameter1, \\\n"
+ "\t\t parameter2); \\\n"
+ "\t}",
+ Tab);
+ Tab.TabWidth = 4;
+ Tab.IndentWidth = 8;
+ verifyFormat("class TabWidth4Indent8 {\n"
+ "\t\tvoid f() {\n"
+ "\t\t\t\tsomeFunction(parameter1,\n"
+ "\t\t\t\t\t\t\t parameter2);\n"
+ "\t\t}\n"
+ "};",
+ Tab);
+ Tab.TabWidth = 4;
+ Tab.IndentWidth = 4;
+ verifyFormat("class TabWidth4Indent4 {\n"
+ "\tvoid f() {\n"
+ "\t\tsomeFunction(parameter1,\n"
+ "\t\t\t\t\t parameter2);\n"
+ "\t}\n"
+ "};",
+ Tab);
+ Tab.TabWidth = 8;
+ Tab.IndentWidth = 4;
+ verifyFormat("class TabWidth8Indent4 {\n"
+ " void f() {\n"
+ "\tsomeFunction(parameter1,\n"
+ "\t\t parameter2);\n"
+ " }\n"
+ "};",
+ Tab);
+ Tab.TabWidth = 8;
+ Tab.IndentWidth = 8;
+ EXPECT_EQ("/*\n"
+ "\t a\t\tcomment\n"
+ "\t in multiple lines\n"
+ " */",
+ format(" /*\t \t \n"
+ " \t \t a\t\tcomment\t \t\n"
+ " \t \t in multiple lines\t\n"
+ " \t */",
+ Tab));
+ verifyFormat("{\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "\taaaaaaaaaaaaaaaaaaaaaaaaaaaa();\n"
+ "};",
+ Tab);
+ verifyFormat("enum AA {\n"
+ "\ta1, // Force multiple lines\n"
+ "\ta2,\n"
+ "\ta3\n"
+ "};",
+ Tab);
+ EXPECT_EQ("if (aaaaaaaa && // q\n"
+ " bb) // w\n"
+ "\t;",
+ format("if (aaaaaaaa &&// q\n"
+ "bb)// w\n"
+ ";",
+ Tab));
+ verifyFormat("class X {\n"
+ "\tvoid f() {\n"
+ "\t\tsomeFunction(parameter1,\n"
+ "\t\t\t parameter2);\n"
+ "\t}\n"
+ "};",
+ Tab);
+ verifyFormat("{\n"
+ "\tQ(\n"
+ "\t {\n"
+ "\t\t int a;\n"
+ "\t\t someFunction(aaaaaaaa,\n"
+ "\t\t\t\t bbbbbbb);\n"
+ "\t },\n"
+ "\t p);\n"
+ "}",
+ Tab);
+ EXPECT_EQ("{\n"
+ "\t/* aaaa\n"
+ "\t bbbb */\n"
+ "}",
+ format("{\n"
+ "/* aaaa\n"
+ " bbbb */\n"
+ "}",
+ Tab));
+ EXPECT_EQ("{\n"
+ "\t/*\n"
+ "\t aaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ "\t bbbbbbbbbbbbb\n"
+ "\t*/\n"
+ "}",
+ format("{\n"
+ "/*\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n"
+ "*/\n"
+ "}",
+ Tab));
+ EXPECT_EQ("{\n"
+ "\t// aaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ "\t// bbbbbbbbbbbbb\n"
+ "}",
+ format("{\n"
+ "\t// aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n"
+ "}",
+ Tab));
+ EXPECT_EQ("{\n"
+ "\t/*\n"
+ "\t aaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ "\t bbbbbbbbbbbbb\n"
+ "\t*/\n"
+ "}",
+ format("{\n"
+ "\t/*\n"
+ "\t aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb\n"
+ "\t*/\n"
+ "}",
+ Tab));
+ EXPECT_EQ("{\n"
+ "\t/*\n"
+ "\n"
+ "\t*/\n"
+ "}",
+ format("{\n"
+ "\t/*\n"
+ "\n"
+ "\t*/\n"
+ "}",
+ Tab));
+ EXPECT_EQ("{\n"
+ "\t/*\n"
+ " asdf\n"
+ "\t*/\n"
+ "}",
+ format("{\n"
+ "\t/*\n"
+ " asdf\n"
+ "\t*/\n"
+ "}",
+ Tab));
+ EXPECT_EQ("/*\n"
+ "\t a\t\tcomment\n"
+ "\t in multiple lines\n"
+ " */",
+ format(" /*\t \t \n"
+ " \t \t a\t\tcomment\t \t\n"
+ " \t \t in multiple lines\t\n"
+ " \t */",
+ Tab));
+ EXPECT_EQ("/* some\n"
+ " comment */",
+ format(" \t \t /* some\n"
+ " \t \t comment */",
+ Tab));
+ EXPECT_EQ("int a; /* some\n"
+ " comment */",
+ format(" \t \t int a; /* some\n"
+ " \t \t comment */",
+ Tab));
+ EXPECT_EQ("int a; /* some\n"
+ "comment */",
+ format(" \t \t int\ta; /* some\n"
+ " \t \t comment */",
+ Tab));
+ EXPECT_EQ("f(\"\t\t\"); /* some\n"
+ " comment */",
+ format(" \t \t f(\"\t\t\"); /* some\n"
+ " \t \t comment */",
+ Tab));
+ EXPECT_EQ("{\n"
+ " /*\n"
+ " * Comment\n"
+ " */\n"
+ " int i;\n"
+ "}",
+ format("{\n"
+ "\t/*\n"
+ "\t * Comment\n"
+ "\t */\n"
+ "\t int i;\n"
+ "}"));
+ Tab.AlignConsecutiveAssignments = true;
+ Tab.AlignConsecutiveDeclarations = true;
+ Tab.TabWidth = 4;
+ Tab.IndentWidth = 4;
+ verifyFormat("class Assign {\n"
+ "\tvoid f() {\n"
+ "\t\tint x = 123;\n"
+ "\t\tint random = 4;\n"
+ "\t\tstd::string alphabet =\n"
+ "\t\t\t\"abcdefghijklmnopqrstuvwxyz\";\n"
+ "\t}\n"
+ "};",
+ Tab);
}
TEST_F(FormatTest, CalculatesOriginalColumn) {
@@ -8644,7 +8990,8 @@ TEST_F(FormatTest, ConfigurableSpacesInParentheses) {
verifyFormat("#define x (( int )-1)", Spaces);
// Run the first set of tests again with:
- Spaces.SpacesInParentheses = false, Spaces.SpaceInEmptyParentheses = true;
+ Spaces.SpacesInParentheses = false;
+ Spaces.SpaceInEmptyParentheses = true;
Spaces.SpacesInCStyleCastParentheses = true;
verifyFormat("call(x, y, z);", Spaces);
verifyFormat("call( );", Spaces);
@@ -9814,8 +10161,10 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(AlwaysBreakTemplateDeclarations);
CHECK_PARSE_BOOL(BinPackArguments);
CHECK_PARSE_BOOL(BinPackParameters);
+ CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations);
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma);
+ CHECK_PARSE_BOOL(BreakStringLiterals);
CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine);
CHECK_PARSE_BOOL(DerivePointerAlignment);
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
@@ -9870,6 +10219,7 @@ TEST_F(FormatTest, ParsesConfiguration) {
SpacesBeforeTrailingComments, 1234u);
CHECK_PARSE("IndentWidth: 32", IndentWidth, 32u);
CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u);
+ CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$");
Style.PointerAlignment = FormatStyle::PAS_Middle;
CHECK_PARSE("PointerAlignment: Left", PointerAlignment,
@@ -9923,6 +10273,8 @@ TEST_F(FormatTest, ParsesConfiguration) {
CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never);
CHECK_PARSE("UseTab: ForIndentation", UseTab, FormatStyle::UT_ForIndentation);
CHECK_PARSE("UseTab: Always", UseTab, FormatStyle::UT_Always);
+ CHECK_PARSE("UseTab: ForContinuationAndIndentation", UseTab,
+ FormatStyle::UT_ForContinuationAndIndentation);
// For backward compatibility:
CHECK_PARSE("UseTab: false", UseTab, FormatStyle::UT_Never);
CHECK_PARSE("UseTab: true", UseTab, FormatStyle::UT_Always);
@@ -10028,6 +10380,7 @@ TEST_F(FormatTest, ParsesConfiguration) {
" - Regex: .*\n"
" Priority: 1",
IncludeCategories, ExpectedCategories);
+ CHECK_PARSE("IncludeIsMainRegex: 'abc$'", IncludeIsMainRegex, "abc$");
}
TEST_F(FormatTest, ParsesConfigurationWithLanguages) {
@@ -10309,6 +10662,15 @@ TEST_F(FormatTest, ConstructorInitializerIndentWidth) {
": aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n"
" aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}",
Style);
+ Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
+ verifyFormat(
+ "SomeLongTemplateVariableName<\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>",
+ Style);
+ verifyFormat(
+ "bool smaller = 1 < bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);",
+ Style);
}
TEST_F(FormatTest, BreakConstructorInitializersBeforeComma) {
@@ -10328,6 +10690,9 @@ TEST_F(FormatTest, BreakConstructorInitializersBeforeComma) {
verifyFormat("SomeClass::Constructor()\n"
" : a(a) {}",
Style);
+ verifyFormat("SomeClass::Constructor() noexcept\n"
+ " : a(a) {}",
+ Style);
verifyFormat("SomeClass::Constructor()\n"
" : a(a)\n"
" , b(b)\n"
@@ -10637,6 +11002,10 @@ TEST_F(FormatTest, FormatsLambdas) {
" return aaaaaaaaaaaaaaaaa;\n"
" });",
getLLVMStyleWithColumns(70));
+ verifyFormat("[]() //\n"
+ " -> int {\n"
+ " return 1; //\n"
+ "};");
// Multiple lambdas in the same parentheses change indentation rules.
verifyFormat("SomeFunction(\n"
@@ -11114,6 +11483,113 @@ TEST_F(FormatTest, FormatsTableGenCode) {
verifyFormat("include \"a.td\"\ninclude \"b.td\"", Style);
}
+// Since this test case uses UNIX-style file path. We disable it for MS
+// compiler.
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+
+TEST(FormatStyle, GetStyleOfFile) {
+ vfs::InMemoryFileSystem FS;
+ // Test 1: format file in the same directory.
+ ASSERT_TRUE(
+ FS.addFile("/a/.clang-format", 0,
+ llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: LLVM")));
+ ASSERT_TRUE(
+ FS.addFile("/a/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;")));
+ auto Style1 = getStyle("file", "/a/.clang-format", "Google", &FS);
+ ASSERT_EQ(Style1, getLLVMStyle());
+
+ // Test 2: fallback to default.
+ ASSERT_TRUE(
+ FS.addFile("/b/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;")));
+ auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", &FS);
+ ASSERT_EQ(Style2, getMozillaStyle());
+
+ // Test 3: format file in parent directory.
+ ASSERT_TRUE(
+ FS.addFile("/c/.clang-format", 0,
+ llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google")));
+ ASSERT_TRUE(FS.addFile("/c/sub/sub/sub/test.cpp", 0,
+ llvm::MemoryBuffer::getMemBuffer("int i;")));
+ auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", &FS);
+ ASSERT_EQ(Style3, getGoogleStyle());
+}
+
+#endif // _MSC_VER
+
+class ReplacementTest : public ::testing::Test {
+protected:
+ tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
+ llvm::StringRef ReplacementText) {
+ return tooling::Replacement(Context.Sources, Start, Length,
+ ReplacementText);
+ }
+
+ RewriterTestContext Context;
+};
+
+TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
+ // Column limit is 20.
+ std::string Code = "Type *a =\n"
+ " new Type();\n"
+ "g(iiiii, 0, jjjjj,\n"
+ " 0, kkkkk, 0, mm);\n"
+ "int bad = format ;";
+ std::string Expected = "auto a = new Type();\n"
+ "g(iiiii, nullptr,\n"
+ " jjjjj, nullptr,\n"
+ " kkkkk, nullptr,\n"
+ " mm);\n"
+ "int bad = format ;";
+ FileID ID = Context.createInMemoryFile("format.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID, 1, 1), 6, "auto "));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID, 3, 10), 1, "nullptr"));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID, 4, 3), 1, "nullptr"));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID, 4, 13), 1, "nullptr"));
+
+ format::FormatStyle Style = format::getLLVMStyle();
+ Style.ColumnLimit = 20; // Set column limit to 20 to increase readibility.
+ auto FormattedReplaces = formatReplacements(Code, Replaces, Style);
+ EXPECT_TRUE(static_cast<bool>(FormattedReplaces))
+ << llvm::toString(FormattedReplaces.takeError()) << "\n";
+ auto Result = applyAllReplacements(Code, *FormattedReplaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ EXPECT_EQ(Expected, *Result);
+}
+
+TEST_F(ReplacementTest, SortIncludesAfterReplacement) {
+ std::string Code = "#include \"a.h\"\n"
+ "#include \"c.h\"\n"
+ "\n"
+ "int main() {\n"
+ " return 0;\n"
+ "}";
+ std::string Expected = "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "\n"
+ "int main() {\n"
+ " return 0;\n"
+ "}";
+ FileID ID = Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID, 1, 1), 0, "#include \"b.h\"\n"));
+
+ format::FormatStyle Style = format::getLLVMStyle();
+ Style.SortIncludes = true;
+ auto FormattedReplaces = formatReplacements(Code, Replaces, Style);
+ EXPECT_TRUE(static_cast<bool>(FormattedReplaces))
+ << llvm::toString(FormattedReplaces.takeError()) << "\n";
+ auto Result = applyAllReplacements(Code, *FormattedReplaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ EXPECT_EQ(Expected, *Result);
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp
index 0e0f64a31ac6..2819383a3585 100644
--- a/unittests/Format/FormatTestJS.cpp
+++ b/unittests/Format/FormatTestJS.cpp
@@ -28,10 +28,10 @@ protected:
tooling::Replacements Replaces =
reformat(Style, Code, Ranges, "<stdin>", &IncompleteFormat);
EXPECT_FALSE(IncompleteFormat);
- std::string Result = applyAllReplacements(Code, Replaces);
- EXPECT_NE("", Result);
- DEBUG(llvm::errs() << "\n" << Result << "\n\n");
- return Result;
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+ return *Result;
}
static std::string format(
@@ -49,11 +49,24 @@ protected:
static void verifyFormat(
llvm::StringRef Code,
const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_JavaScript)) {
- std::string result = format(test::messUp(Code), Style);
- EXPECT_EQ(Code.str(), result) << "Formatted:\n" << result;
+ std::string Result = format(test::messUp(Code), Style);
+ EXPECT_EQ(Code.str(), Result) << "Formatted:\n" << Result;
+ }
+
+ static void verifyFormat(
+ llvm::StringRef Expected,
+ llvm::StringRef Code,
+ const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_JavaScript)) {
+ std::string Result = format(Code, Style);
+ EXPECT_EQ(Expected.str(), Result) << "Formatted:\n" << Result;
}
};
+TEST_F(FormatTestJS, BlockComments) {
+ verifyFormat("/* aaaaaaaaaaaaa */ aaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);");
+}
+
TEST_F(FormatTestJS, UnderstandsJavaScriptOperators) {
verifyFormat("a == = b;");
verifyFormat("a != = b;");
@@ -86,6 +99,17 @@ TEST_F(FormatTestJS, UnderstandsJavaScriptOperators) {
verifyFormat("var b = a.map((x) => x + 1);");
verifyFormat("return ('aaa') in bbbb;");
+ verifyFormat("var x = aaaaaaaaaaaaaaaaaaaaaaaaa() in\n"
+ " aaaa.aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;");
+ FormatStyle Style = getGoogleJSStyleWithColumns(80);
+ Style.AlignOperands = true;
+ verifyFormat("var x = aaaaaaaaaaaaaaaaaaaaaaaaa() in\n"
+ " aaaa.aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;",
+ Style);
+ Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All;
+ verifyFormat("var x = aaaaaaaaaaaaaaaaaaaaaaaaa()\n"
+ " in aaaa.aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;",
+ Style);
// ES6 spread operator.
verifyFormat("someFunction(...a);");
@@ -106,6 +130,11 @@ TEST_F(FormatTestJS, ReservedWords) {
verifyFormat("x.class.struct = 1;");
verifyFormat("x.case = 1;");
verifyFormat("x.interface = 1;");
+ verifyFormat("x.for = 1;");
+ verifyFormat("x.of() = 1;");
+ verifyFormat("x.in() = 1;");
+ verifyFormat("x.let() = 1;");
+ verifyFormat("x.var() = 1;");
verifyFormat("x = {\n"
" a: 12,\n"
" interface: 1,\n"
@@ -113,6 +142,9 @@ TEST_F(FormatTestJS, ReservedWords) {
"};");
verifyFormat("var struct = 2;");
verifyFormat("var union = 2;");
+ verifyFormat("var interface = 2;");
+ verifyFormat("interface = 2;");
+ verifyFormat("x = interface instanceof y;");
}
TEST_F(FormatTestJS, CppKeywords) {
@@ -122,6 +154,7 @@ TEST_F(FormatTestJS, CppKeywords) {
TEST_F(FormatTestJS, ES6DestructuringAssignment) {
verifyFormat("var [a, b, c] = [1, 2, 3];");
+ verifyFormat("const [a, b, c] = [1, 2, 3];");
verifyFormat("let [a, b, c] = [1, 2, 3];");
verifyFormat("var {a, b} = {a: 1, b: 2};");
verifyFormat("let {a, b} = {a: 1, b: 2};");
@@ -189,6 +222,18 @@ TEST_F(FormatTestJS, ContainerLiterals) {
" b: 2,\n"
" [c]: 3,\n"
"};");
+
+ // Object literals can leave out labels.
+ verifyFormat("f({a}, () => {\n"
+ " g(); //\n"
+ "});");
+
+ // Keys can be quoted.
+ verifyFormat("var x = {\n"
+ " a: a,\n"
+ " b: b,\n"
+ " 'c': c,\n"
+ "};");
}
TEST_F(FormatTestJS, MethodsInObjectLiterals) {
@@ -234,7 +279,7 @@ TEST_F(FormatTestJS, SpacesInContainerLiterals) {
verifyFormat("f({'a': [{}]});");
}
-TEST_F(FormatTestJS, SingleQuoteStrings) {
+TEST_F(FormatTestJS, SingleQuotedStrings) {
verifyFormat("this.function('', true);");
}
@@ -261,6 +306,8 @@ TEST_F(FormatTestJS, GoogModules) {
getGoogleJSStyleWithColumns(40));
verifyFormat("goog.setTestOnly('this.is.really.absurdly.long');",
getGoogleJSStyleWithColumns(40));
+ verifyFormat("goog.forwardDeclare('this.is.really.absurdly.long');",
+ getGoogleJSStyleWithColumns(40));
// These should be wrapped normally.
verifyFormat(
@@ -268,6 +315,15 @@ TEST_F(FormatTestJS, GoogModules) {
" goog.module.get('my.long.module.name.followedBy.MyLongClassName');");
}
+TEST_F(FormatTestJS, FormatsNamespaces) {
+ verifyFormat("namespace Foo {\n"
+ " export let x = 1;\n"
+ "}\n");
+ verifyFormat("declare namespace Foo {\n"
+ " export let x: number;\n"
+ "}\n");
+}
+
TEST_F(FormatTestJS, FormatsFreestandingFunctions) {
verifyFormat("function outer1(a, b) {\n"
" function inner1(a, b) { return a; }\n"
@@ -280,6 +336,44 @@ TEST_F(FormatTestJS, FormatsFreestandingFunctions) {
verifyFormat("function f() {}");
}
+TEST_F(FormatTestJS, GeneratorFunctions) {
+ verifyFormat("function* f() {\n"
+ " let x = 1;\n"
+ " yield x;\n"
+ " yield* something();\n"
+ "}");
+ verifyFormat("function*\n"
+ " f() {\n"
+ "}",
+ getGoogleJSStyleWithColumns(8));
+ verifyFormat("export function* f() {\n"
+ " yield 1;\n"
+ "}\n");
+ verifyFormat("class X {\n"
+ " * generatorMethod() { yield x; }\n"
+ "}");
+}
+
+TEST_F(FormatTestJS, AsyncFunctions) {
+ verifyFormat("async function f() {\n"
+ " let x = 1;\n"
+ " return fetch(x);\n"
+ "}");
+ verifyFormat("async function* f() {\n"
+ " yield fetch(x);\n"
+ "}");
+ verifyFormat("export async function f() {\n"
+ " return fetch(x);\n"
+ "}");
+ verifyFormat("class X {\n"
+ " async asyncMethod() { return fetch(1); }\n"
+ "}");
+ verifyFormat("function initialize() {\n"
+ " // Comment.\n"
+ " return async.then();\n"
+ "}\n");
+}
+
TEST_F(FormatTestJS, ArrayLiterals) {
verifyFormat("var aaaaa: List<SomeThing> =\n"
" [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];");
@@ -579,9 +673,15 @@ TEST_F(FormatTestJS, ReturnStatements) {
TEST_F(FormatTestJS, ForLoops) {
verifyFormat("for (var i in [2, 3]) {\n"
"}");
+ verifyFormat("for (var i of [2, 3]) {\n"
+ "}");
+ verifyFormat("for (let {a, b} of x) {\n"
+ "}");
+ verifyFormat("for (let {a, b} in x) {\n"
+ "}");
}
-TEST_F(FormatTestJS, AutomaticSemicolonInsertion) {
+TEST_F(FormatTestJS, WrapRespectsAutomaticSemicolonInsertion) {
// The following statements must not wrap, as otherwise the program meaning
// would change due to automatic semicolon insertion.
// See http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1.
@@ -591,6 +691,60 @@ TEST_F(FormatTestJS, AutomaticSemicolonInsertion) {
verifyFormat("throw aaaaa;", getGoogleJSStyleWithColumns(10));
verifyFormat("aaaaaaaaa++;", getGoogleJSStyleWithColumns(10));
verifyFormat("aaaaaaaaa--;", getGoogleJSStyleWithColumns(10));
+ verifyFormat("return [\n"
+ " aaa\n"
+ "];",
+ getGoogleJSStyleWithColumns(12));
+}
+
+TEST_F(FormatTestJS, AutomaticSemicolonInsertionHeuristic) {
+ verifyFormat("a\n"
+ "b;",
+ " a \n"
+ " b ;");
+ verifyFormat("a()\n"
+ "b;",
+ " a ()\n"
+ " b ;");
+ verifyFormat("a[b]\n"
+ "c;",
+ "a [b]\n"
+ "c ;");
+ verifyFormat("1\n"
+ "a;",
+ "1 \n"
+ "a ;");
+ verifyFormat("a\n"
+ "1;",
+ "a \n"
+ "1 ;");
+ verifyFormat("a\n"
+ "'x';",
+ "a \n"
+ " 'x';");
+ verifyFormat("a++\n"
+ "b;",
+ "a ++\n"
+ "b ;");
+ verifyFormat("a\n"
+ "!b && c;",
+ "a \n"
+ " ! b && c;");
+ verifyFormat("a\n"
+ "if (1) f();",
+ " a\n"
+ " if (1) f();");
+ verifyFormat("a\n"
+ "class X {}",
+ " a\n"
+ " class X {}");
+ verifyFormat("var a", "var\n"
+ "a");
+ verifyFormat("x instanceof String", "x\n"
+ "instanceof\n"
+ "String");
+ verifyFormat("function f(@Foo bar) {}", "function f(@Foo\n"
+ " bar) {}");
}
TEST_F(FormatTestJS, ClosureStyleCasts) {
@@ -691,13 +845,13 @@ TEST_F(FormatTestJS, RegexLiteralSpecialCharacters) {
verifyFormat("var regex = /\a\\//g;");
verifyFormat("var regex = /a\\//;\n"
"var x = 0;");
- EXPECT_EQ("var regex = /'/g;", format("var regex = /'/g ;"));
- EXPECT_EQ("var regex = /'/g; //'", format("var regex = /'/g ; //'"));
- EXPECT_EQ("var regex = /\\/*/;\n"
- "var x = 0;",
- format("var regex = /\\/*/;\n"
- "var x=0;"));
- EXPECT_EQ("var x = /a\\//;", format("var x = /a\\// \n;"));
+ verifyFormat("var regex = /'/g;", "var regex = /'/g ;");
+ verifyFormat("var regex = /'/g; //'", "var regex = /'/g ; //'");
+ verifyFormat("var regex = /\\/*/;\n"
+ "var x = 0;",
+ "var regex = /\\/*/;\n"
+ "var x=0;");
+ verifyFormat("var x = /a\\//;", "var x = /a\\// \n;");
verifyFormat("var regex = /\"/;", getGoogleJSStyleWithColumns(16));
verifyFormat("var regex =\n"
" /\"/;",
@@ -738,6 +892,7 @@ TEST_F(FormatTestJS, TypeAnnotations) {
verifyFormat("function x(): {x: string} {\n return {x: 'x'};\n}");
verifyFormat("function x(y: string): string {\n return 'x';\n}");
verifyFormat("for (var y: string in x) {\n x();\n}");
+ verifyFormat("for (var y: string of x) {\n x();\n}");
verifyFormat("function x(y: {a?: number;} = {}): number {\n"
" return 12;\n"
"}");
@@ -751,6 +906,22 @@ TEST_F(FormatTestJS, TypeAnnotations) {
getGoogleJSStyleWithColumns(60));
}
+TEST_F(FormatTestJS, UnionIntersectionTypes) {
+ verifyFormat("let x: A|B = A | B;");
+ verifyFormat("let x: A&B|C = A & B;");
+ verifyFormat("let x: Foo<A|B> = new Foo<A|B>();");
+ verifyFormat("function(x: A|B): C&D {}");
+ verifyFormat("function(x: A|B = A | B): C&D {}");
+ verifyFormat("function x(path: number|string) {}");
+ verifyFormat("function x(): string|number {}");
+ verifyFormat("type Foo = Bar|Baz;");
+ verifyFormat("type Foo = Bar<X>|Baz;");
+ verifyFormat("type Foo = (Bar<X>|Baz);");
+ verifyFormat("let x: Bar|Baz;");
+ verifyFormat("let x: Bar<X>|Baz;");
+ verifyFormat("let x: (Foo|Bar)[];");
+}
+
TEST_F(FormatTestJS, ClassDeclarations) {
verifyFormat("class C {\n x: string = 12;\n}");
verifyFormat("class C {\n x(): string => 12;\n}");
@@ -783,12 +954,18 @@ TEST_F(FormatTestJS, ClassDeclarations) {
" },\n"
" };\n"
"}");
+ verifyFormat("@Component({\n"
+ " moduleId: module.id,\n"
+ "})\n"
+ "class SessionListComponent implements OnDestroy, OnInit {\n"
+ "}");
}
TEST_F(FormatTestJS, InterfaceDeclarations) {
verifyFormat("interface I {\n"
" x: string;\n"
" enum: string[];\n"
+ " enum?: string[];\n"
"}\n"
"var y;");
// Ensure that state is reset after parsing the interface.
@@ -829,30 +1006,32 @@ TEST_F(FormatTestJS, MetadataAnnotations) {
" return 'y';\n"
" }\n"
"}");
+ verifyFormat("class C {\n"
+ " private x(@A x: string) {}\n"
+ "}");
verifyFormat("class X {}\n"
"class Y {}");
}
+TEST_F(FormatTestJS, TypeAliases) {
+ verifyFormat("type X = number;\n"
+ "class C {}");
+ verifyFormat("type X<Y> = Z<Y>;");
+ verifyFormat("type X = {\n"
+ " y: number\n"
+ "};\n"
+ "class C {}");
+}
+
TEST_F(FormatTestJS, Modules) {
verifyFormat("import SomeThing from 'some/module.js';");
verifyFormat("import {X, Y} from 'some/module.js';");
verifyFormat("import a, {X, Y} from 'some/module.js';");
- verifyFormat("import {\n"
- " VeryLongImportsAreAnnoying,\n"
- " VeryLongImportsAreAnnoying,\n"
- " VeryLongImportsAreAnnoying,\n"
- " VeryLongImportsAreAnnoying\n"
- "} from 'some/module.js';");
- verifyFormat("import {\n"
- " X,\n"
- " Y,\n"
- "} from 'some/module.js';");
- verifyFormat("import {\n"
- " X,\n"
- " Y,\n"
- "} from 'some/long/module.js';",
- getGoogleJSStyleWithColumns(20));
+ verifyFormat("import {X, Y,} from 'some/module.js';");
verifyFormat("import {X as myLocalX, Y as myLocalY} from 'some/module.js';");
+ // Ensure Automatic Semicolon Insertion does not break on "as\n".
+ verifyFormat("import {X as myX} from 'm';", "import {X as\n"
+ " myX} from 'm';");
verifyFormat("import * as lib from 'some/module.js';");
verifyFormat("var x = {import: 1};\nx.import = 2;");
@@ -862,13 +1041,26 @@ TEST_F(FormatTestJS, Modules) {
verifyFormat("export function A() {}\n"
"export default function B() {}\n"
"export function C() {}");
+ verifyFormat("export default () => {\n"
+ " let x = 1;\n"
+ " return x;\n"
+ "}");
verifyFormat("export const x = 12;");
verifyFormat("export default class X {}");
verifyFormat("export {X, Y} from 'some/module.js';");
+ verifyFormat("export {X, Y,} from 'some/module.js';");
+ verifyFormat("export {SomeVeryLongExport as X, "
+ "SomeOtherVeryLongExport as Y} from 'some/module.js';");
+ // export without 'from' is wrapped.
+ verifyFormat("export let someRatherLongVariableName =\n"
+ " someSurprisinglyLongVariable + someOtherRatherLongVar;");
+ // ... but not if from is just an identifier.
verifyFormat("export {\n"
- " X,\n"
- " Y,\n"
- "} from 'some/module.js';");
+ " from as from,\n"
+ " someSurprisinglyLongVariable as\n"
+ " from\n"
+ "};",
+ getGoogleJSStyleWithColumns(20));
verifyFormat("export class C {\n"
" x: number;\n"
" y: string;\n"
@@ -903,42 +1095,74 @@ TEST_F(FormatTestJS, Modules) {
"}");
}
+TEST_F(FormatTestJS, ImportWrapping) {
+ verifyFormat("import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,"
+ " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying"
+ "} from 'some/module.js';");
+ FormatStyle Style = getGoogleJSStyleWithColumns(80);
+ Style.JavaScriptWrapImports = true;
+ verifyFormat("import {\n"
+ " VeryLongImportsAreAnnoying,\n"
+ " VeryLongImportsAreAnnoying,\n"
+ " VeryLongImportsAreAnnoying,\n"
+ "} from 'some/module.js';",
+ Style);
+ verifyFormat("import {\n"
+ " A,\n"
+ " A,\n"
+ "} from 'some/module.js';",
+ Style);
+ verifyFormat("export {\n"
+ " A,\n"
+ " A,\n"
+ "} from 'some/module.js';",
+ Style);
+}
+
TEST_F(FormatTestJS, TemplateStrings) {
// Keeps any whitespace/indentation within the template string.
- EXPECT_EQ("var x = `hello\n"
+ verifyFormat("var x = `hello\n"
" ${ name }\n"
" !`;",
- format("var x = `hello\n"
+ "var x = `hello\n"
" ${ name }\n"
- " !`;"));
+ " !`;");
verifyFormat("var x =\n"
" `hello ${world}` >= some();",
getGoogleJSStyleWithColumns(34)); // Barely doesn't fit.
verifyFormat("var x = `hello ${world}` >= some();",
getGoogleJSStyleWithColumns(35)); // Barely fits.
- EXPECT_EQ("var x = `hello\n"
+ verifyFormat("var x = `hellö ${wörld}` >= söme();",
+ getGoogleJSStyleWithColumns(35)); // Fits due to UTF-8.
+ verifyFormat("var x = `hello\n"
" ${world}` >=\n"
" some();",
- format("var x =\n"
+ "var x =\n"
" `hello\n"
" ${world}` >= some();",
- getGoogleJSStyleWithColumns(21))); // Barely doesn't fit.
- EXPECT_EQ("var x = `hello\n"
+ getGoogleJSStyleWithColumns(21)); // Barely doesn't fit.
+ verifyFormat("var x = `hello\n"
" ${world}` >= some();",
- format("var x =\n"
+ "var x =\n"
" `hello\n"
" ${world}` >= some();",
- getGoogleJSStyleWithColumns(22))); // Barely fits.
+ getGoogleJSStyleWithColumns(22)); // Barely fits.
verifyFormat("var x =\n"
" `h`;",
getGoogleJSStyleWithColumns(11));
- EXPECT_EQ(
- "var x =\n `multi\n line`;",
- format("var x = `multi\n line`;", getGoogleJSStyleWithColumns(13)));
+ verifyFormat("var x =\n `multi\n line`;", "var x = `multi\n line`;",
+ getGoogleJSStyleWithColumns(13));
verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
" `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`);");
+ // Repro for an obscure width-miscounting issue with template strings.
+ verifyFormat(
+ "someLongVariable =\n"
+ " "
+ "`${logPrefix[11]}/${logPrefix[12]}/${logPrefix[13]}${logPrefix[14]}`;",
+ "someLongVariable = "
+ "`${logPrefix[11]}/${logPrefix[12]}/${logPrefix[13]}${logPrefix[14]}`;");
// Make sure template strings get a proper ColumnWidth assigned, even if they
// are first token in line.
@@ -950,42 +1174,51 @@ TEST_F(FormatTestJS, TemplateStrings) {
verifyFormat("var x = `hello` == `hello`;");
// Comments in template strings.
- EXPECT_EQ("var x = `//a`;\n"
- "var y;",
- format("var x =\n `//a`;\n"
- "var y ;"));
- EXPECT_EQ("var x = `/*a`;\n"
+ verifyFormat("var x = `//a`;\n"
"var y;",
- format("var x =\n `/*a`;\n"
- "var y;"));
+ "var x =\n `//a`;\n"
+ "var y ;");
+ verifyFormat("var x = `/*a`;\n"
+ "var y;",
+ "var x =\n `/*a`;\n"
+ "var y;");
// Unterminated string literals in a template string.
verifyFormat("var x = `'`; // comment with matching quote '\n"
"var y;");
verifyFormat("var x = `\"`; // comment with matching quote \"\n"
"var y;");
- EXPECT_EQ("it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa);",
- format("it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa) ;",
- getGoogleJSStyleWithColumns(40)));
+ verifyFormat("it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa);",
+ "it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa) ;",
+ getGoogleJSStyleWithColumns(40));
// Backticks in a comment - not a template string.
- EXPECT_EQ("var x = 1 // `/*a`;\n"
- " ;",
- format("var x =\n 1 // `/*a`;\n"
- " ;"));
- EXPECT_EQ("/* ` */ var x = 1; /* ` */",
- format("/* ` */ var x\n= 1; /* ` */"));
+ verifyFormat("var x = 1 // `/*a`;\n"
+ " ;",
+ "var x =\n 1 // `/*a`;\n"
+ " ;");
+ verifyFormat("/* ` */ var x = 1; /* ` */", "/* ` */ var x\n= 1; /* ` */");
// Comment spans multiple template strings.
- EXPECT_EQ("var x = `/*a`;\n"
- "var y = ` */ `;",
- format("var x =\n `/*a`;\n"
- "var y =\n ` */ `;"));
+ verifyFormat("var x = `/*a`;\n"
+ "var y = ` */ `;",
+ "var x =\n `/*a`;\n"
+ "var y =\n ` */ `;");
// Escaped backtick.
- EXPECT_EQ("var x = ` \\` a`;\n"
- "var y;",
- format("var x = ` \\` a`;\n"
- "var y;"));
+ verifyFormat("var x = ` \\` a`;\n"
+ "var y;",
+ "var x = ` \\` a`;\n"
+ "var y;");
}
-TEST_F(FormatTestJS, CastSyntax) { verifyFormat("var x = <type>foo;"); }
+TEST_F(FormatTestJS, CastSyntax) {
+ verifyFormat("var x = <type>foo;");
+ verifyFormat("var x = foo as type;");
+ verifyFormat("let x = (a + b) as\n"
+ " LongTypeIsLong;",
+ getGoogleJSStyleWithColumns(20));
+ verifyFormat("foo = <Bar[]>[\n"
+ " 1, //\n"
+ " 2\n"
+ "];");
+}
TEST_F(FormatTestJS, TypeArguments) {
verifyFormat("class X<Y> {}");
@@ -1020,7 +1253,6 @@ TEST_F(FormatTestJS, OptionalTypes) {
verifyFormat("interface X {\n"
" y?(): z;\n"
"}");
- verifyFormat("x ? 1 : 2;");
verifyFormat("constructor({aa}: {\n"
" aa?: string,\n"
" aaaaaaaa?: string,\n"
@@ -1052,13 +1284,78 @@ TEST_F(FormatTestJS, WrapAfterParen) {
}
TEST_F(FormatTestJS, JSDocAnnotations) {
- EXPECT_EQ("/**\n"
- " * @export {this.is.a.long.path.to.a.Type}\n"
- " */",
- format("/**\n"
- " * @export {this.is.a.long.path.to.a.Type}\n"
- " */",
- getGoogleJSStyleWithColumns(20)));
+ verifyFormat("/**\n"
+ " * @export {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ "/**\n"
+ " * @export {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ getGoogleJSStyleWithColumns(20));
+}
+
+TEST_F(FormatTestJS, RequoteStringsSingle) {
+ verifyFormat("var x = 'foo';", "var x = \"foo\";");
+ verifyFormat("var x = 'fo\\'o\\'';", "var x = \"fo'o'\";");
+ verifyFormat("var x = 'fo\\'o\\'';", "var x = \"fo\\'o'\";");
+ verifyFormat(
+ "var x =\n"
+ " 'foo\\'';",
+ // Code below is 15 chars wide, doesn't fit into the line with the
+ // \ escape added.
+ "var x = \"foo'\";", getGoogleJSStyleWithColumns(15));
+ // Removes no-longer needed \ escape from ".
+ verifyFormat("var x = 'fo\"o';", "var x = \"fo\\\"o\";");
+ // Code below fits into 15 chars *after* removing the \ escape.
+ verifyFormat("var x = 'fo\"o';", "var x = \"fo\\\"o\";",
+ getGoogleJSStyleWithColumns(15));
+ verifyFormat("// clang-format off\n"
+ "let x = \"double\";\n"
+ "// clang-format on\n"
+ "let x = 'single';\n",
+ "// clang-format off\n"
+ "let x = \"double\";\n"
+ "// clang-format on\n"
+ "let x = \"single\";\n");
+}
+
+TEST_F(FormatTestJS, RequoteStringsDouble) {
+ FormatStyle DoubleQuotes = getGoogleStyle(FormatStyle::LK_JavaScript);
+ DoubleQuotes.JavaScriptQuotes = FormatStyle::JSQS_Double;
+ verifyFormat("var x = \"foo\";", DoubleQuotes);
+ verifyFormat("var x = \"foo\";", "var x = 'foo';", DoubleQuotes);
+ verifyFormat("var x = \"fo'o\";", "var x = 'fo\\'o';", DoubleQuotes);
+}
+
+TEST_F(FormatTestJS, RequoteStringsLeave) {
+ FormatStyle LeaveQuotes = getGoogleStyle(FormatStyle::LK_JavaScript);
+ LeaveQuotes.JavaScriptQuotes = FormatStyle::JSQS_Leave;
+ verifyFormat("var x = \"foo\";", LeaveQuotes);
+ verifyFormat("var x = 'foo';", LeaveQuotes);
+}
+
+TEST_F(FormatTestJS, SupportShebangLines) {
+ verifyFormat("#!/usr/bin/env node\n"
+ "var x = hello();",
+ "#!/usr/bin/env node\n"
+ "var x = hello();");
+}
+
+TEST_F(FormatTestJS, NonNullAssertionOperator) {
+ verifyFormat("let x = foo!.bar();\n");
+ verifyFormat("let x = foo ? bar! : baz;\n");
+ verifyFormat("let x = !foo;\n");
+ verifyFormat("let x = foo[0]!;\n");
+ verifyFormat("let x = (foo)!;\n");
+ verifyFormat("let x = {foo: 1}!;\n");
+}
+
+TEST_F(FormatTestJS, Conditional) {
+ verifyFormat("y = x ? 1 : 2;");
+ verifyFormat("x ? 1 : 2;");
+ verifyFormat("class Foo {\n"
+ " field = true ? 1 : 2;\n"
+ " method(a = true ? 1 : 2) {}\n"
+ "}");
}
} // end namespace tooling
diff --git a/unittests/Format/FormatTestJava.cpp b/unittests/Format/FormatTestJava.cpp
index 8fadfc09b3ed..dfc3debc46e2 100644
--- a/unittests/Format/FormatTestJava.cpp
+++ b/unittests/Format/FormatTestJava.cpp
@@ -25,10 +25,10 @@ protected:
DEBUG(llvm::errs() << Code << "\n\n");
std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
tooling::Replacements Replaces = reformat(Style, Code, Ranges);
- std::string Result = applyAllReplacements(Code, Replaces);
- EXPECT_NE("", Result);
- DEBUG(llvm::errs() << "\n" << Result << "\n\n");
- return Result;
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+ return *Result;
}
static std::string
@@ -312,6 +312,9 @@ TEST_F(FormatTestJava, Annotations) {
" String bbbbbbbbbbbbbbb) {}\n"
"}",
getStyleWithColumns(60));
+ verifyFormat("@Annotation(\"Some\"\n"
+ " + \" text\")\n"
+ "List<Integer> list;");
}
TEST_F(FormatTestJava, Generics) {
diff --git a/unittests/Format/FormatTestProto.cpp b/unittests/Format/FormatTestProto.cpp
index d3d3d42aa84a..6881af4361cf 100644
--- a/unittests/Format/FormatTestProto.cpp
+++ b/unittests/Format/FormatTestProto.cpp
@@ -25,10 +25,10 @@ protected:
DEBUG(llvm::errs() << Code << "\n\n");
std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
tooling::Replacements Replaces = reformat(Style, Code, Ranges);
- std::string Result = applyAllReplacements(Code, Replaces);
- EXPECT_NE("", Result);
- DEBUG(llvm::errs() << "\n" << Result << "\n\n");
- return Result;
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+ return *Result;
}
static std::string format(llvm::StringRef Code) {
@@ -74,8 +74,11 @@ TEST_F(FormatTestProto, FormatsEnums) {
" TYPE_B = 2;\n"
"};");
verifyFormat("enum Type {\n"
+ " UNKNOWN = 0 [(some_options) = {a: aa, b: bb}];\n"
+ "};");
+ verifyFormat("enum Type {\n"
" UNKNOWN = 0 [(some_options) = {\n"
- " a: aa,\n"
+ " a: aa, // wrap\n"
" b: bb\n"
" }];\n"
"};");
@@ -153,10 +156,7 @@ TEST_F(FormatTestProto, FormatsOptions) {
" field_a: OK\n"
" field_b: \"OK\"\n"
" field_c: \"OK\"\n"
- " msg_field: {\n"
- " field_d: 123\n"
- " field_e: OK\n"
- " }\n"
+ " msg_field: {field_d: 123 field_e: OK}\n"
"};");
verifyFormat("option (MyProto.options) = {\n"
" field_a: OK // Comment\n"
@@ -189,5 +189,26 @@ TEST_F(FormatTestProto, ExtendingMessage) {
"}");
}
+TEST_F(FormatTestProto, FormatsImports) {
+ verifyFormat("import \"a.proto\";\n"
+ "import \"b.proto\";\n"
+ "// comment\n"
+ "message A {\n"
+ "}");
+
+ verifyFormat("import public \"a.proto\";\n"
+ "import \"b.proto\";\n"
+ "// comment\n"
+ "message A {\n"
+ "}");
+
+ // Missing semicolons should not confuse clang-format.
+ verifyFormat("import \"a.proto\"\n"
+ "import \"b.proto\"\n"
+ "// comment\n"
+ "message A {\n"
+ "}");
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/unittests/Format/FormatTestSelective.cpp b/unittests/Format/FormatTestSelective.cpp
index 699600c42d9f..2bc60fd1e0d3 100644
--- a/unittests/Format/FormatTestSelective.cpp
+++ b/unittests/Format/FormatTestSelective.cpp
@@ -28,10 +28,10 @@ protected:
tooling::Replacements Replaces =
reformat(Style, Code, Ranges, "<stdin>", &IncompleteFormat);
EXPECT_FALSE(IncompleteFormat) << Code << "\n\n";
- std::string Result = applyAllReplacements(Code, Replaces);
- EXPECT_NE("", Result);
- DEBUG(llvm::errs() << "\n" << Result << "\n\n");
- return Result;
+ auto Result = applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(Result));
+ DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+ return *Result;
}
FormatStyle Style = getLLVMStyle();
@@ -278,6 +278,23 @@ TEST_F(FormatTestSelective, IndividualStatementsOfNestedBlocks) {
" };\n"
"});",
0, 0));
+ EXPECT_EQ("SomeFunction(\n"
+ " [] {\n"
+ " int i;\n"
+ " return i;\n" // Format this line.
+ " },\n"
+ " [] {\n"
+ " return 2;\n" // Don't fix this.
+ " });",
+ format("SomeFunction(\n"
+ " [] {\n"
+ " int i;\n"
+ " return i;\n" // Format this line.
+ " },\n"
+ " [] {\n"
+ " return 2;\n" // Don't fix this.
+ " });",
+ 40, 0));
}
TEST_F(FormatTestSelective, WrongIndent) {
@@ -495,6 +512,18 @@ TEST_F(FormatTestSelective, StopFormattingWhenLeavingScope) {
15, 0));
}
+TEST_F(FormatTestSelective, SelectivelyRequoteJavaScript) {
+ Style = getGoogleStyle(FormatStyle::LK_JavaScript);
+ EXPECT_EQ(
+ "var x = \"a\";\n"
+ "var x = 'a';\n"
+ "var x = \"a\";",
+ format("var x = \"a\";\n"
+ "var x = \"a\";\n"
+ "var x = \"a\";",
+ 20, 0));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
diff --git a/unittests/Format/Makefile b/unittests/Format/Makefile
deleted file mode 100644
index f95d6d34127b..000000000000
--- a/unittests/Format/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-##===- unittests/Format/Makefile ---------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-CLANG_LEVEL = ../..
-TESTNAME = Format
-include $(CLANG_LEVEL)/../../Makefile.config
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
-USEDLIBS = clangFormat.a clangTooling.a clangToolingCore.a clangFrontend.a \
- clangSerialization.a clangDriver.a clangParse.a clangRewrite.a \
- clangRewriteFrontend.a clangSema.a clangAnalysis.a clangEdit.a \
- clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
-
-include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Format/SortImportsTestJS.cpp b/unittests/Format/SortImportsTestJS.cpp
new file mode 100644
index 000000000000..77c37e337ddf
--- /dev/null
+++ b/unittests/Format/SortImportsTestJS.cpp
@@ -0,0 +1,269 @@
+//===- unittest/Format/SortImportsTestJS.cpp - JS import sort unit tests --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FormatTestUtils.h"
+#include "clang/Format/Format.h"
+#include "llvm/Support/Debug.h"
+#include "gtest/gtest.h"
+
+#define DEBUG_TYPE "format-test"
+
+namespace clang {
+namespace format {
+namespace {
+
+class SortImportsTestJS : public ::testing::Test {
+protected:
+ std::string sort(StringRef Code, unsigned Offset = 0, unsigned Length = 0) {
+ StringRef FileName = "input.js";
+ if (Length == 0U)
+ Length = Code.size() - Offset;
+ std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
+ auto Sorted =
+ applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName));
+ EXPECT_TRUE(static_cast<bool>(Sorted));
+ auto Formatted = applyAllReplacements(
+ *Sorted, reformat(Style, *Sorted, Ranges, FileName));
+ EXPECT_TRUE(static_cast<bool>(Formatted));
+ return *Formatted;
+ }
+
+ void verifySort(llvm::StringRef Expected, llvm::StringRef Code,
+ unsigned Offset = 0, unsigned Length = 0) {
+ std::string Result = sort(Code, Offset, Length);
+ EXPECT_EQ(Expected.str(), Result) << "Expected:\n"
+ << Expected << "\nActual:\n"
+ << Result;
+ }
+
+ FormatStyle Style = getGoogleStyle(FormatStyle::LK_JavaScript);
+};
+
+TEST_F(SortImportsTestJS, AlreadySorted) {
+ verifySort("import {sym} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "\n"
+ "let x = 1;",
+ "import {sym} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "\n"
+ "let x = 1;");
+}
+
+TEST_F(SortImportsTestJS, BasicSorting) {
+ verifySort("import {sym} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "\n"
+ "let x = 1;",
+ "import {sym} from 'a';\n"
+ "import {sym} from 'c';\n"
+ "import {sym} from 'b';\n"
+ "let x = 1;");
+}
+
+TEST_F(SortImportsTestJS, WrappedImportStatements) {
+ verifySort("import {sym1, sym2} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "\n"
+ "1;",
+ "import\n"
+ " {sym}\n"
+ " from 'b';\n"
+ "import {\n"
+ " sym1,\n"
+ " sym2\n"
+ "} from 'a';\n"
+ "1;");
+}
+
+TEST_F(SortImportsTestJS, SeparateMainCodeBody) {
+ verifySort("import {sym} from 'a';"
+ "\n"
+ "let x = 1;\n",
+ "import {sym} from 'a'; let x = 1;\n");
+}
+
+TEST_F(SortImportsTestJS, Comments) {
+ verifySort("/** @fileoverview This is a great file. */\n"
+ "// A very important import follows.\n"
+ "import {sym} from 'a'; /* more comments */\n"
+ "import {sym} from 'b'; // from //foo:bar\n",
+ "/** @fileoverview This is a great file. */\n"
+ "import {sym} from 'b'; // from //foo:bar\n"
+ "// A very important import follows.\n"
+ "import {sym} from 'a'; /* more comments */\n");
+}
+
+TEST_F(SortImportsTestJS, SortStar) {
+ verifySort("import * as foo from 'a';\n"
+ "import {sym} from 'a';\n"
+ "import * as bar from 'b';\n",
+ "import {sym} from 'a';\n"
+ "import * as foo from 'a';\n"
+ "import * as bar from 'b';\n");
+}
+
+TEST_F(SortImportsTestJS, AliasesSymbols) {
+ verifySort("import {sym1 as alias1} from 'b';\n"
+ "import {sym2 as alias2, sym3 as alias3} from 'c';\n",
+ "import {sym2 as alias2, sym3 as alias3} from 'c';\n"
+ "import {sym1 as alias1} from 'b';\n");
+}
+
+TEST_F(SortImportsTestJS, SortSymbols) {
+ verifySort("import {sym1, sym2 as a, sym3} from 'b';\n",
+ "import {sym2 as a, sym1, sym3} from 'b';\n");
+ verifySort("import {sym1 /* important! */, /*!*/ sym2 as a} from 'b';\n",
+ "import {/*!*/ sym2 as a, sym1 /* important! */} from 'b';\n");
+ verifySort("import {sym1, sym2} from 'b';\n", "import {\n"
+ " sym2 \n"
+ ",\n"
+ " sym1 \n"
+ "} from 'b';\n");
+}
+
+TEST_F(SortImportsTestJS, GroupImports) {
+ verifySort("import {a} from 'absolute';\n"
+ "\n"
+ "import {b} from '../parent';\n"
+ "import {b} from '../parent/nested';\n"
+ "\n"
+ "import {b} from './relative/path';\n"
+ "import {b} from './relative/path/nested';\n"
+ "\n"
+ "let x = 1;\n",
+ "import {b} from './relative/path/nested';\n"
+ "import {b} from './relative/path';\n"
+ "import {b} from '../parent/nested';\n"
+ "import {b} from '../parent';\n"
+ "import {a} from 'absolute';\n"
+ "let x = 1;\n");
+}
+
+TEST_F(SortImportsTestJS, Exports) {
+ verifySort("import {S} from 'bpath';\n"
+ "\n"
+ "import {T} from './cpath';\n"
+ "\n"
+ "export {A, B} from 'apath';\n"
+ "export {P} from '../parent';\n"
+ "export {R} from './relative';\n"
+ "export {S};\n"
+ "\n"
+ "let x = 1;\n"
+ "export y = 1;\n",
+ "export {R} from './relative';\n"
+ "import {T} from './cpath';\n"
+ "export {S};\n"
+ "export {A, B} from 'apath';\n"
+ "import {S} from 'bpath';\n"
+ "export {P} from '../parent';\n"
+ "let x = 1;\n"
+ "export y = 1;\n");
+ verifySort("import {S} from 'bpath';\n"
+ "\n"
+ "export {T} from 'epath';\n",
+ "export {T} from 'epath';\n"
+ "import {S} from 'bpath';\n");
+}
+
+TEST_F(SortImportsTestJS, SideEffectImports) {
+ verifySort("import 'ZZside-effect';\n"
+ "import 'AAside-effect';\n"
+ "\n"
+ "import {A} from 'absolute';\n"
+ "\n"
+ "import {R} from './relative';\n",
+ "import {R} from './relative';\n"
+ "import 'ZZside-effect';\n"
+ "import {A} from 'absolute';\n"
+ "import 'AAside-effect';\n");
+}
+
+TEST_F(SortImportsTestJS, AffectedRange) {
+ // Sort excluding a suffix.
+ verifySort("import {sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "import {sym} from 'a';\n"
+ "let x = 1;",
+ "import {sym} from 'c';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'a';\n"
+ "let x = 1;",
+ 0, 30);
+ // Sort excluding a prefix.
+ verifySort("import {sym} from 'c';\n"
+ "import {sym} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "\n"
+ "let x = 1;",
+ "import {sym} from 'c';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'a';\n"
+ "\n"
+ "let x = 1;",
+ 30, 0);
+ // Sort a range within imports.
+ verifySort("import {sym} from 'c';\n"
+ "import {sym} from 'a';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "let x = 1;",
+ "import {sym} from 'c';\n"
+ "import {sym} from 'b';\n"
+ "import {sym} from 'a';\n"
+ "import {sym} from 'c';\n"
+ "let x = 1;",
+ 24, 30);
+}
+
+TEST_F(SortImportsTestJS, SortingCanShrink) {
+ // Sort excluding a suffix.
+ verifySort("import {B} from 'a';\n"
+ "import {A} from 'b';\n"
+ "\n"
+ "1;",
+ "import {A} from 'b';\n"
+ "\n"
+ "import {B} from 'a';\n"
+ "\n"
+ "1;");
+}
+
+TEST_F(SortImportsTestJS, TrailingComma) {
+ verifySort("import {A, B,} from 'aa';\n", "import {B, A,} from 'aa';\n");
+}
+
+TEST_F(SortImportsTestJS, SortCaseInsensitive) {
+ verifySort("import {A} from 'aa';\n"
+ "import {A} from 'Ab';\n"
+ "import {A} from 'b';\n"
+ "import {A} from 'Bc';\n"
+ "\n"
+ "1;",
+ "import {A} from 'b';\n"
+ "import {A} from 'Bc';\n"
+ "import {A} from 'Ab';\n"
+ "import {A} from 'aa';\n"
+ "\n"
+ "1;");
+ verifySort("import {aa, Ab, b, Bc} from 'x';\n"
+ "\n"
+ "1;",
+ "import {b, Bc, Ab, aa} from 'x';\n"
+ "\n"
+ "1;");
+}
+
+} // end namespace
+} // end namespace format
+} // end namespace clang
diff --git a/unittests/Format/SortIncludesTest.cpp b/unittests/Format/SortIncludesTest.cpp
index dbe11749572b..13a1b9adeeec 100644
--- a/unittests/Format/SortIncludesTest.cpp
+++ b/unittests/Format/SortIncludesTest.cpp
@@ -20,17 +20,23 @@ namespace {
class SortIncludesTest : public ::testing::Test {
protected:
- std::string sort(llvm::StringRef Code, StringRef FileName = "input.cpp") {
- std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
- std::string Sorted =
+ std::vector<tooling::Range> GetCodeRange(StringRef Code) {
+ return std::vector<tooling::Range>(1, tooling::Range(0, Code.size()));
+ }
+
+ std::string sort(StringRef Code, StringRef FileName = "input.cpp") {
+ auto Ranges = GetCodeRange(Code);
+ auto Sorted =
applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName));
- return applyAllReplacements(Sorted,
- reformat(Style, Sorted, Ranges, FileName));
+ EXPECT_TRUE(static_cast<bool>(Sorted));
+ auto Result = applyAllReplacements(
+ *Sorted, reformat(Style, *Sorted, Ranges, FileName));
+ EXPECT_TRUE(static_cast<bool>(Result));
+ return *Result;
}
unsigned newCursor(llvm::StringRef Code, unsigned Cursor) {
- std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
- sortIncludes(Style, Code, Ranges, "input.cpp", &Cursor);
+ sortIncludes(Style, Code, GetCodeRange(Code), "input.cpp", &Cursor);
return Cursor;
}
@@ -47,6 +53,17 @@ TEST_F(SortIncludesTest, BasicSorting) {
"#include \"b.h\"\n"));
}
+TEST_F(SortIncludesTest, NoReplacementsForValidIncludes) {
+ // Identical #includes have led to a failure with an unstable sort.
+ std::string Code = "#include <a>\n"
+ "#include <b>\n"
+ "#include <b>\n"
+ "#include <b>\n"
+ "#include <b>\n"
+ "#include <c>\n";
+ EXPECT_TRUE(sortIncludes(Style, Code, GetCodeRange(Code), "a.cc").empty());
+}
+
TEST_F(SortIncludesTest, SupportClangFormatOff) {
EXPECT_EQ("#include <a>\n"
"#include <b>\n"
@@ -161,6 +178,7 @@ TEST_F(SortIncludesTest, HandlesMultilineIncludes) {
}
TEST_F(SortIncludesTest, LeavesMainHeaderFirst) {
+ Style.IncludeIsMainRegex = "([-_](test|unittest))?$";
EXPECT_EQ("#include \"llvm/a.h\"\n"
"#include \"b.h\"\n"
"#include \"c.h\"\n",
@@ -174,7 +192,7 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) {
sort("#include \"llvm/a.h\"\n"
"#include \"c.h\"\n"
"#include \"b.h\"\n",
- "a_main.cc"));
+ "a_test.cc"));
EXPECT_EQ("#include \"llvm/input.h\"\n"
"#include \"b.h\"\n"
"#include \"c.h\"\n",
@@ -183,6 +201,24 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) {
"#include \"b.h\"\n",
"input.mm"));
+ // Don't allow prefixes.
+ EXPECT_EQ("#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "#include \"llvm/not_a.h\"\n",
+ sort("#include \"llvm/not_a.h\"\n"
+ "#include \"c.h\"\n"
+ "#include \"b.h\"\n",
+ "a.cc"));
+
+ // Don't do this for _main and other suffixes.
+ EXPECT_EQ("#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "#include \"llvm/a.h\"\n",
+ sort("#include \"llvm/a.h\"\n"
+ "#include \"c.h\"\n"
+ "#include \"b.h\"\n",
+ "a_main.cc"));
+
// Don't do this in headers.
EXPECT_EQ("#include \"b.h\"\n"
"#include \"c.h\"\n"