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