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