1 //===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===// 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 #include "llvm/Support/Debug.h" 12 #include "gtest/gtest.h" 13 14 #define DEBUG_TYPE "format-test" 15 16 namespace clang { 17 namespace format { 18 19 class FormatTestCSharp : public ::testing::Test { 20 protected: 21 static std::string format(llvm::StringRef Code, unsigned Offset, 22 unsigned Length, const FormatStyle &Style) { 23 LLVM_DEBUG(llvm::errs() << "---\n"); 24 LLVM_DEBUG(llvm::errs() << Code << "\n\n"); 25 std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); 26 tooling::Replacements Replaces = reformat(Style, Code, Ranges); 27 auto Result = applyAllReplacements(Code, Replaces); 28 EXPECT_TRUE(static_cast<bool>(Result)); 29 LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); 30 return *Result; 31 } 32 33 static std::string 34 format(llvm::StringRef Code, 35 const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { 36 return format(Code, 0, Code.size(), Style); 37 } 38 39 static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { 40 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 41 Style.ColumnLimit = ColumnLimit; 42 return Style; 43 } 44 45 static void verifyFormat( 46 llvm::StringRef Code, 47 const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { 48 EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; 49 EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); 50 } 51 }; 52 53 TEST_F(FormatTestCSharp, CSharpClass) { 54 verifyFormat("public class SomeClass\n" 55 "{\n" 56 " void f()\n" 57 " {\n" 58 " }\n" 59 " int g()\n" 60 " {\n" 61 " return 0;\n" 62 " }\n" 63 " void h()\n" 64 " {\n" 65 " while (true)\n" 66 " f();\n" 67 " for (;;)\n" 68 " f();\n" 69 " if (true)\n" 70 " f();\n" 71 " }\n" 72 "}"); 73 74 // Ensure that small and empty classes are handled correctly with condensed 75 // (Google C++-like) brace-breaking style. 76 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 77 Style.BreakBeforeBraces = FormatStyle::BS_Attach; 78 79 verifyFormat("public class SomeEmptyClass {}", Style); 80 81 verifyFormat("public class SomeTinyClass {\n" 82 " int X;\n" 83 "}", 84 Style); 85 verifyFormat("private class SomeTinyClass {\n" 86 " int X;\n" 87 "}", 88 Style); 89 verifyFormat("protected class SomeTinyClass {\n" 90 " int X;\n" 91 "}", 92 Style); 93 verifyFormat("internal class SomeTinyClass {\n" 94 " int X;\n" 95 "}", 96 Style); 97 } 98 99 TEST_F(FormatTestCSharp, AccessModifiers) { 100 verifyFormat("public String toString()\n" 101 "{\n" 102 "}"); 103 verifyFormat("private String toString()\n" 104 "{\n" 105 "}"); 106 verifyFormat("protected String toString()\n" 107 "{\n" 108 "}"); 109 verifyFormat("internal String toString()\n" 110 "{\n" 111 "}"); 112 113 verifyFormat("public override String toString()\n" 114 "{\n" 115 "}"); 116 verifyFormat("private override String toString()\n" 117 "{\n" 118 "}"); 119 verifyFormat("protected override String toString()\n" 120 "{\n" 121 "}"); 122 verifyFormat("internal override String toString()\n" 123 "{\n" 124 "}"); 125 126 verifyFormat("internal static String toString()\n" 127 "{\n" 128 "}"); 129 } 130 131 TEST_F(FormatTestCSharp, NoStringLiteralBreaks) { 132 verifyFormat("foo(" 133 "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 134 "aaaaaa\");"); 135 } 136 137 TEST_F(FormatTestCSharp, CSharpVerbatiumStringLiterals) { 138 verifyFormat("foo(@\"aaaaaaaa\\abc\\aaaa\");"); 139 // @"ABC\" + ToString("B") - handle embedded \ in literal string at 140 // the end 141 // 142 /* 143 * After removal of Lexer change we are currently not able 144 * To handle these cases 145 verifyFormat("string s = @\"ABC\\\" + ToString(\"B\");"); 146 verifyFormat("string s = @\"ABC\"\"DEF\"\"GHI\""); 147 verifyFormat("string s = @\"ABC\"\"DEF\"\"\""); 148 verifyFormat("string s = @\"ABC\"\"DEF\"\"\" + abc"); 149 */ 150 } 151 152 TEST_F(FormatTestCSharp, CSharpInterpolatedStringLiterals) { 153 verifyFormat("foo($\"aaaaaaaa{aaa}aaaa\");"); 154 verifyFormat("foo($\"aaaa{A}\");"); 155 verifyFormat( 156 "foo($\"aaaa{A}" 157 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"); 158 verifyFormat("Name = $\"{firstName} {lastName}\";"); 159 160 // $"ABC\" + ToString("B") - handle embedded \ in literal string at 161 // the end 162 verifyFormat("string s = $\"A{abc}BC\" + ToString(\"B\");"); 163 verifyFormat("$\"{domain}\\\\{user}\""); 164 verifyFormat( 165 "var verbatimInterpolated = $@\"C:\\Users\\{userName}\\Documents\\\";"); 166 } 167 168 TEST_F(FormatTestCSharp, CSharpFatArrows) { 169 verifyFormat("Task serverTask = Task.Run(async() => {"); 170 verifyFormat("public override string ToString() => \"{Name}\\{Age}\";"); 171 } 172 173 TEST_F(FormatTestCSharp, CSharpConditionalExpressions) { 174 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 175 // conditional expression is not seen as a NullConditional. 176 verifyFormat("var y = A < B ? -1 : 1;", Style); 177 } 178 179 TEST_F(FormatTestCSharp, CSharpNullConditional) { 180 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 181 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 182 183 verifyFormat( 184 "public Person(string firstName, string lastName, int? age = null)"); 185 186 verifyFormat("foo () {\n" 187 " switch (args?.Length) {}\n" 188 "}", 189 Style); 190 191 verifyFormat("switch (args?.Length) {}", Style); 192 193 verifyFormat("public static void Main(string[] args)\n" 194 "{\n" 195 " string dirPath = args?[0];\n" 196 "}"); 197 198 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 199 200 verifyFormat("switch(args?.Length) {}", Style); 201 } 202 203 TEST_F(FormatTestCSharp, Attributes) { 204 verifyFormat("[STAThread]\n" 205 "static void Main(string[] args)\n" 206 "{\n" 207 "}"); 208 209 verifyFormat("[TestMethod]\n" 210 "private class Test\n" 211 "{\n" 212 "}"); 213 214 verifyFormat("[TestMethod]\n" 215 "protected class Test\n" 216 "{\n" 217 "}"); 218 219 verifyFormat("[TestMethod]\n" 220 "internal class Test\n" 221 "{\n" 222 "}"); 223 224 verifyFormat("[TestMethod]\n" 225 "class Test\n" 226 "{\n" 227 "}"); 228 229 verifyFormat("[TestMethod]\n" 230 "[DeploymentItem(\"Test.txt\")]\n" 231 "public class Test\n" 232 "{\n" 233 "}"); 234 235 verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" 236 "[System.Runtime.InteropServices.ComVisible(true)]\n" 237 "public sealed class STAThreadAttribute : Attribute\n" 238 "{\n" 239 "}"); 240 241 verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " 242 "provided port\")]\n" 243 "class Test\n" 244 "{\n" 245 "}"); 246 247 verifyFormat("[TestMethod]\n" 248 "public string Host { set; get; }"); 249 250 verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " 251 "listening on provided host\")]\n" 252 "public string Host { set; get; }"); 253 254 verifyFormat( 255 "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n" 256 "// The const char* returned by hello_world must not be deleted.\n" 257 "private static extern IntPtr HelloFromCpp();)"); 258 259 // Class attributes go on their own line and do not affect layout of 260 // interfaces. Line wrapping decisions previously caused each interface to be 261 // on its own line. 262 verifyFormat("[SomeAttribute]\n" 263 "[SomeOtherAttribute]\n" 264 "public class A : IShape, IAnimal, IVehicle\n" 265 "{\n" 266 " int X;\n" 267 "}"); 268 269 // Attributes in a method declaration do not cause line wrapping. 270 verifyFormat("void MethodA([In][Out] ref double x)\n" 271 "{\n" 272 "}"); 273 274 // [] in an attribute do not cause premature line wrapping or indenting. 275 verifyFormat(R"(// 276 public class A 277 { 278 [SomeAttribute(new[] { RED, GREEN, BLUE }, -1.0f, 1.0f)] 279 [DoNotSerialize] 280 public Data MemberVariable; 281 })"); 282 283 // Unwrappable lines go on a line of their own. 284 // 'target:' is not treated as a label. 285 // Modify Style to enforce a column limit. 286 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 287 Style.ColumnLimit = 10; 288 verifyFormat(R"([assembly:InternalsVisibleTo( 289 "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])", 290 Style); 291 } 292 293 TEST_F(FormatTestCSharp, CSharpUsing) { 294 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 295 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 296 verifyFormat("public void foo () {\n" 297 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 298 " using () {}\n" 299 "}", 300 Style); 301 302 // Ensure clang-format affects top-level snippets correctly. 303 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 304 Style); 305 306 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 307 verifyFormat("public void foo() {\n" 308 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 309 " using() {}\n" 310 "}", 311 Style); 312 313 // Ensure clang-format affects top-level snippets correctly. 314 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 315 Style); 316 317 Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 318 verifyFormat("public void foo() {\n" 319 " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" 320 " using () {}\n" 321 "}", 322 Style); 323 324 // Ensure clang-format affects top-level snippets correctly. 325 verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", 326 Style); 327 328 Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; 329 verifyFormat("public void foo() {\n" 330 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 331 " using() {}\n" 332 "}", 333 Style); 334 335 // Ensure clang-format affects top-level snippets correctly. 336 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 337 Style); 338 } 339 340 TEST_F(FormatTestCSharp, CSharpRegions) { 341 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 342 "aaaaaaaaaaaaaaa long region"); 343 } 344 345 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 346 verifyFormat("public enum var\n" 347 "{\n" 348 " none,\n" 349 " @string,\n" 350 " bool,\n" 351 " @enum\n" 352 "}"); 353 } 354 355 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 356 verifyFormat("var test = ABC ?? DEF"); 357 verifyFormat("string myname = name ?? \"ABC\";"); 358 verifyFormat("return _name ?? \"DEF\";"); 359 } 360 361 TEST_F(FormatTestCSharp, CSharpNullCoalescingAssignment) { 362 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 363 Style.SpaceBeforeAssignmentOperators = true; 364 365 verifyFormat(R"(test ??= ABC;)", Style); 366 verifyFormat(R"(test ??= true;)", Style); 367 368 Style.SpaceBeforeAssignmentOperators = false; 369 370 verifyFormat(R"(test??= ABC;)", Style); 371 verifyFormat(R"(test??= true;)", Style); 372 } 373 374 TEST_F(FormatTestCSharp, CSharpNullForgiving) { 375 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 376 377 verifyFormat("var test = null!;", Style); 378 verifyFormat("string test = someFunctionCall()! + \"ABC\"!", Style); 379 verifyFormat("int test = (1! + 2 + bar! + foo())!", Style); 380 verifyFormat(R"(test ??= !foo!;)", Style); 381 verifyFormat("test = !bar! ?? !foo!;", Style); 382 verifyFormat("bool test = !(!true && !true! || !null && !null! || !false && " 383 "!false! && !bar()! + (!foo()))!", 384 Style); 385 386 // Check that line break keeps identifier with the bang. 387 Style.ColumnLimit = 14; 388 389 verifyFormat("var test =\n" 390 " foo!;", 391 Style); 392 } 393 394 TEST_F(FormatTestCSharp, AttributesIndentation) { 395 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 396 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 397 398 verifyFormat("[STAThread]\n" 399 "static void Main(string[] args)\n" 400 "{\n" 401 "}", 402 Style); 403 404 verifyFormat("[STAThread]\n" 405 "void " 406 "veryLooooooooooooooongFunctionName(string[] args)\n" 407 "{\n" 408 "}", 409 Style); 410 411 verifyFormat("[STAThread]\n" 412 "veryLoooooooooooooooooooongReturnType " 413 "veryLooooooooooooooongFunctionName(string[] args)\n" 414 "{\n" 415 "}", 416 Style); 417 418 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 419 "public override X Y()\n" 420 "{\n" 421 "}\n", 422 Style); 423 424 verifyFormat("[SuppressMessage]\n" 425 "public X Y()\n" 426 "{\n" 427 "}\n", 428 Style); 429 430 verifyFormat("[SuppressMessage]\n" 431 "public override X Y()\n" 432 "{\n" 433 "}\n", 434 Style); 435 436 verifyFormat("public A(B b) : base(b)\n" 437 "{\n" 438 " [SuppressMessage]\n" 439 " public override X Y()\n" 440 " {\n" 441 " }\n" 442 "}\n", 443 Style); 444 445 verifyFormat("public A : Base\n" 446 "{\n" 447 "}\n" 448 "[Test]\n" 449 "public Foo()\n" 450 "{\n" 451 "}\n", 452 Style); 453 454 verifyFormat("namespace\n" 455 "{\n" 456 "public A : Base\n" 457 "{\n" 458 "}\n" 459 "[Test]\n" 460 "public Foo()\n" 461 "{\n" 462 "}\n" 463 "}\n", 464 Style); 465 } 466 467 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 468 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 469 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 470 471 verifyFormat("List<string> list;", Style); 472 verifyFormat("Dictionary<string, string> dict;", Style); 473 474 verifyFormat("for (int i = 0; i < size (); i++) {\n" 475 "}", 476 Style); 477 verifyFormat("foreach (var x in y) {\n" 478 "}", 479 Style); 480 verifyFormat("switch (x) {}", Style); 481 verifyFormat("do {\n" 482 "} while (x);", 483 Style); 484 485 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 486 487 verifyFormat("List<string> list;", Style); 488 verifyFormat("Dictionary<string, string> dict;", Style); 489 490 verifyFormat("for(int i = 0; i < size(); i++) {\n" 491 "}", 492 Style); 493 verifyFormat("foreach(var x in y) {\n" 494 "}", 495 Style); 496 verifyFormat("switch(x) {}", Style); 497 verifyFormat("do {\n" 498 "} while(x);", 499 Style); 500 } 501 502 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 503 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 504 505 verifyFormat("(int)x / y;", Style); 506 507 Style.SpaceAfterCStyleCast = true; 508 verifyFormat("(int) x / y;", Style); 509 } 510 511 TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { 512 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 513 514 verifyFormat(R"(string str = @"""";)", Style); 515 verifyFormat(R"(string str = @"""Hello world""";)", Style); 516 verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); 517 } 518 519 TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { 520 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 521 522 verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); 523 verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); 524 verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); 525 } 526 527 TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { 528 // Use MS style as Google Style inserts a line break before multiline strings. 529 530 // verifyFormat does not understand multiline C# string-literals 531 // so check the format explicitly. 532 533 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 534 535 std::string Code = R"(string s1 = $@"some code: 536 class {className} {{ 537 {className}() {{}} 538 }}";)"; 539 540 EXPECT_EQ(Code, format(Code, Style)); 541 542 // Multiline string in the middle of a function call. 543 Code = R"( 544 var x = foo(className, $@"some code: 545 class {className} {{ 546 {className}() {{}} 547 }}", 548 y);)"; // y aligned with `className` arg. 549 550 EXPECT_EQ(Code, format(Code, Style)); 551 552 // Interpolated string with embedded multiline string. 553 Code = R"(Console.WriteLine($"{string.Join(@", 554 ", values)}");)"; 555 556 EXPECT_EQ(Code, format(Code, Style)); 557 } 558 559 TEST_F(FormatTestCSharp, CSharpLambdas) { 560 FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); 561 FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 562 563 verifyFormat(R"(// 564 class MyClass { 565 Action<string> greet = name => { 566 string greeting = $"Hello {name}!"; 567 Console.WriteLine(greeting); 568 }; 569 })", 570 GoogleStyle); 571 572 // Microsoft Style: 573 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#statement-lambdas 574 verifyFormat(R"(// 575 class MyClass 576 { 577 Action<string> greet = name => 578 { 579 string greeting = $"Hello {name}!"; 580 Console.WriteLine(greeting); 581 }; 582 })", 583 MicrosoftStyle); 584 } 585 586 TEST_F(FormatTestCSharp, CSharpObjectInitializers) { 587 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 588 589 // Start code fragments with a comment line so that C++ raw string literals 590 // as seen are identical to expected formatted code. 591 592 verifyFormat(R"(// 593 Shape[] shapes = new[] { 594 new Circle { 595 Radius = 2.7281, 596 Colour = Colours.Red, 597 }, 598 new Square { 599 Side = 101.1, 600 Colour = Colours.Yellow, 601 }, 602 };)", 603 Style); 604 605 // Omitted final `,`s will change the formatting. 606 verifyFormat(R"(// 607 Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, 608 new Square { Side = 101.1, Colour = Colours.Yellow } };)", 609 Style); 610 611 // Lambdas can be supplied as initialiser arguments. 612 verifyFormat(R"(// 613 private Transformer _transformer = new X.Y { 614 Filler = (Shape shape) => { return new Transform.Fill(shape, RED); }, 615 Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); }, 616 };)", 617 Style); 618 619 // Dictionary initialisation. 620 verifyFormat(R"(// 621 var myDict = new Dictionary<string, string> { 622 ["name"] = _donald, 623 ["age"] = Convert.ToString(DateTime.Today.Year - 1934), 624 ["type"] = _duck, 625 };)", 626 Style); 627 } 628 629 TEST_F(FormatTestCSharp, CSharpArrayInitializers) { 630 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 631 632 verifyFormat(R"(// 633 private MySet<Node>[] setPoints = { 634 new Point<Node>(), 635 new Point<Node>(), 636 };)", 637 Style); 638 } 639 640 TEST_F(FormatTestCSharp, CSharpNamedArguments) { 641 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 642 643 verifyFormat(R"(// 644 PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");)", 645 Style); 646 647 // Ensure that trailing comments do not cause problems. 648 verifyFormat(R"(// 649 PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment 650 sellerName: "Gift Shop");)", 651 Style); 652 653 verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); 654 } 655 656 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { 657 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 658 659 verifyFormat("int Value { get }", Style); 660 verifyFormat("int Value { get; }", Style); 661 verifyFormat("int Value { internal get; }", Style); 662 verifyFormat("int Value { get; } = 0", Style); 663 verifyFormat("int Value { set }", Style); 664 verifyFormat("int Value { set; }", Style); 665 verifyFormat("int Value { internal set; }", Style); 666 verifyFormat("int Value { set; } = 0", Style); 667 verifyFormat("int Value { get; set }", Style); 668 verifyFormat("int Value { set; get }", Style); 669 verifyFormat("int Value { get; private set; }", Style); 670 verifyFormat("int Value { get; set; }", Style); 671 verifyFormat("int Value { get; set; } = 0", Style); 672 verifyFormat("int Value { internal get; internal set; }", Style); 673 674 // Do not wrap expression body definitions. 675 verifyFormat(R"(// 676 public string Name { 677 get => _name; 678 set => _name = value; 679 })", 680 Style); 681 682 // Examples taken from 683 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties 684 verifyFormat(R"( 685 // Expression body definitions 686 public class SaleItem { 687 public decimal Price { 688 get => _cost; 689 set => _cost = value; 690 } 691 })", 692 Style); 693 694 verifyFormat(R"( 695 // Properties with backing fields 696 class TimePeriod { 697 public double Hours { 698 get { return _seconds / 3600; } 699 set { 700 if (value < 0 || value > 24) 701 throw new ArgumentOutOfRangeException($"{nameof(value)} must be between 0 and 24."); 702 _seconds = value * 3600; 703 } 704 } 705 })", 706 Style); 707 708 verifyFormat(R"( 709 // Auto-implemented properties 710 public class SaleItem { 711 public decimal Price { get; set; } 712 })", 713 Style); 714 715 // Add column limit to wrap long lines. 716 Style.ColumnLimit = 100; 717 718 // Examples with assignment to default value. 719 verifyFormat(R"( 720 // Long assignment to default value 721 class MyClass { 722 public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } = 723 VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 724 DefaultThirdArgument); 725 })", 726 Style); 727 728 verifyFormat(R"( 729 // Long assignment to default value with expression body 730 class MyClass { 731 public override VeryLongNamedTypeIndeed VeryLongNamedValue { 732 get => veryLongNamedField; 733 set => veryLongNamedField = value; 734 } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 735 DefaultThirdArgument); 736 })", 737 Style); 738 739 // Brace wrapping and single-lining of accessor can be controlled by config. 740 Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; 741 Style.BreakBeforeBraces = FormatStyle::BS_Custom; 742 Style.BraceWrapping.AfterFunction = true; 743 744 verifyFormat(R"(// 745 class TimePeriod { 746 public double Hours 747 { 748 get { 749 return _seconds / 3600; 750 } 751 set { 752 _seconds = value * 3600; 753 } 754 } 755 })", 756 Style); 757 758 // Microsoft style trivial property accessors have no line break before the 759 // opening brace. 760 auto MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 761 verifyFormat(R"(// 762 public class SaleItem 763 { 764 public decimal Price { get; set; } 765 })", 766 MicrosoftStyle); 767 } 768 769 TEST_F(FormatTestCSharp, CSharpSpaces) { 770 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 771 Style.SpaceBeforeSquareBrackets = false; 772 Style.SpacesInSquareBrackets = false; 773 Style.SpaceBeforeCpp11BracedList = true; 774 Style.Cpp11BracedListStyle = false; 775 Style.SpacesInContainerLiterals = false; 776 Style.SpaceAfterCStyleCast = false; 777 778 verifyFormat(R"(new Car { "Door", 0.1 })", Style); 779 verifyFormat(R"(new Car { 0.1, "Door" })", Style); 780 verifyFormat(R"(new string[] { "A" })", Style); 781 verifyFormat(R"(new string[] {})", Style); 782 verifyFormat(R"(new Car { someVariableName })", Style); 783 verifyFormat(R"(new Car { someVariableName })", Style); 784 verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)", 785 Style); 786 verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); 787 verifyFormat(R"(bool[] xs = { true, true };)", Style); 788 verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); 789 verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); 790 verifyFormat(R"(private float[,] Values;)", Style); 791 verifyFormat(R"(Result this[Index x] => Foo(x);)", Style); 792 793 verifyFormat(R"(char[,,] rawCharArray = MakeCharacterGrid();)", Style); 794 verifyFormat(R"(var (key, value))", Style); 795 796 // `&&` is not seen as a reference. 797 verifyFormat(R"(A == typeof(X) && someBool)", Style); 798 799 // Not seen as a C-style cast. 800 verifyFormat(R"(// 801 foreach ((A a, B b) in someList) { 802 })", 803 Style); 804 805 // space after lock in `lock (processes)`. 806 verifyFormat("lock (process)", Style); 807 808 Style.SpacesInSquareBrackets = true; 809 verifyFormat(R"(private float[ , ] Values;)", Style); 810 verifyFormat(R"(string dirPath = args?[ 0 ];)", Style); 811 verifyFormat(R"(char[ ,, ] rawCharArray = MakeCharacterGrid();)", Style); 812 813 // Method returning tuple 814 verifyFormat(R"(public (string name, int age) methodTuple() {})", Style); 815 verifyFormat(R"(private (string name, int age) methodTuple() {})", Style); 816 verifyFormat(R"(protected (string name, int age) methodTuple() {})", Style); 817 verifyFormat(R"(virtual (string name, int age) methodTuple() {})", Style); 818 verifyFormat(R"(extern (string name, int age) methodTuple() {})", Style); 819 verifyFormat(R"(static (string name, int age) methodTuple() {})", Style); 820 verifyFormat(R"(internal (string name, int age) methodTuple() {})", Style); 821 verifyFormat(R"(abstract (string name, int age) methodTuple() {})", Style); 822 verifyFormat(R"(sealed (string name, int age) methodTuple() {})", Style); 823 verifyFormat(R"(override (string name, int age) methodTuple() {})", Style); 824 verifyFormat(R"(async (string name, int age) methodTuple() {})", Style); 825 verifyFormat(R"(unsafe (string name, int age) methodTuple() {})", Style); 826 } 827 828 TEST_F(FormatTestCSharp, CSharpNullableTypes) { 829 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 830 Style.SpacesInSquareBrackets = false; 831 832 verifyFormat(R"(// 833 public class A { 834 void foo() { 835 int? value = some.bar(); 836 } 837 })", 838 Style); // int? is nullable not a conditional expression. 839 840 verifyFormat(R"(void foo(int? x, int? y, int? z) {})", 841 Style); // Nullables in function definitions. 842 843 verifyFormat(R"(public float? Value;)", Style); // no space before `?`. 844 845 verifyFormat(R"(int?[] arr = new int?[10];)", 846 Style); // An array of a nullable type. 847 848 verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. 849 850 verifyFormat(R"(var x = new MyContainer<int?>();)", Style); // Generics. 851 } 852 853 TEST_F(FormatTestCSharp, CSharpArraySubscripts) { 854 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 855 856 // Do not format array subscript operators as attributes. 857 verifyFormat(R"(// 858 if (someThings[index].Contains(myThing)) { 859 })", 860 Style); 861 862 verifyFormat(R"(// 863 if (someThings[i][j][k].Contains(myThing)) { 864 })", 865 Style); 866 } 867 868 TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { 869 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 870 871 EXPECT_TRUE(Style.BraceWrapping.SplitEmptyRecord); 872 873 verifyFormat("class ItemFactory<T>\n" 874 " where T : new() {\n" 875 "}", 876 Style); 877 878 verifyFormat("class Dictionary<TKey, TVal>\n" 879 " where TKey : IComparable<TKey>\n" 880 " where TVal : IMyInterface {\n" 881 " public void MyMethod<T>(T t)\n" 882 " where T : IMyInterface {\n" 883 " doThing();\n" 884 " }\n" 885 "}", 886 Style); 887 888 verifyFormat("class ItemFactory<T>\n" 889 " where T : new(), IAnInterface<T>, IAnotherInterface<T>, " 890 "IAnotherInterfaceStill<T> {\n" 891 "}", 892 Style); 893 894 Style.ColumnLimit = 50; // Force lines to be wrapped. 895 verifyFormat(R"(// 896 class ItemFactory<T, U> 897 where T : new(), 898 IAnInterface<T>, 899 IAnotherInterface<T, U>, 900 IAnotherInterfaceStill<T, U> { 901 })", 902 Style); 903 904 // In other languages `where` can be used as a normal identifier. 905 // This example is in C++! 906 verifyFormat(R"(// 907 class A { 908 int f(int where) {} 909 };)", 910 getGoogleStyle(FormatStyle::LK_Cpp)); 911 } 912 913 } // namespace format 914 } // end namespace clang 915