forked from OSchip/llvm-project
[clang-format] Fix SeparateDefinitionBlocks issues
Fixes https://github.com/llvm/llvm-project/issues/52976. - Make no formatting for macros - Attach comment with definition headers - Make no change on use of empty lines at block start/end - Fix misrecognition of keyword namespace Differential Revision: https://reviews.llvm.org/D116663 Reviewed By: MyDeveloperDay, HazardyKnusperkeks, curdeius
This commit is contained in:
parent
8f553da492
commit
ee25a327aa
|
@ -31,15 +31,16 @@ std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze(
|
|||
|
||||
void DefinitionBlockSeparator::separateBlocks(
|
||||
SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result) {
|
||||
const bool IsNeverStyle =
|
||||
Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never;
|
||||
auto LikelyDefinition = [this](const AnnotatedLine *Line) {
|
||||
if (Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition())
|
||||
if ((Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) ||
|
||||
Line->startsWithNamespace())
|
||||
return true;
|
||||
FormatToken *CurrentToken = Line->First;
|
||||
while (CurrentToken) {
|
||||
if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct,
|
||||
tok::kw_namespace, tok::kw_enum) ||
|
||||
(Style.Language == FormatStyle::LK_JavaScript &&
|
||||
CurrentToken->TokenText == "function"))
|
||||
if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_enum) ||
|
||||
(Style.isJavaScript() && CurrentToken->TokenText == "function"))
|
||||
return true;
|
||||
CurrentToken = CurrentToken->Next;
|
||||
}
|
||||
|
@ -54,11 +55,14 @@ void DefinitionBlockSeparator::separateBlocks(
|
|||
Env.getSourceManager().getBufferData(Env.getFileID()),
|
||||
Style.UseCRLF)
|
||||
: Style.UseCRLF);
|
||||
for (unsigned I = 0; I < Lines.size(); I++) {
|
||||
for (unsigned I = 0; I < Lines.size(); ++I) {
|
||||
const auto &CurrentLine = Lines[I];
|
||||
if (CurrentLine->InPPDirective)
|
||||
continue;
|
||||
FormatToken *TargetToken = nullptr;
|
||||
AnnotatedLine *TargetLine;
|
||||
auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
|
||||
AnnotatedLine *OpeningLine = nullptr;
|
||||
const auto InsertReplacement = [&](const int NewlineToInsert) {
|
||||
assert(TargetLine);
|
||||
assert(TargetToken);
|
||||
|
@ -72,9 +76,18 @@ void DefinitionBlockSeparator::separateBlocks(
|
|||
TargetToken->SpacesRequiredBefore - 1,
|
||||
TargetToken->StartsColumn);
|
||||
};
|
||||
const auto IsPPConditional = [&](const size_t LineIndex) {
|
||||
const auto &Line = Lines[LineIndex];
|
||||
return Line->First->is(tok::hash) && Line->First->Next &&
|
||||
Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else,
|
||||
tok::pp_ifndef, tok::pp_elifndef,
|
||||
tok::pp_elifdef, tok::pp_elif,
|
||||
tok::pp_endif);
|
||||
};
|
||||
const auto FollowingOtherOpening = [&]() {
|
||||
return OpeningLineIndex == 0 ||
|
||||
Lines[OpeningLineIndex - 1]->Last->opensScope();
|
||||
Lines[OpeningLineIndex - 1]->Last->opensScope() ||
|
||||
IsPPConditional(OpeningLineIndex - 1);
|
||||
};
|
||||
const auto HasEnumOnLine = [CurrentLine]() {
|
||||
FormatToken *CurrentToken = CurrentLine->First;
|
||||
|
@ -87,17 +100,29 @@ void DefinitionBlockSeparator::separateBlocks(
|
|||
};
|
||||
|
||||
bool IsDefBlock = false;
|
||||
const auto MayPrecedeDefinition = [&](const int Direction = -1) {
|
||||
const size_t OperateIndex = OpeningLineIndex + Direction;
|
||||
assert(OperateIndex < Lines.size());
|
||||
const auto &OperateLine = Lines[OperateIndex];
|
||||
return (Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare)) ||
|
||||
OperateLine->First->is(tok::comment);
|
||||
};
|
||||
|
||||
if (HasEnumOnLine()) {
|
||||
// We have no scope opening/closing information for enum.
|
||||
IsDefBlock = true;
|
||||
OpeningLineIndex = I;
|
||||
TargetLine = CurrentLine;
|
||||
TargetToken = CurrentLine->First;
|
||||
while (OpeningLineIndex > 0 && MayPrecedeDefinition())
|
||||
--OpeningLineIndex;
|
||||
OpeningLine = Lines[OpeningLineIndex];
|
||||
TargetLine = OpeningLine;
|
||||
TargetToken = TargetLine->First;
|
||||
if (!FollowingOtherOpening())
|
||||
InsertReplacement(NewlineCount);
|
||||
else
|
||||
else if (IsNeverStyle)
|
||||
InsertReplacement(OpeningLineIndex != 0);
|
||||
TargetLine = CurrentLine;
|
||||
TargetToken = TargetLine->First;
|
||||
while (TargetToken && !TargetToken->is(tok::r_brace))
|
||||
TargetToken = TargetToken->Next;
|
||||
if (!TargetToken) {
|
||||
|
@ -108,40 +133,48 @@ void DefinitionBlockSeparator::separateBlocks(
|
|||
if (OpeningLineIndex > Lines.size())
|
||||
continue;
|
||||
// Handling the case that opening bracket has its own line.
|
||||
OpeningLineIndex -= Lines[OpeningLineIndex]->First->TokenText == "{";
|
||||
AnnotatedLine *OpeningLine = Lines[OpeningLineIndex];
|
||||
OpeningLineIndex -= Lines[OpeningLineIndex]->First->is(tok::l_brace);
|
||||
OpeningLine = Lines[OpeningLineIndex];
|
||||
// Closing a function definition.
|
||||
if (LikelyDefinition(OpeningLine)) {
|
||||
IsDefBlock = true;
|
||||
if (OpeningLineIndex > 0) {
|
||||
OpeningLineIndex -=
|
||||
Style.Language == FormatStyle::LK_CSharp &&
|
||||
Lines[OpeningLineIndex - 1]->First->is(tok::l_square);
|
||||
OpeningLine = Lines[OpeningLineIndex];
|
||||
}
|
||||
while (OpeningLineIndex > 0 && MayPrecedeDefinition())
|
||||
--OpeningLineIndex;
|
||||
OpeningLine = Lines[OpeningLineIndex];
|
||||
TargetLine = OpeningLine;
|
||||
TargetToken = TargetLine->First;
|
||||
if (!FollowingOtherOpening()) {
|
||||
// Avoid duplicated replacement.
|
||||
if (!TargetToken->opensScope())
|
||||
if (TargetToken->isNot(tok::l_brace))
|
||||
InsertReplacement(NewlineCount);
|
||||
} else
|
||||
} else if (IsNeverStyle)
|
||||
InsertReplacement(OpeningLineIndex != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Not the last token.
|
||||
if (IsDefBlock && I + 1 < Lines.size()) {
|
||||
TargetLine = Lines[I + 1];
|
||||
OpeningLineIndex = I + 1;
|
||||
TargetLine = Lines[OpeningLineIndex];
|
||||
TargetToken = TargetLine->First;
|
||||
|
||||
// No empty line for continuously closing scopes. The token will be
|
||||
// handled in another case if the line following is opening a
|
||||
// definition.
|
||||
if (!TargetToken->closesScope()) {
|
||||
if (!LikelyDefinition(TargetLine))
|
||||
if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) {
|
||||
// Check whether current line may be precedings of a definition line.
|
||||
while (OpeningLineIndex + 1 < Lines.size() &&
|
||||
MayPrecedeDefinition(/*Direction=*/0))
|
||||
++OpeningLineIndex;
|
||||
TargetLine = Lines[OpeningLineIndex];
|
||||
if (!LikelyDefinition(TargetLine)) {
|
||||
TargetLine = Lines[I + 1];
|
||||
TargetToken = TargetLine->First;
|
||||
InsertReplacement(NewlineCount);
|
||||
} else {
|
||||
}
|
||||
} else if (IsNeverStyle) {
|
||||
TargetLine = Lines[I + 1];
|
||||
TargetToken = TargetLine->First;
|
||||
InsertReplacement(OpeningLineIndex != 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,8 +57,9 @@ protected:
|
|||
InverseStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
|
||||
EXPECT_EQ(ExpectedCode.str(), separateDefinitionBlocks(ExpectedCode, Style))
|
||||
<< "Expected code is not stable";
|
||||
std::string InverseResult = separateDefinitionBlocks(Code, InverseStyle);
|
||||
EXPECT_NE(Code.str(), InverseResult)
|
||||
std::string InverseResult =
|
||||
separateDefinitionBlocks(ExpectedCode, InverseStyle);
|
||||
EXPECT_NE(ExpectedCode.str(), InverseResult)
|
||||
<< "Inverse formatting makes no difference";
|
||||
std::string CodeToFormat =
|
||||
HasOriginalCode ? Code.str() : removeEmptyLines(Code);
|
||||
|
@ -129,49 +130,166 @@ TEST_F(DefinitionBlockSeparatorTest, Basic) {
|
|||
Style);
|
||||
}
|
||||
|
||||
TEST_F(DefinitionBlockSeparatorTest, UntouchBlockStartStyle) {
|
||||
// Returns a std::pair of two strings, with the first one for passing into
|
||||
// Always test and the second one be the expected result of the first string.
|
||||
auto MakeUntouchTest = [&](std::string BlockHeader, std::string BlockChanger,
|
||||
std::string BlockFooter, bool BlockEndNewLine) {
|
||||
std::string CodePart1 = "enum Foo { FOO, BAR };\n"
|
||||
"\n"
|
||||
"/*\n"
|
||||
"test1\n"
|
||||
"test2\n"
|
||||
"*/\n"
|
||||
"int foo(int i, int j) {\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n";
|
||||
std::string CodePart2 = "/* Comment block in one line*/\n"
|
||||
"enum Bar { FOOBAR, BARFOO };\n"
|
||||
"\n"
|
||||
"int bar3(int j, int k) {\n"
|
||||
" // A comment\n"
|
||||
" int r = j % k;\n"
|
||||
" return r;\n"
|
||||
"}\n";
|
||||
std::string CodePart3 = "int bar2(int j, int k) {\n"
|
||||
" int r = j / k;\n"
|
||||
" return r;\n"
|
||||
"}\n";
|
||||
std::string ConcatAll = BlockHeader + CodePart1 + BlockChanger + CodePart2 +
|
||||
BlockFooter + (BlockEndNewLine ? "\n" : "") +
|
||||
CodePart3;
|
||||
return std::make_pair(BlockHeader + removeEmptyLines(CodePart1) +
|
||||
BlockChanger + removeEmptyLines(CodePart2) +
|
||||
BlockFooter + removeEmptyLines(CodePart3),
|
||||
ConcatAll);
|
||||
};
|
||||
|
||||
FormatStyle AlwaysStyle = getLLVMStyle();
|
||||
AlwaysStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
|
||||
|
||||
FormatStyle NeverStyle = getLLVMStyle();
|
||||
NeverStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
|
||||
|
||||
auto TestKit = MakeUntouchTest("#ifdef FOO\n\n", "\n#elifndef BAR\n\n",
|
||||
"\n#endif\n\n", false);
|
||||
verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
|
||||
verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
|
||||
|
||||
TestKit =
|
||||
MakeUntouchTest("#ifdef FOO\n", "#elifndef BAR\n", "#endif\n", false);
|
||||
verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
|
||||
verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
|
||||
|
||||
TestKit = MakeUntouchTest("namespace Ns {\n\n",
|
||||
"\n} // namespace Ns\n\n"
|
||||
"namespace {\n\n",
|
||||
"\n} // namespace\n", true);
|
||||
verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
|
||||
verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
|
||||
|
||||
TestKit = MakeUntouchTest("namespace Ns {\n",
|
||||
"} // namespace Ns\n\n"
|
||||
"namespace {\n",
|
||||
"} // namespace\n", true);
|
||||
verifyFormat(TestKit.first, AlwaysStyle, TestKit.second);
|
||||
verifyFormat(TestKit.second, NeverStyle, removeEmptyLines(TestKit.second));
|
||||
}
|
||||
|
||||
TEST_F(DefinitionBlockSeparatorTest, Always) {
|
||||
FormatStyle Style = getLLVMStyle();
|
||||
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
|
||||
std::string Prefix = "namespace {\n";
|
||||
std::string Postfix = "enum Foo { FOO, BAR };\n"
|
||||
"\n"
|
||||
"int foo(int i, int j) {\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
std::string Infix = "\n"
|
||||
"// Enum test1\n"
|
||||
"// Enum test2\n"
|
||||
"enum Foo { FOO, BAR };\n"
|
||||
"\n"
|
||||
"/*\n"
|
||||
"test1\n"
|
||||
"test2\n"
|
||||
"*/\n"
|
||||
"int foo(int i, int j) {\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"// Foobar\n"
|
||||
"int i, j, k;\n"
|
||||
"\n"
|
||||
"// Comment for function\n"
|
||||
"// Comment line 2\n"
|
||||
"// Comment line 3\n"
|
||||
"int bar(int j, int k) {\n"
|
||||
" int r = j * k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int bar2(int j, int k) {\n"
|
||||
" int r = j / k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* Comment block in one line*/\n"
|
||||
"enum Bar { FOOBAR, BARFOO };\n"
|
||||
"\n"
|
||||
"int bar3(int j, int k) {\n"
|
||||
" // A comment\n"
|
||||
" int r = j % k;\n"
|
||||
" return r;\n"
|
||||
"}\n";
|
||||
std::string Postfix = "\n"
|
||||
"} // namespace\n"
|
||||
"\n"
|
||||
"namespace T {\n"
|
||||
"int i, j, k;\n"
|
||||
"\n"
|
||||
"int bar(int j, int k) {\n"
|
||||
" int r = j * k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"enum Bar { FOOBAR, BARFOO };\n"
|
||||
"} // namespace";
|
||||
verifyFormat(Prefix + "\n\n\n" + removeEmptyLines(Postfix), Style,
|
||||
Prefix + Postfix);
|
||||
"} // namespace T";
|
||||
verifyFormat(Prefix + removeEmptyLines(Infix) + removeEmptyLines(Postfix),
|
||||
Style, Prefix + Infix + Postfix);
|
||||
}
|
||||
|
||||
TEST_F(DefinitionBlockSeparatorTest, Never) {
|
||||
FormatStyle Style = getLLVMStyle();
|
||||
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never;
|
||||
std::string Prefix = "namespace {\n";
|
||||
std::string Postfix = "enum Foo { FOO, BAR };\n"
|
||||
std::string Postfix = "// Enum test1\n"
|
||||
"// Enum test2\n"
|
||||
"enum Foo { FOO, BAR };\n"
|
||||
"\n"
|
||||
"/*\n"
|
||||
"test1\n"
|
||||
"test2\n"
|
||||
"*/\n"
|
||||
"int foo(int i, int j) {\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"// Foobar\n"
|
||||
"int i, j, k;\n"
|
||||
"\n"
|
||||
"// Comment for function\n"
|
||||
"// Comment line 2\n"
|
||||
"// Comment line 3\n"
|
||||
"int bar(int j, int k) {\n"
|
||||
" int r = j * k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int bar2(int j, int k) {\n"
|
||||
" int r = j / k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* Comment block in one line*/\n"
|
||||
"enum Bar { FOOBAR, BARFOO };\n"
|
||||
"\n"
|
||||
"int bar3(int j, int k) {\n"
|
||||
" // A comment\n"
|
||||
" int r = j % k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"} // namespace";
|
||||
verifyFormat(Prefix + "\n\n\n" + Postfix, Style,
|
||||
Prefix + removeEmptyLines(Postfix));
|
||||
|
@ -181,31 +299,57 @@ TEST_F(DefinitionBlockSeparatorTest, OpeningBracketOwnsLine) {
|
|||
FormatStyle Style = getLLVMStyle();
|
||||
Style.BreakBeforeBraces = FormatStyle::BS_Allman;
|
||||
Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
|
||||
verifyFormat("enum Foo\n"
|
||||
verifyFormat("namespace NS\n"
|
||||
"{\n"
|
||||
"// Enum test1\n"
|
||||
"// Enum test2\n"
|
||||
"enum Foo\n"
|
||||
"{\n"
|
||||
" FOO,\n"
|
||||
" BAR\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"/*\n"
|
||||
"test1\n"
|
||||
"test2\n"
|
||||
"*/\n"
|
||||
"int foo(int i, int j)\n"
|
||||
"{\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"// Foobar\n"
|
||||
"int i, j, k;\n"
|
||||
"\n"
|
||||
"// Comment for function\n"
|
||||
"// Comment line 2\n"
|
||||
"// Comment line 3\n"
|
||||
"int bar(int j, int k)\n"
|
||||
"{\n"
|
||||
" int r = j * k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int bar2(int j, int k)\n"
|
||||
"{\n"
|
||||
" int r = j / k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"enum Bar\n"
|
||||
"{\n"
|
||||
" FOOBAR,\n"
|
||||
" BARFOO\n"
|
||||
"};",
|
||||
"};\n"
|
||||
"\n"
|
||||
"int bar3(int j, int k)\n"
|
||||
"{\n"
|
||||
" // A comment\n"
|
||||
" int r = j % k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"} // namespace NS",
|
||||
Style);
|
||||
}
|
||||
|
||||
|
@ -215,21 +359,42 @@ TEST_F(DefinitionBlockSeparatorTest, Leave) {
|
|||
Style.MaxEmptyLinesToKeep = 3;
|
||||
std::string LeaveAs = "namespace {\n"
|
||||
"\n"
|
||||
"// Enum test1\n"
|
||||
"// Enum test2\n"
|
||||
"enum Foo { FOO, BAR };\n"
|
||||
"\n\n\n"
|
||||
"/*\n"
|
||||
"test1\n"
|
||||
"test2\n"
|
||||
"*/\n"
|
||||
"int foo(int i, int j) {\n"
|
||||
" int r = i + j;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"// Foobar\n"
|
||||
"int i, j, k;\n"
|
||||
"\n"
|
||||
"// Comment for function\n"
|
||||
"// Comment line 2\n"
|
||||
"// Comment line 3\n"
|
||||
"int bar(int j, int k) {\n"
|
||||
" int r = j * k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int bar2(int j, int k) {\n"
|
||||
" int r = j / k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"// Comment for inline enum\n"
|
||||
"enum Bar { FOOBAR, BARFOO };\n"
|
||||
"int bar3(int j, int k) {\n"
|
||||
" // A comment\n"
|
||||
" int r = j % k;\n"
|
||||
" return r;\n"
|
||||
"}\n"
|
||||
"} // namespace";
|
||||
verifyFormat(LeaveAs, Style, LeaveAs);
|
||||
}
|
||||
|
@ -251,6 +416,7 @@ TEST_F(DefinitionBlockSeparatorTest, CSharp) {
|
|||
"internal static String toString() {\r\n"
|
||||
"}\r\n"
|
||||
"\r\n"
|
||||
"// Comment for enum\r\n"
|
||||
"public enum var {\r\n"
|
||||
" none,\r\n"
|
||||
" @string,\r\n"
|
||||
|
@ -258,6 +424,7 @@ TEST_F(DefinitionBlockSeparatorTest, CSharp) {
|
|||
" @enum\r\n"
|
||||
"}\r\n"
|
||||
"\r\n"
|
||||
"// Test\r\n"
|
||||
"[STAThread]\r\n"
|
||||
"static void Main(string[] args) {\r\n"
|
||||
" Console.WriteLine(\"HelloWorld\");\r\n"
|
||||
|
|
Loading…
Reference in New Issue