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, CSharpNullConditional) { 174 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 175 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 176 177 verifyFormat( 178 "public Person(string firstName, string lastName, int? age = null)"); 179 180 verifyFormat("foo () {\n" 181 " switch (args?.Length) {}\n" 182 "}", 183 Style); 184 185 verifyFormat("switch (args?.Length) {}", Style); 186 187 verifyFormat("public static void Main(string[] args)\n" 188 "{\n" 189 " string dirPath = args?[0];\n" 190 "}"); 191 192 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 193 194 verifyFormat("switch(args?.Length) {}", Style); 195 } 196 197 TEST_F(FormatTestCSharp, Attributes) { 198 verifyFormat("[STAThread]\n" 199 "static void Main(string[] args)\n" 200 "{\n" 201 "}"); 202 203 verifyFormat("[TestMethod]\n" 204 "private class Test\n" 205 "{\n" 206 "}"); 207 208 verifyFormat("[TestMethod]\n" 209 "protected class Test\n" 210 "{\n" 211 "}"); 212 213 verifyFormat("[TestMethod]\n" 214 "internal class Test\n" 215 "{\n" 216 "}"); 217 218 verifyFormat("[TestMethod]\n" 219 "class Test\n" 220 "{\n" 221 "}"); 222 223 verifyFormat("[TestMethod]\n" 224 "[DeploymentItem(\"Test.txt\")]\n" 225 "public class Test\n" 226 "{\n" 227 "}"); 228 229 verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" 230 "[System.Runtime.InteropServices.ComVisible(true)]\n" 231 "public sealed class STAThreadAttribute : Attribute\n" 232 "{\n" 233 "}"); 234 235 verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " 236 "provided port\")]\n" 237 "class Test\n" 238 "{\n" 239 "}"); 240 241 verifyFormat("[TestMethod]\n" 242 "public string Host\n" 243 "{ set; get; }"); 244 245 verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " 246 "listening on provided host\")]\n" 247 "public string Host\n" 248 "{ set; get; }"); 249 250 verifyFormat( 251 "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n" 252 "// The const char* returned by hello_world must not be deleted.\n" 253 "private static extern IntPtr HelloFromCpp();)"); 254 255 // Class attributes go on their own line and do not affect layout of 256 // interfaces. Line wrapping decisions previously caused each interface to be 257 // on its own line. 258 verifyFormat("[SomeAttribute]\n" 259 "[SomeOtherAttribute]\n" 260 "public class A : IShape, IAnimal, IVehicle\n" 261 "{\n" 262 " int X;\n" 263 "}"); 264 265 // Attributes in a method declaration do not cause line wrapping. 266 verifyFormat("void MethodA([In][Out] ref double x)\n" 267 "{\n" 268 "}"); 269 270 // Unwrappable lines go on a line of their own. 271 // 'target:' is not treated as a label. 272 // Modify Style to enforce a column limit. 273 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 274 Style.ColumnLimit = 10; 275 verifyFormat(R"([assembly:InternalsVisibleTo( 276 "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])", 277 Style); 278 } 279 280 TEST_F(FormatTestCSharp, CSharpUsing) { 281 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 282 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 283 verifyFormat("public void foo () {\n" 284 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 285 " using () {}\n" 286 "}", 287 Style); 288 289 // Ensure clang-format affects top-level snippets correctly. 290 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 291 Style); 292 293 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 294 verifyFormat("public void foo() {\n" 295 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 296 " using() {}\n" 297 "}", 298 Style); 299 300 // Ensure clang-format affects top-level snippets correctly. 301 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 302 Style); 303 304 Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 305 verifyFormat("public void foo() {\n" 306 " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" 307 " using () {}\n" 308 "}", 309 Style); 310 311 // Ensure clang-format affects top-level snippets correctly. 312 verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", 313 Style); 314 315 Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; 316 verifyFormat("public void foo() {\n" 317 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 318 " using() {}\n" 319 "}", 320 Style); 321 322 // Ensure clang-format affects top-level snippets correctly. 323 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 324 Style); 325 } 326 327 TEST_F(FormatTestCSharp, CSharpRegions) { 328 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 329 "aaaaaaaaaaaaaaa long region"); 330 } 331 332 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 333 verifyFormat("public enum var { none, @string, bool, @enum }"); 334 } 335 336 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 337 verifyFormat("var test = ABC ?? DEF"); 338 verifyFormat("string myname = name ?? \"ABC\";"); 339 verifyFormat("return _name ?? \"DEF\";"); 340 } 341 342 TEST_F(FormatTestCSharp, AttributesIndentation) { 343 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 344 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 345 346 verifyFormat("[STAThread]\n" 347 "static void Main(string[] args)\n" 348 "{\n" 349 "}", 350 Style); 351 352 verifyFormat("[STAThread]\n" 353 "void " 354 "veryLooooooooooooooongFunctionName(string[] args)\n" 355 "{\n" 356 "}", 357 Style); 358 359 verifyFormat("[STAThread]\n" 360 "veryLoooooooooooooooooooongReturnType " 361 "veryLooooooooooooooongFunctionName(string[] args)\n" 362 "{\n" 363 "}", 364 Style); 365 366 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 367 "public override X Y()\n" 368 "{\n" 369 "}\n", 370 Style); 371 372 verifyFormat("[SuppressMessage]\n" 373 "public X Y()\n" 374 "{\n" 375 "}\n", 376 Style); 377 378 verifyFormat("[SuppressMessage]\n" 379 "public override X Y()\n" 380 "{\n" 381 "}\n", 382 Style); 383 384 verifyFormat("public A(B b) : base(b)\n" 385 "{\n" 386 " [SuppressMessage]\n" 387 " public override X Y()\n" 388 " {\n" 389 " }\n" 390 "}\n", 391 Style); 392 393 verifyFormat("public A : Base\n" 394 "{\n" 395 "}\n" 396 "[Test]\n" 397 "public Foo()\n" 398 "{\n" 399 "}\n", 400 Style); 401 402 verifyFormat("namespace\n" 403 "{\n" 404 "public A : Base\n" 405 "{\n" 406 "}\n" 407 "[Test]\n" 408 "public Foo()\n" 409 "{\n" 410 "}\n" 411 "}\n", 412 Style); 413 } 414 415 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 416 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 417 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 418 419 verifyFormat("List<string> list;", Style); 420 verifyFormat("Dictionary<string, string> dict;", Style); 421 422 verifyFormat("for (int i = 0; i < size (); i++) {\n" 423 "}", 424 Style); 425 verifyFormat("foreach (var x in y) {\n" 426 "}", 427 Style); 428 verifyFormat("switch (x) {}", Style); 429 verifyFormat("do {\n" 430 "} while (x);", 431 Style); 432 433 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 434 435 verifyFormat("List<string> list;", Style); 436 verifyFormat("Dictionary<string, string> dict;", Style); 437 438 verifyFormat("for(int i = 0; i < size(); i++) {\n" 439 "}", 440 Style); 441 verifyFormat("foreach(var x in y) {\n" 442 "}", 443 Style); 444 verifyFormat("switch(x) {}", Style); 445 verifyFormat("do {\n" 446 "} while(x);", 447 Style); 448 } 449 450 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 451 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 452 453 verifyFormat("(int)x / y;", Style); 454 455 Style.SpaceAfterCStyleCast = true; 456 verifyFormat("(int) x / y;", Style); 457 } 458 459 TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { 460 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 461 462 verifyFormat(R"(string str = @"""";)", Style); 463 verifyFormat(R"(string str = @"""Hello world""";)", Style); 464 verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); 465 } 466 467 TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { 468 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 469 470 verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); 471 verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); 472 verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); 473 } 474 475 TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { 476 // Use MS style as Google Style inserts a line break before multiline strings. 477 478 // verifyFormat does not understand multiline C# string-literals 479 // so check the format explicitly. 480 481 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 482 483 std::string Code = R"(string s1 = $@"some code: 484 class {className} {{ 485 {className}() {{}} 486 }}";)"; 487 488 EXPECT_EQ(Code, format(Code, Style)); 489 490 // Multiline string in the middle of a function call. 491 Code = R"( 492 var x = foo(className, $@"some code: 493 class {className} {{ 494 {className}() {{}} 495 }}", 496 y);)"; // y aligned with `className` arg. 497 498 EXPECT_EQ(Code, format(Code, Style)); 499 500 // Interpolated string with embedded multiline string. 501 Code = R"(Console.WriteLine($"{string.Join(@", 502 ", values)}");)"; 503 504 EXPECT_EQ(Code, format(Code, Style)); 505 } 506 507 TEST_F(FormatTestCSharp, CSharpObjectInitializers) { 508 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 509 510 // Start code fragments with a comment line so that C++ raw string literals 511 // as seen are identical to expected formatted code. 512 513 verifyFormat(R"(// 514 Shape[] shapes = new[] { 515 new Circle { 516 Radius = 2.7281, 517 Colour = Colours.Red, 518 }, 519 new Square { 520 Side = 101.1, 521 Colour = Colours.Yellow, 522 }, 523 };)", 524 Style); 525 526 // Omitted final `,`s will change the formatting. 527 verifyFormat(R"(// 528 Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, 529 new Square { 530 Side = 101.1, 531 Colour = Colours.Yellow, 532 } };)", 533 Style); 534 } 535 536 TEST_F(FormatTestCSharp, CSharpNamedArguments) { 537 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 538 539 verifyFormat(R"(// 540 PrintOrderDetails(orderNum: 31, productName: "Red Mug", 541 sellerName: "Gift Shop");)", 542 Style); 543 544 // Ensure that trailing comments do not cause problems. 545 verifyFormat(R"(// 546 PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment 547 sellerName: "Gift Shop");)", 548 Style); 549 550 verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); 551 } 552 553 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { 554 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 555 556 verifyFormat("int Value { get }", Style); 557 verifyFormat("int Value { get; }", Style); 558 verifyFormat("int Value { internal get; }", Style); 559 verifyFormat("int Value { get; } = 0", Style); 560 verifyFormat("int Value { set }", Style); 561 verifyFormat("int Value { set; }", Style); 562 verifyFormat("int Value { internal set; }", Style); 563 verifyFormat("int Value { set; } = 0", Style); 564 verifyFormat("int Value { get; set }", Style); 565 verifyFormat("int Value { set; get }", Style); 566 verifyFormat("int Value { get; private set; }", Style); 567 verifyFormat("int Value { get; set; }", Style); 568 verifyFormat("int Value { get; set; } = 0", Style); 569 verifyFormat("int Value { internal get; internal set; }", Style); 570 571 // Do not wrap expression body definitions. 572 verifyFormat(R"(// 573 public string Name { 574 get => _name; 575 set => _name = value; 576 })", 577 Style); 578 } 579 580 TEST_F(FormatTestCSharp, CSharpSpaces) { 581 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 582 Style.SpaceBeforeSquareBrackets = false; 583 Style.SpacesInSquareBrackets = false; 584 Style.SpaceBeforeCpp11BracedList = true; 585 Style.Cpp11BracedListStyle = false; 586 Style.SpacesInContainerLiterals = false; 587 588 verifyFormat(R"(new Car { "Door", 0.1 })", Style); 589 verifyFormat(R"(new Car { 0.1, "Door" })", Style); 590 verifyFormat(R"(new string[] { "A" })", Style); 591 verifyFormat(R"(new string[] {})", Style); 592 verifyFormat(R"(new Car { someVariableName })", Style); 593 verifyFormat(R"(new Car { someVariableName })", Style); 594 verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)", 595 Style); 596 verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); 597 verifyFormat(R"(bool[] xs = { true, true };)", Style); 598 verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); 599 verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); 600 verifyFormat(R"(private float[,] Values;)", Style); 601 602 Style.SpacesInSquareBrackets = true; 603 verifyFormat(R"(private float[, ] Values;)", Style); 604 } 605 606 TEST_F(FormatTestCSharp, CSharpNullableTypes) { 607 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 608 Style.SpacesInSquareBrackets = false; 609 610 verifyFormat(R"(public float? Value;)", Style); // no space before `?`. 611 612 // Erroneous spaces in square brackets here will be tackled in a follow-up 613 // patch and are not addressed by setting 614 // `Style.SpacesInSquareBrackets = false;` 615 verifyFormat(R"(int?[] arr = new int?[ 10 ];)", 616 Style); // An array of a nullable type. 617 } 618 619 } // namespace format 620 } // end namespace clang 621