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 bool Inverse = true) { 48 ::testing::ScopedTrace t(File, Line, ::testing::Message() << Code.str()); 49 bool HasOriginalCode = true; 50 if (ExpectedCode == "") { 51 ExpectedCode = Code; 52 HasOriginalCode = false; 53 } 54 55 EXPECT_EQ(ExpectedCode, separateDefinitionBlocks(ExpectedCode, Style)) 56 << "Expected code is not stable"; 57 if (Inverse) { 58 FormatStyle InverseStyle = Style; 59 if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always) 60 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 61 else 62 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 63 EXPECT_NE(ExpectedCode, 64 separateDefinitionBlocks(ExpectedCode, InverseStyle)) 65 << "Inverse formatting makes no difference"; 66 } 67 std::string CodeToFormat = 68 HasOriginalCode ? Code.str() : removeEmptyLines(Code); 69 std::string Result = separateDefinitionBlocks(CodeToFormat, Style); 70 EXPECT_EQ(ExpectedCode, Result) << "Test failed. Formatted:\n" << Result; 71 } 72 73 static std::string removeEmptyLines(llvm::StringRef Code) { 74 std::string Result = ""; 75 for (auto Char : Code.str()) { 76 if (Result.size()) { 77 auto LastChar = Result.back(); 78 if ((Char == '\n' && LastChar == '\n') || 79 (Char == '\r' && (LastChar == '\r' || LastChar == '\n'))) 80 continue; 81 } 82 Result.push_back(Char); 83 } 84 return Result; 85 } 86 }; 87 88 #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__) 89 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 };\n", 143 Style); 144 145 FormatStyle BreakAfterReturnTypeStyle = Style; 146 BreakAfterReturnTypeStyle.AlwaysBreakAfterReturnType = 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 "}\n", 161 BreakAfterReturnTypeStyle); 162 } 163 164 TEST_F(DefinitionBlockSeparatorTest, FormatConflict) { 165 FormatStyle Style = getLLVMStyle(); 166 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 167 llvm::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 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 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 282 TEST_F(DefinitionBlockSeparatorTest, Always) { 283 FormatStyle Style = getLLVMStyle(); 284 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 285 std::string Prefix = "namespace {\n"; 286 std::string Infix = "\n" 287 "// Enum test1\n" 288 "// Enum test2\n" 289 "enum Foo { FOO, BAR };\n" 290 "\n" 291 "/*\n" 292 "test1\n" 293 "test2\n" 294 "*/\n" 295 "/*const*/ int foo(int i, int j) {\n" 296 " int r = i + j;\n" 297 " return r;\n" 298 "}\n" 299 "\n" 300 "// Foobar\n" 301 "int i, j, k;\n" 302 "\n" 303 "// Comment for function\n" 304 "// Comment line 2\n" 305 "// Comment line 3\n" 306 "int bar(int j, int k) {\n" 307 " {\n" 308 " int r = j * k;\n" 309 " return r;\n" 310 " }\n" 311 "}\n" 312 "\n" 313 "int bar2(int j, int k) {\n" 314 " int r = j / k;\n" 315 " return r;\n" 316 "}\n" 317 "\n" 318 "/* Comment block in one line*/\n" 319 "enum Bar { FOOBAR, BARFOO };\n" 320 "\n" 321 "int bar3(int j, int k, const enum Bar b) {\n" 322 " // A comment\n" 323 " int r = j % k;\n" 324 " if (struct S = getS()) {\n" 325 " // if condition\n" 326 " }\n" 327 " return r;\n" 328 "}\n"; 329 std::string Postfix = "\n" 330 "} // namespace\n" 331 "\n" 332 "namespace T {\n" 333 "int i, j, k;\n" 334 "} // namespace T"; 335 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix), 336 Style, Prefix + Infix + Postfix); 337 } 338 339 TEST_F(DefinitionBlockSeparatorTest, Never) { 340 FormatStyle Style = getLLVMStyle(); 341 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 342 std::string Prefix = "namespace {\n"; 343 std::string Postfix = "// Enum test1\n" 344 "// Enum test2\n" 345 "enum Foo { FOO, BAR };\n" 346 "\n" 347 "/*\n" 348 "test1\n" 349 "test2\n" 350 "*/\n" 351 "/*const*/ int foo(int i, int j) {\n" 352 " int r = i + j;\n" 353 " return r;\n" 354 "}\n" 355 "\n" 356 "// Foobar\n" 357 "int i, j, k;\n" 358 "\n" 359 "// Comment for function\n" 360 "// Comment line 2\n" 361 "// Comment line 3\n" 362 "int bar(int j, int k) {\n" 363 " {\n" 364 " int r = j * k;\n" 365 " return r;\n" 366 " }\n" 367 "}\n" 368 "\n" 369 "int bar2(int j, int k) {\n" 370 " int r = j / k;\n" 371 " return r;\n" 372 "}\n" 373 "\n" 374 "/* Comment block in one line*/\n" 375 "enum Bar { FOOBAR, BARFOO };\n" 376 "\n" 377 "int bar3(int j, int k, const enum Bar b) {\n" 378 " // A comment\n" 379 " int r = j % k;\n" 380 " if (struct S = getS()) {\n" 381 " // if condition\n" 382 " }\n" 383 " return r;\n" 384 "}\n" 385 "} // namespace"; 386 verifyFormat(Prefix + "\n\n\n" + Postfix, Style, 387 Prefix + removeEmptyLines(Postfix)); 388 } 389 390 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) { 391 FormatStyle Style = getLLVMStyle(); 392 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 393 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 394 verifyFormat("namespace NS\n" 395 "{\n" 396 "// Enum test1\n" 397 "// Enum test2\n" 398 "enum Foo\n" 399 "{\n" 400 " FOO,\n" 401 " BAR\n" 402 "};\n" 403 "\n" 404 "/*\n" 405 "test1\n" 406 "test2\n" 407 "*/\n" 408 "/*const*/ int foo(int i, int j)\n" 409 "{\n" 410 " int r = i + j;\n" 411 " return r;\n" 412 "}\n" 413 "\n" 414 "// Foobar\n" 415 "int i, j, k;\n" 416 "\n" 417 "// Comment for function\n" 418 "// Comment line 2\n" 419 "// Comment line 3\n" 420 "int bar(int j, int k)\n" 421 "{\n" 422 " {\n" 423 " int r = j * k;\n" 424 " return r;\n" 425 " }\n" 426 "}\n" 427 "\n" 428 "int bar2(int j, int k)\n" 429 "{\n" 430 " int r = j / k;\n" 431 " return r;\n" 432 "}\n" 433 "\n" 434 "enum Bar\n" 435 "{\n" 436 " FOOBAR,\n" 437 " BARFOO\n" 438 "};\n" 439 "\n" 440 "int bar3(int j, int k, const enum Bar b)\n" 441 "{\n" 442 " // A comment\n" 443 " int r = j % k;\n" 444 " if (struct S = getS())\n" 445 " {\n" 446 " // if condition\n" 447 " }\n" 448 " return r;\n" 449 "}\n" 450 "} // namespace NS", 451 Style); 452 } 453 454 TEST_F(DefinitionBlockSeparatorTest, TryBlocks) { 455 FormatStyle Style = getLLVMStyle(); 456 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 457 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 458 verifyFormat("void FunctionWithInternalTry()\n" 459 "{\n" 460 " try\n" 461 " {\n" 462 " return;\n" 463 " }\n" 464 " catch (const std::exception &)\n" 465 " {\n" 466 " }\n" 467 "}", 468 Style, "", /*Inverse=*/false); 469 verifyFormat("void FunctionWithTryBlock()\n" 470 "try\n" 471 "{\n" 472 " return;\n" 473 "}\n" 474 "catch (const std::exception &)\n" 475 "{\n" 476 "}", 477 Style, "", /*Inverse=*/false); 478 } 479 480 TEST_F(DefinitionBlockSeparatorTest, Leave) { 481 FormatStyle Style = getLLVMStyle(); 482 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave; 483 Style.MaxEmptyLinesToKeep = 3; 484 std::string LeaveAs = "namespace {\n" 485 "\n" 486 "// Enum test1\n" 487 "// Enum test2\n" 488 "enum Foo { FOO, BAR };\n" 489 "\n\n\n" 490 "/*\n" 491 "test1\n" 492 "test2\n" 493 "*/\n" 494 "/*const*/ int foo(int i, int j) {\n" 495 " int r = i + j;\n" 496 " return r;\n" 497 "}\n" 498 "\n" 499 "// Foobar\n" 500 "int i, j, k;\n" 501 "\n" 502 "// Comment for function\n" 503 "// Comment line 2\n" 504 "// Comment line 3\n" 505 "int bar(int j, int k) {\n" 506 " {\n" 507 " int r = j * k;\n" 508 " return r;\n" 509 " }\n" 510 "}\n" 511 "\n" 512 "int bar2(int j, int k) {\n" 513 " int r = j / k;\n" 514 " return r;\n" 515 "}\n" 516 "\n" 517 "// Comment for inline enum\n" 518 "enum Bar { FOOBAR, BARFOO };\n" 519 "int bar3(int j, int k, const enum Bar b) {\n" 520 " // A comment\n" 521 " int r = j % k;\n" 522 " if (struct S = getS()) {\n" 523 " // if condition\n" 524 " }\n" 525 " return r;\n" 526 "}\n" 527 "} // namespace"; 528 verifyFormat(LeaveAs, Style, LeaveAs); 529 } 530 531 TEST_F(DefinitionBlockSeparatorTest, CSharp) { 532 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); 533 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 534 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 535 Style.AllowShortEnumsOnASingleLine = false; 536 verifyFormat("namespace {\r\n" 537 "public class SomeTinyClass {\r\n" 538 " int X;\r\n" 539 "}\r\n" 540 "\r\n" 541 "public class AnotherTinyClass {\r\n" 542 " int Y;\r\n" 543 "}\r\n" 544 "\r\n" 545 "internal static String toString() {\r\n" 546 "}\r\n" 547 "\r\n" 548 "// Comment for enum\r\n" 549 "public enum var {\r\n" 550 " none,\r\n" 551 " @string,\r\n" 552 " bool,\r\n" 553 " @enum\r\n" 554 "}\r\n" 555 "\r\n" 556 "// Test\r\n" 557 "[STAThread]\r\n" 558 "static void Main(string[] args) {\r\n" 559 " Console.WriteLine(\"HelloWorld\");\r\n" 560 "}\r\n" 561 "\r\n" 562 "static decimal Test() {\r\n" 563 "}\r\n" 564 "}\r\n" 565 "\r\n" 566 "public class FoobarClass {\r\n" 567 " int foobar;\r\n" 568 "}", 569 Style); 570 } 571 572 TEST_F(DefinitionBlockSeparatorTest, JavaScript) { 573 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript); 574 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 575 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 576 Style.AllowShortEnumsOnASingleLine = false; 577 verifyFormat("export const enum Foo {\n" 578 " A = 1,\n" 579 " B\n" 580 "}\n" 581 "\n" 582 "export function A() {\n" 583 "}\n" 584 "\n" 585 "export default function B() {\n" 586 "}\n" 587 "\n" 588 "export function C() {\n" 589 "}\n" 590 "\n" 591 "var t, p, q;\n" 592 "\n" 593 "export abstract class X {\n" 594 " y: number;\n" 595 "}\n" 596 "\n" 597 "export const enum Bar {\n" 598 " D = 1,\n" 599 " E\n" 600 "}", 601 Style); 602 } 603 } // namespace 604 } // namespace format 605 } // namespace clang 606