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 = separateDefinitionBlocks(Code, InverseStyle);
61     EXPECT_NE(Code.str(), InverseResult)
62         << "Inverse formatting makes no difference";
63     std::string CodeToFormat =
64         HasOriginalCode ? Code.str() : removeEmptyLines(Code);
65     std::string Result = separateDefinitionBlocks(CodeToFormat, Style);
66     EXPECT_EQ(ExpectedCode.str(), Result) << "Test failed. Formatted:\n"
67                                           << Result;
68   }
69 
70   static std::string removeEmptyLines(llvm::StringRef Code) {
71     std::string Result = "";
72     for (auto Char : Code.str()) {
73       if (Result.size()) {
74         auto LastChar = Result.back();
75         if ((Char == '\n' && LastChar == '\n') ||
76             (Char == '\r' && (LastChar == '\r' || LastChar == '\n')))
77           continue;
78       }
79       Result.push_back(Char);
80     }
81     return Result;
82   }
83 };
84 
85 TEST_F(DefinitionBlockSeparatorTest, Basic) {
86   FormatStyle Style = getLLVMStyle();
87   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
88   verifyFormat("int foo(int i, int j) {\n"
89                "  int r = i + j;\n"
90                "  return r;\n"
91                "}\n"
92                "\n"
93                "int bar(int j, int k) {\n"
94                "  int r = j + k;\n"
95                "  return r;\n"
96                "}",
97                Style);
98 
99   verifyFormat("struct foo {\n"
100                "  int i, j;\n"
101                "};\n"
102                "\n"
103                "struct bar {\n"
104                "  int j, k;\n"
105                "};",
106                Style);
107 
108   verifyFormat("class foo {\n"
109                "  int i, j;\n"
110                "};\n"
111                "\n"
112                "class bar {\n"
113                "  int j, k;\n"
114                "};",
115                Style);
116 
117   verifyFormat("namespace foo {\n"
118                "int i, j;\n"
119                "}\n"
120                "\n"
121                "namespace bar {\n"
122                "int j, k;\n"
123                "}",
124                Style);
125 
126   verifyFormat("enum Foo { FOO, BAR };\n"
127                "\n"
128                "enum Bar { FOOBAR, BARFOO };\n",
129                Style);
130 }
131 
132 TEST_F(DefinitionBlockSeparatorTest, Always) {
133   FormatStyle Style = getLLVMStyle();
134   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
135   std::string Prefix = "namespace {\n";
136   std::string Postfix = "enum Foo { FOO, BAR };\n"
137                         "\n"
138                         "int foo(int i, int j) {\n"
139                         "  int r = i + j;\n"
140                         "  return r;\n"
141                         "}\n"
142                         "\n"
143                         "int i, j, k;\n"
144                         "\n"
145                         "int bar(int j, int k) {\n"
146                         "  int r = j * k;\n"
147                         "  return r;\n"
148                         "}\n"
149                         "\n"
150                         "enum Bar { FOOBAR, BARFOO };\n"
151                         "} // namespace";
152   verifyFormat(Prefix + "\n\n\n" + removeEmptyLines(Postfix), Style,
153                Prefix + Postfix);
154 }
155 
156 TEST_F(DefinitionBlockSeparatorTest, Never) {
157   FormatStyle Style = getLLVMStyle();
158   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
159   std::string Prefix = "namespace {\n";
160   std::string Postfix = "enum Foo { FOO, BAR };\n"
161                         "\n"
162                         "int foo(int i, int j) {\n"
163                         "  int r = i + j;\n"
164                         "  return r;\n"
165                         "}\n"
166                         "\n"
167                         "int i, j, k;\n"
168                         "\n"
169                         "int bar(int j, int k) {\n"
170                         "  int r = j * k;\n"
171                         "  return r;\n"
172                         "}\n"
173                         "\n"
174                         "enum Bar { FOOBAR, BARFOO };\n"
175                         "} // namespace";
176   verifyFormat(Prefix + "\n\n\n" + Postfix, Style,
177                Prefix + removeEmptyLines(Postfix));
178 }
179 
180 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {
181   FormatStyle Style = getLLVMStyle();
182   Style.BreakBeforeBraces = FormatStyle::BS_Allman;
183   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
184   verifyFormat("enum Foo\n"
185                "{\n"
186                "  FOO,\n"
187                "  BAR\n"
188                "};\n"
189                "\n"
190                "int foo(int i, int j)\n"
191                "{\n"
192                "  int r = i + j;\n"
193                "  return r;\n"
194                "}\n"
195                "\n"
196                "int i, j, k;\n"
197                "\n"
198                "int bar(int j, int k)\n"
199                "{\n"
200                "  int r = j * k;\n"
201                "  return r;\n"
202                "}\n"
203                "\n"
204                "enum Bar\n"
205                "{\n"
206                "  FOOBAR,\n"
207                "  BARFOO\n"
208                "};",
209                Style);
210 }
211 
212 TEST_F(DefinitionBlockSeparatorTest, Leave) {
213   FormatStyle Style = getLLVMStyle();
214   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
215   Style.MaxEmptyLinesToKeep = 3;
216   std::string LeaveAs = "namespace {\n"
217                         "\n"
218                         "enum Foo { FOO, BAR };\n"
219                         "\n\n\n"
220                         "int foo(int i, int j) {\n"
221                         "  int r = i + j;\n"
222                         "  return r;\n"
223                         "}\n"
224                         "\n"
225                         "int i, j, k;\n"
226                         "\n"
227                         "int bar(int j, int k) {\n"
228                         "  int r = j * k;\n"
229                         "  return r;\n"
230                         "}\n"
231                         "\n"
232                         "enum Bar { FOOBAR, BARFOO };\n"
233                         "} // namespace";
234   verifyFormat(LeaveAs, Style, LeaveAs);
235 }
236 
237 TEST_F(DefinitionBlockSeparatorTest, CSharp) {
238   FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp);
239   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
240   Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
241   Style.AllowShortEnumsOnASingleLine = false;
242   verifyFormat("namespace {\r\n"
243                "public class SomeTinyClass {\r\n"
244                "  int X;\r\n"
245                "}\r\n"
246                "\r\n"
247                "public class AnotherTinyClass {\r\n"
248                "  int Y;\r\n"
249                "}\r\n"
250                "\r\n"
251                "internal static String toString() {\r\n"
252                "}\r\n"
253                "\r\n"
254                "public enum var {\r\n"
255                "  none,\r\n"
256                "  @string,\r\n"
257                "  bool,\r\n"
258                "  @enum\r\n"
259                "}\r\n"
260                "\r\n"
261                "[STAThread]\r\n"
262                "static void Main(string[] args) {\r\n"
263                "  Console.WriteLine(\"HelloWorld\");\r\n"
264                "}\r\n"
265                "\r\n"
266                "static decimal Test() {\r\n"
267                "}\r\n"
268                "}\r\n"
269                "\r\n"
270                "public class FoobarClass {\r\n"
271                "  int foobar;\r\n"
272                "}",
273                Style);
274 }
275 
276 TEST_F(DefinitionBlockSeparatorTest, JavaScript) {
277   FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript);
278   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
279   Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
280   Style.AllowShortEnumsOnASingleLine = false;
281   verifyFormat("export const enum Foo {\n"
282                "  A = 1,\n"
283                "  B\n"
284                "}\n"
285                "\n"
286                "export function A() {\n"
287                "}\n"
288                "\n"
289                "export default function B() {\n"
290                "}\n"
291                "\n"
292                "export function C() {\n"
293                "}\n"
294                "\n"
295                "var t, p, q;\n"
296                "\n"
297                "export abstract class X {\n"
298                "  y: number;\n"
299                "}\n"
300                "\n"
301                "export const enum Bar {\n"
302                "  D = 1,\n"
303                "  E\n"
304                "}",
305                Style);
306 }
307 } // namespace
308 } // namespace format
309 } // namespace clang
310