xref: /llvm-project/clang/unittests/Format/FormatTestCSharp.cpp (revision fe61bc1a0b5a00badae9334e01e103769af4fa6c)
1cbb726d0SPaul Hoad //===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===//
2cbb726d0SPaul Hoad //
3cbb726d0SPaul Hoad // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4cbb726d0SPaul Hoad // See https://llvm.org/LICENSE.txt for license information.
5cbb726d0SPaul Hoad // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cbb726d0SPaul Hoad //
7cbb726d0SPaul Hoad //===----------------------------------------------------------------------===//
8cbb726d0SPaul Hoad 
9cbb726d0SPaul Hoad #include "FormatTestUtils.h"
10cbb726d0SPaul Hoad #include "clang/Format/Format.h"
11cbb726d0SPaul Hoad #include "llvm/Support/Debug.h"
12cbb726d0SPaul Hoad #include "gtest/gtest.h"
13cbb726d0SPaul Hoad 
14cbb726d0SPaul Hoad #define DEBUG_TYPE "format-test"
15cbb726d0SPaul Hoad 
16cbb726d0SPaul Hoad namespace clang {
17cbb726d0SPaul Hoad namespace format {
18cbb726d0SPaul Hoad 
19cbb726d0SPaul Hoad class FormatTestCSharp : public ::testing::Test {
20cbb726d0SPaul Hoad protected:
21cbb726d0SPaul Hoad   static std::string format(llvm::StringRef Code, unsigned Offset,
22cbb726d0SPaul Hoad                             unsigned Length, const FormatStyle &Style) {
23cbb726d0SPaul Hoad     LLVM_DEBUG(llvm::errs() << "---\n");
24cbb726d0SPaul Hoad     LLVM_DEBUG(llvm::errs() << Code << "\n\n");
25cbb726d0SPaul Hoad     std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
26cbb726d0SPaul Hoad     tooling::Replacements Replaces = reformat(Style, Code, Ranges);
27cbb726d0SPaul Hoad     auto Result = applyAllReplacements(Code, Replaces);
28cbb726d0SPaul Hoad     EXPECT_TRUE(static_cast<bool>(Result));
29cbb726d0SPaul Hoad     LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
30cbb726d0SPaul Hoad     return *Result;
31cbb726d0SPaul Hoad   }
32cbb726d0SPaul Hoad 
33cbb726d0SPaul Hoad   static std::string
34cbb726d0SPaul Hoad   format(llvm::StringRef Code,
35a2f963bbSPaul Hoad          const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) {
36cbb726d0SPaul Hoad     return format(Code, 0, Code.size(), Style);
37cbb726d0SPaul Hoad   }
38cbb726d0SPaul Hoad 
39cbb726d0SPaul Hoad   static FormatStyle getStyleWithColumns(unsigned ColumnLimit) {
40a2f963bbSPaul Hoad     FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp);
41cbb726d0SPaul Hoad     Style.ColumnLimit = ColumnLimit;
42cbb726d0SPaul Hoad     return Style;
43cbb726d0SPaul Hoad   }
44cbb726d0SPaul Hoad 
45cbb726d0SPaul Hoad   static void verifyFormat(
46cbb726d0SPaul Hoad       llvm::StringRef Code,
47a2f963bbSPaul Hoad       const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) {
48cbb726d0SPaul Hoad     EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable";
49cbb726d0SPaul Hoad     EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
50cbb726d0SPaul Hoad   }
51cbb726d0SPaul Hoad };
52cbb726d0SPaul Hoad 
53cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpClass) {
54a2f963bbSPaul Hoad   verifyFormat("public class SomeClass\n"
55a2f963bbSPaul Hoad                "{\n"
56a2f963bbSPaul Hoad                "    void f()\n"
57a2f963bbSPaul Hoad                "    {\n"
58a2f963bbSPaul Hoad                "    }\n"
59a2f963bbSPaul Hoad                "    int g()\n"
60a2f963bbSPaul Hoad                "    {\n"
61a2f963bbSPaul Hoad                "        return 0;\n"
62a2f963bbSPaul Hoad                "    }\n"
63a2f963bbSPaul Hoad                "    void h()\n"
64a2f963bbSPaul Hoad                "    {\n"
65a2f963bbSPaul Hoad                "        while (true)\n"
66a2f963bbSPaul Hoad                "            f();\n"
67a2f963bbSPaul Hoad                "        for (;;)\n"
68a2f963bbSPaul Hoad                "            f();\n"
69a2f963bbSPaul Hoad                "        if (true)\n"
70a2f963bbSPaul Hoad                "            f();\n"
71cbb726d0SPaul Hoad                "    }\n"
72cbb726d0SPaul Hoad                "}");
73f40a7972SJonathan Coe 
74f40a7972SJonathan Coe   // Ensure that small and empty classes are handled correctly with condensed
75f40a7972SJonathan Coe   // (Google C++-like) brace-breaking style.
76f40a7972SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
77f40a7972SJonathan Coe   Style.BreakBeforeBraces = FormatStyle::BS_Attach;
78f40a7972SJonathan Coe 
79f40a7972SJonathan Coe   verifyFormat("public class SomeEmptyClass {}", Style);
80f40a7972SJonathan Coe 
81f40a7972SJonathan Coe   verifyFormat("public class SomeTinyClass {\n"
82f40a7972SJonathan Coe                "  int X;\n"
83f40a7972SJonathan Coe                "}",
84f40a7972SJonathan Coe                Style);
85f40a7972SJonathan Coe   verifyFormat("private class SomeTinyClass {\n"
86f40a7972SJonathan Coe                "  int X;\n"
87f40a7972SJonathan Coe                "}",
88f40a7972SJonathan Coe                Style);
89f40a7972SJonathan Coe   verifyFormat("protected class SomeTinyClass {\n"
90f40a7972SJonathan Coe                "  int X;\n"
91f40a7972SJonathan Coe                "}",
92f40a7972SJonathan Coe                Style);
93f40a7972SJonathan Coe   verifyFormat("internal class SomeTinyClass {\n"
94f40a7972SJonathan Coe                "  int X;\n"
95f40a7972SJonathan Coe                "}",
96f40a7972SJonathan Coe                Style);
97cbb726d0SPaul Hoad }
98cbb726d0SPaul Hoad 
99cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, AccessModifiers) {
100a2f963bbSPaul Hoad   verifyFormat("public String toString()\n"
101a2f963bbSPaul Hoad                "{\n"
102a2f963bbSPaul Hoad                "}");
103a2f963bbSPaul Hoad   verifyFormat("private String toString()\n"
104a2f963bbSPaul Hoad                "{\n"
105a2f963bbSPaul Hoad                "}");
106a2f963bbSPaul Hoad   verifyFormat("protected String toString()\n"
107a2f963bbSPaul Hoad                "{\n"
108a2f963bbSPaul Hoad                "}");
109a2f963bbSPaul Hoad   verifyFormat("internal String toString()\n"
110a2f963bbSPaul Hoad                "{\n"
111a2f963bbSPaul Hoad                "}");
112cbb726d0SPaul Hoad 
113a2f963bbSPaul Hoad   verifyFormat("public override String toString()\n"
114a2f963bbSPaul Hoad                "{\n"
115a2f963bbSPaul Hoad                "}");
116a2f963bbSPaul Hoad   verifyFormat("private override String toString()\n"
117a2f963bbSPaul Hoad                "{\n"
118a2f963bbSPaul Hoad                "}");
119a2f963bbSPaul Hoad   verifyFormat("protected override String toString()\n"
120a2f963bbSPaul Hoad                "{\n"
121a2f963bbSPaul Hoad                "}");
122a2f963bbSPaul Hoad   verifyFormat("internal override String toString()\n"
123a2f963bbSPaul Hoad                "{\n"
124a2f963bbSPaul Hoad                "}");
125cbb726d0SPaul Hoad 
126a2f963bbSPaul Hoad   verifyFormat("internal static String toString()\n"
127a2f963bbSPaul Hoad                "{\n"
128a2f963bbSPaul Hoad                "}");
129cbb726d0SPaul Hoad }
130cbb726d0SPaul Hoad 
131cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, NoStringLiteralBreaks) {
132cbb726d0SPaul Hoad   verifyFormat("foo("
133cbb726d0SPaul Hoad                "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
134cbb726d0SPaul Hoad                "aaaaaa\");");
135cbb726d0SPaul Hoad }
136cbb726d0SPaul Hoad 
137cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpVerbatiumStringLiterals) {
138cbb726d0SPaul Hoad   verifyFormat("foo(@\"aaaaaaaa\\abc\\aaaa\");");
139cbb726d0SPaul Hoad   // @"ABC\" + ToString("B") - handle embedded \ in literal string at
140cbb726d0SPaul Hoad   // the end
141cbb726d0SPaul Hoad   //
142cbb726d0SPaul Hoad   /*
143cbb726d0SPaul Hoad    * After removal of Lexer change we are currently not able
144cbb726d0SPaul Hoad    * To handle these cases
145cbb726d0SPaul Hoad    verifyFormat("string s = @\"ABC\\\" + ToString(\"B\");");
146cbb726d0SPaul Hoad    verifyFormat("string s = @\"ABC\"\"DEF\"\"GHI\"");
147cbb726d0SPaul Hoad    verifyFormat("string s = @\"ABC\"\"DEF\"\"\"");
148cbb726d0SPaul Hoad    verifyFormat("string s = @\"ABC\"\"DEF\"\"\" + abc");
149cbb726d0SPaul Hoad   */
150cbb726d0SPaul Hoad }
151cbb726d0SPaul Hoad 
152cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpInterpolatedStringLiterals) {
153cbb726d0SPaul Hoad   verifyFormat("foo($\"aaaaaaaa{aaa}aaaa\");");
154cbb726d0SPaul Hoad   verifyFormat("foo($\"aaaa{A}\");");
155cbb726d0SPaul Hoad   verifyFormat(
156cbb726d0SPaul Hoad       "foo($\"aaaa{A}"
157cbb726d0SPaul Hoad       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");");
158cbb726d0SPaul Hoad   verifyFormat("Name = $\"{firstName} {lastName}\";");
159cbb726d0SPaul Hoad 
160cbb726d0SPaul Hoad   // $"ABC\" + ToString("B") - handle embedded \ in literal string at
161cbb726d0SPaul Hoad   // the end
162cbb726d0SPaul Hoad   verifyFormat("string s = $\"A{abc}BC\" + ToString(\"B\");");
163cbb726d0SPaul Hoad   verifyFormat("$\"{domain}\\\\{user}\"");
164cbb726d0SPaul Hoad   verifyFormat(
165cbb726d0SPaul Hoad       "var verbatimInterpolated = $@\"C:\\Users\\{userName}\\Documents\\\";");
166cbb726d0SPaul Hoad }
167cbb726d0SPaul Hoad 
168cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpFatArrows) {
169cbb726d0SPaul Hoad   verifyFormat("Task serverTask = Task.Run(async() => {");
170cbb726d0SPaul Hoad   verifyFormat("public override string ToString() => \"{Name}\\{Age}\";");
171cbb726d0SPaul Hoad }
172cbb726d0SPaul Hoad 
173c3af063cSJonathan Coe TEST_F(FormatTestCSharp, CSharpConditionalExpressions) {
174c3af063cSJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
175c3af063cSJonathan Coe   // conditional expression is not seen as a NullConditional.
176c3af063cSJonathan Coe   verifyFormat("var y = A < B ? -1 : 1;", Style);
177c3af063cSJonathan Coe }
178c3af063cSJonathan Coe 
179cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpNullConditional) {
1804c056583SPaul Hoad   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
1814c056583SPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
1824c056583SPaul Hoad 
183cbb726d0SPaul Hoad   verifyFormat(
184cbb726d0SPaul Hoad       "public Person(string firstName, string lastName, int? age = null)");
185cbb726d0SPaul Hoad 
1864c056583SPaul Hoad   verifyFormat("foo () {\n"
1874c056583SPaul Hoad                "  switch (args?.Length) {}\n"
1884c056583SPaul Hoad                "}",
1894c056583SPaul Hoad                Style);
1904c056583SPaul Hoad 
1914c056583SPaul Hoad   verifyFormat("switch (args?.Length) {}", Style);
192cbb726d0SPaul Hoad 
193a2f963bbSPaul Hoad   verifyFormat("public static void Main(string[] args)\n"
194a2f963bbSPaul Hoad                "{\n"
195a2f963bbSPaul Hoad                "    string dirPath = args?[0];\n"
196a2f963bbSPaul Hoad                "}");
1974c056583SPaul Hoad 
1984c056583SPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Never;
1994c056583SPaul Hoad 
2004c056583SPaul Hoad   verifyFormat("switch(args?.Length) {}", Style);
201cbb726d0SPaul Hoad }
202cbb726d0SPaul Hoad 
203cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, Attributes) {
204cbb726d0SPaul Hoad   verifyFormat("[STAThread]\n"
205a2f963bbSPaul Hoad                "static void Main(string[] args)\n"
206a2f963bbSPaul Hoad                "{\n"
207a2f963bbSPaul Hoad                "}");
208cbb726d0SPaul Hoad 
209cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
210a2f963bbSPaul Hoad                "private class Test\n"
211a2f963bbSPaul Hoad                "{\n"
212a2f963bbSPaul Hoad                "}");
213cbb726d0SPaul Hoad 
214cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
215a2f963bbSPaul Hoad                "protected class Test\n"
216a2f963bbSPaul Hoad                "{\n"
217a2f963bbSPaul Hoad                "}");
218cbb726d0SPaul Hoad 
219cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
220a2f963bbSPaul Hoad                "internal class Test\n"
221a2f963bbSPaul Hoad                "{\n"
222a2f963bbSPaul Hoad                "}");
223cbb726d0SPaul Hoad 
224cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
225a2f963bbSPaul Hoad                "class Test\n"
226a2f963bbSPaul Hoad                "{\n"
227a2f963bbSPaul Hoad                "}");
228cbb726d0SPaul Hoad 
229cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
230cbb726d0SPaul Hoad                "[DeploymentItem(\"Test.txt\")]\n"
231a2f963bbSPaul Hoad                "public class Test\n"
232a2f963bbSPaul Hoad                "{\n"
233a2f963bbSPaul Hoad                "}");
234cbb726d0SPaul Hoad 
235cbb726d0SPaul Hoad   verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n"
236cbb726d0SPaul Hoad                "[System.Runtime.InteropServices.ComVisible(true)]\n"
237a2f963bbSPaul Hoad                "public sealed class STAThreadAttribute : Attribute\n"
238a2f963bbSPaul Hoad                "{\n"
239a2f963bbSPaul Hoad                "}");
240cbb726d0SPaul Hoad 
241cbb726d0SPaul Hoad   verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on "
242cbb726d0SPaul Hoad                "provided port\")]\n"
243a2f963bbSPaul Hoad                "class Test\n"
244a2f963bbSPaul Hoad                "{\n"
245a2f963bbSPaul Hoad                "}");
246cbb726d0SPaul Hoad 
247cbb726d0SPaul Hoad   verifyFormat("[TestMethod]\n"
248a2f963bbSPaul Hoad                "public string Host\n"
2492bd6974aSJonathan Coe                "{ set; get; }");
250cbb726d0SPaul Hoad 
251cbb726d0SPaul Hoad   verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server "
252cbb726d0SPaul Hoad                "listening on provided host\")]\n"
253a2f963bbSPaul Hoad                "public string Host\n"
2542bd6974aSJonathan Coe                "{ set; get; }");
25550d8977cSJonathan Coe 
25650d8977cSJonathan Coe   verifyFormat(
25750d8977cSJonathan Coe       "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n"
25850d8977cSJonathan Coe       "// The const char* returned by hello_world must not be deleted.\n"
25950d8977cSJonathan Coe       "private static extern IntPtr HelloFromCpp();)");
260ca1fd460SJonathan Coe 
261b46f925dSJonathan Coe   // Class attributes go on their own line and do not affect layout of
262b46f925dSJonathan Coe   // interfaces. Line wrapping decisions previously caused each interface to be
263b46f925dSJonathan Coe   // on its own line.
264b46f925dSJonathan Coe   verifyFormat("[SomeAttribute]\n"
265b46f925dSJonathan Coe                "[SomeOtherAttribute]\n"
266b46f925dSJonathan Coe                "public class A : IShape, IAnimal, IVehicle\n"
267b46f925dSJonathan Coe                "{\n"
268b46f925dSJonathan Coe                "    int X;\n"
269b46f925dSJonathan Coe                "}");
270b46f925dSJonathan Coe 
271b46f925dSJonathan Coe   // Attributes in a method declaration do not cause line wrapping.
272b46f925dSJonathan Coe   verifyFormat("void MethodA([In][Out] ref double x)\n"
273b46f925dSJonathan Coe                "{\n"
274b46f925dSJonathan Coe                "}");
275b46f925dSJonathan Coe 
2769f8a7e82SJonathan Coe   // [] in an attribute do not cause premature line wrapping or indenting.
2779f8a7e82SJonathan Coe   verifyFormat(R"(//
2789f8a7e82SJonathan Coe public class A
2799f8a7e82SJonathan Coe {
2809f8a7e82SJonathan Coe     [SomeAttribute(new[] { RED, GREEN, BLUE }, -1.0f, 1.0f)]
2819f8a7e82SJonathan Coe     [DoNotSerialize]
2829f8a7e82SJonathan Coe     public Data MemberVariable;
2839f8a7e82SJonathan Coe })");
2849f8a7e82SJonathan Coe 
285ca1fd460SJonathan Coe   //  Unwrappable lines go on a line of their own.
286ca1fd460SJonathan Coe   // 'target:' is not treated as a label.
287ca1fd460SJonathan Coe   // Modify Style to enforce a column limit.
288ca1fd460SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
289ca1fd460SJonathan Coe   Style.ColumnLimit = 10;
290ca1fd460SJonathan Coe   verifyFormat(R"([assembly:InternalsVisibleTo(
291ca1fd460SJonathan Coe     "SomeAssembly, PublicKey=SomePublicKeyThatExceedsTheColumnLimit")])",
292ca1fd460SJonathan Coe                Style);
293cbb726d0SPaul Hoad }
294cbb726d0SPaul Hoad 
295719087bbSPaul Hoad TEST_F(FormatTestCSharp, CSharpUsing) {
296719087bbSPaul Hoad   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
297719087bbSPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
298719087bbSPaul Hoad   verifyFormat("public void foo () {\n"
299719087bbSPaul Hoad                "  using (StreamWriter sw = new StreamWriter (filenameA)) {}\n"
3001e0174a9SKrasimir Georgiev                "  using () {}\n"
301719087bbSPaul Hoad                "}",
302719087bbSPaul Hoad                Style);
303719087bbSPaul Hoad 
3041e0174a9SKrasimir Georgiev   // Ensure clang-format affects top-level snippets correctly.
3054c056583SPaul Hoad   verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}",
3064c056583SPaul Hoad                Style);
3074c056583SPaul Hoad 
308719087bbSPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Never;
309719087bbSPaul Hoad   verifyFormat("public void foo() {\n"
310719087bbSPaul Hoad                "  using(StreamWriter sw = new StreamWriter(filenameB)) {}\n"
3111e0174a9SKrasimir Georgiev                "  using() {}\n"
312719087bbSPaul Hoad                "}",
313719087bbSPaul Hoad                Style);
3144c056583SPaul Hoad 
3151e0174a9SKrasimir Georgiev   // Ensure clang-format affects top-level snippets correctly.
3161e0174a9SKrasimir Georgiev   verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}",
3171e0174a9SKrasimir Georgiev                Style);
3181e0174a9SKrasimir Georgiev 
3191e0174a9SKrasimir Georgiev   Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements;
3201e0174a9SKrasimir Georgiev   verifyFormat("public void foo() {\n"
3211e0174a9SKrasimir Georgiev                "  using (StreamWriter sw = new StreamWriter(filenameA)) {}\n"
3221e0174a9SKrasimir Georgiev                "  using () {}\n"
3231e0174a9SKrasimir Georgiev                "}",
3241e0174a9SKrasimir Georgiev                Style);
3251e0174a9SKrasimir Georgiev 
3261e0174a9SKrasimir Georgiev   // Ensure clang-format affects top-level snippets correctly.
3271e0174a9SKrasimir Georgiev   verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}",
3281e0174a9SKrasimir Georgiev                Style);
3291e0174a9SKrasimir Georgiev 
3301e0174a9SKrasimir Georgiev   Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses;
3311e0174a9SKrasimir Georgiev   verifyFormat("public void foo() {\n"
3321e0174a9SKrasimir Georgiev                "  using (StreamWriter sw = new StreamWriter (filenameA)) {}\n"
3331e0174a9SKrasimir Georgiev                "  using() {}\n"
3341e0174a9SKrasimir Georgiev                "}",
3351e0174a9SKrasimir Georgiev                Style);
3361e0174a9SKrasimir Georgiev 
3371e0174a9SKrasimir Georgiev   // Ensure clang-format affects top-level snippets correctly.
3384c056583SPaul Hoad   verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}",
3394c056583SPaul Hoad                Style);
340719087bbSPaul Hoad }
341719087bbSPaul Hoad 
342cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpRegions) {
343cbb726d0SPaul Hoad   verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa "
344cbb726d0SPaul Hoad                "aaaaaaaaaaaaaaa long region");
345cbb726d0SPaul Hoad }
346cbb726d0SPaul Hoad 
347cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) {
348cbb726d0SPaul Hoad   verifyFormat("public enum var { none, @string, bool, @enum }");
349cbb726d0SPaul Hoad }
350cbb726d0SPaul Hoad 
351cbb726d0SPaul Hoad TEST_F(FormatTestCSharp, CSharpNullCoalescing) {
352cbb726d0SPaul Hoad   verifyFormat("var test = ABC ?? DEF");
353cbb726d0SPaul Hoad   verifyFormat("string myname = name ?? \"ABC\";");
354cbb726d0SPaul Hoad   verifyFormat("return _name ?? \"DEF\";");
355cbb726d0SPaul Hoad }
356cbb726d0SPaul Hoad 
357a2f963bbSPaul Hoad TEST_F(FormatTestCSharp, AttributesIndentation) {
358a2f963bbSPaul Hoad   FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp);
359a2f963bbSPaul Hoad   Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None;
360a2f963bbSPaul Hoad 
361a2f963bbSPaul Hoad   verifyFormat("[STAThread]\n"
362a2f963bbSPaul Hoad                "static void Main(string[] args)\n"
363a2f963bbSPaul Hoad                "{\n"
364a2f963bbSPaul Hoad                "}",
365a2f963bbSPaul Hoad                Style);
366a2f963bbSPaul Hoad 
367a2f963bbSPaul Hoad   verifyFormat("[STAThread]\n"
368a2f963bbSPaul Hoad                "void "
369a2f963bbSPaul Hoad                "veryLooooooooooooooongFunctionName(string[] args)\n"
370a2f963bbSPaul Hoad                "{\n"
371a2f963bbSPaul Hoad                "}",
372a2f963bbSPaul Hoad                Style);
373a2f963bbSPaul Hoad 
374a2f963bbSPaul Hoad   verifyFormat("[STAThread]\n"
375a2f963bbSPaul Hoad                "veryLoooooooooooooooooooongReturnType "
376a2f963bbSPaul Hoad                "veryLooooooooooooooongFunctionName(string[] args)\n"
377a2f963bbSPaul Hoad                "{\n"
378a2f963bbSPaul Hoad                "}",
379a2f963bbSPaul Hoad                Style);
380a2f963bbSPaul Hoad 
381a2f963bbSPaul Hoad   verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n"
382a2f963bbSPaul Hoad                "public override X Y()\n"
383a2f963bbSPaul Hoad                "{\n"
384a2f963bbSPaul Hoad                "}\n",
385a2f963bbSPaul Hoad                Style);
386a2f963bbSPaul Hoad 
387a2f963bbSPaul Hoad   verifyFormat("[SuppressMessage]\n"
388a2f963bbSPaul Hoad                "public X Y()\n"
389a2f963bbSPaul Hoad                "{\n"
390a2f963bbSPaul Hoad                "}\n",
391a2f963bbSPaul Hoad                Style);
392a2f963bbSPaul Hoad 
393a2f963bbSPaul Hoad   verifyFormat("[SuppressMessage]\n"
394a2f963bbSPaul Hoad                "public override X Y()\n"
395a2f963bbSPaul Hoad                "{\n"
396a2f963bbSPaul Hoad                "}\n",
397a2f963bbSPaul Hoad                Style);
398a2f963bbSPaul Hoad 
399a2f963bbSPaul Hoad   verifyFormat("public A(B b) : base(b)\n"
400a2f963bbSPaul Hoad                "{\n"
401a2f963bbSPaul Hoad                "    [SuppressMessage]\n"
402a2f963bbSPaul Hoad                "    public override X Y()\n"
403a2f963bbSPaul Hoad                "    {\n"
404a2f963bbSPaul Hoad                "    }\n"
405a2f963bbSPaul Hoad                "}\n",
406a2f963bbSPaul Hoad                Style);
4072f209ccfSmydeveloperday 
4082f209ccfSmydeveloperday   verifyFormat("public A : Base\n"
4092f209ccfSmydeveloperday                "{\n"
4102f209ccfSmydeveloperday                "}\n"
4112f209ccfSmydeveloperday                "[Test]\n"
4122f209ccfSmydeveloperday                "public Foo()\n"
4132f209ccfSmydeveloperday                "{\n"
4142f209ccfSmydeveloperday                "}\n",
4152f209ccfSmydeveloperday                Style);
4162f209ccfSmydeveloperday 
4172f209ccfSmydeveloperday   verifyFormat("namespace\n"
4182f209ccfSmydeveloperday                "{\n"
4192f209ccfSmydeveloperday                "public A : Base\n"
4202f209ccfSmydeveloperday                "{\n"
4212f209ccfSmydeveloperday                "}\n"
4222f209ccfSmydeveloperday                "[Test]\n"
4232f209ccfSmydeveloperday                "public Foo()\n"
4242f209ccfSmydeveloperday                "{\n"
4252f209ccfSmydeveloperday                "}\n"
4262f209ccfSmydeveloperday                "}\n",
4272f209ccfSmydeveloperday                Style);
428a2f963bbSPaul Hoad }
429a2f963bbSPaul Hoad 
4304c056583SPaul Hoad TEST_F(FormatTestCSharp, CSharpSpaceBefore) {
4314c056583SPaul Hoad   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
4324c056583SPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
4334c056583SPaul Hoad 
4344c056583SPaul Hoad   verifyFormat("List<string> list;", Style);
4354c056583SPaul Hoad   verifyFormat("Dictionary<string, string> dict;", Style);
4364c056583SPaul Hoad 
4374c056583SPaul Hoad   verifyFormat("for (int i = 0; i < size (); i++) {\n"
4384c056583SPaul Hoad                "}",
4394c056583SPaul Hoad                Style);
4404c056583SPaul Hoad   verifyFormat("foreach (var x in y) {\n"
4414c056583SPaul Hoad                "}",
4424c056583SPaul Hoad                Style);
4434c056583SPaul Hoad   verifyFormat("switch (x) {}", Style);
4444c056583SPaul Hoad   verifyFormat("do {\n"
4454c056583SPaul Hoad                "} while (x);",
4464c056583SPaul Hoad                Style);
4474c056583SPaul Hoad 
4484c056583SPaul Hoad   Style.SpaceBeforeParens = FormatStyle::SBPO_Never;
4494c056583SPaul Hoad 
4504c056583SPaul Hoad   verifyFormat("List<string> list;", Style);
4514c056583SPaul Hoad   verifyFormat("Dictionary<string, string> dict;", Style);
4524c056583SPaul Hoad 
4534c056583SPaul Hoad   verifyFormat("for(int i = 0; i < size(); i++) {\n"
4544c056583SPaul Hoad                "}",
4554c056583SPaul Hoad                Style);
4564c056583SPaul Hoad   verifyFormat("foreach(var x in y) {\n"
4574c056583SPaul Hoad                "}",
4584c056583SPaul Hoad                Style);
4594c056583SPaul Hoad   verifyFormat("switch(x) {}", Style);
4604c056583SPaul Hoad   verifyFormat("do {\n"
4614c056583SPaul Hoad                "} while(x);",
4624c056583SPaul Hoad                Style);
4634c056583SPaul Hoad }
4644c056583SPaul Hoad 
465d82adf32Smydeveloperday TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) {
466d82adf32Smydeveloperday   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
467d82adf32Smydeveloperday 
468d82adf32Smydeveloperday   verifyFormat("(int)x / y;", Style);
469d82adf32Smydeveloperday 
470d82adf32Smydeveloperday   Style.SpaceAfterCStyleCast = true;
471d82adf32Smydeveloperday   verifyFormat("(int) x / y;", Style);
472d82adf32Smydeveloperday }
473d82adf32Smydeveloperday 
47436a8f7f6SKrasimir Georgiev TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) {
47536a8f7f6SKrasimir Georgiev   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
47636a8f7f6SKrasimir Georgiev 
477f9f0919dSJonathan Coe   verifyFormat(R"(string str = @"""";)", Style);
478f9f0919dSJonathan Coe   verifyFormat(R"(string str = @"""Hello world""";)", Style);
479f9f0919dSJonathan Coe   verifyFormat(R"(string str = $@"""Hello {friend}""";)", Style);
48036a8f7f6SKrasimir Georgiev }
48136a8f7f6SKrasimir Georgiev 
4829d212e83SJonathan Coe TEST_F(FormatTestCSharp, CSharpQuotesInInterpolatedStrings) {
4839d212e83SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
4849d212e83SJonathan Coe 
4859d212e83SJonathan Coe   verifyFormat(R"(string str1 = $"{null ?? "null"}";)", Style);
4869d212e83SJonathan Coe   verifyFormat(R"(string str2 = $"{{{braceCount} braces";)", Style);
4879d212e83SJonathan Coe   verifyFormat(R"(string str3 = $"{braceCount}}} braces";)", Style);
4889d212e83SJonathan Coe }
4899d212e83SJonathan Coe 
490f9f0919dSJonathan Coe TEST_F(FormatTestCSharp, CSharpNewlinesInVerbatimStrings) {
491f9f0919dSJonathan Coe   // Use MS style as Google Style inserts a line break before multiline strings.
492f9f0919dSJonathan Coe 
493f9f0919dSJonathan Coe   // verifyFormat does not understand multiline C# string-literals
494f9f0919dSJonathan Coe   // so check the format explicitly.
495f9f0919dSJonathan Coe 
496f9f0919dSJonathan Coe   FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp);
497f9f0919dSJonathan Coe 
498f9f0919dSJonathan Coe   std::string Code = R"(string s1 = $@"some code:
499f9f0919dSJonathan Coe   class {className} {{
500f9f0919dSJonathan Coe     {className}() {{}}
501f9f0919dSJonathan Coe   }}";)";
502f9f0919dSJonathan Coe 
503f9f0919dSJonathan Coe   EXPECT_EQ(Code, format(Code, Style));
504f9f0919dSJonathan Coe 
505f9f0919dSJonathan Coe   // Multiline string in the middle of a function call.
506f9f0919dSJonathan Coe   Code = R"(
507f9f0919dSJonathan Coe var x = foo(className, $@"some code:
508f9f0919dSJonathan Coe   class {className} {{
509f9f0919dSJonathan Coe     {className}() {{}}
510f9f0919dSJonathan Coe   }}",
511f9f0919dSJonathan Coe             y);)"; // y aligned with `className` arg.
512f9f0919dSJonathan Coe 
513f9f0919dSJonathan Coe   EXPECT_EQ(Code, format(Code, Style));
514f9f0919dSJonathan Coe 
515f9f0919dSJonathan Coe   // Interpolated string with embedded multiline string.
516f9f0919dSJonathan Coe   Code = R"(Console.WriteLine($"{string.Join(@",
517f9f0919dSJonathan Coe 		", values)}");)";
518f9f0919dSJonathan Coe 
519f9f0919dSJonathan Coe   EXPECT_EQ(Code, format(Code, Style));
520f9f0919dSJonathan Coe }
521f9f0919dSJonathan Coe 
5220bb60e29SJonathan Coe TEST_F(FormatTestCSharp, CSharpObjectInitializers) {
5230bb60e29SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
5240bb60e29SJonathan Coe 
525a11ff39bSJonathan Coe   // Start code fragments with a comment line so that C++ raw string literals
5260bb60e29SJonathan Coe   // as seen are identical to expected formatted code.
5270bb60e29SJonathan Coe 
5280bb60e29SJonathan Coe   verifyFormat(R"(//
5290bb60e29SJonathan Coe Shape[] shapes = new[] {
5300bb60e29SJonathan Coe     new Circle {
5310bb60e29SJonathan Coe         Radius = 2.7281,
5320bb60e29SJonathan Coe         Colour = Colours.Red,
5330bb60e29SJonathan Coe     },
5340bb60e29SJonathan Coe     new Square {
5350bb60e29SJonathan Coe         Side = 101.1,
5360bb60e29SJonathan Coe         Colour = Colours.Yellow,
5370bb60e29SJonathan Coe     },
5380bb60e29SJonathan Coe };)",
5390bb60e29SJonathan Coe                Style);
5400bb60e29SJonathan Coe 
5410bb60e29SJonathan Coe   // Omitted final `,`s will change the formatting.
5420bb60e29SJonathan Coe   verifyFormat(R"(//
5430bb60e29SJonathan Coe Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red },
5440bb60e29SJonathan Coe                          new Square {
5450bb60e29SJonathan Coe                              Side = 101.1,
5460bb60e29SJonathan Coe                              Colour = Colours.Yellow,
5470bb60e29SJonathan Coe                          } };)",
5480bb60e29SJonathan Coe                Style);
5497d2fdd3fSJonathan Coe 
5507d2fdd3fSJonathan Coe   // Lambdas can be supplied as initialiser arguments.
5517d2fdd3fSJonathan Coe   verifyFormat(R"(//
5527d2fdd3fSJonathan Coe private Transformer _transformer = new X.Y {
5537d2fdd3fSJonathan Coe     Filler = (Shape shape) => { return new Transform.Fill(shape, RED); },
5547d2fdd3fSJonathan Coe     Scaler = (Shape shape) => { return new Transform.Resize(shape, 0.1); },
5557d2fdd3fSJonathan Coe };)",
5567d2fdd3fSJonathan Coe                Style);
5570bb60e29SJonathan Coe }
5580bb60e29SJonathan Coe 
559a11ff39bSJonathan Coe TEST_F(FormatTestCSharp, CSharpNamedArguments) {
560a11ff39bSJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
561a11ff39bSJonathan Coe 
562a11ff39bSJonathan Coe   verifyFormat(R"(//
563a11ff39bSJonathan Coe PrintOrderDetails(orderNum: 31, productName: "Red Mug",
564a11ff39bSJonathan Coe                   sellerName: "Gift Shop");)",
565a11ff39bSJonathan Coe                Style);
566a11ff39bSJonathan Coe 
567a11ff39bSJonathan Coe   // Ensure that trailing comments do not cause problems.
568a11ff39bSJonathan Coe   verifyFormat(R"(//
569a11ff39bSJonathan Coe PrintOrderDetails(orderNum: 31, productName: "Red Mug",  // comment
570a11ff39bSJonathan Coe                   sellerName: "Gift Shop");)",
571a11ff39bSJonathan Coe                Style);
5727dfe0cc7SJonathan Coe 
5737dfe0cc7SJonathan Coe   verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style);
574a11ff39bSJonathan Coe }
575a11ff39bSJonathan Coe 
5762bd6974aSJonathan Coe TEST_F(FormatTestCSharp, CSharpPropertyAccessors) {
5772bd6974aSJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
5782bd6974aSJonathan Coe 
5792bd6974aSJonathan Coe   verifyFormat("int Value { get }", Style);
5802bd6974aSJonathan Coe   verifyFormat("int Value { get; }", Style);
5812bd6974aSJonathan Coe   verifyFormat("int Value { internal get; }", Style);
5822bd6974aSJonathan Coe   verifyFormat("int Value { get; } = 0", Style);
5832bd6974aSJonathan Coe   verifyFormat("int Value { set }", Style);
5842bd6974aSJonathan Coe   verifyFormat("int Value { set; }", Style);
5852bd6974aSJonathan Coe   verifyFormat("int Value { internal set; }", Style);
5862bd6974aSJonathan Coe   verifyFormat("int Value { set; } = 0", Style);
5872bd6974aSJonathan Coe   verifyFormat("int Value { get; set }", Style);
5882bd6974aSJonathan Coe   verifyFormat("int Value { set; get }", Style);
5892bd6974aSJonathan Coe   verifyFormat("int Value { get; private set; }", Style);
5902bd6974aSJonathan Coe   verifyFormat("int Value { get; set; }", Style);
5912bd6974aSJonathan Coe   verifyFormat("int Value { get; set; } = 0", Style);
5922bd6974aSJonathan Coe   verifyFormat("int Value { internal get; internal set; }", Style);
5932bd6974aSJonathan Coe 
5942bd6974aSJonathan Coe   // Do not wrap expression body definitions.
5952bd6974aSJonathan Coe   verifyFormat(R"(//
5962bd6974aSJonathan Coe public string Name {
5972bd6974aSJonathan Coe   get => _name;
5982bd6974aSJonathan Coe   set => _name = value;
5992bd6974aSJonathan Coe })",
6002bd6974aSJonathan Coe                Style);
601e8c5fea2SJonathan Coe }
602e8c5fea2SJonathan Coe 
603e8c5fea2SJonathan Coe TEST_F(FormatTestCSharp, CSharpSpaces) {
604e8c5fea2SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
605e8c5fea2SJonathan Coe   Style.SpaceBeforeSquareBrackets = false;
606e8c5fea2SJonathan Coe   Style.SpacesInSquareBrackets = false;
607e8c5fea2SJonathan Coe   Style.SpaceBeforeCpp11BracedList = true;
608e8c5fea2SJonathan Coe   Style.Cpp11BracedListStyle = false;
609e8c5fea2SJonathan Coe   Style.SpacesInContainerLiterals = false;
610e8c5fea2SJonathan Coe 
611e8c5fea2SJonathan Coe   verifyFormat(R"(new Car { "Door", 0.1 })", Style);
612e8c5fea2SJonathan Coe   verifyFormat(R"(new Car { 0.1, "Door" })", Style);
613e8c5fea2SJonathan Coe   verifyFormat(R"(new string[] { "A" })", Style);
614e8c5fea2SJonathan Coe   verifyFormat(R"(new string[] {})", Style);
615e8c5fea2SJonathan Coe   verifyFormat(R"(new Car { someVariableName })", Style);
616e8c5fea2SJonathan Coe   verifyFormat(R"(new Car { someVariableName })", Style);
617e8c5fea2SJonathan Coe   verifyFormat(R"(new Dictionary<string, string> { ["Key"] = "Value" };)",
618e8c5fea2SJonathan Coe                Style);
619e8c5fea2SJonathan Coe   verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style);
620e8c5fea2SJonathan Coe   verifyFormat(R"(bool[] xs = { true, true };)", Style);
621e8c5fea2SJonathan Coe   verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style);
622e8c5fea2SJonathan Coe   verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style);
623548e540dSJonathan Coe   verifyFormat(R"(private float[,] Values;)", Style);
624548e540dSJonathan Coe 
625548e540dSJonathan Coe   Style.SpacesInSquareBrackets = true;
626548e540dSJonathan Coe   verifyFormat(R"(private float[ , ] Values;)", Style);
6279c4afce7SJonathan Coe   verifyFormat(R"(string dirPath = args?[ 0 ];)", Style);
6285f52a93bSJonathan Coe }
6295f52a93bSJonathan Coe 
6305f52a93bSJonathan Coe TEST_F(FormatTestCSharp, CSharpNullableTypes) {
6315f52a93bSJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
6325f52a93bSJonathan Coe   Style.SpacesInSquareBrackets = false;
6335f52a93bSJonathan Coe 
634*fe61bc1aSJonathan Coe   verifyFormat(R"(//
635*fe61bc1aSJonathan Coe public class A {
636*fe61bc1aSJonathan Coe   void foo() { int? value = some.bar(); }
637*fe61bc1aSJonathan Coe })",
638*fe61bc1aSJonathan Coe                Style); // int? is nullable not a conditional expression.
639*fe61bc1aSJonathan Coe 
640*fe61bc1aSJonathan Coe   verifyFormat(R"(void foo(int? x, int? y, int? z) {})",
641*fe61bc1aSJonathan Coe                Style); // Nullables in function definitions.
642*fe61bc1aSJonathan Coe 
6435f52a93bSJonathan Coe   verifyFormat(R"(public float? Value;)", Style); // no space before `?`.
644*fe61bc1aSJonathan Coe 
6455f52a93bSJonathan Coe   verifyFormat(R"(int?[] arr = new int?[10];)",
6465f52a93bSJonathan Coe                Style); // An array of a nullable type.
6472bd6974aSJonathan Coe }
6482bd6974aSJonathan Coe 
649736fef97SJonathan Coe TEST_F(FormatTestCSharp, CSharpArraySubscripts) {
650736fef97SJonathan Coe   FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
651736fef97SJonathan Coe 
652736fef97SJonathan Coe   // Do not format array subscript operators as attributes.
653736fef97SJonathan Coe   verifyFormat(R"(if (someThings[index].Contains(myThing)) {)", Style);
654736fef97SJonathan Coe   verifyFormat(R"(if (someThings[i][j][k].Contains(myThing)) {)", Style);
655736fef97SJonathan Coe }
656736fef97SJonathan Coe 
657cbb726d0SPaul Hoad } // namespace format
658cbb726d0SPaul Hoad } // end namespace clang
659