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