1//===- DefinitionBlockSeparatorTest.cpp - Formatting unit tests -----------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "FormatTestUtils.h"
10#include "clang/Format/Format.h"
11
12#include "llvm/Support/Debug.h"
13#include "gtest/gtest.h"
14
15#define DEBUG_TYPE "definition-block-separator-test"
16
17namespace clang {
18namespace format {
19namespace {
20
21class DefinitionBlockSeparatorTest : public ::testing::Test {
22protected:
23 static std::string
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, FileName: "<stdin>");
30 auto Result = applyAllReplacements(Code, Replaces);
31 EXPECT_TRUE(static_cast<bool>(Result));
32 LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
33 return *Result;
34 }
35
36 static std::string
37 separateDefinitionBlocks(llvm::StringRef Code,
38 const FormatStyle &Style = getLLVMStyle()) {
39 return separateDefinitionBlocks(
40 Code,
41 /*Ranges=*/{1, tooling::Range(0, Code.size())}, Style);
42 }
43
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 == "") {
51 ExpectedCode = Code;
52 HasOriginalCode = false;
53 }
54
55 EXPECT_EQ(ExpectedCode, separateDefinitionBlocks(ExpectedCode, Style))
56 << "Expected code is not stable";
57 if (Inverse) {
58 FormatStyle InverseStyle = Style;
59 if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always)
60 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
61 else
62 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
63 EXPECT_NE(ExpectedCode,
64 separateDefinitionBlocks(ExpectedCode, InverseStyle))
65 << "Inverse formatting makes no difference";
66 }
67 std::string CodeToFormat =
68 HasOriginalCode ? Code.str() : removeEmptyLines(Code);
69 std::string Result = separateDefinitionBlocks(Code: CodeToFormat, Style);
70 EXPECT_EQ(ExpectedCode, Result) << "Test failed. Formatted:\n" << Result;
71 }
72
73 static std::string removeEmptyLines(llvm::StringRef Code) {
74 std::string Result = "";
75 for (auto Char : Code.str()) {
76 if (Result.size()) {
77 auto LastChar = Result.back();
78 if ((Char == '\n' && LastChar == '\n') ||
79 (Char == '\r' && (LastChar == '\r' || LastChar == '\n'))) {
80 continue;
81 }
82 }
83 Result.push_back(c: Char);
84 }
85 return Result;
86 }
87};
88
89#define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)
90
91TEST_F(DefinitionBlockSeparatorTest, Basic) {
92 FormatStyle Style = getLLVMStyle();
93 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
94 verifyFormat("int foo(int i, int j) {\n"
95 " int r = i + j;\n"
96 " return r;\n"
97 "}\n"
98 "\n"
99 "int bar(int j, int k) {\n"
100 " int r = j + k;\n"
101 " return r;\n"
102 "}",
103 Style);
104
105 verifyFormat("struct foo {\n"
106 " int i, j;\n"
107 "};\n"
108 "\n"
109 "struct bar {\n"
110 " int j, k;\n"
111 "};",
112 Style);
113
114 verifyFormat("union foo {\n"
115 " int i, j;\n"
116 "};\n"
117 "\n"
118 "union bar {\n"
119 " int j, k;\n"
120 "};",
121 Style);
122
123 verifyFormat("class foo {\n"
124 " int i, j;\n"
125 "};\n"
126 "\n"
127 "class bar {\n"
128 " int j, k;\n"
129 "};",
130 Style);
131
132 verifyFormat("namespace foo {\n"
133 "int i, j;\n"
134 "}\n"
135 "\n"
136 "namespace bar {\n"
137 "int j, k;\n"
138 "}",
139 Style);
140
141 verifyFormat("enum Foo { FOO, BAR };\n"
142 "\n"
143 "enum Bar { FOOBAR, BARFOO };",
144 Style);
145
146 FormatStyle BreakAfterReturnTypeStyle = Style;
147 BreakAfterReturnTypeStyle.BreakAfterReturnType = FormatStyle::RTBS_All;
148 // Test uppercased long typename
149 verifyFormat("class Foo {\n"
150 " void\n"
151 " Bar(int t, int p) {\n"
152 " int r = t + p;\n"
153 " return r;\n"
154 " }\n"
155 "\n"
156 " HRESULT\n"
157 " Foobar(int t, int p) {\n"
158 " int r = t * p;\n"
159 " return r;\n"
160 " }\n"
161 "}",
162 BreakAfterReturnTypeStyle);
163}
164
165TEST_F(DefinitionBlockSeparatorTest, FormatConflict) {
166 FormatStyle Style = getLLVMStyle();
167 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
168 llvm::StringRef Code = "class Test {\n"
169 "public:\n"
170 " static void foo() {\n"
171 " int t;\n"
172 " return 1;\n"
173 " }\n"
174 "};";
175 std::vector<tooling::Range> Ranges = {1, tooling::Range(0, Code.size())};
176 EXPECT_EQ(reformat(Style, Code, Ranges, "<stdin>").size(), 0u);
177}
178
179TEST_F(DefinitionBlockSeparatorTest, CommentBlock) {
180 FormatStyle Style = getLLVMStyle();
181 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
182 std::string Prefix = "enum Foo { FOO, BAR };\n"
183 "\n"
184 "/*\n"
185 "test1\n"
186 "test2\n"
187 "*/\n"
188 "int foo(int i, int j) {\n"
189 " int r = i + j;\n"
190 " return r;\n"
191 "}\n";
192 std::string Suffix = "enum Bar { FOOBAR, BARFOO };\n"
193 "\n"
194 "/* Comment block in one line*/\n"
195 "int bar3(int j, int k) {\n"
196 " // A comment\n"
197 " int r = j % k;\n"
198 " return r;\n"
199 "}\n";
200 std::string CommentedCode = "/*\n"
201 "int bar2(int j, int k) {\n"
202 " int r = j / k;\n"
203 " return r;\n"
204 "}\n"
205 "*/\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);
212}
213
214TEST_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"
220 "\n"
221 "/*\n"
222 "test1\n"
223 "test2\n"
224 "*/\n"
225 "int foo(int i, int j) {\n"
226 " int r = i + j;\n"
227 " return r;\n"
228 "}\n";
229 std::string CodePart2 = "/* Comment block in one line*/\n"
230 "enum Bar { FOOBAR, BARFOO };\n"
231 "\n"
232 "int bar3(int j, int k) {\n"
233 " // A comment\n"
234 " int r = j % k;\n"
235 " return r;\n"
236 "}\n";
237 std::string CodePart3 = "int bar2(int j, int k) {\n"
238 " int r = j / k;\n"
239 " return r;\n"
240 "}\n";
241 std::string ConcatAll = BlockHeader + CodePart1 + BlockChanger + CodePart2 +
242 BlockFooter + (BlockEndNewLine ? "\n" : "") +
243 CodePart3;
244 return std::make_pair(x: BlockHeader + removeEmptyLines(Code: CodePart1) +
245 BlockChanger + removeEmptyLines(Code: CodePart2) +
246 BlockFooter + removeEmptyLines(Code: CodePart3),
247 y&: ConcatAll);
248 };
249
250 FormatStyle AlwaysStyle = getLLVMStyle();
251 AlwaysStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
252
253 FormatStyle NeverStyle = getLLVMStyle();
254 NeverStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
255
256 auto TestKit = MakeUntouchTest("/* FOOBAR */\n"
257 "#ifdef FOO\n\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));
261
262 TestKit = MakeUntouchTest("/* FOOBAR */\n"
263 "#ifdef FOO\n",
264 "#elifndef BAR\n", "#endif\n", false);
265 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
266 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
267
268 TestKit = MakeUntouchTest("namespace Ns {\n\n",
269 "\n} // namespace Ns\n\n"
270 "namespace {\n\n",
271 "\n} // namespace\n", true);
272 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
273 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
274
275 TestKit = MakeUntouchTest("namespace Ns {\n",
276 "} // namespace Ns\n\n"
277 "namespace {\n",
278 "} // namespace\n", true);
279 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
280 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
281}
282
283TEST_F(DefinitionBlockSeparatorTest, Always) {
284 FormatStyle Style = getLLVMStyle();
285 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
286
287 verifyFormat("// clang-format off\n"
288 "template<class T>\n"
289 "concept C = not A<S<T>>;\n"
290 "// clang-format on\n"
291 "\n"
292 "struct E {};",
293 Style);
294
295 std::string Prefix = "namespace {\n";
296 std::string Infix = "\n"
297 "// Enum test1\n"
298 "// Enum test2\n"
299 "enum Foo { FOO, BAR };\n"
300 "\n"
301 "/*\n"
302 "test1\n"
303 "test2\n"
304 "*/\n"
305 "/*const*/ int foo(int i, int j) {\n"
306 " int r = i + j;\n"
307 " return r;\n"
308 "}\n"
309 "\n"
310 "// Foobar\n"
311 "int i, j, k;\n"
312 "\n"
313 "// Comment for function\n"
314 "// Comment line 2\n"
315 "// Comment line 3\n"
316 "int bar(int j, int k) {\n"
317 " {\n"
318 " int r = j * k;\n"
319 " return r;\n"
320 " }\n"
321 "}\n"
322 "\n"
323 "int bar2(int j, int k) {\n"
324 " int r = j / k;\n"
325 " return r;\n"
326 "}\n"
327 "\n"
328 "/* Comment block in one line*/\n"
329 "enum Bar { FOOBAR, BARFOO };\n"
330 "\n"
331 "int bar3(int j, int k, const enum Bar b) {\n"
332 " // A comment\n"
333 " int r = j % k;\n"
334 " if (struct S = getS()) {\n"
335 " // if condition\n"
336 " }\n"
337 " return r;\n"
338 "}\n";
339 std::string Postfix = "\n"
340 "} // namespace\n"
341 "\n"
342 "namespace T {\n"
343 "int i, j, k;\n"
344 "} // namespace T";
345 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix),
346 Style, Prefix + Infix + Postfix);
347}
348
349TEST_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"
354 "// Enum test2\n"
355 "enum Foo { FOO, BAR };\n"
356 "\n"
357 "/*\n"
358 "test1\n"
359 "test2\n"
360 "*/\n"
361 "/*const*/ int foo(int i, int j) {\n"
362 " int r = i + j;\n"
363 " return r;\n"
364 "}\n"
365 "\n"
366 "// Foobar\n"
367 "int i, j, k;\n"
368 "\n"
369 "// Comment for function\n"
370 "// Comment line 2\n"
371 "// Comment line 3\n"
372 "int bar(int j, int k) {\n"
373 " {\n"
374 " int r = j * k;\n"
375 " return r;\n"
376 " }\n"
377 "}\n"
378 "\n"
379 "int bar2(int j, int k) {\n"
380 " int r = j / k;\n"
381 " return r;\n"
382 "}\n"
383 "\n"
384 "/* Comment block in one line*/\n"
385 "enum Bar { FOOBAR, BARFOO };\n"
386 "\n"
387 "int bar3(int j, int k, const enum Bar b) {\n"
388 " // A comment\n"
389 " int r = j % k;\n"
390 " if (struct S = getS()) {\n"
391 " // if condition\n"
392 " }\n"
393 " return r;\n"
394 "}\n"
395 "} // namespace";
396 verifyFormat(Prefix + "\n\n\n" + Postfix, Style,
397 Prefix + removeEmptyLines(Postfix));
398}
399
400TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {
401 FormatStyle Style = getLLVMStyle();
402 Style.BreakBeforeBraces = FormatStyle::BS_Allman;
403 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
404 verifyFormat("namespace NS\n"
405 "{\n"
406 "// Enum test1\n"
407 "// Enum test2\n"
408 "enum Foo\n"
409 "{\n"
410 " FOO,\n"
411 " BAR\n"
412 "};\n"
413 "\n"
414 "/*\n"
415 "test1\n"
416 "test2\n"
417 "*/\n"
418 "/*const*/ int foo(int i, int j)\n"
419 "{\n"
420 " int r = i + j;\n"
421 " return r;\n"
422 "}\n"
423 "\n"
424 "// Foobar\n"
425 "int i, j, k;\n"
426 "\n"
427 "// Comment for function\n"
428 "// Comment line 2\n"
429 "// Comment line 3\n"
430 "int bar(int j, int k)\n"
431 "{\n"
432 " {\n"
433 " int r = j * k;\n"
434 " return r;\n"
435 " }\n"
436 "}\n"
437 "\n"
438 "int bar2(int j, int k)\n"
439 "{\n"
440 " int r = j / k;\n"
441 " return r;\n"
442 "}\n"
443 "\n"
444 "enum Bar\n"
445 "{\n"
446 " FOOBAR,\n"
447 " BARFOO\n"
448 "};\n"
449 "\n"
450 "int bar3(int j, int k, const enum Bar b)\n"
451 "{\n"
452 " // A comment\n"
453 " int r = j % k;\n"
454 " if (struct S = getS())\n"
455 " {\n"
456 " // if condition\n"
457 " }\n"
458 " return r;\n"
459 "}\n"
460 "} // namespace NS",
461 Style);
462}
463
464TEST_F(DefinitionBlockSeparatorTest, TryBlocks) {
465 FormatStyle Style = getLLVMStyle();
466 Style.BreakBeforeBraces = FormatStyle::BS_Allman;
467 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
468 verifyFormat("void FunctionWithInternalTry()\n"
469 "{\n"
470 " try\n"
471 " {\n"
472 " return;\n"
473 " }\n"
474 " catch (const std::exception &)\n"
475 " {\n"
476 " }\n"
477 "}",
478 Style, "", /*Inverse=*/false);
479 verifyFormat("void FunctionWithTryBlock()\n"
480 "try\n"
481 "{\n"
482 " return;\n"
483 "}\n"
484 "catch (const std::exception &)\n"
485 "{\n"
486 "}",
487 Style, "", /*Inverse=*/false);
488}
489
490TEST_F(DefinitionBlockSeparatorTest, Leave) {
491 FormatStyle Style = getLLVMStyle();
492 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
493 Style.MaxEmptyLinesToKeep = 3;
494 std::string LeaveAs = "namespace {\n"
495 "\n"
496 "// Enum test1\n"
497 "// Enum test2\n"
498 "enum Foo { FOO, BAR };\n"
499 "\n\n\n"
500 "/*\n"
501 "test1\n"
502 "test2\n"
503 "*/\n"
504 "/*const*/ int foo(int i, int j) {\n"
505 " int r = i + j;\n"
506 " return r;\n"
507 "}\n"
508 "\n"
509 "// Foobar\n"
510 "int i, j, k;\n"
511 "\n"
512 "// Comment for function\n"
513 "// Comment line 2\n"
514 "// Comment line 3\n"
515 "int bar(int j, int k) {\n"
516 " {\n"
517 " int r = j * k;\n"
518 " return r;\n"
519 " }\n"
520 "}\n"
521 "\n"
522 "int bar2(int j, int k) {\n"
523 " int r = j / k;\n"
524 " return r;\n"
525 "}\n"
526 "\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"
530 " // A comment\n"
531 " int r = j % k;\n"
532 " if (struct S = getS()) {\n"
533 " // if condition\n"
534 " }\n"
535 " return r;\n"
536 "}\n"
537 "} // namespace";
538 verifyFormat(LeaveAs, Style, LeaveAs);
539}
540
541TEST_F(DefinitionBlockSeparatorTest, CSharp) {
542 FormatStyle Style = getLLVMStyle(Language: 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"
548 " int X;\r\n"
549 "}\r\n"
550 "\r\n"
551 "public class AnotherTinyClass {\r\n"
552 " int Y;\r\n"
553 "}\r\n"
554 "\r\n"
555 "internal static String toString() {\r\n"
556 "}\r\n"
557 "\r\n"
558 "// Comment for enum\r\n"
559 "public enum var {\r\n"
560 " none,\r\n"
561 " @string,\r\n"
562 " bool,\r\n"
563 " @enum\r\n"
564 "}\r\n"
565 "\r\n"
566 "// Test\r\n"
567 "[STAThread]\r\n"
568 "static void Main(string[] args) {\r\n"
569 " Console.WriteLine(\"HelloWorld\");\r\n"
570 "}\r\n"
571 "\r\n"
572 "static decimal Test() {\r\n"
573 "}\r\n"
574 "}\r\n"
575 "\r\n"
576 "public class FoobarClass {\r\n"
577 " int foobar;\r\n"
578 "}",
579 Style);
580}
581
582TEST_F(DefinitionBlockSeparatorTest, JavaScript) {
583 FormatStyle Style = getLLVMStyle(Language: 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"
588 " A = 1,\n"
589 " B\n"
590 "}\n"
591 "\n"
592 "export function A() {\n"
593 "}\n"
594 "\n"
595 "export default function B() {\n"
596 "}\n"
597 "\n"
598 "export function C() {\n"
599 "}\n"
600 "\n"
601 "var t, p, q;\n"
602 "\n"
603 "export abstract class X {\n"
604 " y: number;\n"
605 "}\n"
606 "\n"
607 "export const enum Bar {\n"
608 " D = 1,\n"
609 " E\n"
610 "}",
611 Style);
612}
613} // namespace
614} // namespace format
615} // namespace clang
616

source code of clang/unittests/Format/DefinitionBlockSeparatorTest.cpp