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