[clang-format] Fix alignment in #else preprocessor blocks
Summary: clang-format makes multiple passes when #if/#else preprocessor blocks are found. It will make one pass for normal code and code in the #if block, and then it will make another pass for just the code in #else blocks. This often results in invalid alignment inside the else blocks because they do not have any scope or indentAndNestingLevel context from their surrounding tokens/lines. This patch remedies that by caching any initial indentAndNestingLevel from a second pass and not breaking/returning early when a scope change is detected. Fixes #36070 Reviewers: HazardyKnusperkeks, MyDeveloperDay Tags: clang, clang-format Differential Revision: https://reviews.llvm.org/D134042
This commit is contained in:
@@ -522,6 +522,13 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
|
||||
? Changes[StartAt].indentAndNestingLevel()
|
||||
: std::tuple<unsigned, unsigned, unsigned>();
|
||||
|
||||
// Keep track if the first token has a non-zero indent and nesting level.
|
||||
// This can happen when aligning the contents of "#else" preprocessor blocks,
|
||||
// which is done separately.
|
||||
bool HasInitialIndentAndNesting =
|
||||
StartAt == 0 &&
|
||||
IndentAndNestingLevel > std::tuple<unsigned, unsigned, unsigned>();
|
||||
|
||||
// Keep track of the number of commas before the matching tokens, we will only
|
||||
// align a sequence of matching tokens if they are preceded by the same number
|
||||
// of commas.
|
||||
@@ -556,8 +563,19 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
|
||||
|
||||
unsigned i = StartAt;
|
||||
for (unsigned e = Changes.size(); i != e; ++i) {
|
||||
if (Changes[i].indentAndNestingLevel() < IndentAndNestingLevel)
|
||||
break;
|
||||
if (Changes[i].indentAndNestingLevel() < IndentAndNestingLevel) {
|
||||
if (!HasInitialIndentAndNesting)
|
||||
break;
|
||||
// The contents of preprocessor blocks are aligned separately.
|
||||
// If the initial preprocessor block is indented or nested (e.g. it's in
|
||||
// a function), do not align and exit after finishing this scope block.
|
||||
// Instead, align, and then lower the baseline indent and nesting level
|
||||
// in order to continue aligning subsequent blocks.
|
||||
EndOfSequence = i;
|
||||
AlignCurrentSequence();
|
||||
IndentAndNestingLevel =
|
||||
Changes[i].indentAndNestingLevel(); // new baseline
|
||||
}
|
||||
|
||||
if (Changes[i].NewlinesBefore != 0) {
|
||||
CommasBeforeMatch = 0;
|
||||
|
||||
@@ -5825,6 +5825,98 @@ TEST_F(FormatTest, IndentPreprocessorDirectives) {
|
||||
Style);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, FormatAlignInsidePreprocessorElseBlock) {
|
||||
FormatStyle Style = getLLVMStyle();
|
||||
Style.AlignConsecutiveAssignments.Enabled = true;
|
||||
Style.AlignConsecutiveDeclarations.Enabled = true;
|
||||
|
||||
// Test with just #if blocks.
|
||||
verifyFormat("void f1() {\n"
|
||||
"#if 1\n"
|
||||
" int foo = 1;\n"
|
||||
" int foobar = 2;\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
"#if 1\n"
|
||||
"int baz = 3;\n"
|
||||
"#endif\n"
|
||||
"void f2() {\n"
|
||||
"#if 1\n"
|
||||
" char *foobarbaz = \"foobarbaz\";\n"
|
||||
" int quux = 4;\n"
|
||||
"}",
|
||||
Style);
|
||||
|
||||
// Test with just #else blocks.
|
||||
verifyFormat("void f1() {\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
" int foo = 1;\n"
|
||||
" int foobar = 2;\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
"int baz = 3;\n"
|
||||
"#endif\n"
|
||||
"void f2() {\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
" char *foobarbaz = \"foobarbaz\";\n"
|
||||
" int quux = 4;\n"
|
||||
"}",
|
||||
Style);
|
||||
|
||||
// Test with a mix of #if and #else blocks.
|
||||
verifyFormat("void f1() {\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
" int foo = 1;\n"
|
||||
" int foobar = 2;\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
"#if 1\n"
|
||||
"int baz = 3;\n"
|
||||
"#endif\n"
|
||||
"void f2() {\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
" // prevent alignment with #else in f1\n"
|
||||
" char *foobarbaz = \"foobarbaz\";\n"
|
||||
" int quux = 4;\n"
|
||||
"}",
|
||||
Style);
|
||||
|
||||
// Test with nested #if and #else blocks.
|
||||
verifyFormat("void f1() {\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
"#if 2\n"
|
||||
"#else\n"
|
||||
" int foo = 1;\n"
|
||||
" int foobar = 2;\n"
|
||||
"#endif\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
"#if 1\n"
|
||||
"#else\n"
|
||||
"#if 2\n"
|
||||
"int baz = 3;\n"
|
||||
"#endif\n"
|
||||
"#endif\n"
|
||||
"void f2() {\n"
|
||||
"#if 1\n"
|
||||
"#if 2\n"
|
||||
"#else\n"
|
||||
" // prevent alignment with #else in f1\n"
|
||||
" char *foobarbaz = \"foobarbaz\";\n"
|
||||
" int quux = 4;\n"
|
||||
"#endif\n"
|
||||
"#endif\n"
|
||||
"}",
|
||||
Style);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, FormatHashIfNotAtStartOfLine) {
|
||||
verifyFormat("{\n { a #c; }\n}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user