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