https://bugs.llvm.org/show_bug.cgi?id=47936
Using the MultiLine setting for BraceWrapping.AfterControlStatement appears to disable AllowShortFunctionsOnASingleLine, even in cases without any control statements
Differential D114521
[clang-format] [PR47936] AfterControlStatement: MultiLine breaks AllowShortFunctionsOnASingleLine MyDeveloperDay on Nov 24 2021, 3:16 AM. Authored by
Details https://bugs.llvm.org/show_bug.cgi?id=47936 Using the MultiLine setting for BraceWrapping.AfterControlStatement appears to disable AllowShortFunctionsOnASingleLine, even in cases without any control statements
Diff Detail
Event TimelineComment Actions LGTM, but I have one question. You mentioned in the bug ticket comment that "this exposes a greater issue in AllowShortXXX". Have you found other cases that misbehave? If so, then it would probably be good to add them. Comment Actions There also seems to be a behavior caused by AfterControlStatement = Multiline. This code: if (condition) { contrinue; } Is broken up into lines: if (condition) { continue; } I am not sure if this is intended behavior or not. Because when having this configuration: BreakBeforeBraces: Custom BraceWrapping: AfterControlStatement: Never # Always works too! AllowShortBlocksOnASingleLine: Always AllowShortIfStatementsOnASingleLine: WithoutElse # any value except Never clang-format would leave that code unchanged. The same goes to loops (and AllowShortLoopsOnASingleLine) Comment Actions So when I took a look I think I stumbled on what @KyrBoh said below, but I'm not convinced its related. (so really want to deal with this separately) What it seems is "AllowShortIfStatementsOnASingleLine" ONLY seems to work when there are no {} (this seems to be by design https://clang.llvm.org/docs/ClangFormatStyleOptions.html) i.e. for BasedOnStyle: LLVM AllowShortIfStatementsOnASingleLine: AllIfsAndElse ColumnLimit: 80 I get class Foo { foo() { if (x) bar(); if (x) { bar(); } } }; So I think that was just me not understanding that limitation, (although I agree it doesn't seem to follow other options of a similar name) Comment Actions However if we add AllowShortBlocksOnASingleLine : Always then it will be class Foo { foo() { if (x) bar(); if (x) { bar(); } } }; but if we set BasedOnStyle: LLVM AllowShortIfStatementsOnASingleLine: Never ColumnLimit: 80 AllowShortBlocksOnASingleLine : Always we get class Foo { foo() { if (x) bar(); if (x) { bar(); } } }; which feels like if(x){ bar(); } is primarily controlled by AllowShortBlocksOnASingleLine i.e. setting it to Never will override the AllowShortIfStatementsOnASingleLine setting for if statements with braces. However if AllowShortBlocksOnASingleLine is Always then if AllowShortIfStatementsOnASingleLine is set to Never then it wins. Comment Actions
AFAIR, there are 2 settings (in case of control statements):
So these two should be considered in pair. BTW, the documentation for AllowShortIfStatementsOnASingleLine seems to be a bit misleading EDIT, nevermind: seen the coment above. @curdeius Comment Actions
That is related. But I'd rather handle it separately because the fix for the function and the fix for a while or if will need to be different. It really comes down to this line.. if (Line.First == Line.Last && Line.First->isNot(TT_FunctionLBrace) in the if case and in the while case the { isn't labeled like it is for the function M=0 C=0 T=Unknown S=1 F=0 B=0 BK=1 P=0 Name=l_brace L=1 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text='{' To be honest this is something we should think about, if every { and } every (,) and [,] got given a Type labels this would be super good at disambiguating them This is not something new, we've been doing it with the ')' of a cstyle cast for years CastRParen M=0 C=0 T=Unknown S=1 F=0 B=0 BK=0 P=0 Name=int L=3 PPK=2 FakeLParens=2/ FakeRParens=0 II=0x25a969b1660 Text='int' M=0 C=1 T=StartOfName S=1 F=0 B=0 BK=0 P=220 Name=identifier L=5 PPK=2 FakeLParens= FakeRParens=0 II=0x25a969b8768 Text='a' M=0 C=0 T=BinaryOperator S=1 F=0 B=0 BK=0 P=22 Name=equal L=7 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text='=' M=0 C=1 T=Unknown S=1 F=0 B=0 BK=0 P=22 Name=l_paren L=9 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text='(' M=0 C=0 T=Unknown S=0 F=0 B=0 BK=0 P=59 Name=int L=12 PPK=2 FakeLParens= FakeRParens=0 II=0x25a969b1660 Text='int' M=0 C=0 T=CastRParen S=0 F=0 B=0 BK=0 P=43 Name=r_paren L=13 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text=')' but in this case, the real problem here is that Multiline just too lax, basically Multiline doesn't in my view seem to be doing what it expects From this is seems it should ONLY be returning 0 (meaning don't join the line), if the "clause" in the (if,for,while) contains multiple conditions that have been broken over several lines. I just don't think this is sufficiently constrained. // Don't merge a trailing multi-line control statement block like: // } else if (foo && // bar) // { <-- current Line // baz(); // } if (Line.First == Line.Last && Style.BraceWrapping.AfterControlStatement == FormatStyle::BWACS_MultiLine) return 0; Comment Actions I think the problem here is encapsulated by adding the following tests Style.BraceWrapping.AfterFunction = true; Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_AllIfsAndElse; Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always; Style.AllowShortLoopsOnASingleLine = true; Style.ColumnLimit = 80; Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Never; verifyFormat("void shortfunction() { bar(); }", Style); verifyFormat("if (x) { bar(); }", Style); verifyFormat("for (;;) { bar(); }", Style); verifyFormat("while (true) { bar(); }", Style); Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_MultiLine; verifyFormat("void shortfunction() { bar(); }", Style); verifyFormat("if (x) { bar(); }", Style); >>> FAILS ^^^^^^^^^^^^^^^^^^^^^^^^^^^ verifyFormat("for (;;) { bar(); }", Style); >>> FAILS ^^^^^^^^^^^^^^^^^^^^^^^^^^^ verifyFormat("while (true) { bar(); }", Style); >>> FAILS ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Never; Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; Style.AllowShortLoopsOnASingleLine = false; verifyFormat("void shortfunction()\n" "{\n" " foo();\n" "}", Style); verifyFormat("if (x) {\n" " foo();\n" "}", Style); verifyFormat("for (;;) {\n" " foo();\n" "}", Style); verifyFormat("while (true) {\n" " foo();\n" "}", Style); Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_MultiLine; verifyFormat("void shortfunction()\n" "{\n" " foo();\n" "}", Style); verifyFormat("if (x) {\n" " foo();\n" "}", Style); verifyFormat("for (;;) {\n" " foo();\n" "}", Style); verifyFormat("while (true) {\n" " foo();\n" "}", Style); Comment Actions The if/for/while case is way more involved and requires a change in a separate piece of code, I'm going to land this and we can handle 52598 separately |