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