diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1368,6 +1368,25 @@ +.. _AlwaysBreakBeforeFunctionParameters: + +**AlwaysBreakBeforeFunctionParameters** (``Boolean``) :versionbadge:`clang-format 16.0` :ref:`¶ ` + If ``true``, always break before function parameters in a declaration and + a definition. + + This flag is meant to align function parameters starting on the line + following a function declaration or definition. Thus, it will only take + effect if a function declares a parameter (or multiple parameters). + Example uses + ``AlwaysBreakAfterReturnType`` set to ``All``. + + .. code-block:: c++ + + true: false: + int someFunction( vs. int someFunction(int arg1, int arg2); + int arg1, + int arg2); + .. _AlwaysBreakBeforeMultilineStrings: **AlwaysBreakBeforeMultilineStrings** (``Boolean``) :versionbadge:`clang-format 3.4` :ref:`¶ ` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -807,6 +807,23 @@ /// \version 3.8 ReturnTypeBreakingStyle AlwaysBreakAfterReturnType; + /// If ``true``, always break before function parameters in a declaration and + /// a definition. + /// + /// This flag is meant to align function parameters starting on the line + /// following a function declaration or definition. Thus, it will only take + /// effect if a function declares a parameter (or multiple parameters). + /// Example uses + /// ``AlwaysBreakAfterReturnType`` set to ``All``. + /// \code + /// true: false: + /// int someFunction( vs. int someFunction(int arg1, int arg2); + /// int arg1, + /// int arg2); + /// \endcode + /// \version 16.0 + bool AlwaysBreakBeforeFunctionParameters; + /// If ``true``, always break before multiline string literals. /// /// This flag is mean to make cases where there are multiple multiline strings diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -354,6 +354,12 @@ auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack); return LambdaBodyLength > getColumnLimit(State); } + // Check if we want to break before function parameters in declarations + if (startsNextParameter(Current, Style) && + Style.AlwaysBreakBeforeFunctionParameters && + State.Line->MustBeDeclaration) { + return true; + } if (Current.MustBreakBefore || (Current.is(TT_InlineASMColon) && (Style.BreakBeforeInlineASMColon == FormatStyle::BBIAS_Always || @@ -1055,7 +1061,9 @@ // If we are breaking after '(', '{', '<', or this is the break after a ':' // to start a member initializater list in a constructor, this should not // be considered bin packing unless the relevant AllowAll option is false or - // this is a dict/object literal. + // this is a dict/object literal. Break if + // AlwaysBreakBeforeFunctionParameters is true and it's a function + // declaration. bool PreviousIsBreakingCtorInitializerColon = PreviousNonComment && PreviousNonComment->is(TT_CtorInitializerColon) && Style.BreakConstructorInitializers == FormatStyle::BCIS_AfterColon; @@ -1070,7 +1078,9 @@ !State.Line->MustBeDeclaration) || (!AllowAllConstructorInitializersOnNextLine && PreviousIsBreakingCtorInitializerColon) || - Previous.is(TT_DictLiteral)) { + Previous.is(TT_DictLiteral) || + (Style.AlwaysBreakBeforeFunctionParameters && + State.Line->MustBeDeclaration)) { CurrentState.BreakBeforeParameter = true; } diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -873,6 +873,8 @@ Style.AlwaysBreakAfterReturnType); IO.mapOptional("AlwaysBreakBeforeMultilineStrings", Style.AlwaysBreakBeforeMultilineStrings); + IO.mapOptional("AlwaysBreakBeforeFunctionParameters", + Style.AlwaysBreakBeforeFunctionParameters); IO.mapOptional("AlwaysBreakTemplateDeclarations", Style.AlwaysBreakTemplateDeclarations); IO.mapOptional("AttributeMacros", Style.AttributeMacros); @@ -1331,6 +1333,7 @@ LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; + LLVMStyle.AlwaysBreakBeforeFunctionParameters = false; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_MultiLine; LLVMStyle.AttributeMacros.push_back("__capability"); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4847,6 +4847,14 @@ return true; } + // If AlwaysBreakBeforeFunctionParameters is true, we want to break before + // the next parameter, if there is one. + if (Left.is(tok::l_paren) && Style.AlwaysBreakBeforeFunctionParameters && + !Right.is(tok::r_paren) && Left.Previous && + Left.Previous->is(TT_FunctionDeclarationName)) { + return true; + } + // If the last token before a '}', ']', or ')' is a comma or a trailing // comment, the intention is to insert a line break after it in order to make // shuffling around entries easier. Import statements, especially in diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -149,6 +149,7 @@ CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine); CHECK_PARSE_BOOL(AllowShortEnumsOnASingleLine); CHECK_PARSE_BOOL(AllowShortLoopsOnASingleLine); + CHECK_PARSE_BOOL(AlwaysBreakBeforeFunctionParameters); CHECK_PARSE_BOOL(BinPackArguments); CHECK_PARSE_BOOL(BinPackParameters); CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25429,6 +25429,50 @@ verifyFormat("auto x = 5s .count() == 5;"); } +TEST_F(FormatTest, BreakBeforeParameterList) { + FormatStyle Style = getLLVMStyle(); + EXPECT_EQ(Style.AlwaysBreakBeforeFunctionParameters, false); + + // verify that there is no break by default + verifyFormat("int function1(int param1, int param2, int param3);\n" + "int function2();\n", + "int function1(int param1, int param2, int param3);\n" + "int function2();\n", + Style); + + verifyFormat("void function1(int param1, int param2, int param3) {}\n", + "void function1(int param1, int param2, int param3) {}\n", + Style); + + // verify that there is a break when told to break + Style.AlwaysBreakBeforeFunctionParameters = true; + verifyFormat("int function1(\n" // the formatted part + " int param1,\n" + " int param2,\n" + " int param3);\n" + "int function2();\n", + // the original + "int function1(int param1, int param2, int param3);\n" + "int function2();\n", + Style); + + verifyFormat("void function1(\n" // the formatted part + " int param1,\n" + " int param2,\n" + " int param3) {}\n", + // the original + "void function1(int param1, int param2, int param3) {}\n", + Style); + + // verify that having no parameters doesn't affect the parentheses + verifyFormat("void function1() {}\n", // the formatted part + "void function1() {}\n", // the original + Style); + + verifyFormat("void function1();\n", // the formatted part + "void function1();\n", // the original + Style); +} } // namespace } // namespace test } // namespace format