1 //===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "FormatTestUtils.h" 10 #include "clang/Format/Format.h" 11 #include "llvm/Support/Debug.h" 12 #include "gtest/gtest.h" 13 14 #define DEBUG_TYPE "format-test" 15 16 namespace clang { 17 namespace format { 18 19 class FormatTestCSharp : public ::testing::Test { 20 protected: 21 static std::string format(llvm::StringRef Code, unsigned Offset, 22 unsigned Length, const FormatStyle &Style) { 23 LLVM_DEBUG(llvm::errs() << "---\n"); 24 LLVM_DEBUG(llvm::errs() << Code << "\n\n"); 25 std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); 26 tooling::Replacements Replaces = reformat(Style, Code, Ranges); 27 auto Result = applyAllReplacements(Code, Replaces); 28 EXPECT_TRUE(static_cast<bool>(Result)); 29 LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); 30 return *Result; 31 } 32 33 static std::string 34 format(llvm::StringRef Code, 35 const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { 36 return format(Code, 0, Code.size(), Style); 37 } 38 39 static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { 40 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 41 Style.ColumnLimit = ColumnLimit; 42 return Style; 43 } 44 45 static void verifyFormat( 46 llvm::StringRef Code, 47 const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) { 48 EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; 49 EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); 50 } 51 }; 52 53 TEST_F(FormatTestCSharp, CSharpClass) { 54 verifyFormat("public class SomeClass\n" 55 "{\n" 56 " void f()\n" 57 " {\n" 58 " }\n" 59 " int g()\n" 60 " {\n" 61 " return 0;\n" 62 " }\n" 63 " void h()\n" 64 " {\n" 65 " while (true)\n" 66 " f();\n" 67 " for (;;)\n" 68 " f();\n" 69 " if (true)\n" 70 " f();\n" 71 " }\n" 72 "}"); 73 } 74 75 TEST_F(FormatTestCSharp, AccessModifiers) { 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 "}"); 88 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 "}"); 101 102 verifyFormat("internal static String toString()\n" 103 "{\n" 104 "}"); 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 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 151 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 152 153 verifyFormat( 154 "public Person(string firstName, string lastName, int? age=null)"); 155 156 verifyFormat("foo () {\n" 157 " switch (args?.Length) {}\n" 158 "}", 159 Style); 160 161 verifyFormat("switch (args?.Length) {}", Style); 162 163 verifyFormat("public static void Main(string[] args)\n" 164 "{\n" 165 " string dirPath = args?[0];\n" 166 "}"); 167 168 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 169 170 verifyFormat("switch(args?.Length) {}", Style); 171 } 172 173 TEST_F(FormatTestCSharp, Attributes) { 174 verifyFormat("[STAThread]\n" 175 "static void Main(string[] args)\n" 176 "{\n" 177 "}"); 178 179 verifyFormat("[TestMethod]\n" 180 "private class Test\n" 181 "{\n" 182 "}"); 183 184 verifyFormat("[TestMethod]\n" 185 "protected class Test\n" 186 "{\n" 187 "}"); 188 189 verifyFormat("[TestMethod]\n" 190 "internal class Test\n" 191 "{\n" 192 "}"); 193 194 verifyFormat("[TestMethod]\n" 195 "class Test\n" 196 "{\n" 197 "}"); 198 199 verifyFormat("[TestMethod]\n" 200 "[DeploymentItem(\"Test.txt\")]\n" 201 "public class Test\n" 202 "{\n" 203 "}"); 204 205 verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" 206 "[System.Runtime.InteropServices.ComVisible(true)]\n" 207 "public sealed class STAThreadAttribute : Attribute\n" 208 "{\n" 209 "}"); 210 211 verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " 212 "provided port\")]\n" 213 "class Test\n" 214 "{\n" 215 "}"); 216 217 verifyFormat("[TestMethod]\n" 218 "public string Host\n" 219 "{\n" 220 " set;\n" 221 " get;\n" 222 "}"); 223 224 verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " 225 "listening on provided host\")]\n" 226 "public string Host\n" 227 "{\n" 228 " set;\n" 229 " get;\n" 230 "}"); 231 } 232 233 TEST_F(FormatTestCSharp, CSharpUsing) { 234 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 235 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 236 verifyFormat("public void foo () {\n" 237 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 238 " using () {}\n" 239 "}", 240 Style); 241 242 // Ensure clang-format affects top-level snippets correctly. 243 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 244 Style); 245 246 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 247 verifyFormat("public void foo() {\n" 248 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 249 " using() {}\n" 250 "}", 251 Style); 252 253 // Ensure clang-format affects top-level snippets correctly. 254 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 255 Style); 256 257 Style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 258 verifyFormat("public void foo() {\n" 259 " using (StreamWriter sw = new StreamWriter(filenameA)) {}\n" 260 " using () {}\n" 261 "}", 262 Style); 263 264 // Ensure clang-format affects top-level snippets correctly. 265 verifyFormat("using (StreamWriter sw = new StreamWriter(filenameB)) {}", 266 Style); 267 268 Style.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; 269 verifyFormat("public void foo() {\n" 270 " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" 271 " using() {}\n" 272 "}", 273 Style); 274 275 // Ensure clang-format affects top-level snippets correctly. 276 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 277 Style); 278 } 279 280 TEST_F(FormatTestCSharp, CSharpRegions) { 281 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 282 "aaaaaaaaaaaaaaa long region"); 283 } 284 285 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 286 verifyFormat("public enum var { none, @string, bool, @enum }"); 287 } 288 289 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 290 verifyFormat("var test = ABC ?? DEF"); 291 verifyFormat("string myname = name ?? \"ABC\";"); 292 verifyFormat("return _name ?? \"DEF\";"); 293 } 294 295 TEST_F(FormatTestCSharp, AttributesIndentation) { 296 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 297 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 298 299 verifyFormat("[STAThread]\n" 300 "static void Main(string[] args)\n" 301 "{\n" 302 "}", 303 Style); 304 305 verifyFormat("[STAThread]\n" 306 "void " 307 "veryLooooooooooooooongFunctionName(string[] args)\n" 308 "{\n" 309 "}", 310 Style); 311 312 verifyFormat("[STAThread]\n" 313 "veryLoooooooooooooooooooongReturnType " 314 "veryLooooooooooooooongFunctionName(string[] args)\n" 315 "{\n" 316 "}", 317 Style); 318 319 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 320 "public override X Y()\n" 321 "{\n" 322 "}\n", 323 Style); 324 325 verifyFormat("[SuppressMessage]\n" 326 "public X Y()\n" 327 "{\n" 328 "}\n", 329 Style); 330 331 verifyFormat("[SuppressMessage]\n" 332 "public override X Y()\n" 333 "{\n" 334 "}\n", 335 Style); 336 337 verifyFormat("public A(B b) : base(b)\n" 338 "{\n" 339 " [SuppressMessage]\n" 340 " public override X Y()\n" 341 " {\n" 342 " }\n" 343 "}\n", 344 Style); 345 346 verifyFormat("public A : Base\n" 347 "{\n" 348 "}\n" 349 "[Test]\n" 350 "public Foo()\n" 351 "{\n" 352 "}\n", 353 Style); 354 355 verifyFormat("namespace\n" 356 "{\n" 357 "public A : Base\n" 358 "{\n" 359 "}\n" 360 "[Test]\n" 361 "public Foo()\n" 362 "{\n" 363 "}\n" 364 "}\n", 365 Style); 366 } 367 368 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 369 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 370 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 371 372 verifyFormat("List<string> list;", Style); 373 verifyFormat("Dictionary<string, string> dict;", Style); 374 375 verifyFormat("for (int i = 0; i < size (); i++) {\n" 376 "}", 377 Style); 378 verifyFormat("foreach (var x in y) {\n" 379 "}", 380 Style); 381 verifyFormat("switch (x) {}", Style); 382 verifyFormat("do {\n" 383 "} while (x);", 384 Style); 385 386 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 387 388 verifyFormat("List<string> list;", Style); 389 verifyFormat("Dictionary<string, string> dict;", Style); 390 391 verifyFormat("for(int i = 0; i < size(); i++) {\n" 392 "}", 393 Style); 394 verifyFormat("foreach(var x in y) {\n" 395 "}", 396 Style); 397 verifyFormat("switch(x) {}", Style); 398 verifyFormat("do {\n" 399 "} while(x);", 400 Style); 401 } 402 403 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 404 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 405 406 verifyFormat("(int)x / y;", Style); 407 408 Style.SpaceAfterCStyleCast = true; 409 verifyFormat("(int) x / y;", Style); 410 } 411 412 } // namespace format 413 } // end namespace clang 414