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