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 } 83 Result.push_back(Char); 84 } 85 return Result; 86 } 87 }; 88 89 #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__) 90 91 TEST_F(DefinitionBlockSeparatorTest, Basic) { 92 FormatStyle Style = getLLVMStyle(); 93 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 94 verifyFormat("int foo(int i, int j) {\n" 95 " int r = i + j;\n" 96 " return r;\n" 97 "}\n" 98 "\n" 99 "int bar(int j, int k) {\n" 100 " int r = j + k;\n" 101 " return r;\n" 102 "}", 103 Style); 104 105 verifyFormat("struct foo {\n" 106 " int i, j;\n" 107 "};\n" 108 "\n" 109 "struct bar {\n" 110 " int j, k;\n" 111 "};", 112 Style); 113 114 verifyFormat("union foo {\n" 115 " int i, j;\n" 116 "};\n" 117 "\n" 118 "union bar {\n" 119 " int j, k;\n" 120 "};", 121 Style); 122 123 verifyFormat("class foo {\n" 124 " int i, j;\n" 125 "};\n" 126 "\n" 127 "class bar {\n" 128 " int j, k;\n" 129 "};", 130 Style); 131 132 verifyFormat("namespace foo {\n" 133 "int i, j;\n" 134 "}\n" 135 "\n" 136 "namespace bar {\n" 137 "int j, k;\n" 138 "}", 139 Style); 140 141 verifyFormat("enum Foo { FOO, BAR };\n" 142 "\n" 143 "enum Bar { FOOBAR, BARFOO };\n", 144 Style); 145 146 FormatStyle BreakAfterReturnTypeStyle = Style; 147 BreakAfterReturnTypeStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_All; 148 // Test uppercased long typename 149 verifyFormat("class Foo {\n" 150 " void\n" 151 " Bar(int t, int p) {\n" 152 " int r = t + p;\n" 153 " return r;\n" 154 " }\n" 155 "\n" 156 " HRESULT\n" 157 " Foobar(int t, int p) {\n" 158 " int r = t * p;\n" 159 " return r;\n" 160 " }\n" 161 "}\n", 162 BreakAfterReturnTypeStyle); 163 } 164 165 TEST_F(DefinitionBlockSeparatorTest, FormatConflict) { 166 FormatStyle Style = getLLVMStyle(); 167 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 168 llvm::StringRef Code = "class Test {\n" 169 "public:\n" 170 " static void foo() {\n" 171 " int t;\n" 172 " return 1;\n" 173 " }\n" 174 "};"; 175 std::vector<tooling::Range> Ranges = {1, tooling::Range(0, Code.size())}; 176 EXPECT_EQ(reformat(Style, Code, Ranges, "<stdin>").size(), 0u); 177 } 178 179 TEST_F(DefinitionBlockSeparatorTest, CommentBlock) { 180 FormatStyle Style = getLLVMStyle(); 181 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 182 std::string Prefix = "enum Foo { FOO, BAR };\n" 183 "\n" 184 "/*\n" 185 "test1\n" 186 "test2\n" 187 "*/\n" 188 "int foo(int i, int j) {\n" 189 " int r = i + j;\n" 190 " return r;\n" 191 "}\n"; 192 std::string Suffix = "enum Bar { FOOBAR, BARFOO };\n" 193 "\n" 194 "/* Comment block in one line*/\n" 195 "int bar3(int j, int k) {\n" 196 " // A comment\n" 197 " int r = j % k;\n" 198 " return r;\n" 199 "}\n"; 200 std::string CommentedCode = "/*\n" 201 "int bar2(int j, int k) {\n" 202 " int r = j / k;\n" 203 " return r;\n" 204 "}\n" 205 "*/\n"; 206 verifyFormat(removeEmptyLines(Prefix) + "\n" + CommentedCode + "\n" + 207 removeEmptyLines(Suffix), 208 Style, Prefix + "\n" + CommentedCode + "\n" + Suffix); 209 verifyFormat(removeEmptyLines(Prefix) + "\n" + CommentedCode + 210 removeEmptyLines(Suffix), 211 Style, Prefix + "\n" + CommentedCode + Suffix); 212 } 213 214 TEST_F(DefinitionBlockSeparatorTest, UntouchBlockStartStyle) { 215 // Returns a std::pair of two strings, with the first one for passing into 216 // Always test and the second one be the expected result of the first string. 217 auto MakeUntouchTest = [&](std::string BlockHeader, std::string BlockChanger, 218 std::string BlockFooter, bool BlockEndNewLine) { 219 std::string CodePart1 = "enum Foo { FOO, BAR };\n" 220 "\n" 221 "/*\n" 222 "test1\n" 223 "test2\n" 224 "*/\n" 225 "int foo(int i, int j) {\n" 226 " int r = i + j;\n" 227 " return r;\n" 228 "}\n"; 229 std::string CodePart2 = "/* Comment block in one line*/\n" 230 "enum Bar { FOOBAR, BARFOO };\n" 231 "\n" 232 "int bar3(int j, int k) {\n" 233 " // A comment\n" 234 " int r = j % k;\n" 235 " return r;\n" 236 "}\n"; 237 std::string CodePart3 = "int bar2(int j, int k) {\n" 238 " int r = j / k;\n" 239 " return r;\n" 240 "}\n"; 241 std::string ConcatAll = BlockHeader + CodePart1 + BlockChanger + CodePart2 + 242 BlockFooter + (BlockEndNewLine ? "\n" : "") + 243 CodePart3; 244 return std::make_pair(BlockHeader + removeEmptyLines(CodePart1) + 245 BlockChanger + removeEmptyLines(CodePart2) + 246 BlockFooter + removeEmptyLines(CodePart3), 247 ConcatAll); 248 }; 249 250 FormatStyle AlwaysStyle = getLLVMStyle(); 251 AlwaysStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 252 253 FormatStyle NeverStyle = getLLVMStyle(); 254 NeverStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 255 256 auto TestKit = MakeUntouchTest("/* FOOBAR */\n" 257 "#ifdef FOO\n\n", 258 "\n#elifndef BAR\n\n", "\n#endif\n\n", false); 259 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 260 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 261 262 TestKit = MakeUntouchTest("/* FOOBAR */\n" 263 "#ifdef FOO\n", 264 "#elifndef BAR\n", "#endif\n", false); 265 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 266 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 267 268 TestKit = MakeUntouchTest("namespace Ns {\n\n", 269 "\n} // namespace Ns\n\n" 270 "namespace {\n\n", 271 "\n} // namespace\n", true); 272 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 273 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 274 275 TestKit = MakeUntouchTest("namespace Ns {\n", 276 "} // namespace Ns\n\n" 277 "namespace {\n", 278 "} // namespace\n", true); 279 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 280 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 281 } 282 283 TEST_F(DefinitionBlockSeparatorTest, Always) { 284 FormatStyle Style = getLLVMStyle(); 285 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 286 std::string Prefix = "namespace {\n"; 287 std::string Infix = "\n" 288 "// Enum test1\n" 289 "// Enum test2\n" 290 "enum Foo { FOO, BAR };\n" 291 "\n" 292 "/*\n" 293 "test1\n" 294 "test2\n" 295 "*/\n" 296 "/*const*/ int foo(int i, int j) {\n" 297 " int r = i + j;\n" 298 " return r;\n" 299 "}\n" 300 "\n" 301 "// Foobar\n" 302 "int i, j, k;\n" 303 "\n" 304 "// Comment for function\n" 305 "// Comment line 2\n" 306 "// Comment line 3\n" 307 "int bar(int j, int k) {\n" 308 " {\n" 309 " int r = j * k;\n" 310 " return r;\n" 311 " }\n" 312 "}\n" 313 "\n" 314 "int bar2(int j, int k) {\n" 315 " int r = j / k;\n" 316 " return r;\n" 317 "}\n" 318 "\n" 319 "/* Comment block in one line*/\n" 320 "enum Bar { FOOBAR, BARFOO };\n" 321 "\n" 322 "int bar3(int j, int k, const enum Bar b) {\n" 323 " // A comment\n" 324 " int r = j % k;\n" 325 " if (struct S = getS()) {\n" 326 " // if condition\n" 327 " }\n" 328 " return r;\n" 329 "}\n"; 330 std::string Postfix = "\n" 331 "} // namespace\n" 332 "\n" 333 "namespace T {\n" 334 "int i, j, k;\n" 335 "} // namespace T"; 336 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix), 337 Style, Prefix + Infix + Postfix); 338 } 339 340 TEST_F(DefinitionBlockSeparatorTest, Never) { 341 FormatStyle Style = getLLVMStyle(); 342 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 343 std::string Prefix = "namespace {\n"; 344 std::string Postfix = "// Enum test1\n" 345 "// Enum test2\n" 346 "enum Foo { FOO, BAR };\n" 347 "\n" 348 "/*\n" 349 "test1\n" 350 "test2\n" 351 "*/\n" 352 "/*const*/ int foo(int i, int j) {\n" 353 " int r = i + j;\n" 354 " return r;\n" 355 "}\n" 356 "\n" 357 "// Foobar\n" 358 "int i, j, k;\n" 359 "\n" 360 "// Comment for function\n" 361 "// Comment line 2\n" 362 "// Comment line 3\n" 363 "int bar(int j, int k) {\n" 364 " {\n" 365 " int r = j * k;\n" 366 " return r;\n" 367 " }\n" 368 "}\n" 369 "\n" 370 "int bar2(int j, int k) {\n" 371 " int r = j / k;\n" 372 " return r;\n" 373 "}\n" 374 "\n" 375 "/* Comment block in one line*/\n" 376 "enum Bar { FOOBAR, BARFOO };\n" 377 "\n" 378 "int bar3(int j, int k, const enum Bar b) {\n" 379 " // A comment\n" 380 " int r = j % k;\n" 381 " if (struct S = getS()) {\n" 382 " // if condition\n" 383 " }\n" 384 " return r;\n" 385 "}\n" 386 "} // namespace"; 387 verifyFormat(Prefix + "\n\n\n" + Postfix, Style, 388 Prefix + removeEmptyLines(Postfix)); 389 } 390 391 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) { 392 FormatStyle Style = getLLVMStyle(); 393 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 394 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 395 verifyFormat("namespace NS\n" 396 "{\n" 397 "// Enum test1\n" 398 "// Enum test2\n" 399 "enum Foo\n" 400 "{\n" 401 " FOO,\n" 402 " BAR\n" 403 "};\n" 404 "\n" 405 "/*\n" 406 "test1\n" 407 "test2\n" 408 "*/\n" 409 "/*const*/ int foo(int i, int j)\n" 410 "{\n" 411 " int r = i + j;\n" 412 " return r;\n" 413 "}\n" 414 "\n" 415 "// Foobar\n" 416 "int i, j, k;\n" 417 "\n" 418 "// Comment for function\n" 419 "// Comment line 2\n" 420 "// Comment line 3\n" 421 "int bar(int j, int k)\n" 422 "{\n" 423 " {\n" 424 " int r = j * k;\n" 425 " return r;\n" 426 " }\n" 427 "}\n" 428 "\n" 429 "int bar2(int j, int k)\n" 430 "{\n" 431 " int r = j / k;\n" 432 " return r;\n" 433 "}\n" 434 "\n" 435 "enum Bar\n" 436 "{\n" 437 " FOOBAR,\n" 438 " BARFOO\n" 439 "};\n" 440 "\n" 441 "int bar3(int j, int k, const enum Bar b)\n" 442 "{\n" 443 " // A comment\n" 444 " int r = j % k;\n" 445 " if (struct S = getS())\n" 446 " {\n" 447 " // if condition\n" 448 " }\n" 449 " return r;\n" 450 "}\n" 451 "} // namespace NS", 452 Style); 453 } 454 455 TEST_F(DefinitionBlockSeparatorTest, TryBlocks) { 456 FormatStyle Style = getLLVMStyle(); 457 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 458 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 459 verifyFormat("void FunctionWithInternalTry()\n" 460 "{\n" 461 " try\n" 462 " {\n" 463 " return;\n" 464 " }\n" 465 " catch (const std::exception &)\n" 466 " {\n" 467 " }\n" 468 "}", 469 Style, "", /*Inverse=*/false); 470 verifyFormat("void FunctionWithTryBlock()\n" 471 "try\n" 472 "{\n" 473 " return;\n" 474 "}\n" 475 "catch (const std::exception &)\n" 476 "{\n" 477 "}", 478 Style, "", /*Inverse=*/false); 479 } 480 481 TEST_F(DefinitionBlockSeparatorTest, Leave) { 482 FormatStyle Style = getLLVMStyle(); 483 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave; 484 Style.MaxEmptyLinesToKeep = 3; 485 std::string LeaveAs = "namespace {\n" 486 "\n" 487 "// Enum test1\n" 488 "// Enum test2\n" 489 "enum Foo { FOO, BAR };\n" 490 "\n\n\n" 491 "/*\n" 492 "test1\n" 493 "test2\n" 494 "*/\n" 495 "/*const*/ int foo(int i, int j) {\n" 496 " int r = i + j;\n" 497 " return r;\n" 498 "}\n" 499 "\n" 500 "// Foobar\n" 501 "int i, j, k;\n" 502 "\n" 503 "// Comment for function\n" 504 "// Comment line 2\n" 505 "// Comment line 3\n" 506 "int bar(int j, int k) {\n" 507 " {\n" 508 " int r = j * k;\n" 509 " return r;\n" 510 " }\n" 511 "}\n" 512 "\n" 513 "int bar2(int j, int k) {\n" 514 " int r = j / k;\n" 515 " return r;\n" 516 "}\n" 517 "\n" 518 "// Comment for inline enum\n" 519 "enum Bar { FOOBAR, BARFOO };\n" 520 "int bar3(int j, int k, const enum Bar b) {\n" 521 " // A comment\n" 522 " int r = j % k;\n" 523 " if (struct S = getS()) {\n" 524 " // if condition\n" 525 " }\n" 526 " return r;\n" 527 "}\n" 528 "} // namespace"; 529 verifyFormat(LeaveAs, Style, LeaveAs); 530 } 531 532 TEST_F(DefinitionBlockSeparatorTest, CSharp) { 533 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); 534 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 535 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 536 Style.AllowShortEnumsOnASingleLine = false; 537 verifyFormat("namespace {\r\n" 538 "public class SomeTinyClass {\r\n" 539 " int X;\r\n" 540 "}\r\n" 541 "\r\n" 542 "public class AnotherTinyClass {\r\n" 543 " int Y;\r\n" 544 "}\r\n" 545 "\r\n" 546 "internal static String toString() {\r\n" 547 "}\r\n" 548 "\r\n" 549 "// Comment for enum\r\n" 550 "public enum var {\r\n" 551 " none,\r\n" 552 " @string,\r\n" 553 " bool,\r\n" 554 " @enum\r\n" 555 "}\r\n" 556 "\r\n" 557 "// Test\r\n" 558 "[STAThread]\r\n" 559 "static void Main(string[] args) {\r\n" 560 " Console.WriteLine(\"HelloWorld\");\r\n" 561 "}\r\n" 562 "\r\n" 563 "static decimal Test() {\r\n" 564 "}\r\n" 565 "}\r\n" 566 "\r\n" 567 "public class FoobarClass {\r\n" 568 " int foobar;\r\n" 569 "}", 570 Style); 571 } 572 573 TEST_F(DefinitionBlockSeparatorTest, JavaScript) { 574 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript); 575 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 576 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 577 Style.AllowShortEnumsOnASingleLine = false; 578 verifyFormat("export const enum Foo {\n" 579 " A = 1,\n" 580 " B\n" 581 "}\n" 582 "\n" 583 "export function A() {\n" 584 "}\n" 585 "\n" 586 "export default function B() {\n" 587 "}\n" 588 "\n" 589 "export function C() {\n" 590 "}\n" 591 "\n" 592 "var t, p, q;\n" 593 "\n" 594 "export abstract class X {\n" 595 " y: number;\n" 596 "}\n" 597 "\n" 598 "export const enum Bar {\n" 599 " D = 1,\n" 600 " E\n" 601 "}", 602 Style); 603 } 604 } // namespace 605 } // namespace format 606 } // namespace clang 607