xref: /llvm-project/clang/unittests/Format/FormatTestCSharp.cpp (revision 8fa743ab82027da443bac050e86b70bcdb78cbee)
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