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