| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 1 | //===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===// | 
|  | 2 | // | 
|  | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | 4 | // See https://llvm.org/LICENSE.txt for license information. | 
|  | 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | 6 | // | 
|  | 7 | //===----------------------------------------------------------------------===// | 
|  | 8 |  | 
|  | 9 | #include "FormatTestUtils.h" | 
|  | 10 | #include "clang/Format/Format.h" | 
|  | 11 | #include "llvm/Support/Debug.h" | 
|  | 12 | #include "gtest/gtest.h" | 
|  | 13 |  | 
|  | 14 | #define DEBUG_TYPE "format-test" | 
|  | 15 |  | 
|  | 16 | namespace clang { | 
|  | 17 | namespace format { | 
|  | 18 |  | 
|  | 19 | class FormatTestCSharp : public ::testing::Test { | 
|  | 20 | protected: | 
|  | 21 | static std::string format(llvm::StringRef Code, unsigned Offset, | 
|  | 22 | unsigned Length, const FormatStyle &Style) { | 
|  | 23 | LLVM_DEBUG(llvm::errs() << "---\n"); | 
|  | 24 | LLVM_DEBUG(llvm::errs() << Code << "\n\n"); | 
|  | 25 | std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); | 
|  | 26 | tooling::Replacements Replaces = reformat(Style, Code, Ranges); | 
|  | 27 | auto Result = applyAllReplacements(Code, Replaces); | 
|  | 28 | EXPECT_TRUE(static_cast<bool>(Result)); | 
|  | 29 | LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); | 
|  | 30 | return *Result; | 
|  | 31 | } | 
|  | 32 |  | 
|  | 33 | static std::string | 
|  | 34 | format(llvm::StringRef Code, | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 35 | const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 36 | return format(Code, 0, Code.size(), Style); | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 40 | FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 41 | Style.ColumnLimit = ColumnLimit; | 
|  | 42 | return Style; | 
|  | 43 | } | 
|  | 44 |  | 
|  | 45 | static void verifyFormat( | 
|  | 46 | llvm::StringRef Code, | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 47 | const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 48 | EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; | 
|  | 49 | EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); | 
|  | 50 | } | 
|  | 51 | }; | 
|  | 52 |  | 
|  | 53 | TEST_F(FormatTestCSharp, CSharpClass) { | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 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" | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 72 | "}"); | 
|  | 73 | } | 
|  | 74 |  | 
|  | 75 | TEST_F(FormatTestCSharp, AccessModifiers) { | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 76 | verifyFormat("public String toString()\n" | 
|  | 77 | "{\n" | 
|  | 78 | "}"); | 
|  | 79 | verifyFormat("private String toString()\n" | 
|  | 80 | "{\n" | 
|  | 81 | "}"); | 
|  | 82 | verifyFormat("protected String toString()\n" | 
|  | 83 | "{\n" | 
|  | 84 | "}"); | 
|  | 85 | verifyFormat("internal String toString()\n" | 
|  | 86 | "{\n" | 
|  | 87 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 88 |  | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 89 | verifyFormat("public override String toString()\n" | 
|  | 90 | "{\n" | 
|  | 91 | "}"); | 
|  | 92 | verifyFormat("private override String toString()\n" | 
|  | 93 | "{\n" | 
|  | 94 | "}"); | 
|  | 95 | verifyFormat("protected override String toString()\n" | 
|  | 96 | "{\n" | 
|  | 97 | "}"); | 
|  | 98 | verifyFormat("internal override String toString()\n" | 
|  | 99 | "{\n" | 
|  | 100 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 101 |  | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 102 | verifyFormat("internal static String toString()\n" | 
|  | 103 | "{\n" | 
|  | 104 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 105 | } | 
|  | 106 |  | 
|  | 107 | TEST_F(FormatTestCSharp, NoStringLiteralBreaks) { | 
|  | 108 | verifyFormat("foo(" | 
|  | 109 | "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | 
|  | 110 | "aaaaaa\");"); | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | TEST_F(FormatTestCSharp, CSharpVerbatiumStringLiterals) { | 
|  | 114 | verifyFormat("foo(@\"aaaaaaaa\\abc\\aaaa\");"); | 
|  | 115 | // @"ABC\" + ToString("B") - handle embedded \ in literal string at | 
|  | 116 | // the end | 
|  | 117 | // | 
|  | 118 | /* | 
|  | 119 | * After removal of Lexer change we are currently not able | 
|  | 120 | * To handle these cases | 
|  | 121 | verifyFormat("string s = @\"ABC\\\" + ToString(\"B\");"); | 
|  | 122 | verifyFormat("string s = @\"ABC\"\"DEF\"\"GHI\""); | 
|  | 123 | verifyFormat("string s = @\"ABC\"\"DEF\"\"\""); | 
|  | 124 | verifyFormat("string s = @\"ABC\"\"DEF\"\"\" + abc"); | 
|  | 125 | */ | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | TEST_F(FormatTestCSharp, CSharpInterpolatedStringLiterals) { | 
|  | 129 | verifyFormat("foo($\"aaaaaaaa{aaa}aaaa\");"); | 
|  | 130 | verifyFormat("foo($\"aaaa{A}\");"); | 
|  | 131 | verifyFormat( | 
|  | 132 | "foo($\"aaaa{A}" | 
|  | 133 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"); | 
|  | 134 | verifyFormat("Name = $\"{firstName} {lastName}\";"); | 
|  | 135 |  | 
|  | 136 | // $"ABC\" + ToString("B") - handle embedded \ in literal string at | 
|  | 137 | // the end | 
|  | 138 | verifyFormat("string s = $\"A{abc}BC\" + ToString(\"B\");"); | 
|  | 139 | verifyFormat("$\"{domain}\\\\{user}\""); | 
|  | 140 | verifyFormat( | 
|  | 141 | "var verbatimInterpolated = $@\"C:\\Users\\{userName}\\Documents\\\";"); | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | TEST_F(FormatTestCSharp, CSharpFatArrows) { | 
|  | 145 | verifyFormat("Task serverTask = Task.Run(async() => {"); | 
|  | 146 | verifyFormat("public override string ToString() => \"{Name}\\{Age}\";"); | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | TEST_F(FormatTestCSharp, CSharpNullConditional) { | 
|  | 150 | verifyFormat( | 
|  | 151 | "public Person(string firstName, string lastName, int? age=null)"); | 
|  | 152 |  | 
|  | 153 | verifyFormat("switch(args?.Length)"); | 
|  | 154 |  | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 155 | verifyFormat("public static void Main(string[] args)\n" | 
|  | 156 | "{\n" | 
|  | 157 | "    string dirPath = args?[0];\n" | 
|  | 158 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 159 | } | 
|  | 160 |  | 
|  | 161 | TEST_F(FormatTestCSharp, Attributes) { | 
|  | 162 | verifyFormat("[STAThread]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 163 | "static void Main(string[] args)\n" | 
|  | 164 | "{\n" | 
|  | 165 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 166 |  | 
|  | 167 | verifyFormat("[TestMethod]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 168 | "private class Test\n" | 
|  | 169 | "{\n" | 
|  | 170 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 171 |  | 
|  | 172 | verifyFormat("[TestMethod]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 173 | "protected class Test\n" | 
|  | 174 | "{\n" | 
|  | 175 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 176 |  | 
|  | 177 | verifyFormat("[TestMethod]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 178 | "internal class Test\n" | 
|  | 179 | "{\n" | 
|  | 180 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 181 |  | 
|  | 182 | verifyFormat("[TestMethod]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 183 | "class Test\n" | 
|  | 184 | "{\n" | 
|  | 185 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 186 |  | 
|  | 187 | verifyFormat("[TestMethod]\n" | 
|  | 188 | "[DeploymentItem(\"Test.txt\")]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 189 | "public class Test\n" | 
|  | 190 | "{\n" | 
|  | 191 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 192 |  | 
|  | 193 | verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" | 
|  | 194 | "[System.Runtime.InteropServices.ComVisible(true)]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 195 | "public sealed class STAThreadAttribute : Attribute\n" | 
|  | 196 | "{\n" | 
|  | 197 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 198 |  | 
|  | 199 | verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " | 
|  | 200 | "provided port\")]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 201 | "class Test\n" | 
|  | 202 | "{\n" | 
|  | 203 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 204 |  | 
|  | 205 | verifyFormat("[TestMethod]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 206 | "public string Host\n" | 
|  | 207 | "{\n" | 
|  | 208 | "    set;\n" | 
|  | 209 | "    get;\n" | 
|  | 210 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 211 |  | 
|  | 212 | verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " | 
|  | 213 | "listening on provided host\")]\n" | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 214 | "public string Host\n" | 
|  | 215 | "{\n" | 
|  | 216 | "    set;\n" | 
|  | 217 | "    get;\n" | 
|  | 218 | "}"); | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 219 | } | 
|  | 220 |  | 
| Paul Hoad | 719087b | 2019-09-12 10:18:53 +0000 | [diff] [blame] | 221 | TEST_F(FormatTestCSharp, CSharpUsing) { | 
|  | 222 | FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); | 
|  | 223 | Style.SpaceBeforeParens = FormatStyle::SBPO_Always; | 
|  | 224 | verifyFormat("public void foo() {\n" | 
|  | 225 | "  using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" | 
|  | 226 | "}", | 
|  | 227 | Style); | 
|  | 228 |  | 
|  | 229 | Style.SpaceBeforeParens = FormatStyle::SBPO_Never; | 
|  | 230 | verifyFormat("public void foo() {\n" | 
|  | 231 | "  using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" | 
|  | 232 | "}", | 
|  | 233 | Style); | 
|  | 234 | } | 
|  | 235 |  | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 236 | TEST_F(FormatTestCSharp, CSharpRegions) { | 
|  | 237 | verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " | 
|  | 238 | "aaaaaaaaaaaaaaa long region"); | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { | 
|  | 242 | verifyFormat("public enum var { none, @string, bool, @enum }"); | 
|  | 243 | } | 
|  | 244 |  | 
|  | 245 | TEST_F(FormatTestCSharp, CSharpNullCoalescing) { | 
|  | 246 | verifyFormat("var test = ABC ?? DEF"); | 
|  | 247 | verifyFormat("string myname = name ?? \"ABC\";"); | 
|  | 248 | verifyFormat("return _name ?? \"DEF\";"); | 
|  | 249 | } | 
|  | 250 |  | 
| Paul Hoad | a2f963b | 2019-10-04 07:56:49 +0000 | [diff] [blame] | 251 | TEST_F(FormatTestCSharp, AttributesIndentation) { | 
|  | 252 | FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); | 
|  | 253 | Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; | 
|  | 254 |  | 
|  | 255 | verifyFormat("[STAThread]\n" | 
|  | 256 | "static void Main(string[] args)\n" | 
|  | 257 | "{\n" | 
|  | 258 | "}", | 
|  | 259 | Style); | 
|  | 260 |  | 
|  | 261 | verifyFormat("[STAThread]\n" | 
|  | 262 | "void " | 
|  | 263 | "veryLooooooooooooooongFunctionName(string[] args)\n" | 
|  | 264 | "{\n" | 
|  | 265 | "}", | 
|  | 266 | Style); | 
|  | 267 |  | 
|  | 268 | verifyFormat("[STAThread]\n" | 
|  | 269 | "veryLoooooooooooooooooooongReturnType " | 
|  | 270 | "veryLooooooooooooooongFunctionName(string[] args)\n" | 
|  | 271 | "{\n" | 
|  | 272 | "}", | 
|  | 273 | Style); | 
|  | 274 |  | 
|  | 275 | verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" | 
|  | 276 | "public override X Y()\n" | 
|  | 277 | "{\n" | 
|  | 278 | "}\n", | 
|  | 279 | Style); | 
|  | 280 |  | 
|  | 281 | verifyFormat("[SuppressMessage]\n" | 
|  | 282 | "public X Y()\n" | 
|  | 283 | "{\n" | 
|  | 284 | "}\n", | 
|  | 285 | Style); | 
|  | 286 |  | 
|  | 287 | verifyFormat("[SuppressMessage]\n" | 
|  | 288 | "public override X Y()\n" | 
|  | 289 | "{\n" | 
|  | 290 | "}\n", | 
|  | 291 | Style); | 
|  | 292 |  | 
|  | 293 | verifyFormat("public A(B b) : base(b)\n" | 
|  | 294 | "{\n" | 
|  | 295 | "    [SuppressMessage]\n" | 
|  | 296 | "    public override X Y()\n" | 
|  | 297 | "    {\n" | 
|  | 298 | "    }\n" | 
|  | 299 | "}\n", | 
|  | 300 | Style); | 
|  | 301 | } | 
|  | 302 |  | 
| Paul Hoad | cbb726d | 2019-03-21 13:09:22 +0000 | [diff] [blame] | 303 | } // namespace format | 
|  | 304 | } // end namespace clang |