620 lines · cpp
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-exception6//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 17namespace clang {18namespace format {19namespace {20 21class DefinitionBlockSeparatorTest : public testing::Test {22protected:23 static std::string24 separateDefinitionBlocks(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::string37 separateDefinitionBlocks(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, StringRef Code,45 const FormatStyle &Style = getLLVMStyle(),46 StringRef ExpectedCode = "", bool Inverse = true) {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 EXPECT_EQ(ExpectedCode, separateDefinitionBlocks(ExpectedCode, Style))55 << "Expected code is not stable";56 if (Inverse) {57 FormatStyle InverseStyle = Style;58 if (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always)59 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;60 else61 InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;62 EXPECT_NE(ExpectedCode,63 separateDefinitionBlocks(ExpectedCode, InverseStyle))64 << "Inverse formatting makes no difference";65 }66 std::string CodeToFormat =67 HasOriginalCode ? Code.str() : removeEmptyLines(Code);68 std::string Result = separateDefinitionBlocks(CodeToFormat, Style);69 EXPECT_EQ(ExpectedCode, Result) << "Test failed. Formatted:\n" << Result;70 }71 72 static std::string removeEmptyLines(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 }82 Result.push_back(Char);83 }84 return Result;85 }86};87 88#define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)89 90TEST_F(DefinitionBlockSeparatorTest, Basic) {91 FormatStyle Style = getLLVMStyle();92 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;93 verifyFormat("int foo(int i, int j) {\n"94 " int r = i + j;\n"95 " return r;\n"96 "}\n"97 "\n"98 "int bar(int j, int k) {\n"99 " int r = j + k;\n"100 " return r;\n"101 "}",102 Style);103 104 verifyFormat("struct foo {\n"105 " int i, j;\n"106 "};\n"107 "\n"108 "struct bar {\n"109 " int j, k;\n"110 "};",111 Style);112 113 verifyFormat("union foo {\n"114 " int i, j;\n"115 "};\n"116 "\n"117 "union bar {\n"118 " int j, k;\n"119 "};",120 Style);121 122 verifyFormat("class foo {\n"123 " int i, j;\n"124 "};\n"125 "\n"126 "class bar {\n"127 " int j, k;\n"128 "};",129 Style);130 131 verifyFormat("namespace foo {\n"132 "int i, j;\n"133 "}\n"134 "\n"135 "namespace bar {\n"136 "int j, k;\n"137 "}",138 Style);139 140 verifyFormat("enum Foo { FOO, BAR };\n"141 "\n"142 "enum Bar { FOOBAR, BARFOO };",143 Style);144 145 FormatStyle BreakAfterReturnTypeStyle = Style;146 BreakAfterReturnTypeStyle.BreakAfterReturnType = FormatStyle::RTBS_All;147 // Test uppercased long typename148 verifyFormat("class Foo {\n"149 " void\n"150 " Bar(int t, int p) {\n"151 " int r = t + p;\n"152 " return r;\n"153 " }\n"154 "\n"155 " HRESULT\n"156 " Foobar(int t, int p) {\n"157 " int r = t * p;\n"158 " return r;\n"159 " }\n"160 "}",161 BreakAfterReturnTypeStyle);162}163 164TEST_F(DefinitionBlockSeparatorTest, FormatConflict) {165 FormatStyle Style = getLLVMStyle();166 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;167 StringRef Code = "class Test {\n"168 "public:\n"169 " static void foo() {\n"170 " int t;\n"171 " return 1;\n"172 " }\n"173 "};";174 std::vector<tooling::Range> Ranges = {1, tooling::Range(0, Code.size())};175 EXPECT_EQ(reformat(Style, Code, Ranges, "<stdin>").size(), 0u);176}177 178TEST_F(DefinitionBlockSeparatorTest, CommentBlock) {179 FormatStyle Style = getLLVMStyle();180 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;181 std::string Prefix = "enum Foo { FOO, BAR };\n"182 "\n"183 "/*\n"184 "test1\n"185 "test2\n"186 "*/\n"187 "int foo(int i, int j) {\n"188 " int r = i + j;\n"189 " return r;\n"190 "}\n";191 std::string Suffix = "enum Bar { FOOBAR, BARFOO };\n"192 "\n"193 "/* Comment block in one line*/\n"194 "int bar3(int j, int k) {\n"195 " // A comment\n"196 " int r = j % k;\n"197 " return r;\n"198 "}\n";199 std::string CommentedCode = "/*\n"200 "int bar2(int j, int k) {\n"201 " int r = j / k;\n"202 " return r;\n"203 "}\n"204 "*/\n";205 verifyFormat(removeEmptyLines(Prefix) + "\n" + CommentedCode + "\n" +206 removeEmptyLines(Suffix),207 Style, Prefix + "\n" + CommentedCode + "\n" + Suffix);208 verifyFormat(removeEmptyLines(Prefix) + "\n" + CommentedCode +209 removeEmptyLines(Suffix),210 Style, Prefix + "\n" + CommentedCode + Suffix);211}212 213TEST_F(DefinitionBlockSeparatorTest, UntouchBlockStartStyle) {214 // Returns a std::pair of two strings, with the first one for passing into215 // Always test and the second one be the expected result of the first string.216 auto MakeUntouchTest = [&](std::string BlockHeader, std::string BlockChanger,217 std::string BlockFooter, bool BlockEndNewLine) {218 std::string CodePart1 = "enum Foo { FOO, BAR };\n"219 "\n"220 "/*\n"221 "test1\n"222 "test2\n"223 "*/\n"224 "int foo(int i, int j) {\n"225 " int r = i + j;\n"226 " return r;\n"227 "}\n";228 std::string CodePart2 = "/* Comment block in one line*/\n"229 "enum Bar { FOOBAR, BARFOO };\n"230 "\n"231 "int bar3(int j, int k) {\n"232 " // A comment\n"233 " int r = j % k;\n"234 " return r;\n"235 "}\n";236 std::string CodePart3 = "int bar2(int j, int k) {\n"237 " int r = j / k;\n"238 " return r;\n"239 "}\n";240 std::string ConcatAll = BlockHeader + CodePart1 + BlockChanger + CodePart2 +241 BlockFooter + (BlockEndNewLine ? "\n" : "") +242 CodePart3;243 return std::make_pair(BlockHeader + removeEmptyLines(CodePart1) +244 BlockChanger + removeEmptyLines(CodePart2) +245 BlockFooter + removeEmptyLines(CodePart3),246 ConcatAll);247 };248 249 FormatStyle AlwaysStyle = getLLVMStyle();250 AlwaysStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;251 252 FormatStyle NeverStyle = getLLVMStyle();253 NeverStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;254 255 auto TestKit = MakeUntouchTest("/* FOOBAR */\n"256 "#ifdef FOO\n\n",257 "\n#elifndef BAR\n\n", "\n#endif\n\n", false);258 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);259 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));260 261 TestKit = MakeUntouchTest("/* FOOBAR */\n"262 "#ifdef FOO\n",263 "#elifndef BAR\n", "#endif\n", false);264 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);265 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));266 267 TestKit = MakeUntouchTest("namespace Ns {\n\n",268 "\n} // namespace Ns\n\n"269 "namespace {\n\n",270 "\n} // namespace\n", true);271 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);272 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));273 274 TestKit = MakeUntouchTest("namespace Ns {\n",275 "} // namespace Ns\n\n"276 "namespace {\n",277 "} // namespace\n", true);278 verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);279 verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));280}281 282TEST_F(DefinitionBlockSeparatorTest, Always) {283 FormatStyle Style = getLLVMStyle();284 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;285 286 verifyFormat("// clang-format off\n"287 "template<class T>\n"288 "concept C = not A<S<T>>;\n"289 "// clang-format on\n"290 "\n"291 "struct E {};",292 Style);293 294 std::string Prefix = "namespace {\n";295 std::string Infix = "\n"296 "// Enum test1\n"297 "// Enum test2\n"298 "enum Foo { FOO, BAR };\n"299 "\n"300 "/*\n"301 "test1\n"302 "test2\n"303 "*/\n"304 "/*const*/ int foo(int i, int j) {\n"305 " int r = i + j;\n"306 " return r;\n"307 "}\n"308 "\n"309 "// Foobar\n"310 "int i, j, k;\n"311 "\n"312 "// Comment for function\n"313 "// Comment line 2\n"314 "// Comment line 3\n"315 "int bar(int j, int k) {\n"316 " {\n"317 " int r = j * k;\n"318 " return r;\n"319 " }\n"320 "}\n"321 "\n"322 "int bar2(int j, int k) {\n"323 " int r = j / k;\n"324 " return r;\n"325 "}\n"326 "\n"327 "/* Comment block in one line*/\n"328 "enum Bar { FOOBAR, BARFOO };\n"329 "\n"330 "int bar3(int j, int k, const enum Bar b) {\n"331 " // A comment\n"332 " int r = j % k;\n"333 " if (struct S = getS()) {\n"334 " // if condition\n"335 " }\n"336 " return r;\n"337 "}\n";338 std::string Postfix = "\n"339 "} // namespace\n"340 "\n"341 "namespace T {\n"342 "int i, j, k;\n"343 "} // namespace T";344 verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix),345 Style, Prefix + Infix + Postfix);346}347 348TEST_F(DefinitionBlockSeparatorTest, Never) {349 FormatStyle Style = getLLVMStyle();350 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never;351 std::string Prefix = "namespace {\n";352 std::string Postfix = "// Enum test1\n"353 "// Enum test2\n"354 "enum Foo { FOO, BAR };\n"355 "\n"356 "/*\n"357 "test1\n"358 "test2\n"359 "*/\n"360 "/*const*/ int foo(int i, int j) {\n"361 " int r = i + j;\n"362 " return r;\n"363 "}\n"364 "\n"365 "// Foobar\n"366 "int i, j, k;\n"367 "\n"368 "// Comment for function\n"369 "// Comment line 2\n"370 "// Comment line 3\n"371 "int bar(int j, int k) {\n"372 " {\n"373 " int r = j * k;\n"374 " return r;\n"375 " }\n"376 "}\n"377 "\n"378 "int bar2(int j, int k) {\n"379 " int r = j / k;\n"380 " return r;\n"381 "}\n"382 "\n"383 "/* Comment block in one line*/\n"384 "enum Bar { FOOBAR, BARFOO };\n"385 "\n"386 "int bar3(int j, int k, const enum Bar b) {\n"387 " // A comment\n"388 " int r = j % k;\n"389 " if (struct S = getS()) {\n"390 " // if condition\n"391 " }\n"392 " return r;\n"393 "}\n"394 "} // namespace";395 verifyFormat(Prefix + "\n\n\n" + Postfix, Style,396 Prefix + removeEmptyLines(Postfix));397}398 399TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {400 FormatStyle Style = getLLVMStyle();401 Style.BreakBeforeBraces = FormatStyle::BS_Allman;402 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;403 verifyFormat("namespace NS\n"404 "{\n"405 "// Enum test1\n"406 "// Enum test2\n"407 "enum Foo\n"408 "{\n"409 " FOO,\n"410 " BAR\n"411 "};\n"412 "\n"413 "/*\n"414 "test1\n"415 "test2\n"416 "*/\n"417 "/*const*/ int foo(int i, int j)\n"418 "{\n"419 " int r = i + j;\n"420 " return r;\n"421 "}\n"422 "\n"423 "// Foobar\n"424 "int i, j, k;\n"425 "\n"426 "// Comment for function\n"427 "// Comment line 2\n"428 "// Comment line 3\n"429 "int bar(int j, int k)\n"430 "{\n"431 " {\n"432 " int r = j * k;\n"433 " return r;\n"434 " }\n"435 "}\n"436 "\n"437 "int bar2(int j, int k)\n"438 "{\n"439 " int r = j / k;\n"440 " return r;\n"441 "}\n"442 "\n"443 "enum Bar\n"444 "{\n"445 " FOOBAR,\n"446 " BARFOO\n"447 "};\n"448 "\n"449 "int bar3(int j, int k, const enum Bar b)\n"450 "{\n"451 " // A comment\n"452 " int r = j % k;\n"453 " if (struct S = getS())\n"454 " {\n"455 " // if condition\n"456 " }\n"457 " return r;\n"458 "}\n"459 "} // namespace NS",460 Style);461}462 463TEST_F(DefinitionBlockSeparatorTest, TryBlocks) {464 FormatStyle Style = getLLVMStyle();465 Style.BreakBeforeBraces = FormatStyle::BS_Allman;466 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;467 verifyFormat("void FunctionWithInternalTry()\n"468 "{\n"469 " try\n"470 " {\n"471 " return;\n"472 " }\n"473 " catch (const std::exception &)\n"474 " {\n"475 " }\n"476 "}",477 Style, "", /*Inverse=*/false);478 verifyFormat("void FunctionWithTryBlock()\n"479 "try\n"480 "{\n"481 " return;\n"482 "}\n"483 "catch (const std::exception &)\n"484 "{\n"485 "}",486 Style, "", /*Inverse=*/false);487}488 489TEST_F(DefinitionBlockSeparatorTest, Leave) {490 FormatStyle Style = getLLVMStyle();491 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;492 Style.MaxEmptyLinesToKeep = 3;493 std::string LeaveAs = "namespace {\n"494 "\n"495 "// Enum test1\n"496 "// Enum test2\n"497 "enum Foo { FOO, BAR };\n"498 "\n\n\n"499 "/*\n"500 "test1\n"501 "test2\n"502 "*/\n"503 "/*const*/ int foo(int i, int j) {\n"504 " int r = i + j;\n"505 " return r;\n"506 "}\n"507 "\n"508 "// Foobar\n"509 "int i, j, k;\n"510 "\n"511 "// Comment for function\n"512 "// Comment line 2\n"513 "// Comment line 3\n"514 "int bar(int j, int k) {\n"515 " {\n"516 " int r = j * k;\n"517 " return r;\n"518 " }\n"519 "}\n"520 "\n"521 "int bar2(int j, int k) {\n"522 " int r = j / k;\n"523 " return r;\n"524 "}\n"525 "\n"526 "// Comment for inline enum\n"527 "enum Bar { FOOBAR, BARFOO };\n"528 "int bar3(int j, int k, const enum Bar b) {\n"529 " // A comment\n"530 " int r = j % k;\n"531 " if (struct S = getS()) {\n"532 " // if condition\n"533 " }\n"534 " return r;\n"535 "}\n"536 "} // namespace";537 verifyFormat(LeaveAs, Style, LeaveAs);538}539 540TEST_F(DefinitionBlockSeparatorTest, CSharp) {541 FormatStyle Style = getLLVMStyle(FormatStyle::LK_CSharp);542 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;543 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;544 Style.AllowShortEnumsOnASingleLine = false;545 verifyFormat("namespace {\r\n"546 "public class SomeTinyClass {\r\n"547 " int X;\r\n"548 "}\r\n"549 "\r\n"550 "public class AnotherTinyClass {\r\n"551 " int Y;\r\n"552 "}\r\n"553 "\r\n"554 "internal static String toString() {\r\n"555 "}\r\n"556 "\r\n"557 "// Comment for enum\r\n"558 "public enum var {\r\n"559 " none,\r\n"560 " @string,\r\n"561 " bool,\r\n"562 " @enum\r\n"563 "}\r\n"564 "\r\n"565 "// Test\r\n"566 "[STAThread]\r\n"567 "static void Main(string[] args) {\r\n"568 " Console.WriteLine(\"HelloWorld\");\r\n"569 "}\r\n"570 "\r\n"571 "static decimal Test() {\r\n"572 "}\r\n"573 "}\r\n"574 "\r\n"575 "public class FoobarClass {\r\n"576 " int foobar;\r\n"577 "}\r\n"578 "\r\n"579 "public class LogFactory<TLogger>\r\n"580 " where TLogger : class, new() {\r\n"581 " int i;\r\n"582 "}",583 Style);584}585 586TEST_F(DefinitionBlockSeparatorTest, JavaScript) {587 FormatStyle Style = getLLVMStyle(FormatStyle::LK_JavaScript);588 Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;589 Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;590 Style.AllowShortEnumsOnASingleLine = false;591 verifyFormat("export const enum Foo {\n"592 " A = 1,\n"593 " B\n"594 "}\n"595 "\n"596 "export function A() {\n"597 "}\n"598 "\n"599 "export default function B() {\n"600 "}\n"601 "\n"602 "export function C() {\n"603 "}\n"604 "\n"605 "var t, p, q;\n"606 "\n"607 "export abstract class X {\n"608 " y: number;\n"609 "}\n"610 "\n"611 "export const enum Bar {\n"612 " D = 1,\n"613 " E\n"614 "}",615 Style);616}617} // namespace618} // namespace format619} // namespace clang620