From a83e2dbb1e1904dda43869597e12f9af20e0d1c8 Mon Sep 17 00:00:00 2001 From: Ronald Wampler Date: Tue, 26 Mar 2019 20:18:14 +0000 Subject: [PATCH] [clang-format] Add style option AllowShortLambdasOnASingleLine Summary: This option `AllowShortLambdasOnASingleLine` similar to the other `AllowShort*` options, but applied to C++ lambdas. Reviewers: djasper, klimek Reviewed By: klimek Subscribers: MyDeveloperDay, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D57687 llvm-svn: 357027 --- clang/docs/ClangFormatStyleOptions.rst | 39 ++++++++++++++++++++++++++++++++ clang/include/clang/Format/Format.h | 34 ++++++++++++++++++++++++++++ clang/lib/Format/Format.cpp | 14 ++++++++++++ clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/TokenAnnotator.cpp | 25 +++++++++++++++----- clang/lib/Format/UnwrappedLineParser.cpp | 1 + clang/unittests/Format/FormatTest.cpp | 37 ++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 6 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 0ddc313..0614af36 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -449,6 +449,45 @@ the configuration (without a prefix: ``Auto``). return; } +**AllowShortLambdasOnASingleLine** (``ShortLambdaStyle``) + Dependent on the value, ``auto lambda []() { return 0; }`` can be put on a + single line. + + Possible values: + + * ``SLS_None`` (in configuration: ``None``) + Never merge lambdas into a single line. + + * ``SLS_Empty`` (in configuration: ``Empty``) + Only merge empty lambdas. + + .. code-block:: c++ + + auto lambda = [](int a) {} + auto lambda2 = [](int a) { + return a; + }; + + * ``SLS_Inline`` (in configuration: ``Inline``) + Merge lambda into a single line if argument of a function. + + .. code-block:: c++ + + auto lambda = [](int a) { + return a; + }; + sort(a.begin(), a.end(), ()[] { return x < y; }) + + * ``SLS_All`` (in configuration: ``All``) + Merge all lambdas fitting on a single line. + + .. code-block:: c++ + + auto lambda = [](int a) {} + auto lambda2 = [](int a) { return a; }; + + + **AllowShortLoopsOnASingleLine** (``bool``) If ``true``, ``while (true) continue;`` can be put on a single line. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index fd84c92..f9d3f03 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -306,6 +306,39 @@ struct FormatStyle { /// If ``true``, ``if (a) return;`` can be put on a single line. ShortIfStyle AllowShortIfStatementsOnASingleLine; + /// Different styles for merging short lambdas containing at most one + /// statement. + enum ShortLambdaStyle { + /// Never merge lambdas into a single line. + SLS_None, + /// Only merge empty lambdas. + /// \code + /// auto lambda = [](int a) {} + /// auto lambda2 = [](int a) { + /// return a; + /// }; + /// \endcode + SLS_Empty, + /// Merge lambda into a single line if argument of a function. + /// \code + /// auto lambda = [](int a) { + /// return a; + /// }; + /// sort(a.begin(), a.end(), ()[] { return x < y; }) + /// \endcode + SLS_Inline, + /// Merge all lambdas fitting on a single line. + /// \code + /// auto lambda = [](int a) {} + /// auto lambda2 = [](int a) { return a; }; + /// \endcode + SLS_All, + }; + + /// Dependent on the value, ``auto lambda []() { return 0; }`` can be put on a + /// single line. + ShortLambdaStyle AllowShortLambdasOnASingleLine; + /// If ``true``, ``while (true) continue;`` can be put on a single /// line. bool AllowShortLoopsOnASingleLine; @@ -1805,6 +1838,7 @@ struct FormatStyle { R.AllowShortFunctionsOnASingleLine && AllowShortIfStatementsOnASingleLine == R.AllowShortIfStatementsOnASingleLine && + AllowShortLambdasOnASingleLine == R.AllowShortLambdasOnASingleLine && AllowShortLoopsOnASingleLine == R.AllowShortLoopsOnASingleLine && AlwaysBreakAfterReturnType == R.AlwaysBreakAfterReturnType && AlwaysBreakBeforeMultilineStrings == diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 2d1adb5..39651ab 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -119,6 +119,17 @@ template <> struct ScalarEnumerationTraits { } }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::ShortLambdaStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::SLS_None); + IO.enumCase(Value, "false", FormatStyle::SLS_None); + IO.enumCase(Value, "Empty", FormatStyle::SLS_Empty); + IO.enumCase(Value, "Inline", FormatStyle::SLS_Inline); + IO.enumCase(Value, "All", FormatStyle::SLS_All); + IO.enumCase(Value, "true", FormatStyle::SLS_All); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::BinPackStyle &Value) { IO.enumCase(Value, "Auto", FormatStyle::BPS_Auto); @@ -347,6 +358,8 @@ template <> struct MappingTraits { Style.AllowShortCaseLabelsOnASingleLine); IO.mapOptional("AllowShortFunctionsOnASingleLine", Style.AllowShortFunctionsOnASingleLine); + IO.mapOptional("AllowShortLambdasOnASingleLine", + Style.AllowShortLambdasOnASingleLine); IO.mapOptional("AllowShortIfStatementsOnASingleLine", Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", @@ -653,6 +666,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowShortBlocksOnASingleLine = false; LLVMStyle.AllowShortCaseLabelsOnASingleLine = false; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; + LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 461e070..f54ffe9 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -65,6 +65,7 @@ namespace format { TYPE(JsTypeOperator) \ TYPE(JsTypeOptionalQuestion) \ TYPE(LambdaArrow) \ + TYPE(LambdaLBrace) \ TYPE(LambdaLSquare) \ TYPE(LeadingJavaAnnotation) \ TYPE(LineComment) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 04fc3a6..c7b148c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1191,11 +1191,11 @@ private: // Reset token type in case we have already looked at it and then // recovered from an error (e.g. failure to find the matching >). - if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_ForEachMacro, - TT_FunctionLBrace, TT_ImplicitStringLiteral, - TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, - TT_OverloadedOperator, TT_RegexLiteral, - TT_TemplateString, TT_ObjCStringLiteral)) + if (!CurrentToken->isOneOf( + TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro, + TT_FunctionLBrace, TT_ImplicitStringLiteral, TT_InlineASMBrace, + TT_JsFatArrow, TT_LambdaArrow, TT_OverloadedOperator, + TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral)) CurrentToken->Type = TT_Unknown; CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; @@ -2896,7 +2896,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, // Returns 'true' if 'Tok' is a brace we'd want to break before in Allman style. static bool isAllmanBrace(const FormatToken &Tok) { return Tok.is(tok::l_brace) && Tok.BlockKind == BK_Block && - !Tok.isOneOf(TT_ObjCBlockLBrace, TT_DictLiteral); + !Tok.isOneOf(TT_ObjCBlockLBrace, TT_LambdaLBrace, TT_DictLiteral); } bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, @@ -3024,6 +3024,19 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, if (Left.is(TT_ObjCBlockLBrace) && !Style.AllowShortBlocksOnASingleLine) return true; + if (Left.is(TT_LambdaLBrace)) { + if (Left.MatchingParen && Left.MatchingParen->Next && + Left.MatchingParen->Next->isOneOf(tok::comma, tok::r_paren) && + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline) + return false; + + if (Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_None || + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline || + (!Left.Children.empty() && + Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Empty)) + return true; + } + // Put multiple C# attributes on a new line. if (Style.isCSharp() && ((Left.is(TT_AttributeSquare) && Left.is(tok::r_square)) || diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 5c7ab12..010b086 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1475,6 +1475,7 @@ bool UnwrappedLineParser::tryToParseLambda() { return true; } } + FormatTok->Type = TT_LambdaLBrace; LSquare.Type = TT_LambdaLSquare; parseChildBlock(); return true; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index f9b2fe2..6453d24 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12432,6 +12432,43 @@ TEST_F(FormatTest, FormatsLambdas) { " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n" " //\n" " });"); + + FormatStyle DoNotMerge = getLLVMStyle(); + DoNotMerge.AllowShortLambdasOnASingleLine = FormatStyle::SLS_None; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() { return b; };", DoNotMerge); + verifyFormat("auto c = []() {\n" + "};", + " auto c = []() {};", DoNotMerge); + + FormatStyle MergeEmptyOnly = getLLVMStyle(); + MergeEmptyOnly.AllowShortLambdasOnASingleLine = FormatStyle::SLS_Empty; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() {\n" + " return b;\n" + " };", + MergeEmptyOnly); + verifyFormat("auto c = []() {};", + "auto c = []() {\n" + "};", + MergeEmptyOnly); + + FormatStyle MergeInline = getLLVMStyle(); + MergeInline.AllowShortLambdasOnASingleLine = FormatStyle::SLS_Inline; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() { return b; };", MergeInline); + verifyFormat("function([]() { return b; })", "function([]() { return b; })", + MergeInline); + verifyFormat("function([]() { return b; }, a)", + "function([]() { return b; }, a)", MergeInline); + verifyFormat("function(a, []() { return b; })", + "function(a, []() { return b; })", MergeInline); } TEST_F(FormatTest, EmptyLinesInLambdas) { -- 2.7.4