1 //===- DefinitionBlockSeparatorTest.cpp - Formatting unit tests -----------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "FormatTestUtils.h"
10 #include "clang/Format/Format.h"
12 #include "llvm/Support/Debug.h"
13 #include "gtest/gtest.h"
15 #define DEBUG_TYPE "definition-block-separator-test"
21 class DefinitionBlockSeparatorTest
: public ::testing::Test
{
24 separateDefinitionBlocks(llvm::StringRef Code
,
25 const std::vector
<tooling::Range
> &Ranges
,
26 const FormatStyle
&Style
= getLLVMStyle()) {
27 LLVM_DEBUG(llvm::errs() << "---\n");
28 LLVM_DEBUG(llvm::errs() << Code
<< "\n\n");
29 tooling::Replacements Replaces
= reformat(Style
, Code
, Ranges
, "<stdin>");
30 auto Result
= applyAllReplacements(Code
, Replaces
);
31 EXPECT_TRUE(static_cast<bool>(Result
));
32 LLVM_DEBUG(llvm::errs() << "\n" << *Result
<< "\n\n");
37 separateDefinitionBlocks(llvm::StringRef Code
,
38 const FormatStyle
&Style
= getLLVMStyle()) {
39 return separateDefinitionBlocks(
41 /*Ranges=*/{1, tooling::Range(0, Code
.size())}, Style
);
44 static void _verifyFormat(const char *File
, int Line
, llvm::StringRef Code
,
45 const FormatStyle
&Style
= getLLVMStyle(),
46 llvm::StringRef ExpectedCode
= "",
47 bool Inverse
= true) {
48 ::testing::ScopedTrace
t(File
, Line
, ::testing::Message() << Code
.str());
49 bool HasOriginalCode
= true;
50 if (ExpectedCode
== "") {
52 HasOriginalCode
= false;
55 EXPECT_EQ(ExpectedCode
, separateDefinitionBlocks(ExpectedCode
, Style
))
56 << "Expected code is not stable";
58 FormatStyle InverseStyle
= Style
;
59 if (Style
.SeparateDefinitionBlocks
== FormatStyle::SDS_Always
)
60 InverseStyle
.SeparateDefinitionBlocks
= FormatStyle::SDS_Never
;
62 InverseStyle
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
63 EXPECT_NE(ExpectedCode
,
64 separateDefinitionBlocks(ExpectedCode
, InverseStyle
))
65 << "Inverse formatting makes no difference";
67 std::string CodeToFormat
=
68 HasOriginalCode
? Code
.str() : removeEmptyLines(Code
);
69 std::string Result
= separateDefinitionBlocks(CodeToFormat
, Style
);
70 EXPECT_EQ(ExpectedCode
, Result
) << "Test failed. Formatted:\n" << Result
;
73 static std::string
removeEmptyLines(llvm::StringRef Code
) {
74 std::string Result
= "";
75 for (auto Char
: Code
.str()) {
77 auto LastChar
= Result
.back();
78 if ((Char
== '\n' && LastChar
== '\n') ||
79 (Char
== '\r' && (LastChar
== '\r' || LastChar
== '\n'))) {
83 Result
.push_back(Char
);
89 #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)
91 TEST_F(DefinitionBlockSeparatorTest
, Basic
) {
92 FormatStyle Style
= getLLVMStyle();
93 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
94 verifyFormat("int foo(int i, int j) {\n"
99 "int bar(int j, int k) {\n"
105 verifyFormat("struct foo {\n"
114 verifyFormat("union foo {\n"
123 verifyFormat("class foo {\n"
132 verifyFormat("namespace foo {\n"
141 verifyFormat("enum Foo { FOO, BAR };\n"
143 "enum Bar { FOOBAR, BARFOO };",
146 FormatStyle BreakAfterReturnTypeStyle
= Style
;
147 BreakAfterReturnTypeStyle
.AlwaysBreakAfterReturnType
= FormatStyle::RTBS_All
;
148 // Test uppercased long typename
149 verifyFormat("class Foo {\n"
151 " Bar(int t, int p) {\n"
157 " Foobar(int t, int p) {\n"
162 BreakAfterReturnTypeStyle
);
165 TEST_F(DefinitionBlockSeparatorTest
, FormatConflict
) {
166 FormatStyle Style
= getLLVMStyle();
167 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
168 llvm::StringRef Code
= "class Test {\n"
170 " static void foo() {\n"
175 std::vector
<tooling::Range
> Ranges
= {1, tooling::Range(0, Code
.size())};
176 EXPECT_EQ(reformat(Style
, Code
, Ranges
, "<stdin>").size(), 0u);
179 TEST_F(DefinitionBlockSeparatorTest
, CommentBlock
) {
180 FormatStyle Style
= getLLVMStyle();
181 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
182 std::string Prefix
= "enum Foo { FOO, BAR };\n"
188 "int foo(int i, int j) {\n"
192 std::string Suffix
= "enum Bar { FOOBAR, BARFOO };\n"
194 "/* Comment block in one line*/\n"
195 "int bar3(int j, int k) {\n"
200 std::string CommentedCode
= "/*\n"
201 "int bar2(int j, int k) {\n"
206 verifyFormat(removeEmptyLines(Prefix
) + "\n" + CommentedCode
+ "\n" +
207 removeEmptyLines(Suffix
),
208 Style
, Prefix
+ "\n" + CommentedCode
+ "\n" + Suffix
);
209 verifyFormat(removeEmptyLines(Prefix
) + "\n" + CommentedCode
+
210 removeEmptyLines(Suffix
),
211 Style
, Prefix
+ "\n" + CommentedCode
+ Suffix
);
214 TEST_F(DefinitionBlockSeparatorTest
, UntouchBlockStartStyle
) {
215 // Returns a std::pair of two strings, with the first one for passing into
216 // Always test and the second one be the expected result of the first string.
217 auto MakeUntouchTest
= [&](std::string BlockHeader
, std::string BlockChanger
,
218 std::string BlockFooter
, bool BlockEndNewLine
) {
219 std::string CodePart1
= "enum Foo { FOO, BAR };\n"
225 "int foo(int i, int j) {\n"
229 std::string CodePart2
= "/* Comment block in one line*/\n"
230 "enum Bar { FOOBAR, BARFOO };\n"
232 "int bar3(int j, int k) {\n"
237 std::string CodePart3
= "int bar2(int j, int k) {\n"
241 std::string ConcatAll
= BlockHeader
+ CodePart1
+ BlockChanger
+ CodePart2
+
242 BlockFooter
+ (BlockEndNewLine
? "\n" : "") +
244 return std::make_pair(BlockHeader
+ removeEmptyLines(CodePart1
) +
245 BlockChanger
+ removeEmptyLines(CodePart2
) +
246 BlockFooter
+ removeEmptyLines(CodePart3
),
250 FormatStyle AlwaysStyle
= getLLVMStyle();
251 AlwaysStyle
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
253 FormatStyle NeverStyle
= getLLVMStyle();
254 NeverStyle
.SeparateDefinitionBlocks
= FormatStyle::SDS_Never
;
256 auto TestKit
= MakeUntouchTest("/* FOOBAR */\n"
258 "\n#elifndef BAR\n\n", "\n#endif\n\n", false);
259 verifyFormat(TestKit
.first
, AlwaysStyle
, TestKit
.second
);
260 verifyFormat(TestKit
.second
, NeverStyle
, removeEmptyLines(TestKit
.second
));
262 TestKit
= MakeUntouchTest("/* FOOBAR */\n"
264 "#elifndef BAR\n", "#endif\n", false);
265 verifyFormat(TestKit
.first
, AlwaysStyle
, TestKit
.second
);
266 verifyFormat(TestKit
.second
, NeverStyle
, removeEmptyLines(TestKit
.second
));
268 TestKit
= MakeUntouchTest("namespace Ns {\n\n",
269 "\n} // namespace Ns\n\n"
271 "\n} // namespace\n", true);
272 verifyFormat(TestKit
.first
, AlwaysStyle
, TestKit
.second
);
273 verifyFormat(TestKit
.second
, NeverStyle
, removeEmptyLines(TestKit
.second
));
275 TestKit
= MakeUntouchTest("namespace Ns {\n",
276 "} // namespace Ns\n\n"
278 "} // namespace\n", true);
279 verifyFormat(TestKit
.first
, AlwaysStyle
, TestKit
.second
);
280 verifyFormat(TestKit
.second
, NeverStyle
, removeEmptyLines(TestKit
.second
));
283 TEST_F(DefinitionBlockSeparatorTest
, Always
) {
284 FormatStyle Style
= getLLVMStyle();
285 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
287 verifyFormat("// clang-format off\n"
288 "template<class T>\n"
289 "concept C = not A<S<T>>;\n"
290 "// clang-format on\n"
295 std::string Prefix
= "namespace {\n";
296 std::string Infix
= "\n"
299 "enum Foo { FOO, BAR };\n"
305 "/*const*/ int foo(int i, int j) {\n"
313 "// Comment for function\n"
314 "// Comment line 2\n"
315 "// Comment line 3\n"
316 "int bar(int j, int k) {\n"
323 "int bar2(int j, int k) {\n"
328 "/* Comment block in one line*/\n"
329 "enum Bar { FOOBAR, BARFOO };\n"
331 "int bar3(int j, int k, const enum Bar b) {\n"
334 " if (struct S = getS()) {\n"
339 std::string Postfix
= "\n"
345 verifyFormat(Prefix
+ removeEmptyLines(Infix
) + removeEmptyLines(Postfix
),
346 Style
, Prefix
+ Infix
+ Postfix
);
349 TEST_F(DefinitionBlockSeparatorTest
, Never
) {
350 FormatStyle Style
= getLLVMStyle();
351 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Never
;
352 std::string Prefix
= "namespace {\n";
353 std::string Postfix
= "// Enum test1\n"
355 "enum Foo { FOO, BAR };\n"
361 "/*const*/ int foo(int i, int j) {\n"
369 "// Comment for function\n"
370 "// Comment line 2\n"
371 "// Comment line 3\n"
372 "int bar(int j, int k) {\n"
379 "int bar2(int j, int k) {\n"
384 "/* Comment block in one line*/\n"
385 "enum Bar { FOOBAR, BARFOO };\n"
387 "int bar3(int j, int k, const enum Bar b) {\n"
390 " if (struct S = getS()) {\n"
396 verifyFormat(Prefix
+ "\n\n\n" + Postfix
, Style
,
397 Prefix
+ removeEmptyLines(Postfix
));
400 TEST_F(DefinitionBlockSeparatorTest
, OpeningBracketOwnsLine
) {
401 FormatStyle Style
= getLLVMStyle();
402 Style
.BreakBeforeBraces
= FormatStyle::BS_Allman
;
403 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
404 verifyFormat("namespace NS\n"
418 "/*const*/ int foo(int i, int j)\n"
427 "// Comment for function\n"
428 "// Comment line 2\n"
429 "// Comment line 3\n"
430 "int bar(int j, int k)\n"
438 "int bar2(int j, int k)\n"
450 "int bar3(int j, int k, const enum Bar b)\n"
454 " if (struct S = getS())\n"
464 TEST_F(DefinitionBlockSeparatorTest
, TryBlocks
) {
465 FormatStyle Style
= getLLVMStyle();
466 Style
.BreakBeforeBraces
= FormatStyle::BS_Allman
;
467 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
468 verifyFormat("void FunctionWithInternalTry()\n"
474 " catch (const std::exception &)\n"
478 Style
, "", /*Inverse=*/false);
479 verifyFormat("void FunctionWithTryBlock()\n"
484 "catch (const std::exception &)\n"
487 Style
, "", /*Inverse=*/false);
490 TEST_F(DefinitionBlockSeparatorTest
, Leave
) {
491 FormatStyle Style
= getLLVMStyle();
492 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Leave
;
493 Style
.MaxEmptyLinesToKeep
= 3;
494 std::string LeaveAs
= "namespace {\n"
498 "enum Foo { FOO, BAR };\n"
504 "/*const*/ int foo(int i, int j) {\n"
512 "// Comment for function\n"
513 "// Comment line 2\n"
514 "// Comment line 3\n"
515 "int bar(int j, int k) {\n"
522 "int bar2(int j, int k) {\n"
527 "// Comment for inline enum\n"
528 "enum Bar { FOOBAR, BARFOO };\n"
529 "int bar3(int j, int k, const enum Bar b) {\n"
532 " if (struct S = getS()) {\n"
538 verifyFormat(LeaveAs
, Style
, LeaveAs
);
541 TEST_F(DefinitionBlockSeparatorTest
, CSharp
) {
542 FormatStyle Style
= getLLVMStyle(FormatStyle::LK_CSharp
);
543 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
544 Style
.AllowShortFunctionsOnASingleLine
= FormatStyle::SFS_None
;
545 Style
.AllowShortEnumsOnASingleLine
= false;
546 verifyFormat("namespace {\r\n"
547 "public class SomeTinyClass {\r\n"
551 "public class AnotherTinyClass {\r\n"
555 "internal static String toString() {\r\n"
558 "// Comment for enum\r\n"
559 "public enum var {\r\n"
568 "static void Main(string[] args) {\r\n"
569 " Console.WriteLine(\"HelloWorld\");\r\n"
572 "static decimal Test() {\r\n"
576 "public class FoobarClass {\r\n"
582 TEST_F(DefinitionBlockSeparatorTest
, JavaScript
) {
583 FormatStyle Style
= getLLVMStyle(FormatStyle::LK_JavaScript
);
584 Style
.SeparateDefinitionBlocks
= FormatStyle::SDS_Always
;
585 Style
.AllowShortFunctionsOnASingleLine
= FormatStyle::SFS_None
;
586 Style
.AllowShortEnumsOnASingleLine
= false;
587 verifyFormat("export const enum Foo {\n"
592 "export function A() {\n"
595 "export default function B() {\n"
598 "export function C() {\n"
603 "export abstract class X {\n"
607 "export const enum Bar {\n"
614 } // namespace format