[clang-format] Add new option BeforeLambdaBody in Allman style.

This option add a line break then a lambda is inside a function call.

Reviewers : djasper, klimek, krasimir, MyDeveloperDay

Reviewed By: MyDeveloperDay

Differential Revision: https://reviews.llvm.org/D44609
This commit is contained in:
Wawha 2020-02-13 20:25:34 +01:00
parent ef7488ef20
commit fa0118e6e5
7 changed files with 680 additions and 287 deletions

View File

@ -968,6 +968,24 @@ the configuration (without a prefix: ``Auto``).
} else {
}
* ``bool BeforeLambdaBody`` Wrap lambda block.
.. code-block:: c++
true:
connect(
[]()
{
foo();
bar();
});
false:
connect([]() {
foo();
bar();
});
* ``bool IndentBraces`` Indent the wrapped braces themselves.
* ``bool SplitEmptyFunction`` If ``false``, empty function body can be put on a single line.

View File

@ -221,6 +221,25 @@ clang-format
multiple lines. It is currently only available for JavaScript and disabled by
default (``TCS_None``).
- Option ``BraceWrapping.BeforeLambdaBody`` has been added to manage lambda
line break inside function parameter call in Allman style.
.. code-block:: c++
true:
connect(
[]()
{
foo();
bar();
});
false:
connect([]() {
foo();
bar();
});
libclang
--------

View File

@ -992,6 +992,23 @@ struct FormatStyle {
/// }
/// \endcode
bool BeforeElse;
/// Wrap lambda block.
/// \code
/// true:
/// connect(
/// []()
/// {
/// foo();
/// bar();
/// });
///
/// false:
/// connect([]() {
/// foo();
/// bar();
/// });
/// \endcode
bool BeforeLambdaBody;
/// Indent the wrapped braces themselves.
bool IndentBraces;
/// If ``false``, empty function body can be put on a single line.

View File

@ -329,6 +329,11 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
bool ContinuationIndenter::mustBreak(const LineState &State) {
const FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *Current.Previous;
if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore &&
Current.is(TT_LambdaLBrace)) {
auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack);
return (LambdaBodyLength > getColumnLimit(State));
}
if (Current.MustBreakBefore || Current.is(TT_InlineASMColon))
return true;
if (State.Stack.back().BreakBeforeClosingBrace &&
@ -1081,6 +1086,18 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
return State.Stack.back().Indent;
}
static bool hasNestedBlockInlined(const FormatToken *Previous,
const FormatToken &Current,
const FormatStyle &Style) {
if (Previous->isNot(tok::l_paren))
return true;
if (Previous->ParameterCount > 1)
return true;
// Also a nested block if contains a lambda inside function with 1 parameter
return (Style.BraceWrapping.BeforeLambdaBody && Current.is(TT_LambdaLSquare));
}
unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
bool DryRun, bool Newline) {
assert(State.Stack.size());
@ -1183,8 +1200,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
Previous->isOneOf(TT_BinaryOperator, TT_ConditionalExpr)) &&
!Previous->isOneOf(TT_DictLiteral, TT_ObjCMethodExpr)) {
State.Stack.back().NestedBlockInlined =
!Newline &&
(Previous->isNot(tok::l_paren) || Previous->ParameterCount > 1);
!Newline && hasNestedBlockInlined(Previous, Current, Style);
}
moveStatePastFakeLParens(State, Newline);
@ -1421,7 +1437,21 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State,
ParenState(&Current, NewIndent, LastSpace, AvoidBinPacking, NoLineBreak));
State.Stack.back().NestedBlockIndent = NestedBlockIndent;
State.Stack.back().BreakBeforeParameter = BreakBeforeParameter;
State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1;
State.Stack.back().HasMultipleNestedBlocks = (Current.BlockParameterCount > 1);
if (Style.BraceWrapping.BeforeLambdaBody &&
Current.Next != nullptr && Current.Tok.is(tok::l_paren)) {
// Search for any parameter that is a lambda
FormatToken const *next = Current.Next;
while (next != nullptr) {
if (next->is(TT_LambdaLSquare)) {
State.Stack.back().HasMultipleNestedBlocks = true;
break;
}
next = next->Next;
}
}
State.Stack.back().IsInsideObjCArrayLiteral =
Current.is(TT_ArrayInitializerLSquare) && Current.Previous &&
Current.Previous->is(tok::at);

View File

