1 //===- DefinitionBlockSeparatorTest.cpp - Formatting unit tests -----------===// 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 12 #include "llvm/Support/Debug.h" 13 #include "gtest/gtest.h" 14 15 #define DEBUG_TYPE "definition-block-separator-test" 16 17 namespace clang { 18 namespace format { 19 namespace { 20 21 class DefinitionBlockSeparatorTest : public ::testing::Test { 22 protected: 23 static std::string 24 separateDefinitionBlocks(llvm::StringRef Code, 25 const std::vector<tooling::Range> &Ranges, 26 const FormatStyle &Style = getLLVMStyle()) { 27 LLVM_DEBUG(llvm::errs() << "---\n"); 28 LLVM_DEBUG(llvm::errs() << Code << "\n\n"); 29 tooling::Replacements Replaces = reformat(Style, Code, Ranges, "<stdin>"); 30 auto Result = applyAllReplacements(Code, Replaces); 31 EXPECT_TRUE(static_cast<bool>(Result)); 32 LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); 33 return *Result; 34 } 35 36 static std::string 37 separateDefinitionBlocks(llvm::StringRef Code, 38 const FormatStyle &Style = getLLVMStyle()) { 39 return separateDefinitionBlocks( 40 Code, 41 /*Ranges=*/{1, tooling::Range(0, Code.size())}, Style); 42 } 43 44 static void _verifyFormat(const char *File, int Line, llvm::StringRef Code, 45 const FormatStyle &Style = getLLVMStyle(), 46 llvm::StringRef ExpectedCode = "") { 47 ::testing::ScopedTrace t(File, Line, ::testing::Message() << Code.str()); 48 bool HasOriginalCode = true; 49 if (ExpectedCode == "") { 50 ExpectedCode = Code; 51 HasOriginalCode = false; 52 } 53 54 FormatStyle InverseStyle = Style; 55 if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always) 56 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 57 else 58 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 59 EXPECT_EQ(ExpectedCode.str(), separateDefinitionBlocks(ExpectedCode, Style)) 60 << "Expected code is not stable"; 61 std::string InverseResult = 62 separateDefinitionBlocks(ExpectedCode, InverseStyle); 63 EXPECT_NE(ExpectedCode.str(), InverseResult) 64 << "Inverse formatting makes no difference"; 65 std::string CodeToFormat = 66 HasOriginalCode ? Code.str() : removeEmptyLines(Code); 67 std::string Result = separateDefinitionBlocks(CodeToFormat, Style); 68 EXPECT_EQ(ExpectedCode.str(), Result) << "Test failed. Formatted:\n" 69 << Result; 70 } 71 72 static std::string removeEmptyLines(llvm::StringRef Code) { 73 std::string Result = ""; 74 for (auto Char : Code.str()) { 75 if (Result.size()) { 76 auto LastChar = Result.back(); 77 if ((Char == '\n' && LastChar == '\n') || 78 (Char == '\r' && (LastChar == '\r' || LastChar == '\n'))) 79 continue; 80 } 81 Result.push_back(Char); 82 } 83 return Result; 84 } 85 }; 86 87 #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__) 88 89 TEST_F(DefinitionBlockSeparatorTest, Basic) { 90 FormatStyle Style = getLLVMStyle(); 91 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 92 verifyFormat("int foo(int i, int j) {\n" 93 " int r = i + j;\n" 94 " return r;\n" 95 "}\n" 96 "\n" 97 "int bar(int j, int k) {\n" 98 " int r = j + k;\n" 99 " return r;\n" 100 "}", 101 Style); 102 103 verifyFormat("struct foo {\n" 104 " int i, j;\n" 105 "};\n" 106 "\n" 107 "struct bar {\n" 108 " int j, k;\n" 109 "};", 110 Style); 111 112 verifyFormat("class foo {\n" 113 " int i, j;\n" 114 "};\n" 115 "\n" 116 "class bar {\n" 117 " int j, k;\n" 118 "};", 119 Style); 120 121 verifyFormat("namespace foo {\n" 122 "int i, j;\n" 123 "}\n" 124 "\n" 125 "namespace bar {\n" 126 "int j, k;\n" 127 "}", 128 Style); 129 130 verifyFormat("enum Foo { FOO, BAR };\n" 131 "\n" 132 "enum Bar { FOOBAR, BARFOO };\n", 133 Style); 134 } 135 136 TEST_F(DefinitionBlockSeparatorTest, UntouchBlockStartStyle) { 137 // Returns a std::pair of two strings, with the first one for passing into 138 // Always test and the second one be the expected result of the first string. 139 auto MakeUntouchTest = [&](std::string BlockHeader, std::string BlockChanger, 140 std::string BlockFooter, bool BlockEndNewLine) { 141 std::string CodePart1 = "enum Foo { FOO, BAR };\n" 142 "\n" 143 "/*\n" 144 "test1\n" 145 "test2\n" 146 "*/\n" 147 "int foo(int i, int j) {\n" 148 " int r = i + j;\n" 149 " return r;\n" 150 "}\n"; 151 std::string CodePart2 = "/* Comment block in one line*/\n" 152 "enum Bar { FOOBAR, BARFOO };\n" 153 "\n" 154 "int bar3(int j, int k) {\n" 155 " // A comment\n" 156 " int r = j % k;\n" 157 " return r;\n" 158 "}\n"; 159 std::string CodePart3 = "int bar2(int j, int k) {\n" 160 " int r = j / k;\n" 161 " return r;\n" 162 "}\n"; 163 std::string ConcatAll = BlockHeader + CodePart1 + BlockChanger + CodePart2 + 164 BlockFooter + (BlockEndNewLine ? "\n" : "") + 165 CodePart3; 166 return std::make_pair(BlockHeader + removeEmptyLines(CodePart1) + 167 BlockChanger + removeEmptyLines(CodePart2) + 168 BlockFooter + removeEmptyLines(CodePart3), 169 ConcatAll); 170 }; 171 172 FormatStyle AlwaysStyle = getLLVMStyle(); 173 AlwaysStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 174 175 FormatStyle NeverStyle = getLLVMStyle(); 176 NeverStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 177 178 auto TestKit = MakeUntouchTest("#ifdef FOO\n\n", "\n#elifndef BAR\n\n", 179 "\n#endif\n\n", false); 180 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 181 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 182 183 TestKit = 184 MakeUntouchTest("#ifdef FOO\n", "#elifndef BAR\n", "#endif\n", false); 185 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 186 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 187 188 TestKit = MakeUntouchTest("namespace Ns {\n\n", 189 "\n} // namespace Ns\n\n" 190 "namespace {\n\n", 191 "\n} // namespace\n", true); 192 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 193 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 194 195 TestKit = MakeUntouchTest("namespace Ns {\n", 196 "} // namespace Ns\n\n" 197 "namespace {\n", 198 "} // namespace\n", true); 199 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second); 200 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second)); 201 } 202 203 TEST_F(DefinitionBlockSeparatorTest, Always) { 204 FormatStyle Style = getLLVMStyle(); 205 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 206 std::string Prefix = "namespace {\n"; 207 std::string Infix = "\n" 208 "// Enum test1\n" 209 "// Enum test2\n" 210 "enum Foo { FOO, BAR };\n" 211 "\n" 212 "/*\n" 213 "test1\n" 214 "test2\n" 215 "*/\n" 216 "int foo(int i, int j) {\n" 217 " int r = i + j;\n" 218 " return r;\n" 219 "}\n" 220 "\n" 221 "// Foobar\n" 222 "int i, j, k;\n" 223 "\n" 224 "// Comment for function\n" 225 "// Comment line 2\n" 226 "// Comment line 3\n" 227 "int bar(int j, int k) {\n" 228 " int r = j * k;\n" 229 " return r;\n" 230 "}\n" 231 "\n" 232 "int bar2(int j, int k) {\n" 233 " int r = j / k;\n" 234 " return r;\n" 235 "}\n" 236 "\n" 237 "/* Comment block in one line*/\n" 238 "enum Bar { FOOBAR, BARFOO };\n" 239 "\n" 240 "int bar3(int j, int k) {\n" 241 " // A comment\n" 242 " int r = j % k;\n" 243 " return r;\n" 244 "}\n"; 245 std::string Postfix = "\n" 246 "} // namespace\n" 247 "\n" 248 "namespace T {\n" 249 "int i, j, k;\n" 250 "} // namespace T"; 251 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix), 252 Style, Prefix + Infix + Postfix); 253 } 254 255 TEST_F(DefinitionBlockSeparatorTest, Never) { 256 FormatStyle Style = getLLVMStyle(); 257 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never; 258 std::string Prefix = "namespace {\n"; 259 std::string Postfix = "// Enum test1\n" 260 "// Enum test2\n" 261 "enum Foo { FOO, BAR };\n" 262 "\n" 263 "/*\n" 264 "test1\n" 265 "test2\n" 266 "*/\n" 267 "int foo(int i, int j) {\n" 268 " int r = i + j;\n" 269 " return r;\n" 270 "}\n" 271 "\n" 272 "// Foobar\n" 273 "int i, j, k;\n" 274 "\n" 275 "// Comment for function\n" 276 "// Comment line 2\n" 277 "// Comment line 3\n" 278 "int bar(int j, int k) {\n" 279 " int r = j * k;\n" 280 " return r;\n" 281 "}\n" 282 "\n" 283 "int bar2(int j, int k) {\n" 284 " int r = j / k;\n" 285 " return r;\n" 286 "}\n" 287 "\n" 288 "/* Comment block in one line*/\n" 289 "enum Bar { FOOBAR, BARFOO };\n" 290 "\n" 291 "int bar3(int j, int k) {\n" 292 " // A comment\n" 293 " int r = j % k;\n" 294 " return r;\n" 295 "}\n" 296 "} // namespace"; 297 verifyFormat(Prefix + "\n\n\n" + Postfix, Style, 298 Prefix + removeEmptyLines(Postfix)); 299 } 300 301 TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) { 302 FormatStyle Style = getLLVMStyle(); 303 Style.BreakBeforeBraces = FormatStyle::BS_Allman; 304 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 305 verifyFormat("namespace NS\n" 306 "{\n" 307 "// Enum test1\n" 308 "// Enum test2\n" 309 "enum Foo\n" 310 "{\n" 311 " FOO,\n" 312 " BAR\n" 313 "};\n" 314 "\n" 315 "/*\n" 316 "test1\n" 317 "test2\n" 318 "*/\n" 319 "int foo(int i, int j)\n" 320 "{\n" 321 " int r = i + j;\n" 322 " return r;\n" 323 "}\n" 324 "\n" 325 "// Foobar\n" 326 "int i, j, k;\n" 327 "\n" 328 "// Comment for function\n" 329 "// Comment line 2\n" 330 "// Comment line 3\n" 331 "int bar(int j, int k)\n" 332 "{\n" 333 " int r = j * k;\n" 334 " return r;\n" 335 "}\n" 336 "\n" 337 "int bar2(int j, int k)\n" 338 "{\n" 339 " int r = j / k;\n" 340 " return r;\n" 341 "}\n" 342 "\n" 343 "enum Bar\n" 344 "{\n" 345 " FOOBAR,\n" 346 " BARFOO\n" 347 "};\n" 348 "\n" 349 "int bar3(int j, int k)\n" 350 "{\n" 351 " // A comment\n" 352 " int r = j % k;\n" 353 " return r;\n" 354 "}\n" 355 "} // namespace NS", 356 Style); 357 } 358 359 TEST_F(DefinitionBlockSeparatorTest, Leave) { 360 FormatStyle Style = getLLVMStyle(); 361 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave; 362 Style.MaxEmptyLinesToKeep = 3; 363 std::string LeaveAs = "namespace {\n" 364 "\n" 365 "// Enum test1\n" 366 "// Enum test2\n" 367 "enum Foo { FOO, BAR };\n" 368 "\n\n\n" 369 "/*\n" 370 "test1\n" 371 "test2\n" 372 "*/\n" 373 "int foo(int i, int j) {\n" 374 " int r = i + j;\n" 375 " return r;\n" 376 "}\n" 377 "\n" 378 "// Foobar\n" 379 "int i, j, k;\n" 380 "\n" 381 "// Comment for function\n" 382 "// Comment line 2\n" 383 "// Comment line 3\n" 384 "int bar(int j, int k) {\n" 385 " int r = j * k;\n" 386 " return r;\n" 387 "}\n" 388 "\n" 389 "int bar2(int j, int k) {\n" 390 " int r = j / k;\n" 391 " return r;\n" 392 "}\n" 393 "\n" 394 "// Comment for inline enum\n" 395 "enum Bar { FOOBAR, BARFOO };\n" 396 "int bar3(int j, int k) {\n" 397 " // A comment\n" 398 " int r = j % k;\n" 399 " return r;\n" 400 "}\n" 401 "} // namespace"; 402 verifyFormat(LeaveAs, Style, LeaveAs); 403 } 404 405 TEST_F(DefinitionBlockSeparatorTest, CSharp) { 406 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp); 407 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 408 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 409 Style.AllowShortEnumsOnASingleLine = false; 410 verifyFormat("namespace {\r\n" 411 "public class SomeTinyClass {\r\n" 412 " int X;\r\n" 413 "}\r\n" 414 "\r\n" 415 "public class AnotherTinyClass {\r\n" 416 " int Y;\r\n" 417 "}\r\n" 418 "\r\n" 419 "internal static String toString() {\r\n" 420 "}\r\n" 421 "\r\n" 422 "// Comment for enum\r\n" 423 "public enum var {\r\n" 424 " none,\r\n" 425 " @string,\r\n" 426 " bool,\r\n" 427 " @enum\r\n" 428 "}\r\n" 429 "\r\n" 430 "// Test\r\n" 431 "[STAThread]\r\n" 432 "static void Main(string[] args) {\r\n" 433 " Console.WriteLine(\"HelloWorld\");\r\n" 434 "}\r\n" 435 "\r\n" 436 "static decimal Test() {\r\n" 437 "}\r\n" 438 "}\r\n" 439 "\r\n" 440 "public class FoobarClass {\r\n" 441 " int foobar;\r\n" 442 "}", 443 Style); 444 } 445 446 TEST_F(DefinitionBlockSeparatorTest, JavaScript) { 447 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript); 448 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; 449 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; 450 Style.AllowShortEnumsOnASingleLine = false; 451 verifyFormat("export const enum Foo {\n" 452 " A = 1,\n" 453 " B\n" 454 "}\n" 455 "\n" 456 "export function A() {\n" 457 "}\n" 458 "\n" 459 "export default function B() {\n" 460 "}\n" 461 "\n" 462 "export function C() {\n" 463 "}\n" 464 "\n" 465 "var t, p, q;\n" 466 "\n" 467 "export abstract class X {\n" 468 " y: number;\n" 469 "}\n" 470 "\n" 471 "export const enum Bar {\n" 472 " D = 1,\n" 473 " E\n" 474 "}", 475 Style); 476 } 477 } // namespace 478 } // namespace format 479 } // namespace clang 480