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 // Adjacent properties should not cause line wrapping issues 251 verifyFormat("[JsonProperty(\"foo\")]\n" 252 "public string Foo { set; get; }\n" 253 "[JsonProperty(\"bar\")]\n" 254 "public string Bar { set; get; }\n" 255 "[JsonProperty(\"bar\")]\n" 256 "protected string Bar { set; get; }\n" 257 "[JsonProperty(\"bar\")]\n" 258 "internal string Bar { set; get; }"); 259 260 // Multiple attributes should always be split (not just the first ones) 261 verifyFormat("[XmlIgnore]\n" 262 "[JsonProperty(\"foo\")]\n" 263 "public string Foo { set; get; }"); 264 265 verifyFormat("[XmlIgnore]\n" 266 "[JsonProperty(\"foo\")]\n" 267 "public string Foo { set; get; }\n" 268 "[XmlIgnore]\n" 269 "[JsonProperty(\"bar\")]\n" 270 "public string Bar { set; get; }"); 271 272 verifyFormat("[XmlIgnore]\n" 273 "[ScriptIgnore]\n" 274 "[JsonProperty(\"foo\")]\n" 275 "public string Foo { set; get; }\n" 276 "[XmlIgnore]\n" 277 "[ScriptIgnore]\n" 278 "[JsonProperty(\"bar\")]\n" 279 "public string Bar { set; get; }"); 280 281 verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " 282 "listening on provided host\")]\n" 283 "public string Host { set; get; }"); 284 285 verifyFormat( 286 "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n" 287 "// The const char* returned by hello_world must not be deleted.\n" 288 "private static extern IntPtr HelloFromCpp();)"); 289 290 // Class attributes go on their own line and do not affect layout of 291 // interfaces. Line wrapping decisions previously caused each interface to be 292 // on its own line. 293 verifyFormat("[SomeAttribute]\n" 294 "[SomeOtherAttribute]\n" 295 "public class A : IShape, IAnimal, IVehicle\n" 296 "{\n" 297 " int X;\n" 298 "}"); 299 300 // Attributes in a method declaration do not cause line wrapping. 301 verifyFormat("void MethodA([In][Out] ref double x)\n" 302 "{\n" 303 "}"); 304 305 verifyFormat("void MethodA([In, Out] ref double x)\n" 306 "{\n" 307 "}"); 308 309 verifyFormat("void MethodA([In, Out] double[] x)\n" 310 "{\n" 311 "}"); 312 313 verifyFormat("void MethodA([In] double[] x)\n" 314 "{\n" 315 "}"); 316 317 verifyFormat("void MethodA(int[] x)\n" 318 "{\n" 319 "}"); 320 verifyFormat("void MethodA(int[][] x)\n" 321 "{\n" 322 "}"); 323 verifyFormat("void MethodA([] x)\n" 324 "{\n" 325 "}"); 326 327 verifyFormat("public void Log([CallerLineNumber] int line = -1, " 328 "[CallerFilePath] string path = null,\n" 329 " [CallerMemberName] string name = null)\n" 330 "{\n" 331 "}"); 332 333 // [] in an attribute do not cause premature line wrapping or indenting. 334 verifyFormat(R"(// 335 public class A 336 { 337 [SomeAttribute(new[] { RED, GREEN, BLUE }, -1.0f, 1.0f)] 338 [DoNotSerialize] 339 public Data MemberVariable; 340 })"); 341 342 // Unwrappable lines go on a line of their own. 343 // 'target:' is not treated as a label. 344 // Modify Style to enforce a column limit. 345 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 346 Style.ColumnLimit = 10; 347 verifyFormat(R"([assembly:InternalsVisibleTo( 348 "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])", 349 Style); 350 } 351 352 TEST_F(FormatTestCSharp, CSharpUsing) { 353 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 354 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 355 verifyFormat("public void foo () {\n" 356 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 357 " using () {}\n" 358 "}", 359 Style); 360 361 // Ensure clang-format affects top-level snippets correctly. 362 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 363 Style); 364 365 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 366 verifyFormat("public void foo() {\n" 367 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 368 " using() {}\n" 369 "}", 370 Style); 371 372 // Ensure clang-format affects top-level snippets correctly. 373 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 374 Style); 375 376 Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 377 verifyFormat("public void foo() {\n" 378 " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" 379 " using () {}\n" 380 "}", 381 Style); 382 383 // Ensure clang-format affects top-level snippets correctly. 384 verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", 385 Style); 386 387 Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; 388 verifyFormat("public void foo() {\n" 389 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 390 " using() {}\n" 391 "}", 392 Style); 393 394 // Ensure clang-format affects top-level snippets correctly. 395 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 396 Style); 397 } 398 399 TEST_F(FormatTestCSharp, CSharpRegions) { 400 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 401 "aaaaaaaaaaaaaaa long region"); 402 } 403 404 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 405 verifyFormat("public enum var\n" 406 "{\n" 407 " none,\n" 408 " @string,\n" 409 " bool,\n" 410 " @enum\n" 411 "}"); 412 } 413 414 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 415 verifyFormat("var test = ABC ?? DEF"); 416 verifyFormat("string myname = name ?? \"ABC\";"); 417 verifyFormat("return _name ?? \"DEF\";"); 418 } 419 420 TEST_F(FormatTestCSharp, CSharpNullCoalescingAssignment) { 421 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 422 Style.SpaceBeforeAssignmentOperators = true; 423 424 verifyFormat(R"(test ??= ABC;)", Style); 425 verifyFormat(R"(test ??= true;)", Style); 426 427 Style.SpaceBeforeAssignmentOperators = false; 428 429 verifyFormat(R"(test??= ABC;)", Style); 430 verifyFormat(R"(test??= true;)", Style); 431 } 432 433 TEST_F(FormatTestCSharp, CSharpNullForgiving) { 434 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 435 436 verifyFormat("var test = null!;", Style); 437 verifyFormat("string test = someFunctionCall()! + \"ABC\"!", Style); 438 verifyFormat("int test = (1! + 2 + bar! + foo())!", Style); 439 verifyFormat(R"(test ??= !foo!;)", Style); 440 verifyFormat("test = !bar! ?? !foo!;", Style); 441 verifyFormat("bool test = !(!true && !true! || !null && !null! || !false && " 442 "!false! && !bar()! + (!foo()))!", 443 Style); 444 445 // Check that line break keeps identifier with the bang. 446 Style.ColumnLimit = 14; 447 448 verifyFormat("var test =\n" 449 " foo!;", 450 Style); 451 } 452 453 TEST_F(FormatTestCSharp, AttributesIndentation) { 454 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 455 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 456 457 verifyFormat("[STAThread]\n" 458 "static void Main(string[] args)\n" 459 "{\n" 460 "}", 461 Style); 462 463 verifyFormat("[STAThread]\n" 464 "void " 465 "veryLooooooooooooooongFunctionName(string[] args)\n" 466 "{\n" 467 "}", 468 Style); 469 470 verifyFormat("[STAThread]\n" 471 "veryLoooooooooooooooooooongReturnType " 472 "veryLooooooooooooooongFunctionName(string[] args)\n" 473 "{\n" 474 "}", 475 Style); 476 477 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 478 "public override X Y()\n" 479 "{\n" 480 "}\n", 481 Style); 482 483 verifyFormat("[SuppressMessage]\n" 484 "public X Y()\n" 485 "{\n" 486 "}\n", 487 Style); 488 489 verifyFormat("[SuppressMessage]\n" 490 "public override X Y()\n" 491 "{\n" 492 "}\n", 493 Style); 494 495 verifyFormat("public A(B b) : base(b)\n" 496 "{\n" 497 " [SuppressMessage]\n" 498 " public override X Y()\n" 499 " {\n" 500 " }\n" 501 "}\n", 502 Style); 503 504 verifyFormat("public A : Base\n" 505 "{\n" 506 "}\n" 507 "[Test]\n" 508 "public Foo()\n" 509 "{\n" 510 "}\n", 511 Style); 512 513 verifyFormat("namespace\n" 514 "{\n" 515 "public A : Base\n" 516 "{\n" 517 "}\n" 518 "[Test]\n" 519 "public Foo()\n" 520 "{\n" 521 "}\n" 522 "}\n", 523 Style); 524 } 525 526 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 527 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 528 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 529 530 verifyFormat("List<string> list;", Style); 531 verifyFormat("Dictionary<string, string> dict;", Style); 532 533 verifyFormat("for (int i = 0; i < size (); i++) {\n" 534 "}", 535 Style); 536 verifyFormat("foreach (var x in y) {\n" 537 "}", 538 Style); 539 verifyFormat("switch (x) {}", Style); 540 verifyFormat("do {\n" 541 "} while (x);", 542 Style); 543 544 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 545 546 verifyFormat("List<string> list;", Style); 547 verifyFormat("Dictionary<string, string> dict;", Style); 548 549 verifyFormat("for(int i = 0; i < size(); i++) {\n" 550 "}", 551 Style); 552 verifyFormat("foreach(var x in y) {\n" 553 "}", 554 Style); 555 verifyFormat("switch(x) {}", Style); 556 verifyFormat("do {\n" 557 "} while(x);", 558 Style); 559 } 560 561 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 562 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 563 564 verifyFormat("(int)x / y;", Style); 565 566 Style.SpaceAfterCStyleCast = true; 567 verifyFormat("(int) x / y;", Style); 568 } 569 570 TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { 571 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 572 573 verifyFormat(R"(string str = @"""";)", Style); 574 verifyFormat(R"(string str = @"""Hello world""";)", Style); 575 verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); 576 } 577 578 TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { 579 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 580 581 verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); 582 verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); 583 verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); 584 } 585 586 TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { 587 // Use MS style as Google Style inserts a line break before multiline strings. 588 589 // verifyFormat does not understand multiline C# string-literals 590 // so check the format explicitly. 591 592 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 593 594 std::string Code = R"(string s1 = $@"some code: 595 class {className} {{ 596 {className}() {{}} 597 }}";)"; 598 599 EXPECT_EQ(Code, format(Code, Style)); 600 601 // Multiline string in the middle of a function call. 602 Code = R"( 603 var x = foo(className, $@"some code: 604 class {className} {{ 605 {className}() {{}} 606 }}", 607 y);)"; // y aligned with `className` arg. 608 609 EXPECT_EQ(Code, format(Code, Style)); 610 611 // Interpolated string with embedded multiline string. 612 Code = R"(Console.WriteLine($"{string.Join(@", 613 ", values)}");)"; 614 615 EXPECT_EQ(Code, format(Code, Style)); 616 } 617 618 TEST_F(FormatTestCSharp, CSharpLambdas) { 619 FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); 620 FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 621 622 verifyFormat(R"(// 623 class MyClass { 624 Action<string> greet = name => { 625 string greeting = $"Hello {name}!"; 626 Console.WriteLine(greeting); 627 }; 628 })", 629 GoogleStyle); 630 631 // Microsoft Style: 632 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#statement-lambdas 633 verifyFormat(R"(// 634 class MyClass 635 { 636 Action<string> greet = name => 637 { 638 string greeting = $"Hello {name}!"; 639 Console.WriteLine(greeting); 640 }; 641 })", 642 MicrosoftStyle); 643 644 verifyFormat("void bar()\n" 645 "{\n" 646 " Function(Val, (Action)(() =>\n" 647 " {\n" 648 " lock (mylock)\n" 649 " {\n" 650 " if (true)\n" 651 " {\n" 652 " A.Remove(item);\n" 653 " }\n" 654 " }\n" 655 " }));\n" 656 "}", 657 MicrosoftStyle); 658 659 verifyFormat("void baz()\n" 660 "{\n" 661 " Function(Val, (Action)(() =>\n" 662 " {\n" 663 " using (var a = new Lock())\n" 664 " {\n" 665 " if (true)\n" 666 " {\n" 667 " A.Remove(item);\n" 668 " }\n" 669 " }\n" 670 " }));\n" 671 "}", 672 MicrosoftStyle); 673 674 verifyFormat("void baz()\n" 675 "{\n" 676 " Function(Val, (Action)(() =>\n" 677 " {\n" 678 " if (true)\n" 679 " {\n" 680 " A.Remove(item);\n" 681 " }\n" 682 " }));\n" 683 "}", 684 MicrosoftStyle); 685 686 verifyFormat("void baz()\n" 687 "{\n" 688 " Function(Val, (Action)(() =>\n" 689 " {\n" 690 " do\n" 691 " {\n" 692 " A.Remove(item);\n" 693 " } while (true)\n" 694 " }));\n" 695 "}", 696 MicrosoftStyle); 697 698 verifyFormat("void baz()\n" 699 "{\n" 700 " Function(Val, (Action)(() =>\n" 701 " { A.Remove(item); }));\n" 702 "}", 703 MicrosoftStyle); 704 705 verifyFormat("void bar()\n" 706 "{\n" 707 " Function(Val, (() =>\n" 708 " {\n" 709 " lock (mylock)\n" 710 " {\n" 711 " if (true)\n" 712 " {\n" 713 " A.Remove(item);\n" 714 " }\n" 715 " }\n" 716 " }));\n" 717 "}", 718 MicrosoftStyle); 719 verifyFormat("void bar()\n" 720 "{\n" 721 " Function((() =>\n" 722 " {\n" 723 " lock (mylock)\n" 724 " {\n" 725 " if (true)\n" 726 " {\n" 727 " A.Remove(item);\n" 728 " }\n" 729 " }\n" 730 " }));\n" 731 "}", 732 MicrosoftStyle); 733 734 MicrosoftStyle.IndentWidth = 2; 735 verifyFormat("void bar()\n" 736 "{\n" 737 " Function((() =>\n" 738 " {\n" 739 " lock (mylock)\n" 740 " {\n" 741 " if (true)\n" 742 " {\n" 743 " A.Remove(item);\n" 744 " }\n" 745 " }\n" 746 " }));\n" 747 "}", 748 MicrosoftStyle); 749 verifyFormat("void bar() {\n" 750 " Function((() => {\n" 751 " lock (mylock) {\n" 752 " if (true) {\n" 753 " A.Remove(item);\n" 754 " }\n" 755 " }\n" 756 " }));\n" 757 "}", 758 GoogleStyle); 759 } 760 761 TEST_F(FormatTestCSharp, CSharpObjectInitializers) { 762 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 763 764 // Start code fragments with a comment line so that C++ raw string literals 765 // as seen are identical to expected formatted code. 766 767 verifyFormat(R"(// 768 Shape[] shapes = new[] { 769 new Circle { 770 Radius = 2.7281, 771 Colour = Colours.Red, 772 }, 773 new Square { 774 Side = 101.1, 775 Colour = Colours.Yellow, 776 }, 777 };)", 778 Style); 779 780 // Omitted final `,`s will change the formatting. 781 verifyFormat(R"(// 782 Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, 783 new Square { Side = 101.1, Colour = Colours.Yellow } };)", 784 Style); 785 786 // Lambdas can be supplied as initialiser arguments. 787 verifyFormat(R"(// 788 private Transformer _transformer = new X.Y { 789 Filler = (Shape shape) => { return new Transform.Fill(shape, RED); }, 790 Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); }, 791 };)", 792 Style); 793 794 // Dictionary initialisation. 795 verifyFormat(R"(// 796 var myDict = new Dictionary<string, string> { 797 ["name"] = _donald, 798 ["age"] = Convert.ToString(DateTime.Today.Year - 1934), 799 ["type"] = _duck, 800 };)", 801 Style); 802 } 803 804 TEST_F(FormatTestCSharp, CSharpArrayInitializers) { 805 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 806 807 verifyFormat(R"(// 808 private MySet<Node>[] setPoints = { 809 new Point<Node>(), 810 new Point<Node>(), 811 };)", 812 Style); 813 } 814 815 TEST_F(FormatTestCSharp, CSharpNamedArguments) { 816 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 817 818 verifyFormat(R"(// 819 PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");)", 820 Style); 821 822 // Ensure that trailing comments do not cause problems. 823 verifyFormat(R"(// 824 PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment 825 sellerName: "Gift Shop");)", 826 Style); 827 828 verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); 829 } 830 831 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { 832 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 833 834 verifyFormat("int Value { get }", Style); 835 verifyFormat("int Value { get; }", Style); 836 verifyFormat("int Value { internal get; }", Style); 837 verifyFormat("int Value { get; } = 0", Style); 838 verifyFormat("int Value { set }", Style); 839 verifyFormat("int Value { set; }", Style); 840 verifyFormat("int Value { internal set; }", Style); 841 verifyFormat("int Value { set; } = 0", Style); 842 verifyFormat("int Value { get; set }", Style); 843 verifyFormat("int Value { set; get }", Style); 844 verifyFormat("int Value { get; private set; }", Style); 845 verifyFormat("int Value { get; set; }", Style); 846 verifyFormat("int Value { get; set; } = 0", Style); 847 verifyFormat("int Value { internal get; internal set; }", Style); 848 849 // Do not wrap expression body definitions. 850 verifyFormat(R"(// 851 public string Name { 852 get => _name; 853 set => _name = value; 854 })", 855 Style); 856 857 // Examples taken from 858 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties 859 verifyFormat(R"( 860 // Expression body definitions 861 public class SaleItem { 862 public decimal Price { 863 get => _cost; 864 set => _cost = value; 865 } 866 })", 867 Style); 868 869 verifyFormat(R"( 870 // Properties with backing fields 871 class TimePeriod { 872 public double Hours { 873 get { return _seconds / 3600; } 874 set { 875 if (value < 0 || value > 24) 876 throw new ArgumentOutOfRangeException($"{nameof(value)} must be between 0 and 24."); 877 _seconds = value * 3600; 878 } 879 } 880 })", 881 Style); 882 883 verifyFormat(R"( 884 // Auto-implemented properties 885 public class SaleItem { 886 public decimal Price { get; set; } 887 })", 888 Style); 889 890 // Add column limit to wrap long lines. 891 Style.ColumnLimit = 100; 892 893 // Examples with assignment to default value. 894 verifyFormat(R"( 895 // Long assignment to default value 896 class MyClass { 897 public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } = 898 VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 899 DefaultThirdArgument); 900 })", 901 Style); 902 903 verifyFormat(R"( 904 // Long assignment to default value with expression body 905 class MyClass { 906 public override VeryLongNamedTypeIndeed VeryLongNamedValue { 907 get => veryLongNamedField; 908 set => veryLongNamedField = value; 909 } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 910 DefaultThirdArgument); 911 })", 912 Style); 913 914 // Brace wrapping and single-lining of accessor can be controlled by config. 915 Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; 916 Style.BreakBeforeBraces = FormatStyle::BS_Custom; 917 Style.BraceWrapping.AfterFunction = true; 918 919 verifyFormat(R"(// 920 class TimePeriod { 921 public double Hours 922 { 923 get { 924 return _seconds / 3600; 925 } 926 set { 927 _seconds = value * 3600; 928 } 929 } 930 })", 931 Style); 932 933 // Microsoft style trivial property accessors have no line break before the 934 // opening brace. 935 auto MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); 936 verifyFormat(R"(// 937 public class SaleItem 938 { 939 public decimal Price { get; set; } 940 })", 941 MicrosoftStyle); 942 } 943 944 TEST_F(FormatTestCSharp, CSharpSpaces) { 945 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 946 Style.SpaceBeforeSquareBrackets = false; 947 Style.SpacesInSquareBrackets = false; 948 Style.SpaceBeforeCpp11BracedList = true; 949 Style.Cpp11BracedListStyle = false; 950 Style.SpacesInContainerLiterals = false; 951 Style.SpaceAfterCStyleCast = false; 952 953 verifyFormat(R"(new Car { "Door", 0.1 })", Style); 954 verifyFormat(R"(new Car { 0.1, "Door" })", Style); 955 verifyFormat(R"(new string[] { "A" })", Style); 956 verifyFormat(R"(new string[] {})", Style); 957 verifyFormat(R"(new Car { someVariableName })", Style); 958 verifyFormat(R"(new Car { someVariableName })", Style); 959 verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)", 960 Style); 961 verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); 962 verifyFormat(R"(bool[] xs = { true, true };)", Style); 963 verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); 964 verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); 965 verifyFormat(R"(private float[,] Values;)", Style); 966 verifyFormat(R"(Result this[Index x] => Foo(x);)", Style); 967 968 verifyFormat(R"(char[,,] rawCharArray = MakeCharacterGrid();)", Style); 969 verifyFormat(R"(var (key, value))", Style); 970 971 // `&&` is not seen as a reference. 972 verifyFormat(R"(A == typeof(X) && someBool)", Style); 973 974 // Not seen as a C-style cast. 975 verifyFormat(R"(// 976 foreach ((A a, B b) in someList) { 977 })", 978 Style); 979 980 // space after lock in `lock (processes)`. 981 verifyFormat("lock (process)", Style); 982 983 Style.SpacesInSquareBrackets = true; 984 verifyFormat(R"(private float[ , ] Values;)", Style); 985 verifyFormat(R"(string dirPath = args?[ 0 ];)", Style); 986 verifyFormat(R"(char[ ,, ] rawCharArray = MakeCharacterGrid();)", Style); 987 988 // Method returning tuple 989 verifyFormat(R"(public (string name, int age) methodTuple() {})", Style); 990 verifyFormat(R"(private (string name, int age) methodTuple() {})", Style); 991 verifyFormat(R"(protected (string name, int age) methodTuple() {})", Style); 992 verifyFormat(R"(virtual (string name, int age) methodTuple() {})", Style); 993 verifyFormat(R"(extern (string name, int age) methodTuple() {})", Style); 994 verifyFormat(R"(static (string name, int age) methodTuple() {})", Style); 995 verifyFormat(R"(internal (string name, int age) methodTuple() {})", Style); 996 verifyFormat(R"(abstract (string name, int age) methodTuple() {})", Style); 997 verifyFormat(R"(sealed (string name, int age) methodTuple() {})", Style); 998 verifyFormat(R"(override (string name, int age) methodTuple() {})", Style); 999 verifyFormat(R"(async (string name, int age) methodTuple() {})", Style); 1000 verifyFormat(R"(unsafe (string name, int age) methodTuple() {})", Style); 1001 } 1002 1003 TEST_F(FormatTestCSharp, CSharpNullableTypes) { 1004 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 1005 Style.SpacesInSquareBrackets = false; 1006 1007 verifyFormat(R"(// 1008 public class A { 1009 void foo() { 1010 int? value = some.bar(); 1011 } 1012 })", 1013 Style); // int? is nullable not a conditional expression. 1014 1015 verifyFormat(R"(void foo(int? x, int? y, int? z) {})", 1016 Style); // Nullables in function definitions. 1017 1018 verifyFormat(R"(public float? Value;)", Style); // no space before `?`. 1019 1020 verifyFormat(R"(int?[] arr = new int?[10];)", 1021 Style); // An array of a nullable type. 1022 1023 verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. 1024 1025 verifyFormat(R"(var x = new MyContainer<int?>();)", Style); // Generics. 1026 1027 verifyFormat(R"(// 1028 public interface I { 1029 int? Function(); 1030 })", 1031 Style); // Interface methods. 1032 1033 Style.ColumnLimit = 10; 1034 verifyFormat(R"(// 1035 public VeryLongType? Function( 1036 int arg1, 1037 int arg2) { 1038 // 1039 })", 1040 Style); // ? sticks with identifier. 1041 } 1042 1043 TEST_F(FormatTestCSharp, CSharpArraySubscripts) { 1044 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 1045 1046 // Do not format array subscript operators as attributes. 1047 verifyFormat(R"(// 1048 if (someThings[index].Contains(myThing)) { 1049 })", 1050 Style); 1051 1052 verifyFormat(R"(// 1053 if (someThings[i][j][k].Contains(myThing)) { 1054 })", 1055 Style); 1056 } 1057 1058 TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { 1059 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 1060 1061 EXPECT_TRUE(Style.BraceWrapping.SplitEmptyRecord); 1062 1063 verifyFormat("class ItemFactory<T>\n" 1064 " where T : new() {\n" 1065 "}", 1066 Style); 1067 1068 verifyFormat("class Dictionary<TKey, TVal>\n" 1069 " where TKey : IComparable<TKey>\n" 1070 " where TVal : IMyInterface {\n" 1071 " public void MyMethod<T>(T t)\n" 1072 " where T : IMyInterface {\n" 1073 " doThing();\n" 1074 " }\n" 1075 "}", 1076 Style); 1077 1078 verifyFormat("class ItemFactory<T>\n" 1079 " where T : new(), IAnInterface<T>, IAnotherInterface<T>, " 1080 "IAnotherInterfaceStill<T> {\n" 1081 "}", 1082 Style); 1083 1084 Style.ColumnLimit = 50; // Force lines to be wrapped. 1085 verifyFormat(R"(// 1086 class ItemFactory<T, U> 1087 where T : new(), 1088 IAnInterface<T>, 1089 IAnotherInterface<T, U>, 1090 IAnotherInterfaceStill<T, U> { 1091 })", 1092 Style); 1093 1094 // In other languages `where` can be used as a normal identifier. 1095 // This example is in C++! 1096 verifyFormat(R"(// 1097 class A { 1098 int f(int where) {} 1099 };)", 1100 getGoogleStyle(FormatStyle::LK_Cpp)); 1101 } 1102 1103 } // namespace format 1104 } // end namespace clang 1105