[clang] [clang-format] allow short function body on a single line (PR #151428)
Lidong Yan via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 5 00:54:06 PDT 2025
https://github.com/brandb97 updated https://github.com/llvm/llvm-project/pull/151428
>From 25d45a4ffce718a6a4086d3cd5802d8ff7972164 Mon Sep 17 00:00:00 2001
From: Lidong Yan <yldhome2d2 at gmail.com>
Date: Tue, 5 Aug 2025 15:49:41 +0800
Subject: [PATCH] [clang-format] allow short function body on a single line
clang-format doesn't put short function body on a single line even if
user asks to put short blocks on a single line and do not put a whole
function on a single line. Add tryMergeSimpleBlocks() if we find user
want to put short function body on a single line.
Signed-off-by: Lidong Yan <yldhome2d2 at gmail.com>
---
clang/docs/ClangFormatStyleOptions.rst | 19 ++++++++++++
clang/include/clang/Format/Format.h | 19 ++++++++++++
clang/lib/Format/ContinuationIndenter.cpp | 7 +++++
clang/lib/Format/Format.cpp | 7 +++++
clang/lib/Format/UnwrappedLineFormatter.cpp | 32 ++++++++++++++++++---
clang/lib/Format/UnwrappedLineParser.cpp | 4 +++
clang/unittests/Format/FormatTest.cpp | 30 +++++++++++++++++++
7 files changed, 114 insertions(+), 4 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 02986a94a656c..b33abee391fab 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5405,6 +5405,25 @@ the configuration (without a prefix: ``Auto``).
+.. _PutShortFunctionBodiesOnASingleLine:
+
+**PutShortFunctionBodiesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <PutShortFunctionBodiesOnASingleLine>`
+ Dependent on the value, function body can be put on a single line.
+ Automatically enabled when
+ `AllowShortFunctionsOnASingleLine` is set to `None` and
+ `AllowShortBlocksOnASingleLine` is set to `Always`.
+
+ .. code-block:: c++
+
+ true:
+ int f()
+ { return 0; }
+
+ false:
+ int f() {
+ return 0;
+ }
+
.. _QualifierAlignment:
**QualifierAlignment** (``QualifierAlignmentStyle``) :versionbadge:`clang-format 14` :ref:`¶ <QualifierAlignment>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 31582a40de866..010dc622822c6 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3829,6 +3829,23 @@ struct FormatStyle {
/// \version 13
int PPIndentWidth;
+ /// Dependent on the value, function body can be put on a single line.
+ /// Automatically enabled when
+ /// `AllowShortFunctionsOnASingleLine` is set to `None` and
+ /// `AllowShortBlocksOnASingleLine` is set to `Always`.
+ /// \code
+ /// true:
+ /// int f()
+ /// { return 0; }
+ ///
+ /// false:
+ /// int f() {
+ /// return 0;
+ /// }
+ /// \endcode
+ /// \version 20
+ bool PutShortFunctionBodiesOnASingleLine;
+
/// Different specifiers and qualifiers alignment styles.
enum QualifierAlignmentStyle : int8_t {
/// Don't change specifiers/qualifiers to either Left or Right alignment
@@ -5461,6 +5478,8 @@ struct FormatStyle {
PenaltyExcessCharacter == R.PenaltyExcessCharacter &&
PenaltyReturnTypeOnItsOwnLine == R.PenaltyReturnTypeOnItsOwnLine &&
PointerAlignment == R.PointerAlignment &&
+ PutShortFunctionBodiesOnASingleLine ==
+ R.PutShortFunctionBodiesOnASingleLine &&
QualifierAlignment == R.QualifierAlignment &&
QualifierOrder == R.QualifierOrder &&
RawStringFormats == R.RawStringFormats &&
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 9a10403b858f9..57624edd60c0d 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -390,6 +390,13 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
return true;
}
+ // `PutShortFunctionBodiesOnASingleLine = true` will wrap left brace into
+ // a new line, do not break before the left brace.
+ if (Current.is(TT_FunctionLBrace) && !Style.BraceWrapping.AfterFunction &&
+ Style.PutShortFunctionBodiesOnASingleLine) {
+ return false;
+ }
+
return !State.NoLineBreak && !CurrentState.NoLineBreak;
}
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 063780721423f..da1e5937e3720 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1144,6 +1144,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.PenaltyReturnTypeOnItsOwnLine);
IO.mapOptional("PointerAlignment", Style.PointerAlignment);
IO.mapOptional("PPIndentWidth", Style.PPIndentWidth);
+ IO.mapOptional("PutShortFunctionBodiesOnASingleLine",
+ Style.PutShortFunctionBodiesOnASingleLine);
IO.mapOptional("QualifierAlignment", Style.QualifierAlignment);
// Default Order for Left/Right based Qualifier alignment.
if (Style.QualifierAlignment == FormatStyle::QAS_Right)
@@ -1643,6 +1645,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.PackConstructorInitializers = FormatStyle::PCIS_BinPack;
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.PPIndentWidth = -1;
+ LLVMStyle.PutShortFunctionBodiesOnASingleLine = false;
LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
LLVMStyle.ReflowComments = FormatStyle::RCS_Always;
@@ -3828,6 +3831,10 @@ reformat(const FormatStyle &Style, StringRef Code,
default:
break;
}
+ if (Expanded.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_None &&
+ Expanded.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always) {
+ Expanded.PutShortFunctionBodiesOnASingleLine = true;
+ }
if (Expanded.DisableFormat)
return {tooling::Replacements(), 0};
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 0adf7ee9ed543..d42689b66f049 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -242,13 +242,13 @@ class LineJoiner {
if (Style.ColumnLimit > 0 && Indent > Style.ColumnLimit)
return 0;
- unsigned Limit =
+ unsigned LimitStripIndent =
Style.ColumnLimit == 0 ? UINT_MAX : Style.ColumnLimit - Indent;
// If we already exceed the column limit, we set 'Limit' to 0. The different
// tryMerge..() functions can then decide whether to still do merging.
- Limit = TheLine->Last->TotalLength > Limit
- ? 0
- : Limit - TheLine->Last->TotalLength;
+ unsigned Limit = TheLine->Last->TotalLength > LimitStripIndent
+ ? 0
+ : LimitStripIndent - TheLine->Last->TotalLength;
if (TheLine->Last->is(TT_FunctionLBrace) &&
TheLine->First == TheLine->Last &&
@@ -257,6 +257,12 @@ class LineJoiner {
return tryMergeSimpleBlock(I, E, Limit);
}
+ if (TheLine->Last->is(TT_FunctionLBrace) &&
+ TheLine->First == TheLine->Last &&
+ Style.PutShortFunctionBodiesOnASingleLine) {
+ return tryMergeSimpleBlock(I, E, Limit);
+ }
+
const auto *PreviousLine = I != AnnotatedLines.begin() ? I[-1] : nullptr;
// Handle empty record blocks where the brace has already been wrapped.
if (PreviousLine && TheLine->Last->is(tok::l_brace) &&
@@ -532,6 +538,24 @@ class LineJoiner {
}
return MergedLines;
}
+
+ // UnwrappedLineParser would move the left brace to a new line when
+ // PutShortFunctionBodiesOnASingleLine is enabled. However, if the
+ // function body cannot fit on a single line, and
+ // Style.BraceWrapping.AfterFunction is false, we should merge the
+ // function name and the left brace back onto the same line.
+ if (NextLine.First->is(TT_FunctionLBrace) &&
+ Style.PutShortFunctionBodiesOnASingleLine &&
+ !Style.BraceWrapping.AfterFunction) {
+ unsigned MergedLines = 0;
+ unsigned NextLineLimit =
+ NextLine.Last->TotalLength > LimitStripIndent
+ ? 0
+ : LimitStripIndent - NextLine.Last->TotalLength;
+ MergedLines = tryMergeSimpleBlock(I + 1, E, NextLineLimit);
+ return MergedLines > 0 ? 0 : 1;
+ }
+
auto IsElseLine = [&TheLine]() -> bool {
const FormatToken *First = TheLine->First;
if (First->is(tok::kw_else))
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 91b8fdc8a3c38..09e1e671d7220 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -1921,6 +1921,10 @@ void UnwrappedLineParser::parseStructuralElement(
}
} else if (Style.BraceWrapping.AfterFunction) {
addUnwrappedLine();
+ } else if (Style.PutShortFunctionBodiesOnASingleLine) {
+ // Wrap the left brace here; we'll try to merge it back
+ // later if needed.
+ addUnwrappedLine();
}
if (!Previous || Previous->isNot(TT_TypeDeclarationParen))
FormatTok->setFinalizedType(TT_FunctionLBrace);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 9c5aa11d6e58f..f4028626c4783 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -14941,11 +14941,24 @@ TEST_F(FormatTest, PullTrivialFunctionDefinitionsIntoSingleLine) {
FormatStyle DoNotMerge = getLLVMStyle();
DoNotMerge.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+ FormatStyle MergeBodyOnly = getLLVMStyle();
+ MergeBodyOnly.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+ MergeBodyOnly.PutShortFunctionBodiesOnASingleLine = true;
+
+ FormatStyle MergeBodyIfPossible = getLLVMStyleWithColumns(20);
+ MergeBodyIfPossible.PutShortFunctionBodiesOnASingleLine = true;
+
verifyFormat("void f() { return 42; }");
verifyFormat("void f() {\n"
" return 42;\n"
"}",
DoNotMerge);
+ verifyFormat("void f()\n"
+ "{ return 42; }",
+ MergeBodyOnly);
+ verifyFormat("void long_function_name()\n"
+ "{ return 42; }",
+ MergeBodyIfPossible);
verifyFormat("void f() {\n"
" // Comment\n"
"}");
@@ -14966,6 +14979,23 @@ TEST_F(FormatTest, PullTrivialFunctionDefinitionsIntoSingleLine) {
" int a;\n"
"} // comment",
DoNotMerge);
+ verifyFormat("void f()\n"
+ "{} // comment",
+ MergeBodyOnly);
+ verifyFormat("void f()\n"
+ "{ int a; } // comment",
+ MergeBodyOnly);
+ verifyFormat("void long_function_name()\n"
+ "{} // comment",
+ MergeBodyIfPossible);
+ verifyFormat("void f() {\n"
+ " int a;\n"
+ "} // comment",
+ MergeBodyIfPossible);
+ MergeBodyIfPossible.ColumnLimit = 21;
+ verifyFormat("void f()\n"
+ "{ int a; } // comment",
+ MergeBodyIfPossible);
verifyFormat("void f() {\n"
"} // comment",
getLLVMStyleWithColumns(15));
More information about the cfe-commits
mailing list