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