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, AttributesIndentation) { 362 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 363 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 364 365 verifyFormat("[STAThread]\n" 366 "static void Main(string[] args)\n" 367 "{\n" 368 "}", 369 Style); 370 371 verifyFormat("[STAThread]\n" 372 "void " 373 "veryLooooooooooooooongFunctionName(string[] args)\n" 374 "{\n" 375 "}", 376 Style); 377 378 verifyFormat("[STAThread]\n" 379 "veryLoooooooooooooooooooongReturnType " 380 "veryLooooooooooooooongFunctionName(string[] args)\n" 381 "{\n" 382 "}", 383 Style); 384 385 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 386 "public override X Y()\n" 387 "{\n" 388 "}\n", 389 Style); 390 391 verifyFormat("[SuppressMessage]\n" 392 "public X Y()\n" 393 "{\n" 394 "}\n", 395 Style); 396 397 verifyFormat("[SuppressMessage]\n" 398 "public override X Y()\n" 399 "{\n" 400 "}\n", 401 Style); 402 403 verifyFormat("public A(B b) : base(b)\n" 404 "{\n" 405 " [SuppressMessage]\n" 406 " public override X Y()\n" 407 " {\n" 408 " }\n" 409 "}\n", 410 Style); 411 412 verifyFormat("public A : Base\n" 413 "{\n" 414 "}\n" 415 "[Test]\n" 416 "public Foo()\n" 417 "{\n" 418 "}\n", 419 Style); 420 421 verifyFormat("namespace\n" 422 "{\n" 423 "public A : Base\n" 424 "{\n" 425 "}\n" 426 "[Test]\n" 427 "public Foo()\n" 428 "{\n" 429 "}\n" 430 "}\n", 431 Style); 432 } 433 434 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 435 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 436 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 437 438 verifyFormat("List<string> list;", Style); 439 verifyFormat("Dictionary<string, string> dict;", Style); 440 441 verifyFormat("for (int i = 0; i < size (); i++) {\n" 442 "}", 443 Style); 444 verifyFormat("foreach (var x in y) {\n" 445 "}", 446 Style); 447 verifyFormat("switch (x) {}", Style); 448 verifyFormat("do {\n" 449 "} while (x);", 450 Style); 451 452 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 453 454 verifyFormat("List<string> list;", Style); 455 verifyFormat("Dictionary<string, string> dict;", Style); 456 457 verifyFormat("for(int i = 0; i < size(); i++) {\n" 458 "}", 459 Style); 460 verifyFormat("foreach(var x in y) {\n" 461 "}", 462 Style); 463 verifyFormat("switch(x) {}", Style); 464 verifyFormat("do {\n" 465 "} while(x);", 466 Style); 467 } 468 469 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 470 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 471 472 verifyFormat("(int)x / y;", Style); 473 474 Style.SpaceAfterCStyleCast = true; 475 verifyFormat("(int) x / y;", Style); 476 } 477 478 TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { 479 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 480 481 verifyFormat(R"(string str = @"""";)", Style); 482 verifyFormat(R"(string str = @"""Hello world""";)", Style); 483 verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); 484 } 485 486 TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { 487 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 488 489 verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); 490 verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); 491 verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); 492 } 493 494 TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { 495 // Use MS style as Google Style inserts a line break before multiline strings. 496 497 // verifyFormat does not understand multiline C# string-literals 498 // so check the format explicitly. 499 500 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 501 502 std::string Code = R"(string s1 = $@"some code: 503 class {className} {{ 504 {className}() {{}} 505 }}";)"; 506 507 EXPECT_EQ(Code, format(Code, Style)); 508 509 // Multiline string in the middle of a function call. 510 Code = R"( 511 var x = foo(className, $@"some code: 512 class {className} {{ 513 {className}() {{}} 514 }}", 515 y);)"; // y aligned with `className` arg. 516 517 EXPECT_EQ(Code, format(Code, Style)); 518 519 // Interpolated string with embedded multiline string. 520 Code = R"(Console.WriteLine($"{string.Join(@", 521 ", values)}");)"; 522 523 EXPECT_EQ(Code, format(Code, Style)); 524 } 525 526 TEST_F(FormatTestCSharp, CSharpLambdas) { 527 FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); 528 FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 529 530 verifyFormat(R"(// 531 class MyClass { 532 Action<string> greet = name => { 533 string greeting = $"Hello {name}!"; 534 Console.WriteLine(greeting); 535 }; 536 })", 537 GoogleStyle); 538 539 // Microsoft Style: 540 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#statement-lambdas 541 verifyFormat(R"(// 542 class MyClass 543 { 544 Action<string> greet = name => 545 { 546 string greeting = $"Hello {name}!"; 547 Console.WriteLine(greeting); 548 }; 549 })", 550 MicrosoftStyle); 551 } 552 553 TEST_F(FormatTestCSharp, CSharpObjectInitializers) { 554 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 555 556 // Start code fragments with a comment line so that C++ raw string literals 557 // as seen are identical to expected formatted code. 558 559 verifyFormat(R"(// 560 Shape[] shapes = new[] { 561 new Circle { 562 Radius = 2.7281, 563 Colour = Colours.Red, 564 }, 565 new Square { 566 Side = 101.1, 567 Colour = Colours.Yellow, 568 }, 569 };)", 570 Style); 571 572 // Omitted final `,`s will change the formatting. 573 verifyFormat(R"(// 574 Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, 575 new Square { Side = 101.1, Colour = Colours.Yellow } };)", 576 Style); 577 578 // Lambdas can be supplied as initialiser arguments. 579 verifyFormat(R"(// 580 private Transformer _transformer = new X.Y { 581 Filler = (Shape shape) => { return new Transform.Fill(shape, RED); }, 582 Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); }, 583 };)", 584 Style); 585 586 // Dictionary initialisation. 587 verifyFormat(R"(// 588 var myDict = new Dictionary<string, string> { 589 ["name"] = _donald, 590 ["age"] = Convert.ToString(DateTime.Today.Year - 1934), 591 ["type"] = _duck, 592 };)", 593 Style); 594 } 595 596 TEST_F(FormatTestCSharp, CSharpArrayInitializers) { 597 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 598 599 verifyFormat(R"(// 600 private MySet<Node>[] setPoints = { 601 new Point<Node>(), 602 new Point<Node>(), 603 };)", 604 Style); 605 } 606 607 TEST_F(FormatTestCSharp, CSharpNamedArguments) { 608 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 609 610 verifyFormat(R"(// 611 PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");)", 612 Style); 613 614 // Ensure that trailing comments do not cause problems. 615 verifyFormat(R"(// 616 PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment 617 sellerName: "Gift Shop");)", 618 Style); 619 620 verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); 621 } 622 623 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { 624 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 625 626 verifyFormat("int Value { get }", Style); 627 verifyFormat("int Value { get; }", Style); 628 verifyFormat("int Value { internal get; }", Style); 629 verifyFormat("int Value { get; } = 0", Style); 630 verifyFormat("int Value { set }", Style); 631 verifyFormat("int Value { set; }", Style); 632 verifyFormat("int Value { internal set; }", Style); 633 verifyFormat("int Value { set; } = 0", Style); 634 verifyFormat("int Value { get; set }", Style); 635 verifyFormat("int Value { set; get }", Style); 636 verifyFormat("int Value { get; private set; }", Style); 637 verifyFormat("int Value { get; set; }", Style); 638 verifyFormat("int Value { get; set; } = 0", Style); 639 verifyFormat("int Value { internal get; internal set; }", Style); 640 641 // Do not wrap expression body definitions. 642 verifyFormat(R"(// 643 public string Name { 644 get => _name; 645 set => _name = value; 646 })", 647 Style); 648 649 // Examples taken from 650 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties 651 verifyFormat(R"( 652 // Expression body definitions 653 public class SaleItem { 654 public decimal Price { 655 get => _cost; 656 set => _cost = value; 657 } 658 })", 659 Style); 660 661 verifyFormat(R"( 662 // Properties with backing fields 663 class TimePeriod { 664 public double Hours { 665 get { return _seconds / 3600; } 666 set { 667 if (value < 0 || value > 24) 668 throw new ArgumentOutOfRangeException($"{nameof(value)} must be between 0 and 24."); 669 _seconds = value * 3600; 670 } 671 } 672 })", 673 Style); 674 675 verifyFormat(R"( 676 // Auto-implemented properties 677 public class SaleItem { 678 public decimal Price { get; set; } 679 })", 680 Style); 681 682 // Add column limit to wrap long lines. 683 Style.ColumnLimit = 100; 684 685 // Examples with assignment to default value. 686 verifyFormat(R"( 687 // Long assignment to default value 688 class MyClass { 689 public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } = 690 VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 691 DefaultThirdArgument); 692 })", 693 Style); 694 695 verifyFormat(R"( 696 // Long assignment to default value with expression body 697 class MyClass { 698 public override VeryLongNamedTypeIndeed VeryLongNamedValue { 699 get => veryLongNamedField; 700 set => veryLongNamedField = value; 701 } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 702 DefaultThirdArgument); 703 })", 704 Style); 705 706 // Brace wrapping and single-lining of accessor can be controlled by config. 707 Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; 708 Style.BreakBeforeBraces = FormatStyle::BS_Custom; 709 Style.BraceWrapping.AfterFunction = true; 710 711 verifyFormat(R"(// 712 class TimePeriod { 713 public double Hours 714 { 715 get { 716 return _seconds / 3600; 717 } 718 set { 719 _seconds = value * 3600; 720 } 721 } 722 })", 723 Style); 724 725 // Microsoft style trivial property accessors have no line break before the 726 // opening brace. 727 auto MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 728 verifyFormat(R"(// 729 public class SaleItem 730 { 731 public decimal Price { get; set; } 732 })", 733 MicrosoftStyle); 734 735 } 736 737 TEST_F(FormatTestCSharp, CSharpSpaces) { 738 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 739 Style.SpaceBeforeSquareBrackets = false; 740 Style.SpacesInSquareBrackets = false; 741 Style.SpaceBeforeCpp11BracedList = true; 742 Style.Cpp11BracedListStyle = false; 743 Style.SpacesInContainerLiterals = false; 744 Style.SpaceAfterCStyleCast = false; 745 746 verifyFormat(R"(new Car { "Door", 0.1 })", Style); 747 verifyFormat(R"(new Car { 0.1, "Door" })", Style); 748 verifyFormat(R"(new string[] { "A" })", Style); 749 verifyFormat(R"(new string[] {})", Style); 750 verifyFormat(R"(new Car { someVariableName })", Style); 751 verifyFormat(R"(new Car { someVariableName })", Style); 752 verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)", 753 Style); 754 verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); 755 verifyFormat(R"(bool[] xs = { true, true };)", Style); 756 verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); 757 verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); 758 verifyFormat(R"(private float[,] Values;)", Style); 759 verifyFormat(R"(Result this[Index x] => Foo(x);)", Style); 760 761 verifyFormat(R"(char[,,] rawCharArray = MakeCharacterGrid();)", Style); 762 verifyFormat(R"(var (key, value))", Style); 763 764 // `&&` is not seen as a reference. 765 verifyFormat(R"(A == typeof(X) && someBool)", Style); 766 767 // Not seen as a C-style cast. 768 verifyFormat(R"(// 769 foreach ((A a, B b) in someList) { 770 })", 771 Style); 772 773 // space after lock in `lock (processes)`. 774 verifyFormat("lock (process)", Style); 775 776 Style.SpacesInSquareBrackets = true; 777 verifyFormat(R"(private float[ , ] Values;)", Style); 778 verifyFormat(R"(string dirPath = args?[ 0 ];)", Style); 779 verifyFormat(R"(char[ ,, ] rawCharArray = MakeCharacterGrid();)", Style); 780 } 781 782 TEST_F(FormatTestCSharp, CSharpNullableTypes) { 783 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 784 Style.SpacesInSquareBrackets = false; 785 786 verifyFormat(R"(// 787 public class A { 788 void foo() { 789 int? value = some.bar(); 790 } 791 })", 792 Style); // int? is nullable not a conditional expression. 793 794 verifyFormat(R"(void foo(int? x, int? y, int? z) {})", 795 Style); // Nullables in function definitions. 796 797 verifyFormat(R"(public float? Value;)", Style); // no space before `?`. 798 799 verifyFormat(R"(int?[] arr = new int?[10];)", 800 Style); // An array of a nullable type. 801 802 verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. 803 804 verifyFormat(R"(var x = new MyContainer<int?>();)", Style); // Generics. 805 } 806 807 TEST_F(FormatTestCSharp, CSharpArraySubscripts) { 808 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 809 810 // Do not format array subscript operators as attributes. 811 verifyFormat(R"(// 812 if (someThings[index].Contains(myThing)) { 813 })", 814 Style); 815 816 verifyFormat(R"(// 817 if (someThings[i][j][k].Contains(myThing)) { 818 })", 819 Style); 820 } 821 822 TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { 823 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 824 825 verifyFormat(R"(// 826 class ItemFactory<T> 827 where T : new() {})", 828 Style); 829 830 verifyFormat(R"(// 831 class Dictionary<TKey, TVal> 832 where TKey : IComparable<TKey> 833 where TVal : IMyInterface { 834 public void MyMethod<T>(T t) 835 where T : IMyInterface { 836 doThing(); 837 } 838 })", 839 Style); 840 841 verifyFormat(R"(// 842 class ItemFactory<T> 843 where T : new(), IAnInterface<T>, IAnotherInterface<T>, IAnotherInterfaceStill<T> {})", 844 Style); 845 846 Style.ColumnLimit = 50; // Force lines to be wrapped. 847 verifyFormat(R"(// 848 class ItemFactory<T, U> 849 where T : new(), 850 IAnInterface<T>, 851 IAnotherInterface<T, U>, 852 IAnotherInterfaceStill<T, U> {})", 853 Style); 854 855 // In other languages `where` can be used as a normal identifier. 856 // This example is in C++! 857 verifyFormat(R"(// 858 class A { 859 int f(int where) {} 860 };)", 861 getGoogleStyle(FormatStyle::LK_Cpp)); 862 } 863 864 } // namespace format 865 } // end namespace clang 866