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
17 namespace clang {
18 namespace format {
19 namespace {
20
21 class DefinitionBlockSeparatorTest : public ::testing::Test {
22 protected:
23 static std::string
separateDefinitionBlocks(llvm::StringRef Code,const std::vector<tooling::Range> & Ranges,const FormatStyle & Style=getLLVMStyle ())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");
33 return *Result;
34 }
35
36 static std::string
separateDefinitionBlocks(llvm::StringRef Code,const FormatStyle & Style=getLLVMStyle ())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
_verifyFormat(const char * File,int Line,llvm::StringRef Code,const FormatStyle & Style=getLLVMStyle (),llvm::StringRef ExpectedCode="",bool Inverse=true)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(CodeToFormat, Style);
70 EXPECT_EQ(ExpectedCode, Result) << "Test failed. Formatted:\n" << Result;
71 }
72
removeEmptyLines(llvm::StringRef Code)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(Char);
84 }
85 return Result;
86 }
87 };
88
89 #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)
90
TEST_F(DefinitionBlockSeparatorTest,Basic)91 TEST_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 };\n",
144 Style);
145
146 FormatStyle BreakAfterReturnTypeStyle = Style;
147 BreakAfterReturnTypeStyle.AlwaysBreakAfterReturnType = 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 "}\n",
162 BreakAfterReturnTypeStyle);
163 }
164
TEST_F(DefinitionBlockSeparatorTest,FormatConflict)165 TEST_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
TEST_F(DefinitionBlockSeparatorTest,CommentBlock)179 TEST_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
TEST_F(DefinitionBlockSeparatorTest,UntouchBlockStartStyle)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"
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(BlockHeader + removeEmptyLines(CodePart1) +
245 BlockChanger + removeEmptyLines(CodePart2) +
246 BlockFooter + removeEmptyLines(CodePart3),
247 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
TEST_F(DefinitionBlockSeparatorTest,Always)283 TEST_F(DefinitionBlockSeparatorTest, Always) {
284 FormatStyle Style = getLLVMStyle();
285 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
286 std::string Prefix = "namespace {\n";
287 std::string Infix = "\n"
288 "// Enum test1\n"
289 "// Enum test2\n"
290 "enum Foo { FOO, BAR };\n"
291 "\n"
292 "/*\n"
293 "test1\n"
294 "test2\n"
295 "*/\n"
296 "/*const*/ int foo(int i, int j) {\n"
297 " int r = i + j;\n"
298 " return r;\n"
299 "}\n"
300 "\n"
301 "// Foobar\n"
302 "int i, j, k;\n"
303 "\n"
304 "// Comment for function\n"
305 "// Comment line 2\n"
306 "// Comment line 3\n"
307 "int bar(int j, int k) {\n"
308 " {\n"
309 " int r = j * k;\n"
310 " return r;\n"
311 " }\n"
312 "}\n"
313 "\n"
314 "int bar2(int j, int k) {\n"
315 " int r = j / k;\n"
316 " return r;\n"
317 "}\n"
318 "\n"
319 "/* Comment block in one line*/\n"
320 "enum Bar { FOOBAR, BARFOO };\n"
321 "\n"
322 "int bar3(int j, int k, const enum Bar b) {\n"
323 " // A comment\n"
324 " int r = j % k;\n"
325 " if (struct S = getS()) {\n"
326 " // if condition\n"
327 " }\n"
328 " return r;\n"
329 "}\n";
330 std::string Postfix = "\n"
331 "} // namespace\n"
332 "\n"
333 "namespace T {\n"
334 "int i, j, k;\n"
335 "} // namespace T";
336 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix),
337 Style, Prefix + Infix + Postfix);
338 }
339
TEST_F(DefinitionBlockSeparatorTest,Never)340 TEST_F(DefinitionBlockSeparatorTest, Never) {
341 FormatStyle Style = getLLVMStyle();
342 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
343 std::string Prefix = "namespace {\n";
344 std::string Postfix = "// Enum test1\n"
345 "// Enum test2\n"
346 "enum Foo { FOO, BAR };\n"
347 "\n"
348 "/*\n"
349 "test1\n"
350 "test2\n"
351 "*/\n"
352 "/*const*/ int foo(int i, int j) {\n"
353 " int r = i + j;\n"
354 " return r;\n"
355 "}\n"
356 "\n"
357 "// Foobar\n"
358 "int i, j, k;\n"
359 "\n"
360 "// Comment for function\n"
361 "// Comment line 2\n"
362 "// Comment line 3\n"
363 "int bar(int j, int k) {\n"
364 " {\n"
365 " int r = j * k;\n"
366 " return r;\n"
367 " }\n"
368 "}\n"
369 "\n"
370 "int bar2(int j, int k) {\n"
371 " int r = j / k;\n"
372 " return r;\n"
373 "}\n"
374 "\n"
375 "/* Comment block in one line*/\n"
376 "enum Bar { FOOBAR, BARFOO };\n"
377 "\n"
378 "int bar3(int j, int k, const enum Bar b) {\n"
379 " // A comment\n"
380 " int r = j % k;\n"
381 " if (struct S = getS()) {\n"
382 " // if condition\n"
383 " }\n"
384 " return r;\n"
385 "}\n"
386 "} // namespace";
387 verifyFormat(Prefix + "\n\n\n" + Postfix, Style,
388 Prefix + removeEmptyLines(Postfix));
389 }
390
TEST_F(DefinitionBlockSeparatorTest,OpeningBracketOwnsLine)391 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {
392 FormatStyle Style = getLLVMStyle();
393 Style.BreakBeforeBraces = FormatStyle::BS_Allman;
394 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
395 verifyFormat("namespace NS\n"
396 "{\n"
397 "// Enum test1\n"
398 "// Enum test2\n"
399 "enum Foo\n"
400 "{\n"
401 " FOO,\n"
402 " BAR\n"
403 "};\n"
404 "\n"
405 "/*\n"
406 "test1\n"
407 "test2\n"
408 "*/\n"
409 "/*const*/ int foo(int i, int j)\n"
410 "{\n"
411 " int r = i + j;\n"
412 " return r;\n"
413 "}\n"
414 "\n"
415 "// Foobar\n"
416 "int i, j, k;\n"
417 "\n"
418 "// Comment for function\n"
419 "// Comment line 2\n"
420 "// Comment line 3\n"
421 "int bar(int j, int k)\n"
422 "{\n"
423 " {\n"
424 " int r = j * k;\n"
425 " return r;\n"
426 " }\n"
427 "}\n"
428 "\n"
429 "int bar2(int j, int k)\n"
430 "{\n"
431 " int r = j / k;\n"
432 " return r;\n"
433 "}\n"
434 "\n"
435 "enum Bar\n"
436 "{\n"
437 " FOOBAR,\n"
438 " BARFOO\n"
439 "};\n"
440 "\n"
441 "int bar3(int j, int k, const enum Bar b)\n"
442 "{\n"
443 " // A comment\n"
444 " int r = j % k;\n"
445 " if (struct S = getS())\n"
446 " {\n"
447 " // if condition\n"
448 " }\n"
449 " return r;\n"
450 "}\n"
451 "} // namespace NS",
452 Style);
453 }
454
TEST_F(DefinitionBlockSeparatorTest,TryBlocks)455 TEST_F(DefinitionBlockSeparatorTest, TryBlocks) {
456 FormatStyle Style = getLLVMStyle();
457 Style.BreakBeforeBraces = FormatStyle::BS_Allman;
458 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
459 verifyFormat("void FunctionWithInternalTry()\n"
460 "{\n"
461 " try\n"
462 " {\n"
463 " return;\n"
464 " }\n"
465 " catch (const std::exception &)\n"
466 " {\n"
467 " }\n"
468 "}",
469 Style, "", /*Inverse=*/false);
470 verifyFormat("void FunctionWithTryBlock()\n"
471 "try\n"
472 "{\n"
473 " return;\n"
474 "}\n"
475 "catch (const std::exception &)\n"
476 "{\n"
477 "}",
478 Style, "", /*Inverse=*/false);
479 }
480
TEST_F(DefinitionBlockSeparatorTest,Leave)481 TEST_F(DefinitionBlockSeparatorTest, Leave) {
482 FormatStyle Style = getLLVMStyle();
483 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
484 Style.MaxEmptyLinesToKeep = 3;
485 std::string LeaveAs = "namespace {\n"
486 "\n"
487 "// Enum test1\n"
488 "// Enum test2\n"
489 "enum Foo { FOO, BAR };\n"
490 "\n\n\n"
491 "/*\n"
492 "test1\n"
493 "test2\n"
494 "*/\n"
495 "/*const*/ int foo(int i, int j) {\n"
496 " int r = i + j;\n"
497 " return r;\n"
498 "}\n"
499 "\n"
500 "// Foobar\n"
501 "int i, j, k;\n"
502 "\n"
503 "// Comment for function\n"
504 "// Comment line 2\n"
505 "// Comment line 3\n"
506 "int bar(int j, int k) {\n"
507 " {\n"
508 " int r = j * k;\n"
509 " return r;\n"
510 " }\n"
511 "}\n"
512 "\n"
513 "int bar2(int j, int k) {\n"
514 " int r = j / k;\n"
515 " return r;\n"
516 "}\n"
517 "\n"
518 "// Comment for inline enum\n"
519 "enum Bar { FOOBAR, BARFOO };\n"
520 "int bar3(int j, int k, const enum Bar b) {\n"
521 " // A comment\n"
522 " int r = j % k;\n"
523 " if (struct S = getS()) {\n"
524 " // if condition\n"
525 " }\n"
526 " return r;\n"
527 "}\n"
528 "} // namespace";
529 verifyFormat(LeaveAs, Style, LeaveAs);
530 }
531
TEST_F(DefinitionBlockSeparatorTest,CSharp)532 TEST_F(DefinitionBlockSeparatorTest, CSharp) {
533 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp);
534 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
535 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
536 Style.AllowShortEnumsOnASingleLine = false;
537 verifyFormat("namespace {\r\n"
538 "public class SomeTinyClass {\r\n"
539 " int X;\r\n"
540 "}\r\n"
541 "\r\n"
542 "public class AnotherTinyClass {\r\n"
543 " int Y;\r\n"
544 "}\r\n"
545 "\r\n"
546 "internal static String toString() {\r\n"
547 "}\r\n"
548 "\r\n"
549 "// Comment for enum\r\n"
550 "public enum var {\r\n"
551 " none,\r\n"
552 " @string,\r\n"
553 " bool,\r\n"
554 " @enum\r\n"
555 "}\r\n"
556 "\r\n"
557 "// Test\r\n"
558 "[STAThread]\r\n"
559 "static void Main(string[] args) {\r\n"
560 " Console.WriteLine(\"HelloWorld\");\r\n"
561 "}\r\n"
562 "\r\n"
563 "static decimal Test() {\r\n"
564 "}\r\n"
565 "}\r\n"
566 "\r\n"
567 "public class FoobarClass {\r\n"
568 " int foobar;\r\n"
569 "}",
570 Style);
571 }
572
TEST_F(DefinitionBlockSeparatorTest,JavaScript)573 TEST_F(DefinitionBlockSeparatorTest, JavaScript) {
574 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript);
575 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
576 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
577 Style.AllowShortEnumsOnASingleLine = false;
578 verifyFormat("export const enum Foo {\n"
579 " A = 1,\n"
580 " B\n"
581 "}\n"
582 "\n"
583 "export function A() {\n"
584 "}\n"
585 "\n"
586 "export default function B() {\n"
587 "}\n"
588 "\n"
589 "export function C() {\n"
590 "}\n"
591 "\n"
592 "var t, p, q;\n"
593 "\n"
594 "export abstract class X {\n"
595 " y: number;\n"
596 "}\n"
597 "\n"
598 "export const enum Bar {\n"
599 " D = 1,\n"
600 " E\n"
601 "}",
602 Style);
603 }
604 } // namespace
605 } // namespace format
606 } // namespace clang
607