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\n" 249 "{ set; get; }"); 250 251 verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " 252 "listening on provided host\")]\n" 253 "public string Host\n" 254 "{ set; get; }"); 255 256 verifyFormat( 257 "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n" 258 "// The const char* returned by hello_world must not be deleted.\n" 259 "private static extern IntPtr HelloFromCpp();)"); 260 261 // Class attributes go on their own line and do not affect layout of 262 // interfaces. Line wrapping decisions previously caused each interface to be 263 // on its own line. 264 verifyFormat("[SomeAttribute]\n" 265 "[SomeOtherAttribute]\n" 266 "public class A : IShape, IAnimal, IVehicle\n" 267 "{\n" 268 " int X;\n" 269 "}"); 270 271 // Attributes in a method declaration do not cause line wrapping. 272 verifyFormat("void MethodA([In][Out] ref double x)\n" 273 "{\n" 274 "}"); 275 276 // [] in an attribute do not cause premature line wrapping or indenting. 277 verifyFormat(R"(// 278 public class A 279 { 280 [SomeAttribute(new[] { RED, GREEN, BLUE }, -1.0f, 1.0f)] 281 [DoNotSerialize] 282 public Data MemberVariable; 283 })"); 284 285 // Unwrappable lines go on a line of their own. 286 // 'target:' is not treated as a label. 287 // Modify Style to enforce a column limit. 288 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 289 Style.ColumnLimit = 10; 290 verifyFormat(R"([assembly:InternalsVisibleTo( 291 "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])", 292 Style); 293 } 294 295 TEST_F(FormatTestCSharp, CSharpUsing) { 296 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 297 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 298 verifyFormat("public void foo () {\n" 299 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 300 " using () {}\n" 301 "}", 302 Style); 303 304 // Ensure clang-format affects top-level snippets correctly. 305 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 306 Style); 307 308 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 309 verifyFormat("public void foo() {\n" 310 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 311 " using() {}\n" 312 "}", 313 Style); 314 315 // Ensure clang-format affects top-level snippets correctly. 316 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 317 Style); 318 319 Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 320 verifyFormat("public void foo() {\n" 321 " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" 322 " using () {}\n" 323 "}", 324 Style); 325 326 // Ensure clang-format affects top-level snippets correctly. 327 verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", 328 Style); 329 330 Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; 331 verifyFormat("public void foo() {\n" 332 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 333 " using() {}\n" 334 "}", 335 Style); 336 337 // Ensure clang-format affects top-level snippets correctly. 338 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 339 Style); 340 } 341 342 TEST_F(FormatTestCSharp, CSharpRegions) { 343 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 344 "aaaaaaaaaaaaaaa long region"); 345 } 346 347 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 348 verifyFormat("public enum var\n" 349 "{\n" 350 " none,\n" 351 " @string,\n" 352 " bool,\n" 353 " @enum\n" 354 "}"); 355 } 356 357 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 358 verifyFormat("var test = ABC ?? DEF"); 359 verifyFormat("string myname = name ?? \"ABC\";"); 360 verifyFormat("return _name ?? \"DEF\";"); 361 } 362 363 TEST_F(FormatTestCSharp, AttributesIndentation) { 364 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 365 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 366 367 verifyFormat("[STAThread]\n" 368 "static void Main(string[] args)\n" 369 "{\n" 370 "}", 371 Style); 372 373 verifyFormat("[STAThread]\n" 374 "void " 375 "veryLooooooooooooooongFunctionName(string[] args)\n" 376 "{\n" 377 "}", 378 Style); 379 380 verifyFormat("[STAThread]\n" 381 "veryLoooooooooooooooooooongReturnType " 382 "veryLooooooooooooooongFunctionName(string[] args)\n" 383 "{\n" 384 "}", 385 Style); 386 387 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 388 "public override X Y()\n" 389 "{\n" 390 "}\n", 391 Style); 392 393 verifyFormat("[SuppressMessage]\n" 394 "public X Y()\n" 395 "{\n" 396 "}\n", 397 Style); 398 399 verifyFormat("[SuppressMessage]\n" 400 "public override X Y()\n" 401 "{\n" 402 "}\n", 403 Style); 404 405 verifyFormat("public A(B b) : base(b)\n" 406 "{\n" 407 " [SuppressMessage]\n" 408 " public override X Y()\n" 409 " {\n" 410 " }\n" 411 "}\n", 412 Style); 413 414 verifyFormat("public A : Base\n" 415 "{\n" 416 "}\n" 417 "[Test]\n" 418 "public Foo()\n" 419 "{\n" 420 "}\n", 421 Style); 422 423 verifyFormat("namespace\n" 424 "{\n" 425 "public A : Base\n" 426 "{\n" 427 "}\n" 428 "[Test]\n" 429 "public Foo()\n" 430 "{\n" 431 "}\n" 432 "}\n", 433 Style); 434 } 435 436 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 437 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 438 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 439 440 verifyFormat("List<string> list;", Style); 441 verifyFormat("Dictionary<string, string> dict;", Style); 442 443 verifyFormat("for (int i = 0; i < size (); i++) {\n" 444 "}", 445 Style); 446 verifyFormat("foreach (var x in y) {\n" 447 "}", 448 Style); 449 verifyFormat("switch (x) {}", Style); 450 verifyFormat("do {\n" 451 "} while (x);", 452 Style); 453 454 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 455 456 verifyFormat("List<string> list;", Style); 457 verifyFormat("Dictionary<string, string> dict;", Style); 458 459 verifyFormat("for(int i = 0; i < size(); i++) {\n" 460 "}", 461 Style); 462 verifyFormat("foreach(var x in y) {\n" 463 "}", 464 Style); 465 verifyFormat("switch(x) {}", Style); 466 verifyFormat("do {\n" 467 "} while(x);", 468 Style); 469 } 470 471 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 472 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 473 474 verifyFormat("(int)x / y;", Style); 475 476 Style.SpaceAfterCStyleCast = true; 477 verifyFormat("(int) x / y;", Style); 478 } 479 480 TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { 481 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 482 483 verifyFormat(R"(string str = @"""";)", Style); 484 verifyFormat(R"(string str = @"""Hello world""";)", Style); 485 verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); 486 } 487 488 TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { 489 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 490 491 verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); 492 verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); 493 verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); 494 } 495 496 TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { 497 // Use MS style as Google Style inserts a line break before multiline strings. 498 499 // verifyFormat does not understand multiline C# string-literals 500 // so check the format explicitly. 501 502 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 503 504 std::string Code = R"(string s1 = $@"some code: 505 class {className} {{ 506 {className}() {{}} 507 }}";)"; 508 509 EXPECT_EQ(Code, format(Code, Style)); 510 511 // Multiline string in the middle of a function call. 512 Code = R"( 513 var x = foo(className, $@"some code: 514 class {className} {{ 515 {className}() {{}} 516 }}", 517 y);)"; // y aligned with `className` arg. 518 519 EXPECT_EQ(Code, format(Code, Style)); 520 521 // Interpolated string with embedded multiline string. 522 Code = R"(Console.WriteLine($"{string.Join(@", 523 ", values)}");)"; 524 525 EXPECT_EQ(Code, format(Code, Style)); 526 } 527 528 TEST_F(FormatTestCSharp, CSharpObjectInitializers) { 529 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 530 531 // Start code fragments with a comment line so that C++ raw string literals 532 // as seen are identical to expected formatted code. 533 534 verifyFormat(R"(// 535 Shape[] shapes = new[] { 536 new Circle { 537 Radius = 2.7281, 538 Colour = Colours.Red, 539 }, 540 new Square { 541 Side = 101.1, 542 Colour = Colours.Yellow, 543 }, 544 };)", 545 Style); 546 547 // Omitted final `,`s will change the formatting. 548 verifyFormat(R"(// 549 Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, 550 new Square { Side = 101.1, Colour = Colours.Yellow } };)", 551 Style); 552 553 // Lambdas can be supplied as initialiser arguments. 554 verifyFormat(R"(// 555 private Transformer _transformer = new X.Y { 556 Filler = (Shape shape) => { return new Transform.Fill(shape, RED); }, 557 Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); }, 558 };)", 559 Style); 560 561 // Dictionary initialisation. 562 verifyFormat(R"(// 563 var myDict = new Dictionary<string, string> { 564 ["name"] = _donald, 565 ["age"] = Convert.ToString(DateTime.Today.Year - 1934), 566 ["type"] = _duck, 567 };)", 568 Style); 569 } 570 571 TEST_F(FormatTestCSharp, CSharpArrayInitializers) { 572 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 573 574 verifyFormat(R"(// 575 private MySet<Node>[] setPoints = { 576 new Point<Node>(), 577 new Point<Node>(), 578 };)", 579 Style); 580 } 581 582 TEST_F(FormatTestCSharp, CSharpNamedArguments) { 583 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 584 585 verifyFormat(R"(// 586 PrintOrderDetails(orderNum: 31, productName: "Red Mug", 587 sellerName: "Gift Shop");)", 588 Style); 589 590 // Ensure that trailing comments do not cause problems. 591 verifyFormat(R"(// 592 PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment 593 sellerName: "Gift Shop");)", 594 Style); 595 596 verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); 597 } 598 599 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { 600 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 601 602 verifyFormat("int Value { get }", Style); 603 verifyFormat("int Value { get; }", Style); 604 verifyFormat("int Value { internal get; }", Style); 605 verifyFormat("int Value { get; } = 0", Style); 606 verifyFormat("int Value { set }", Style); 607 verifyFormat("int Value { set; }", Style); 608 verifyFormat("int Value { internal set; }", Style); 609 verifyFormat("int Value { set; } = 0", Style); 610 verifyFormat("int Value { get; set }", Style); 611 verifyFormat("int Value { set; get }", Style); 612 verifyFormat("int Value { get; private set; }", Style); 613 verifyFormat("int Value { get; set; }", Style); 614 verifyFormat("int Value { get; set; } = 0", Style); 615 verifyFormat("int Value { internal get; internal set; }", Style); 616 617 // Do not wrap expression body definitions. 618 verifyFormat(R"(// 619 public string Name { 620 get => _name; 621 set => _name = value; 622 })", 623 Style); 624 625 // Examples taken from 626 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties 627 verifyFormat(R"( 628 // Expression body definitions 629 public class SaleItem { 630 public decimal Price { 631 get => _cost; 632 set => _cost = value; 633 } 634 })", 635 Style); 636 637 verifyFormat(R"( 638 // Properties with backing fields 639 class TimePeriod { 640 public double Hours { 641 get { return _seconds / 3600; } 642 set { 643 if (value < 0 || value > 24) 644 throw new ArgumentOutOfRangeException( 645 $"{nameof(value)} must be between 0 and 24."); 646 _seconds = value * 3600; 647 } 648 } 649 })", 650 Style); 651 652 verifyFormat(R"( 653 // Auto-implemented properties 654 public class SaleItem { 655 public decimal Price { get; set; } 656 })", 657 Style); 658 659 // Add column limit to wrap long lines. 660 Style.ColumnLimit = 100; 661 662 // Examples with assignment to default value. 663 verifyFormat(R"( 664 // Long assignment to default value 665 class MyClass { 666 public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } = 667 VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 668 DefaultThirdArgument); 669 })", 670 Style); 671 672 verifyFormat(R"( 673 // Long assignment to default value with expression body 674 class MyClass { 675 public override VeryLongNamedTypeIndeed VeryLongNamedValue { 676 get => veryLongNamedField; 677 set => veryLongNamedField = value; 678 } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, 679 DefaultThirdArgument); 680 })", 681 Style); 682 683 // Brace wrapping and single-lining of accessor can be controlled by config. 684 Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; 685 Style.BreakBeforeBraces = FormatStyle::BS_Custom; 686 Style.BraceWrapping.AfterFunction = true; 687 688 verifyFormat(R"(// 689 public class SaleItem { 690 public decimal Price 691 { get; set; } 692 })", 693 Style); 694 695 verifyFormat(R"(// 696 class TimePeriod { 697 public double Hours 698 { 699 get { 700 return _seconds / 3600; 701 } 702 set { 703 _seconds = value * 3600; 704 } 705 } 706 })", 707 Style); 708 } 709 710 TEST_F(FormatTestCSharp, CSharpSpaces) { 711 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 712 Style.SpaceBeforeSquareBrackets = false; 713 Style.SpacesInSquareBrackets = false; 714 Style.SpaceBeforeCpp11BracedList = true; 715 Style.Cpp11BracedListStyle = false; 716 Style.SpacesInContainerLiterals = false; 717 Style.SpaceAfterCStyleCast = false; 718 719 verifyFormat(R"(new Car { "Door", 0.1 })", Style); 720 verifyFormat(R"(new Car { 0.1, "Door" })", Style); 721 verifyFormat(R"(new string[] { "A" })", Style); 722 verifyFormat(R"(new string[] {})", Style); 723 verifyFormat(R"(new Car { someVariableName })", Style); 724 verifyFormat(R"(new Car { someVariableName })", Style); 725 verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)", 726 Style); 727 verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); 728 verifyFormat(R"(bool[] xs = { true, true };)", Style); 729 verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); 730 verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); 731 verifyFormat(R"(private float[,] Values;)", Style); 732 verifyFormat(R"(Result this[Index x] => Foo(x);)", Style); 733 734 verifyFormat(R"(char[,,] rawCharArray = MakeCharacterGrid();)", Style); 735 verifyFormat(R"(var (key, value))", Style); 736 737 // `&&` is not seen as a reference. 738 verifyFormat(R"(A == typeof(X) && someBool)", Style); 739 740 // Not seen as a C-style cast. 741 verifyFormat(R"(// 742 foreach ((A a, B b) in someList) { 743 })", 744 Style); 745 746 Style.SpacesInSquareBrackets = true; 747 verifyFormat(R"(private float[ , ] Values;)", Style); 748 verifyFormat(R"(string dirPath = args?[ 0 ];)", Style); 749 verifyFormat(R"(char[ ,, ] rawCharArray = MakeCharacterGrid();)", Style); 750 } 751 752 TEST_F(FormatTestCSharp, CSharpNullableTypes) { 753 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 754 Style.SpacesInSquareBrackets = false; 755 756 verifyFormat(R"(// 757 public class A { 758 void foo() { int? value = some.bar(); } 759 })", 760 Style); // int? is nullable not a conditional expression. 761 762 verifyFormat(R"(void foo(int? x, int? y, int? z) {})", 763 Style); // Nullables in function definitions. 764 765 verifyFormat(R"(public float? Value;)", Style); // no space before `?`. 766 767 verifyFormat(R"(int?[] arr = new int?[10];)", 768 Style); // An array of a nullable type. 769 770 verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. 771 772 verifyFormat(R"(var x = new MyContainer<int?>();)", Style); // Generics. 773 } 774 775 TEST_F(FormatTestCSharp, CSharpArraySubscripts) { 776 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 777 778 // Do not format array subscript operators as attributes. 779 verifyFormat(R"(// 780 if (someThings[index].Contains(myThing)) { 781 })", 782 Style); 783 784 verifyFormat(R"(// 785 if (someThings[i][j][k].Contains(myThing)) { 786 })", 787 Style); 788 } 789 790 TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { 791 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 792 793 verifyFormat(R"(// 794 class ItemFactory<T> 795 where T : new() {})", 796 Style); 797 798 verifyFormat(R"(// 799 class Dictionary<TKey, TVal> 800 where TKey : IComparable<TKey> 801 where TVal : IMyInterface { 802 public void MyMethod<T>(T t) 803 where T : IMyInterface { doThing(); } 804 })", 805 Style); 806 807 verifyFormat(R"(// 808 class ItemFactory<T> 809 where T : new(), 810 IAnInterface<T>, 811 IAnotherInterface<T>, 812 IAnotherInterfaceStill<T> {})", 813 Style); 814 815 Style.ColumnLimit = 50; // Force lines to be wrapped. 816 verifyFormat(R"(// 817 class ItemFactory<T, U> 818 where T : new(), 819 IAnInterface<T>, 820 IAnotherInterface<T, U>, 821 IAnotherInterfaceStill<T, U> {})", 822 Style); 823 824 // In other languages `where` can be used as a normal identifier. 825 // This example is in C++! 826 verifyFormat(R"(// 827 class A { 828 int f(int where) {} 829 };)", 830 getGoogleStyle(FormatStyle::LK_Cpp)); 831 } 832 833 } // namespace format 834 } // end namespace clang 835