@ -581,6 +581,7 @@ template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> {
IO.mapOptional("AfterExternBlock", Wrapping.AfterExternBlock);
IO.mapOptional("BeforeCatch", Wrapping.BeforeCatch);
IO.mapOptional("BeforeElse", Wrapping.BeforeElse);
IO.mapOptional("BeforeLambdaBody", Wrapping.BeforeLambdaBody);
IO.mapOptional("IndentBraces", Wrapping.IndentBraces);
IO.mapOptional("SplitEmptyFunction", Wrapping.SplitEmptyFunction);
IO.mapOptional("SplitEmptyRecord", Wrapping.SplitEmptyRecord);
@ -668,8 +669,8 @@ static FormatStyle expandPresets(const FormatStyle &Style) {
false, false, false,
false, false, false,
false, false, false,
false, true, true,
true};
false, false, true,
true, true};
switch (Style.BreakBeforeBraces) {
case FormatStyle::BS_Linux:
Expanded.BraceWrapping.AfterClass = true;
@ -717,14 +718,15 @@ static FormatStyle expandPresets(const FormatStyle &Style) {
Expanded.BraceWrapping.AfterExternBlock = true;
Expanded.BraceWrapping.BeforeCatch = true;
Expanded.BraceWrapping.BeforeElse = true;
Expanded.BraceWrapping.BeforeLambdaBody = true;
break;
case FormatStyle::BS_GNU:
Expanded.BraceWrapping = {true, true, FormatStyle::BWACS_Always,
true, true, true,
true, true, true,
true, true, true,
true, true, true,
true};
false, true, true,
true, true};
break;
case FormatStyle::BS_WebKit:
Expanded.BraceWrapping.AfterFunction = true;
@ -768,8 +770,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
false, false, false,
false, false, false,
false, false, false,
false, true, true,
true};
false, false, true,
true, true};
LLVMStyle.BreakAfterJavaFieldAnnotations = false;
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon;

View File

