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