diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
commit | 2b6b257f4e5503a7a2675bdb8735693db769f75c (patch) | |
tree | e85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /unittests/Format | |
parent | b4348ed0b7e90c0831b925fbee00b5f179a99796 (diff) |
Notes
Diffstat (limited to 'unittests/Format')
-rw-r--r-- | unittests/Format/CMakeLists.txt | 5 | ||||
-rw-r--r-- | unittests/Format/CleanupTest.cpp | 726 | ||||
-rw-r--r-- | unittests/Format/FormatTest.cpp | 508 | ||||
-rw-r--r-- | unittests/Format/FormatTestJS.cpp | 453 | ||||
-rw-r--r-- | unittests/Format/FormatTestJava.cpp | 11 | ||||
-rw-r--r-- | unittests/Format/FormatTestProto.cpp | 39 | ||||
-rw-r--r-- | unittests/Format/FormatTestSelective.cpp | 37 | ||||
-rw-r--r-- | unittests/Format/Makefile | 19 | ||||
-rw-r--r-- | unittests/Format/SortImportsTestJS.cpp | 269 | ||||
-rw-r--r-- | unittests/Format/SortIncludesTest.cpp | 52 |
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(¶m1, ¶m2)(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" |