@ -3122,6 +3122,56 @@ static bool isAllmanBrace(const FormatToken &Tok) {
!Tok.isOneOf(TT_ObjCBlockLBrace, TT_LambdaLBrace, TT_DictLiteral);
}
// Returns 'true' if 'Tok' is an function argument.
static bool IsFunctionArgument(const FormatToken &Tok) {
return Tok.MatchingParen && Tok.MatchingParen->Next &&
Tok.MatchingParen->Next->isOneOf(tok::comma, tok::r_paren);
}
static bool
isItAnEmptyLambdaAllowed(const FormatToken &Tok,
FormatStyle::ShortLambdaStyle ShortLambdaOption) {
return Tok.Children.empty() && ShortLambdaOption != FormatStyle::SLS_None;
}
static bool
isItAInlineLambdaAllowed(const FormatToken &Tok,
FormatStyle::ShortLambdaStyle ShortLambdaOption) {
return (ShortLambdaOption == FormatStyle::SLS_Inline &&
IsFunctionArgument(Tok)) ||
(ShortLambdaOption == FormatStyle::SLS_All);
}
static bool isOneChildWithoutMustBreakBefore(const FormatToken &Tok) {
if (Tok.Children.size() != 1)
return false;
FormatToken *curElt = Tok.Children[0]->First;
while (curElt) {
if (curElt->MustBreakBefore)
return false;
curElt = curElt->Next;
}
return true;
}
static bool
isAllmanLambdaBrace(const FormatToken &Tok) {
return (Tok.is(tok::l_brace) && Tok.BlockKind == BK_Block &&
!Tok.isOneOf(TT_ObjCBlockLBrace, TT_DictLiteral));
}
static bool
isAllmanBraceIncludedBreakableLambda(const FormatToken &Tok,
FormatStyle::ShortLambdaStyle ShortLambdaOption) {
if (!isAllmanLambdaBrace(Tok))
return false;
if (isItAnEmptyLambdaAllowed(Tok, ShortLambdaOption))
return false;
return !isItAInlineLambdaAllowed(Tok, ShortLambdaOption) ||
!isOneChildWithoutMustBreakBefore(Tok);
}
bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
const FormatToken &Right) {
const FormatToken &Left = *Right.Previous;
@ -3257,6 +3307,14 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
}
if (Right.is(TT_InlineASMBrace))
return Right.HasUnescapedNewline;
auto ShortLambdaOption = Style.AllowShortLambdasOnASingleLine;
if (Style.BraceWrapping.BeforeLambdaBody &&
(isAllmanBraceIncludedBreakableLambda(Left, ShortLambdaOption) ||
isAllmanBraceIncludedBreakableLambda(Right, ShortLambdaOption))) {
return true;
}
if (isAllmanBrace(Left) || isAllmanBrace(Right))
return (Line.startsWith(tok::kw_enum) && Style.BraceWrapping.AfterEnum) ||
(Line.startsWith(tok::kw_typedef, tok::kw_enum) &&
@ -3268,8 +3326,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
return true;
if (Left.is(TT_LambdaLBrace)) {
if (Left.MatchingParen && Left.MatchingParen->Next &&
Left.MatchingParen->Next->isOneOf(tok::comma, tok::r_paren) &&
if (IsFunctionArgument(Left) &&
Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline)
return false;
@ -3667,11 +3724,21 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
if ((Left.is(TT_AttributeSquare) && Right.is(tok::l_square)) ||
(Left.is(tok::r_square) && Right.is(TT_AttributeSquare)))
return false;
auto ShortLambdaOption = Style.AllowShortLambdasOnASingleLine;
if (Style.BraceWrapping.BeforeLambdaBody) {
if (isAllmanLambdaBrace(Left))
return !isItAnEmptyLambdaAllowed(Left, ShortLambdaOption);
if (isAllmanLambdaBrace(Right))
return !isItAnEmptyLambdaAllowed(Right, ShortLambdaOption);
}
return Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace,
tok::kw_class, tok::kw_struct, tok::comment) ||
Right.isMemberAccess() ||
Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow, tok::lessless,
tok::colon, tok::l_square, tok::at) ||
(Style.BraceWrapping.BeforeLambdaBody && Right.is(tok::l_brace)) ||
(Left.is(tok::r_paren) &&
Right.isOneOf(tok::identifier, tok::kw_const)) ||
(Left.is(tok::l_paren) && !Right.is(tok::r_paren)) ||

View File

@ -12673,6 +12673,7 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterExternBlock);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeCatch);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeElse);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeLambdaBody);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord);
@ -13962,6 +13963,245 @@ TEST_F(FormatTest, FormatsLambdas) {
"function([]() { return b; }, a)", MergeInline);
verifyFormat("function(a, []() { return b; })",
"function(a, []() { return b; })", MergeInline);
// Check option "BraceWrapping.BeforeLambdaBody" and different state of
// AllowShortLambdasOnASingleLine
FormatStyle LLVMWithBeforeLambdaBody = getLLVMStyle();
LLVMWithBeforeLambdaBody.BreakBeforeBraces = FormatStyle::BS_Custom;
LLVMWithBeforeLambdaBody.BraceWrapping.BeforeLambdaBody = true;
LLVMWithBeforeLambdaBody.AllowShortLambdasOnASingleLine =
FormatStyle::ShortLambdaStyle::SLS_None;
verifyFormat("FctWithOneNestedLambdaInline_SLS_None(\n"
" []()\n"
" {\n"
" return 17;\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdaEmpty_SLS_None(\n"
" []()\n"
" {\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("auto fct_SLS_None = []()\n"
"{\n"
" return 17;\n"
"};",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_None(\n"
" []()\n"
" {\n"
" return Call(\n"
" []()\n"
" {\n"
" return 17;\n"
" });\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("void Fct()\n"
"{\n"
" return {[]()\n"
" {\n"
" return 17;\n"
" }};\n"
"}",
LLVMWithBeforeLambdaBody);
LLVMWithBeforeLambdaBody.AllowShortLambdasOnASingleLine =
FormatStyle::ShortLambdaStyle::SLS_Empty;
verifyFormat("FctWithOneNestedLambdaInline_SLS_Empty(\n"
" []()\n"
" {\n"
" return 17;\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdaEmpty_SLS_Empty([]() {});",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdaEmptyInsideAVeryVeryVeryVeryVeryVeryVeryL"
"ongFunctionName_SLS_Empty(\n"
" []() {});",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithMultipleParams_SLS_Empty(A, B,\n"
" []()\n"
" {\n"
" return 17;\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("auto fct_SLS_Empty = []()\n"
"{\n"
" return 17;\n"
"};",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_Empty(\n"
" []()\n"
" {\n"
" return Call([]() {});\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_Empty(A,\n"
" []()\n"
" {\n"
" return Call([]() {});\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_Empty(\n"
" []()\n"
" {\n"
" return HereAVeryLongLine(ThatWillBeFormatted, OnMultipleLine,\n"
" AndShouldNotBeConsiderAsInline,\n"
" LambdaBodyMustBeBreak);\n"
" });",
LLVMWithBeforeLambdaBody);
LLVMWithBeforeLambdaBody.AllowShortLambdasOnASingleLine =
FormatStyle::ShortLambdaStyle::SLS_Inline;
verifyFormat("FctWithOneNestedLambdaInline_SLS_Inline([]() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdaEmpty_SLS_Inline([]() {});",
LLVMWithBeforeLambdaBody);
verifyFormat("auto fct_SLS_Inline = []()\n"
"{\n"
" return 17;\n"
"};",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_Inline([]() { return Call([]() { return "
"17; }); });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_Inline(\n"
" []()\n"
" {\n"
" return HereAVeryLongLine(ThatWillBeFormatted, OnMultipleLine,\n"
" AndShouldNotBeConsiderAsInline,\n"
" LambdaBodyMustBeBreak);\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithMultipleParams_SLS_Inline("
"VeryLongParameterThatShouldAskToBeOnMultiLine,\n"
" []() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithMultipleParams_SLS_Inline(FirstParam, []() { return 17; });",
LLVMWithBeforeLambdaBody);
LLVMWithBeforeLambdaBody.AllowShortLambdasOnASingleLine =
FormatStyle::ShortLambdaStyle::SLS_All;
verifyFormat("FctWithOneNestedLambdaInline_SLS_All([]() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdaEmpty_SLS_All([]() {});",
LLVMWithBeforeLambdaBody);
verifyFormat("auto fct_SLS_All = []() { return 17; };",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneParam_SLS_All(\n"
" []()\n"
" {\n"
" // A cool function...\n"
" return 43;\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithMultipleParams_SLS_All("
"VeryLongParameterThatShouldAskToBeOnMultiLine,\n"
" []() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithMultipleParams_SLS_All(A, []() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithMultipleParams_SLS_All(A, B, []() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_All(\n"
" []()\n"
" {\n"
" return HereAVeryLongLine(ThatWillBeFormatted, OnMultipleLine,\n"
" AndShouldNotBeConsiderAsInline,\n"
" LambdaBodyMustBeBreak);\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"auto fct_SLS_All = []()\n"
"{\n"
" return HereAVeryLongLine(ThatWillBeFormatted, OnMultipleLine,\n"
" AndShouldNotBeConsiderAsInline,\n"
" LambdaBodyMustBeBreak);\n"
"};",
LLVMWithBeforeLambdaBody);
LLVMWithBeforeLambdaBody.BinPackParameters = false;
verifyFormat("FctAllOnSameLine_SLS_All([]() { return S; }, Fst, Second);",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_All([]() { return SomeValueNotSoLong; },\n"
" FirstParam,\n"
" SecondParam,\n"
" ThirdParam,\n"
" FourthParam);",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithLongLineInLambda_SLS_All(\n"
" []() { return "
"SomeValueVeryVeryVeryVeryVeryVeryVeryVeryVeryLong; },\n"
" FirstParam,\n"
" SecondParam,\n"
" ThirdParam,\n"
" FourthParam);",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_All(FirstParam,\n"
" SecondParam,\n"
" ThirdParam,\n"
" FourthParam,\n"
" []() { return SomeValueNotSoLong; });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithLongLineInLambda_SLS_All(\n"
" []()\n"
" {\n"
" return "
"HereAVeryLongLineThatWillBeFormattedOnMultipleLineAndShouldNotB"
"eConsiderAsInline;\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"FctWithLongLineInLambda_SLS_All(\n"
" []()\n"
" {\n"
" return HereAVeryLongLine(ThatWillBeFormatted, OnMultipleLine,\n"
" AndShouldNotBeConsiderAsInline,\n"
" LambdaBodyMustBeBreak);\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithTwoParams_SLS_All(\n"
" []()\n"
" {\n"
" // A cool function...\n"
" return 43;\n"
" },\n"
" 87);",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithTwoParams_SLS_All([]() { return 43; }, 87);",
LLVMWithBeforeLambdaBody);
verifyFormat("FctWithOneNestedLambdas_SLS_All([]() { return 17; });",
LLVMWithBeforeLambdaBody);
verifyFormat(
"TwoNestedLambdas_SLS_All([]() { return Call([]() { return 17; }); });",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_All([]() { return Call([]() { return 17; "
"}); }, x);",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_All(\n"
" []()\n"
" {\n"
" // A cool function...\n"
" return Call([]() { return 17; });\n"
" });",
LLVMWithBeforeLambdaBody);
verifyFormat("TwoNestedLambdas_SLS_All(\n"
" []()\n"
" {\n"
" return Call(\n"
" []()\n"
" {\n"
" // A cool function...\n"
" return 17;\n"
" });\n"
" });",
LLVMWithBeforeLambdaBody);
}
TEST_F(FormatTest, EmptyLinesInLambdas) {