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 "}", 239 Style); 240 241 verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", 242 Style); 243 244 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 245 verifyFormat("public void foo() {\n" 246 " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" 247 "}", 248 Style); 249 250 verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", 251 Style); 252 } 253 254 TEST_F(FormatTestCSharp, CSharpRegions) { 255 verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " 256 "aaaaaaaaaaaaaaa long region"); 257 } 258 259 TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { 260 verifyFormat("public enum var { none, @string, bool, @enum }"); 261 } 262 263 TEST_F(FormatTestCSharp, CSharpNullCoalescing) { 264 verifyFormat("var test = ABC ?? DEF"); 265 verifyFormat("string myname = name ?? \"ABC\";"); 266 verifyFormat("return _name ?? \"DEF\";"); 267 } 268 269 TEST_F(FormatTestCSharp, AttributesIndentation) { 270 FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); 271 Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; 272 273 verifyFormat("[STAThread]\n" 274 "static void Main(string[] args)\n" 275 "{\n" 276 "}", 277 Style); 278 279 verifyFormat("[STAThread]\n" 280 "void " 281 "veryLooooooooooooooongFunctionName(string[] args)\n" 282 "{\n" 283 "}", 284 Style); 285 286 verifyFormat("[STAThread]\n" 287 "veryLoooooooooooooooooooongReturnType " 288 "veryLooooooooooooooongFunctionName(string[] args)\n" 289 "{\n" 290 "}", 291 Style); 292 293 verifyFormat("[SuppressMessage(\"A\", \"B\", Justification = \"C\")]\n" 294 "public override X Y()\n" 295 "{\n" 296 "}\n", 297 Style); 298 299 verifyFormat("[SuppressMessage]\n" 300 "public X Y()\n" 301 "{\n" 302 "}\n", 303 Style); 304 305 verifyFormat("[SuppressMessage]\n" 306 "public override X Y()\n" 307 "{\n" 308 "}\n", 309 Style); 310 311 verifyFormat("public A(B b) : base(b)\n" 312 "{\n" 313 " [SuppressMessage]\n" 314 " public override X Y()\n" 315 " {\n" 316 " }\n" 317 "}\n", 318 Style); 319 320 verifyFormat("public A : Base\n" 321 "{\n" 322 "}\n" 323 "[Test]\n" 324 "public Foo()\n" 325 "{\n" 326 "}\n", 327 Style); 328 329 verifyFormat("namespace\n" 330 "{\n" 331 "public A : Base\n" 332 "{\n" 333 "}\n" 334 "[Test]\n" 335 "public Foo()\n" 336 "{\n" 337 "}\n" 338 "}\n", 339 Style); 340 } 341 342 TEST_F(FormatTestCSharp, CSharpSpaceBefore) { 343 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 344 Style.SpaceBeforeParens = FormatStyle::SBPO_Always; 345 346 verifyFormat("List<string> list;", Style); 347 verifyFormat("Dictionary<string, string> dict;", Style); 348 349 verifyFormat("for (int i = 0; i < size (); i++) {\n" 350 "}", 351 Style); 352 verifyFormat("foreach (var x in y) {\n" 353 "}", 354 Style); 355 verifyFormat("switch (x) {}", Style); 356 verifyFormat("do {\n" 357 "} while (x);", 358 Style); 359 360 Style.SpaceBeforeParens = FormatStyle::SBPO_Never; 361 362 verifyFormat("List<string> list;", Style); 363 verifyFormat("Dictionary<string, string> dict;", Style); 364 365 verifyFormat("for(int i = 0; i < size(); i++) {\n" 366 "}", 367 Style); 368 verifyFormat("foreach(var x in y) {\n" 369 "}", 370 Style); 371 verifyFormat("switch(x) {}", Style); 372 verifyFormat("do {\n" 373 "} while(x);", 374 Style); 375 } 376 377 TEST_F(FormatTestCSharp, CSharpSpaceAfterCStyleCast) { 378 FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); 379 380 verifyFormat("(int)x / y;", Style); 381 382 Style.SpaceAfterCStyleCast = true; 383 verifyFormat("(int) x / y;", Style); 384 } 385 386 } // namespace format 387 } // end namespace clang 388