//===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "FormatTestBase.h" #define DEBUG_TYPE "format-test" namespace clang { namespace format { namespace test { namespace { class FormatTestCSharp : public test::FormatTestBase { protected: FormatStyle getDefaultStyle() const override { return getMicrosoftStyle(FormatStyle::LK_CSharp); } static std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length, const FormatStyle &Style) { LLVM_DEBUG(llvm::errs() << "---\n"); LLVM_DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); tooling::Replacements Replaces = reformat(Style, Code, Ranges); auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); return *Result; } static std::string format(llvm::StringRef Code, const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { return format(Code, 0, Code.size(), Style); } static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); Style.ColumnLimit = ColumnLimit; return Style; } }; TEST_F(FormatTestCSharp, CSharpClass) { verifyFormat("public class SomeClass\n" "{\n" " void f()\n" " {\n" " }\n" " int g()\n" " {\n" " return 0;\n" " }\n" " void h()\n" " {\n" " while (true)\n" " f();\n" " for (;;)\n" " f();\n" " if (true)\n" " f();\n" " }\n" "}"); // Ensure that small and empty classes are handled correctly with condensed // (Google C++-like) brace-breaking style. FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.BreakBeforeBraces = FormatStyle::BS_Attach; verifyFormat("public class SomeEmptyClass {}", Style); verifyFormat("public class SomeTinyClass {\n" " int X;\n" "}", Style); verifyFormat("private class SomeTinyClass {\n" " int X;\n" "}", Style); verifyFormat("protected class SomeTinyClass {\n" " int X;\n" "}", Style); verifyFormat("internal class SomeTinyClass {\n" " int X;\n" "}", Style); } TEST_F(FormatTestCSharp, AccessModifiers) { verifyFormat("public String toString()\n" "{\n" "}"); verifyFormat("private String toString()\n" "{\n" "}"); verifyFormat("protected String toString()\n" "{\n" "}"); verifyFormat("internal String toString()\n" "{\n" "}"); verifyFormat("public override String toString()\n" "{\n" "}"); verifyFormat("private override String toString()\n" "{\n" "}"); verifyFormat("protected override String toString()\n" "{\n" "}"); verifyFormat("internal override String toString()\n" "{\n" "}"); verifyFormat("internal static String toString()\n" "{\n" "}"); } TEST_F(FormatTestCSharp, NoStringLiteralBreaks) { // Breaking of interpolated strings is not implemented. auto Style = getDefaultStyle(); Style.ColumnLimit = 40; Style.BreakStringLiterals = true; verifyFormat("foo(" "$\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaa\");", Style); } TEST_F(FormatTestCSharp, StringLiteralBreaks) { // The line is 75 characters long. The default limit for the Microsoft style // is 120. auto Style = getDefaultStyle(); Style.BreakStringLiterals = true; verifyFormat("foo(" "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa\");", Style); // When the column limit is smaller, the string should get broken. Style.ColumnLimit = 40; verifyFormat(R"(foo("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaa");)", "foo(" "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa\");", Style); // The new quotes should be the same as the original. verifyFormat(R"(foo(@"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + @"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + @"aaaaa");)", "foo(" "@\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaa\");", Style); // The operators can be on either line. Style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; verifyFormat(R"(foo("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "a");)", "foo(" "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa\");", Style); Style.AlignOperands = FormatStyle::OAS_AlignAfterOperator; verifyFormat(R"(foo("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "a");)", "foo(" "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa\");", Style); verifyFormat(R"(x = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";)", "x = " "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa\";", Style); } TEST_F(FormatTestCSharp, CSharpVerbatiumStringLiterals) { verifyFormat("foo(@\"aaaaaaaa\\abc\\aaaa\");"); // @"ABC\" + ToString("B") - handle embedded \ in literal string at // the end // /* * After removal of Lexer change we are currently not able * To handle these cases verifyFormat("string s = @\"ABC\\\" + ToString(\"B\");"); verifyFormat("string s = @\"ABC\"\"DEF\"\"GHI\""); verifyFormat("string s = @\"ABC\"\"DEF\"\"\""); verifyFormat("string s = @\"ABC\"\"DEF\"\"\" + abc"); */ } TEST_F(FormatTestCSharp, CSharpInterpolatedStringLiterals) { verifyFormat("foo($\"aaaaaaaa{aaa}aaaa\");"); verifyFormat("foo($\"aaaa{A}\");"); verifyFormat( "foo($\"aaaa{A}" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"); verifyFormat("Name = $\"{firstName} {lastName}\";"); // $"ABC\" + ToString("B") - handle embedded \ in literal string at // the end verifyFormat("string s = $\"A{abc}BC\" + ToString(\"B\");"); verifyFormat("$\"{domain}\\\\{user}\""); verifyFormat( "var verbatimInterpolated = $@\"C:\\Users\\{userName}\\Documents\\\";"); } TEST_F(FormatTestCSharp, CSharpFatArrows) { verifyIncompleteFormat("Task serverTask = Task.Run(async() => {"); verifyFormat("public override string ToString() => \"{Name}\\{Age}\";"); } TEST_F(FormatTestCSharp, CSharpConditionalExpressions) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); // conditional expression is not seen as a NullConditional. verifyFormat("var y = A < B ? -1 : 1;", Style); } TEST_F(FormatTestCSharp, CSharpNullConditional) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeParens = FormatStyle::SBPO_Always; verifyFormat( "public Person(string firstName, string lastName, int? age = null)"); verifyFormat("foo () {\n" " switch (args?.Length) {}\n" "}", Style); verifyFormat("switch (args?.Length) {}", Style); verifyFormat("public static void Main(string[] args)\n" "{\n" " string dirPath = args?[0];\n" "}"); Style.SpaceBeforeParens = FormatStyle::SBPO_Never; verifyFormat("switch(args?.Length) {}", Style); } TEST_F(FormatTestCSharp, Attributes) { verifyFormat("[STAThread]\n" "static void Main(string[] args)\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "private class Test\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "protected class Test\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "internal class Test\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "class Test\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "[DeploymentItem(\"Test.txt\")]\n" "public class Test\n" "{\n" "}"); verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" "[System.Runtime.InteropServices.ComVisible(true)]\n" "public sealed class STAThreadAttribute : Attribute\n" "{\n" "}"); verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " "provided port\")]\n" "class Test\n" "{\n" "}"); verifyFormat("[TestMethod]\n" "public string Host { set; get; }"); // Adjacent properties should not cause line wrapping issues verifyFormat("[JsonProperty(\"foo\")]\n" "public string Foo { set; get; }\n" "[JsonProperty(\"bar\")]\n" "public string Bar { set; get; }\n" "[JsonProperty(\"bar\")]\n" "protected string Bar { set; get; }\n" "[JsonProperty(\"bar\")]\n" "internal string Bar { set; get; }"); // Multiple attributes should always be split (not just the first ones) verifyFormat("[XmlIgnore]\n" "[JsonProperty(\"foo\")]\n" "public string Foo { set; get; }"); verifyFormat("[XmlIgnore]\n" "[JsonProperty(\"foo\")]\n" "public string Foo { set; get; }\n" "[XmlIgnore]\n" "[JsonProperty(\"bar\")]\n" "public string Bar { set; get; }"); verifyFormat("[XmlIgnore]\n" "[ScriptIgnore]\n" "[JsonProperty(\"foo\")]\n" "public string Foo { set; get; }\n" "[XmlIgnore]\n" "[ScriptIgnore]\n" "[JsonProperty(\"bar\")]\n" "public string Bar { set; get; }"); verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " "listening on provided host\")]\n" "public string Host { set; get; }"); verifyIncompleteFormat( "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n" "// The const char* returned by hello_world must not be deleted.\n" "private static extern IntPtr HelloFromCpp();)"); // Class attributes go on their own line and do not affect layout of // interfaces. Line wrapping decisions previously caused each interface to be // on its own line. verifyFormat("[SomeAttribute]\n" "[SomeOtherAttribute]\n" "public class A : IShape, IAnimal, IVehicle\n" "{\n" " int X;\n" "}"); // Attributes in a method declaration do not cause line wrapping. verifyFormat("void MethodA([In][Out] ref double x)\n" "{\n" "}"); verifyFormat("void MethodA([In, Out] ref double x)\n" "{\n" "}"); verifyFormat("void MethodA([In, Out] double[] x)\n" "{\n" "}"); verifyFormat("void MethodA([In] double[] x)\n" "{\n" "}"); verifyFormat("void MethodA(int[] x)\n" "{\n" "}"); verifyFormat("void MethodA(int[][] x)\n" "{\n" "}"); verifyFormat("void MethodA([] x)\n" "{\n" "}"); verifyFormat("public void Log([CallerLineNumber] int line = -1, " "[CallerFilePath] string path = null,\n" " [CallerMemberName] string name = null)\n" "{\n" "}"); // [] in an attribute do not cause premature line wrapping or indenting. verifyFormat(R"(// public class A { [SomeAttribute(new[] { RED, GREEN, BLUE }, -1.0f, 1.0f)] [DoNotSerialize] public Data MemberVariable; })"); // Unwrappable lines go on a line of their own. // 'target:' is not treated as a label. // Modify Style to enforce a column limit. FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.ColumnLimit = 10; verifyFormat(R"([assembly:InternalsVisibleTo( "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])", Style); } TEST_F(FormatTestCSharp, CSharpUsing) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeParens = FormatStyle::SBPO_Always; verifyFormat("public void foo () {\n" " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" " using () {}\n" "}", Style); // Ensure clang-format affects top-level snippets correctly. verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", Style); Style.SpaceBeforeParens = FormatStyle::SBPO_Never; verifyFormat("public void foo() {\n" " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" " using() {}\n" "}", Style); // Ensure clang-format affects top-level snippets correctly. verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", Style); Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; verifyFormat("public void foo() {\n" " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" " using () {}\n" "}", Style); // Ensure clang-format affects top-level snippets correctly. verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", Style); Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; verifyFormat("public void foo() {\n" " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" " using() {}\n" "}", Style); // Ensure clang-format affects top-level snippets correctly. verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", Style); } TEST_F(FormatTestCSharp, CSharpRegions) { verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " "aaaaaaaaaaaaaaa long region"); } TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { // AfterEnum is true by default. verifyFormat("public enum var\n" "{\n" " none,\n" " @string,\n" " bool,\n" " @enum\n" "}"); } TEST_F(FormatTestCSharp, CSharpNullCoalescing) { verifyFormat("var test = ABC ?? DEF"); verifyFormat("string myname = name ?? \"ABC\";"); verifyFormat("return _name ?? \"DEF\";"); } TEST_F(FormatTestCSharp, CSharpNullCoalescingAssignment) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeAssignmentOperators = true; verifyFormat(R"(test ??= ABC;)", Style); verifyFormat(R"(test ??= true;)", Style); Style.SpaceBeforeAssignmentOperators = false; verifyFormat(R"(test??= ABC;)", Style); verifyFormat(R"(test??= true;)", Style); } TEST_F(FormatTestCSharp, CSharpNullForgiving) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat("var test = null!;", Style); verifyFormat("string test = someFunctionCall()! + \"ABC\"!", Style); verifyFormat("int test = (1! + 2 + bar! + foo())!", Style); verifyFormat(R"(test ??= !foo!;)", Style); verifyFormat("test = !bar! ?? !foo!;", Style); verifyFormat("bool test = !(!true && !true! || !null && !null! || !false && " "!false! && !bar()! + (!foo()))!", Style); // Check that line break keeps identifier with the bang. Style.ColumnLimit = 14; verifyFormat("var test =\n" " foo!;", Style); } TEST_F(FormatTestCSharp, AttributesIndentation) { FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; verifyFormat("[STAThread]\n" "static void Main(string[] args)\n" "{\n" "}", Style); verifyFormat("[STAThread]\n" "void " "veryLooooooooooooooongFunctionName(string[] args)\n" "{\n" "}", Style); verifyFormat("[STAThread]\n" "veryLoooooooooooooooooooongReturnType " "veryLooooooooooooooongFunctionName(string[] args)\n" "{\n" "}", Style); verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" "public override X Y()\n" "{\n" "}\n", Style); verifyFormat("[SuppressMessage]\n" "public X Y()\n" "{\n" "}\n", Style); verifyFormat("[SuppressMessage]\n" "public override X Y()\n" "{\n" "}\n", Style); verifyFormat("public A(B b) : base(b)\n" "{\n" " [SuppressMessage]\n" " public override X Y()\n" " {\n" " }\n" "}\n", Style); verifyFormat("public A : Base\n" "{\n" "}\n" "[Test]\n" "public Foo()\n" "{\n" "}\n", Style); verifyFormat("namespace\n" "{\n" "public A : Base\n" "{\n" "}\n" "[Test]\n" "public Foo()\n" "{\n" "}\n" "}\n", Style); } TEST_F(FormatTestCSharp, CSharpSpaceBefore) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeParens = FormatStyle::SBPO_Always; verifyFormat("List list;", Style); verifyFormat("Dictionary dict;", Style); verifyFormat("for (int i = 0; i < size (); i++) {\n" "}", Style); verifyFormat("foreach (var x in y) {\n" "}", Style); verifyFormat("switch (x) {}", Style); verifyFormat("do {\n" "} while (x);", Style); Style.SpaceBeforeParens = FormatStyle::SBPO_Never; verifyFormat("List list;", Style); verifyFormat("Dictionary dict;", Style); verifyFormat("for(int i = 0; i < size(); i++) {\n" "}", Style); verifyFormat("foreach(var x in y) {\n" "}", Style); verifyFormat("switch(x) {}", Style); verifyFormat("do {\n" "} while(x);", Style); } TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat("(int)x / y;", Style); Style.SpaceAfterCStyleCast = true; verifyFormat("(int) x / y;", Style); } TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat(R"(string str = @"""";)", Style); verifyFormat(R"(string str = @"""Hello world""";)", Style); verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style); verifyFormat(R"(return $@"Foo ""/foo?f={Request.Query["f"]}""";)", Style); verifyFormat(R"(return @$"Foo ""/foo?f={Request.Query["f"]}""";)", Style); verifyFormat(R"(return @$"path\to\{specifiedFile}")", Style); } TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style); verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style); verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style); } TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) { // Use MS style as Google Style inserts a line break before multiline strings. // verifyFormat does not understand multiline C# string-literals // so check the format explicitly. FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); std::string Code = R"(string s1 = $@"some code: class {className} {{ {className}() {{}} }}";)"; EXPECT_EQ(Code, format(Code, Style)); // Multiline string in the middle of a function call. Code = R"( var x = foo(className, $@"some code: class {className} {{ {className}() {{}} }}", y);)"; // y aligned with `className` arg. EXPECT_EQ(Code, format(Code, Style)); // Interpolated string with embedded multiline string. Code = R"(Console.WriteLine($"{string.Join(@", ", values)}");)"; EXPECT_EQ(Code, format(Code, Style)); } TEST_F(FormatTestCSharp, CSharpNewOperator) { FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); verifyFormat("public void F() {\n" " var v = new C(() => { var t = 5; });\n" "}", Style); verifyFormat("public void F() {\n" " var v = new C(() => {\n" " try {\n" " } catch {\n" " var t = 5;\n" " }\n" " });\n" "}", Style); } TEST_F(FormatTestCSharp, CSharpLambdas) { FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// class MyClass { Action greet = name => { string greeting = $"Hello {name}!"; Console.WriteLine(greeting); }; })", GoogleStyle); // Microsoft Style: // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#statement-lambdas verifyFormat(R"(// class MyClass { Action greet = name => { string greeting = $"Hello {name}!"; Console.WriteLine(greeting); }; })", MicrosoftStyle); verifyFormat("void bar()\n" "{\n" " Function(Val, (Action)(() =>\n" " {\n" " lock (mylock)\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void baz()\n" "{\n" " Function(Val, (Action)(() =>\n" " {\n" " using (var a = new Lock())\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void baz()\n" "{\n" " Function(Val, (Action)(() =>\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void baz()\n" "{\n" " Function(Val, (Action)(() =>\n" " {\n" " do\n" " {\n" " A.Remove(item);\n" " } while (true)\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void baz()\n" "{\n" " Function(Val, (Action)(() =>\n" " { A.Remove(item); }));\n" "}", MicrosoftStyle); verifyFormat("void bar()\n" "{\n" " Function(Val, (() =>\n" " {\n" " lock (mylock)\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void bar()\n" "{\n" " Function((() =>\n" " {\n" " lock (mylock)\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", MicrosoftStyle); MicrosoftStyle.IndentWidth = 2; verifyFormat("void bar()\n" "{\n" " Function((() =>\n" " {\n" " lock (mylock)\n" " {\n" " if (true)\n" " {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", MicrosoftStyle); verifyFormat("void bar() {\n" " Function((() => {\n" " lock (mylock) {\n" " if (true) {\n" " A.Remove(item);\n" " }\n" " }\n" " }));\n" "}", GoogleStyle); } TEST_F(FormatTestCSharp, CSharpLambdasDontBreakFollowingCodeAlignment) { FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// public class Sample { public void Test() { while (true) { preBindEnumerators.RemoveAll(enumerator => !enumerator.MoveNext()); CodeThatFollowsLambda(); IsWellAligned(); } } })", MicrosoftStyle); verifyFormat(R"(// public class Sample { public void Test() { while (true) { preBindEnumerators.RemoveAll(enumerator => !enumerator.MoveNext()); CodeThatFollowsLambda(); IsWellAligned(); } } })", GoogleStyle); } TEST_F(FormatTestCSharp, CSharpLambdasComplexLambdasDontBreakAlignment) { FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// public class Test { private static void ComplexLambda(BuildReport protoReport) { allSelectedScenes = veryVeryLongCollectionNameThatPutsTheLineLenghtAboveTheThresholds.Where(scene => scene.enabled) .Select(scene => scene.path) .ToArray(); if (allSelectedScenes.Count == 0) { return; } Functions(); AreWell(); Aligned(); AfterLambdaBlock(); } })", MicrosoftStyle); verifyFormat(R"(// public class Test { private static void ComplexLambda(BuildReport protoReport) { allSelectedScenes = veryVeryLongCollectionNameThatPutsTheLineLenghtAboveTheThresholds .Where(scene => scene.enabled) .Select(scene => scene.path) .ToArray(); if (allSelectedScenes.Count == 0) { return; } Functions(); AreWell(); Aligned(); AfterLambdaBlock(); } })", GoogleStyle); } TEST_F(FormatTestCSharp, CSharpLambdasMulipleLambdasDontBreakAlignment) { FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LK_CSharp); FormatStyle MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// public class Test { private static void MultipleLambdas(BuildReport protoReport) { allSelectedScenes = veryVeryLongCollectionNameThatPutsTheLineLenghtAboveTheThresholds.Where(scene => scene.enabled) .Select(scene => scene.path) .ToArray(); preBindEnumerators.RemoveAll(enumerator => !enumerator.MoveNext()); if (allSelectedScenes.Count == 0) { return; } Functions(); AreWell(); Aligned(); AfterLambdaBlock(); } })", MicrosoftStyle); verifyFormat(R"(// public class Test { private static void MultipleLambdas(BuildReport protoReport) { allSelectedScenes = veryVeryLongCollectionNameThatPutsTheLineLenghtAboveTheThresholds .Where(scene => scene.enabled) .Select(scene => scene.path) .ToArray(); preBindEnumerators.RemoveAll(enumerator => !enumerator.MoveNext()); if (allSelectedScenes.Count == 0) { return; } Functions(); AreWell(); Aligned(); AfterLambdaBlock(); } })", GoogleStyle); } TEST_F(FormatTestCSharp, CSharpObjectInitializers) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); // Start code fragments with a comment line so that C++ raw string literals // as seen are identical to expected formatted code. verifyFormat(R"(// Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red, }, new Square { Side = 101.1, Colour = Colours.Yellow, }, };)", Style); // Omitted final `,`s will change the formatting. verifyFormat(R"(// Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, new Square { Side = 101.1, Colour = Colours.Yellow } };)", Style); // Lambdas can be supplied as initialiser arguments. verifyFormat(R"(// private Transformer _transformer = new X.Y { Filler = (Shape shape) => { return new Transform.Fill(shape, RED); }, Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); }, };)", Style); // Dictionary initialisation. verifyFormat(R"(// var myDict = new Dictionary { ["name"] = _donald, ["age"] = Convert.ToString(DateTime.Today.Year - 1934), ["type"] = _duck, };)", Style); } TEST_F(FormatTestCSharp, CSharpArrayInitializers) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// private MySet[] setPoints = { new Point(), new Point(), };)", Style); } TEST_F(FormatTestCSharp, CSharpNamedArguments) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");)", Style); // Ensure that trailing comments do not cause problems. verifyFormat(R"(// PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment sellerName: "Gift Shop");)", Style); verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style); } TEST_F(FormatTestCSharp, CSharpPropertyAccessors) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat("int Value { get }", Style); verifyFormat("int Value { get; }", Style); verifyFormat("int Value { internal get; }", Style); verifyFormat("int Value { get; } = 0", Style); verifyFormat("int Value { set }", Style); verifyFormat("int Value { set; }", Style); verifyFormat("int Value { init; }", Style); verifyFormat("int Value { internal set; }", Style); verifyFormat("int Value { set; } = 0", Style); verifyFormat("int Value { get; set }", Style); verifyFormat("int Value { get; init; }", Style); verifyFormat("int Value { set; get }", Style); verifyFormat("int Value { get; private set; }", Style); verifyFormat("int Value { get; set; }", Style); verifyFormat("int Value { get; set; } = 0", Style); verifyFormat("int Value { internal get; internal set; }", Style); // Do not wrap expression body definitions. verifyFormat(R"(// public string Name { get => _name; set => _name = value; })", Style); verifyFormat(R"(// public string Name { init => _name = value; get => _name; })", Style); verifyFormat(R"(// public string Name { set => _name = value; get => _name; })", Style); // Examples taken from // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties verifyFormat(R"( // Expression body definitions public class SaleItem { public decimal Price { get => _cost; set => _cost = value; } })", Style); verifyFormat(R"( // Properties with backing fields class TimePeriod { public double Hours { get { return _seconds / 3600; } set { if (value < 0 || value > 24) throw new ArgumentOutOfRangeException($"{nameof(value)} must be between 0 and 24."); _seconds = value * 3600; } } })", Style); verifyFormat(R"( // Auto-implemented properties public class SaleItem { public decimal Price { get; set; } })", Style); // Add column limit to wrap long lines. Style.ColumnLimit = 100; // Examples with assignment to default value. verifyFormat(R"( // Long assignment to default value class MyClass { public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, DefaultThirdArgument); })", Style); verifyFormat(R"( // Long assignment to default value with expression body class MyClass { public override VeryLongNamedTypeIndeed VeryLongNamedValue { get => veryLongNamedField; set => veryLongNamedField = value; } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument, DefaultThirdArgument); })", Style); // Brace wrapping and single-lining of accessor can be controlled by config. Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterFunction = true; verifyFormat(R"(// class TimePeriod { public double Hours { get { return _seconds / 3600; } set { _seconds = value * 3600; } } })", Style); // Microsoft style trivial property accessors have no line break before the // opening brace. auto MicrosoftStyle = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat(R"(// public class SaleItem { public decimal Price { get; set; } })", MicrosoftStyle); } TEST_F(FormatTestCSharp, DefaultLiteral) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); verifyFormat( "T[] InitializeArray(int length, T initialValue = default) {}", Style); verifyFormat("System.Numerics.Complex fillValue = default;", Style); verifyFormat("int Value { get } = default;", Style); verifyFormat("int Value { get } = default!;", Style); verifyFormat(R"(// public record Person { public string GetInit { get; init; } = default!; };)", Style); verifyFormat(R"(// public record Person { public string GetSet { get; set; } = default!; };)", Style); } TEST_F(FormatTestCSharp, CSharpSpaces) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeSquareBrackets = false; Style.SpacesInSquareBrackets = false; Style.SpaceBeforeCpp11BracedList = true; Style.Cpp11BracedListStyle = false; Style.SpacesInContainerLiterals = false; Style.SpaceAfterCStyleCast = false; verifyFormat(R"(new Car { "Door", 0.1 })", Style); verifyFormat(R"(new Car { 0.1, "Door" })", Style); verifyFormat(R"(new string[] { "A" })", Style); verifyFormat(R"(new string[] {})", Style); verifyFormat(R"(new Car { someVariableName })", Style); verifyFormat(R"(new Car { someVariableName })", Style); verifyFormat(R"(new Dictionary { ["Key"] = "Value" };)", Style); verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); verifyFormat(R"(bool[] xs = { true, true };)", Style); verifyIncompleteFormat( R"(taskContext.Factory.Run(async () => doThing(args);)", Style); verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); verifyFormat(R"(private float[,] Values;)", Style); verifyFormat(R"(Result this[Index x] => Foo(x);)", Style); verifyFormat(R"(char[,,] rawCharArray = MakeCharacterGrid();)", Style); verifyFormat(R"(var (key, value))", Style); // `&&` is not seen as a reference. verifyFormat(R"(A == typeof(X) && someBool)", Style); // Not seen as a C-style cast. verifyFormat(R"(// foreach ((A a, B b) in someList) { })", Style); // space after lock in `lock (processes)`. verifyFormat("lock (process)", Style); Style.SpacesInSquareBrackets = true; verifyFormat(R"(private float[ , ] Values;)", Style); verifyFormat(R"(string dirPath = args?[ 0 ];)", Style); verifyFormat(R"(char[ ,, ] rawCharArray = MakeCharacterGrid();)", Style); // Method returning tuple verifyFormat(R"(public (string name, int age) methodTuple() {})", Style); verifyFormat(R"(private (string name, int age) methodTuple() {})", Style); verifyFormat(R"(protected (string name, int age) methodTuple() {})", Style); verifyFormat(R"(virtual (string name, int age) methodTuple() {})", Style); verifyFormat(R"(extern (string name, int age) methodTuple() {})", Style); verifyFormat(R"(static (string name, int age) methodTuple() {})", Style); verifyFormat(R"(internal (string name, int age) methodTuple() {})", Style); verifyFormat(R"(abstract (string name, int age) methodTuple() {})", Style); verifyFormat(R"(sealed (string name, int age) methodTuple() {})", Style); verifyFormat(R"(override (string name, int age) methodTuple() {})", Style); verifyFormat(R"(async (string name, int age) methodTuple() {})", Style); verifyFormat(R"(unsafe (string name, int age) methodTuple() {})", Style); Style.SpacesInSquareBrackets = false; Style.SpaceBeforeSquareBrackets = true; verifyFormat("return a is [1, 2, 3];", Style); verifyFormat("return a is [..];", Style); Style.SpaceBeforeSquareBrackets = false; verifyFormat("return a is [1, 2, 3];", Style); verifyFormat("return a is [..];", Style); } TEST_F(FormatTestCSharp, CSharpNullableTypes) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpacesInSquareBrackets = false; verifyFormat(R"(// public class A { void foo() { int? value = some.bar(); } })", Style); // int? is nullable not a conditional expression. verifyFormat(R"(void foo(int? x, int? y, int? z) {})", Style); // Nullables in function definitions. verifyFormat(R"(public float? Value;)", Style); // no space before `?`. verifyFormat(R"(int?[] arr = new int?[10];)", Style); // An array of a nullable type. verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. verifyFormat(R"(var x = new MyContainer();)", Style); // Generics. verifyFormat(R"(// public interface I { int? Function(); })", Style); // Interface methods. Style.ColumnLimit = 10; verifyFormat(R"(// public VeryLongType? Function( int arg1, int arg2) { // })", Style); // ? sticks with identifier. } TEST_F(FormatTestCSharp, CSharpArraySubscripts) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); // Do not format array subscript operators as attributes. verifyFormat(R"(// if (someThings[index].Contains(myThing)) { })", Style); verifyFormat(R"(// if (someThings[i][j][k].Contains(myThing)) { })", Style); } TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); EXPECT_TRUE(Style.BraceWrapping.SplitEmptyRecord); verifyFormat("class ItemFactory\n" " where T : new() {\n" "}", Style); verifyFormat("class Dictionary\n" " where TKey : IComparable\n" " where TVal : IMyInterface {\n" " public void MyMethod(T t)\n" " where T : IMyInterface {\n" " doThing();\n" " }\n" "}", Style); verifyFormat("class ItemFactory\n" " where T : new(), IAnInterface, IAnotherInterface, " "IAnotherInterfaceStill {\n" "}", Style); Style.ColumnLimit = 50; // Force lines to be wrapped. verifyFormat(R"(// class ItemFactory where T : new(), IAnInterface, IAnotherInterface, IAnotherInterfaceStill { })", Style); // In other languages `where` can be used as a normal identifier. // This example is in C++! verifyFormat(R"(// class A { int f(int where) {} };)", getGoogleStyle(FormatStyle::LK_Cpp)); } TEST_F(FormatTestCSharp, CSharpAfterEnum) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterEnum = false; Style.AllowShortEnumsOnASingleLine = false; verifyFormat("enum MyEnum {\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("internal enum MyEnum {\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("public enum MyEnum {\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("protected enum MyEnum {\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("private enum MyEnum {\n" " Foo,\n" " Bar,\n" "}", Style); Style.BraceWrapping.AfterEnum = true; Style.AllowShortEnumsOnASingleLine = false; verifyFormat("enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("internal enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("public enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("protected enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("private enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("/* Foo */ private enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); verifyFormat("/* Foo */ /* Bar */ private enum MyEnum\n" "{\n" " Foo,\n" " Bar,\n" "}", Style); } TEST_F(FormatTestCSharp, CSharpAfterClass) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterClass = false; verifyFormat("class MyClass {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("internal class MyClass {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("public class MyClass {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("protected class MyClass {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("private class MyClass {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("interface Interface {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("internal interface Interface {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("public interface Interface {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("protected interface Interface {\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("private interface Interface {\n" " int a;\n" " int b;\n" "}", Style); Style.BraceWrapping.AfterClass = true; verifyFormat("class MyClass\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("internal class MyClass\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("public class MyClass\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("protected class MyClass\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("private class MyClass\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("internal interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("public interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("protected interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("private interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("/* Foo */ private interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); verifyFormat("/* Foo */ /* Bar */ private interface MyInterface\n" "{\n" " int a;\n" " int b;\n" "}", Style); } TEST_F(FormatTestCSharp, NamespaceIndentation) { FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); Style.NamespaceIndentation = FormatStyle::NI_None; verifyFormat("namespace A\n" "{\n" "public interface Name1\n" "{\n" "}\n" "}\n", Style); verifyFormat("namespace A.B\n" "{\n" "public interface Name1\n" "{\n" "}\n" "}\n", Style); Style.NamespaceIndentation = FormatStyle::NI_Inner; verifyFormat("namespace A\n" "{\n" "namespace B\n" "{\n" " public interface Name1\n" " {\n" " }\n" "}\n" "}\n", Style); Style.NamespaceIndentation = FormatStyle::NI_All; verifyFormat("namespace A.B\n" "{\n" " public interface Name1\n" " {\n" " }\n" "}\n", Style); verifyFormat("namespace A\n" "{\n" " namespace B\n" " {\n" " public interface Name1\n" " {\n" " }\n" " }\n" "}\n", Style); } TEST_F(FormatTestCSharp, SwitchExpression) { FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); verifyFormat("int x = a switch {\n" " 1 => (0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0),\n" " 2 => 1,\n" " _ => 2\n" "};\n", Style); } TEST_F(FormatTestCSharp, EmptyShortBlock) { auto Style = getLLVMStyle(); Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Empty; verifyFormat("try {\n" " doA();\n" "} catch (Exception e) {\n" " e.printStackTrace();\n" "}\n", Style); verifyFormat("try {\n" " doA();\n" "} catch (Exception e) {}\n", Style); } TEST_F(FormatTestCSharp, ShortFunctions) { FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); Style.NamespaceIndentation = FormatStyle::NI_All; Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; verifyFormat("interface Interface {\n" " void f() { return; }\n" "};", Style); verifyFormat("public interface Interface {\n" " void f() { return; }\n" "};", Style); verifyFormat("namespace {\n" " void f() {\n" " return;\n" " }\n" "};", Style); // "union" is not a keyword in C#. verifyFormat("namespace union {\n" " void f() {\n" " return;\n" " }\n" "};", Style); } TEST_F(FormatTestCSharp, BrokenBrackets) { EXPECT_NE("", format("int where b <")); // reduced from crasher } } // namespace } // namespace test } // namespace format } // namespace clang