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