[clang] [clang-format] allow short function body on a single line (PR #151428)
Lidong Yan via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 25 05:11:07 PDT 2025
https://github.com/brandb97 updated https://github.com/llvm/llvm-project/pull/151428
>From 8df95ae522547ba1c4fdd61481bbfcbfa902eec6 Mon Sep 17 00:00:00 2001
From: Lidong Yan <yldhome2d2 at gmail.com>
Date: Mon, 25 Aug 2025 13:19:22 +0800
Subject: [PATCH] [clang-format] allow short function bodies on a single line
When set AllowShortBlocksOnASingleLine as Empty or Always and we
can't put the whole function on a single line, clang-format doesn't
tries to put the function body on a seperate line from function
signature. Add tryMergeLines to put function bodies on a single line
if possible.
Signed-off-by: Lidong Yan <yldhome2d2 at gmail.com>
---
clang/lib/Format/FormatToken.h | 4 ++
clang/lib/Format/UnwrappedLineFormatter.cpp | 58 ++++++++++++++++-----
clang/lib/Format/UnwrappedLineParser.cpp | 11 +++-
clang/unittests/Format/FormatTest.cpp | 32 ++++++++++++
4 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 9252a795a0b5e..4b10ec57c828d 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -360,6 +360,10 @@ struct FormatToken {
/// \c true if it is allowed to break before this token.
unsigned CanBreakBefore : 1;
+ /// \c true if line breaks should not occur when joining this token with
+ /// the previous line.
+ unsigned NoBreakDuringJoinLines : 1;
+
/// \c true if this is the ">" of "template<..>".
unsigned ClosesTemplateDeclaration : 1;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index c938ff3965f9e..1bdf69e74c713 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -242,19 +242,24 @@ class LineJoiner {
if (Style.ColumnLimit > 0 && Indent > Style.ColumnLimit)
return 0;
- unsigned Limit =
+ const 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 &&
- !Style.BraceWrapping.SplitEmptyFunction &&
- NextLine.First->is(tok::r_brace)) {
- return tryMergeSimpleBlock(I, E, Limit);
+ TheLine->First == TheLine->Last) {
+ if (!Style.BraceWrapping.SplitEmptyFunction &&
+ NextLine.First->is(tok::r_brace)) {
+ return tryMergeSimpleBlock(I, E, Limit);
+ }
+ if (Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always &&
+ I + 2 != E && I[2]->First->is(tok::r_brace)) {
+ return tryMergeSimpleBlock(I, E, Limit);
+ }
}
const auto *PreviousLine = I != AnnotatedLines.begin() ? I[-1] : nullptr;
@@ -510,27 +515,51 @@ class LineJoiner {
// Try to merge a function block with left brace wrapped.
if (NextLine.First->is(TT_FunctionLBrace) &&
- Style.BraceWrapping.AfterFunction) {
+ (Style.BraceWrapping.AfterFunction ||
+ Style.AllowShortBlocksOnASingleLine >= FormatStyle::SBS_Empty)) {
if (NextLine.Last->is(TT_LineComment))
return 0;
// Check for Limit <= 2 to account for the " {".
if (Limit <= 2 || (Style.ColumnLimit == 0 && containsMustBreak(TheLine)))
- return 0;
+ if (Style.BraceWrapping.AfterFunction)
+ return 0;
Limit -= 2;
unsigned MergedLines = 0;
+ const bool NextEmptyBlock = NextLine.First == NextLine.Last &&
+ I + 2 != E && I[2]->First->is(tok::r_brace);
+ const bool NextShortBlock = NextLine.First == NextLine.Last &&
+ I + 2 != E && I + 3 != E &&
+ I[3]->First->is(tok::r_brace);
if (MergeShortFunctions ||
(Style.AllowShortFunctionsOnASingleLine >= FormatStyle::SFS_Empty &&
- NextLine.First == NextLine.Last && I + 2 != E &&
- I[2]->First->is(tok::r_brace))) {
+ NextEmptyBlock) ||
+ (Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_All &&
+ NextShortBlock)) {
MergedLines = tryMergeSimpleBlock(I + 1, E, Limit);
// If we managed to merge the block, count the function header, which is
// on a separate line.
if (MergedLines > 0)
++MergedLines;
}
- return MergedLines;
+ // Return early if we shouldn't merge left brace back to function header.
+ if (Style.BraceWrapping.AfterFunction || MergedLines)
+ return MergedLines;
+
+ // If we can't put function body on a single line, we should merge left
+ // brace back to function header.
+ const unsigned NextLineLimit =
+ NextLine.Last->TotalLength > LimitStripIndent
+ ? 0
+ : LimitStripIndent - NextLine.Last->TotalLength;
+ if ((NextShortBlock &&
+ Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always) ||
+ (NextEmptyBlock &&
+ Style.AllowShortBlocksOnASingleLine >= FormatStyle::SBS_Empty)) {
+ MergedLines = tryMergeSimpleBlock(I + 1, E, NextLineLimit);
+ }
+ return MergedLines ? 0 : 1;
}
auto IsElseLine = [&TheLine]() -> bool {
const FormatToken *First = TheLine->First;
@@ -992,7 +1021,8 @@ class LineJoiner {
}
A.Last->Next = B.First;
B.First->Previous = A.Last;
- B.First->CanBreakBefore = true;
+ if (!B.First->NoBreakDuringJoinLines)
+ B.First->CanBreakBefore = true;
unsigned LengthA = A.Last->TotalLength + B.First->SpacesRequiredBefore;
for (FormatToken *Tok = B.First; Tok; Tok = Tok->Next) {
Tok->TotalLength += LengthA;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 91b8fdc8a3c38..3582a599e2959 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -1907,6 +1907,8 @@ void UnwrappedLineParser::parseStructuralElement(
FormatTok->setFinalizedType(TT_BracedListLBrace);
if (!tryToParsePropertyAccessor() && !tryToParseBracedList()) {
IsDecltypeAutoFunction = Line->SeenDecltypeAuto;
+ if (!Previous || Previous->isNot(TT_TypeDeclarationParen))
+ FormatTok->setFinalizedType(TT_FunctionLBrace);
// A block outside of parentheses must be the last part of a
// structural element.
// FIXME: Figure out cases where this is not true, and add projections
@@ -1921,9 +1923,14 @@ void UnwrappedLineParser::parseStructuralElement(
}
} else if (Style.BraceWrapping.AfterFunction) {
addUnwrappedLine();
+ } else if (Style.AllowShortBlocksOnASingleLine >=
+ FormatStyle::SBS_Empty &&
+ FormatTok->is(TT_FunctionLBrace)) {
+ // Wrap left brace here in case that we want
+ // to merge function body on a single line.
+ addUnwrappedLine();
+ FormatTok->NoBreakDuringJoinLines = true;
}
- if (!Previous || Previous->isNot(TT_TypeDeclarationParen))
- FormatTok->setFinalizedType(TT_FunctionLBrace);
parseBlock();
IsDecltypeAutoFunction = false;
addUnwrappedLine();
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 83c664c3b81f3..fef7607c30587 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -14953,11 +14953,25 @@ TEST_F(FormatTest, PullTrivialFunctionDefinitionsIntoSingleLine) {
FormatStyle DoNotMerge = getLLVMStyle();
DoNotMerge.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+ FormatStyle MergeBodyAlways = getLLVMStyle();
+ MergeBodyAlways.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+ MergeBodyAlways.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always;
+
+ FormatStyle MergeBodyIfPossible = getLLVMStyleWithColumns(20);
+ MergeBodyIfPossible.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+ MergeBodyIfPossible.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always;
+
verifyFormat("void f() { return 42; }");
verifyFormat("void f() {\n"
" return 42;\n"
"}",
DoNotMerge);
+ verifyFormat("void f()\n"
+ "{ return 42; }",
+ MergeBodyAlways);
+ verifyFormat("void long_function_name()\n"
+ "{ return 42; }",
+ MergeBodyIfPossible);
verifyFormat("void f() {\n"
" // Comment\n"
"}");
@@ -14982,6 +14996,24 @@ TEST_F(FormatTest, PullTrivialFunctionDefinitionsIntoSingleLine) {
"} // comment",
getLLVMStyleWithColumns(15));
+ verifyFormat("void f()\n"
+ "{} // comment",
+ MergeBodyAlways);
+ verifyFormat("void f()\n"
+ "{ int a; } // comment",
+ MergeBodyAlways);
+ 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() { return 42; }", getLLVMStyleWithColumns(23));
verifyFormat("void f() {\n return 42;\n}", getLLVMStyleWithColumns(22));
More information about the cfe-commits
mailing list