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