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