[clang] [clang-format]: Add `StaticInlineOnly` and `StaticInline` options to `ShortFunctionStyle` (PR #133598)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 29 20:32:07 PDT 2025
https://github.com/irymarchyk updated https://github.com/llvm/llvm-project/pull/133598
>From cc9c8d79396b6be64910eda59c4f7bd1a1d0a839 Mon Sep 17 00:00:00 2001
From: Ivan Rymarchyk <>
Date: Sat, 29 Mar 2025 13:54:32 -0700
Subject: [PATCH 1/2] [clang-format]: Add `StaticInlineOnly` and `StaticInline`
options to `ShortFunctionStyle`
Currently, the `ShortFunctionStyle` option in clang-format lacks the granularity to specifically control the single-line formatting of `static inline` C functions independently from other function types like regular empty functions.
**Problem:**
Users may want to enforce a style where:
1. **Only `static inline` functions** are allowed on a single line (if they fit), forcing all other functions (including empty ones) onto multiple lines. This is useful for keeping utility/helper functions concise while maintaining a consistent multi-line format for primary function definitions.
2. **`static inline` functions *or* empty functions** are allowed on a single line (if they fit), while other non-empty, non-`static inline` functions are forced onto multiple lines. This is a slightly less strict variation.
The existing `ShortFunctionStyle` options do not cover these specific C use cases adequately:
* `None`: Forces all functions multi-line.
* `Empty`: Allows *any* empty function on one line, not just `static inline` ones.
* `All`: Allows any short function on one line.
* `Inline`/`InlineOnly`: Primarily target C++ member functions or C++ free inline functions, not specifically the C `static inline` pattern.
**Proposed Solution:**
Introduce two new values for the `ShortFunctionStyle` enum (currently named `ShortFunctionStyle` internally, likely `SFS_...` values):
1. **`StaticInlineOnly`**
* **Configuration Name:** `StaticInlineOnly`
* **Internal Enum Value (Suggestion):** `SFS_StaticInlineOnly`
* **Behavior:** Allows *only* functions declared with both `static` and `inline` specifiers to be formatted on a single line, provided they fit within the `ColumnLimit`. All other functions (regular, static non-inline, inline non-static, empty or not) must be formatted across multiple lines.
2. **`StaticInline`**
* **Configuration Name:** `StaticInline`
* **Internal Enum Value (Suggestion):** `SFS_StaticInline`
* **Behavior:** Allows functions declared with both `static` and `inline` specifiers *or* functions with an empty body (`{}`) to be formatted on a single line, provided they fit within the `ColumnLimit`. Non-empty functions that are *not* `static inline` must be formatted across multiple lines. This effectively combines the `SFS_Empty` behavior with allowing non-empty `static inline` functions.
**Expected Formatting:**
* **With `ShortFunctionStyle: StaticInlineOnly`**
```c
void f1(void) // Multi-line (not static inline)
{
}
int f2(int a, int b) // Multi-line (not static inline)
{
return a + b;
}
static void f3(void) // Multi-line (not static inline)
{
}
static int f4(int a, int b) // Multi-line (not static inline)
{
return a + b;
}
static inline void f5(void) {} // Single-line allowed
static inline int f6(int a, int b) { return a + b; } // Single-line allowed (if fits)
inline void f7(void) // Multi-line (not static inline)
{
}
```
* **With `ShortFunctionStyle: StaticInline`** (Implies Empty)
```c
void f1(void) {} // Single-line allowed (empty)
int f2(int a, int b) // Multi-line (non-empty, not static inline)
{
return a + b;
}
static void f3(void) {} // Single-line allowed (empty)
static int f4(int a, int b) // Multi-line (non-empty, not static inline)
{
return a + b;
}
static inline void f5(void) {} // Single-line allowed (static inline and empty)
static inline int f6(int a, int b) { return a + b; } // Single-line allowed (static inline, if fits)
inline void f7(void) {} // Single-line allowed (empty)
```
---
clang/docs/ClangFormatStyleOptions.rst | 17 +++++
clang/include/clang/Format/Format.h | 13 ++++
clang/lib/Format/Format.cpp | 2 +
clang/lib/Format/TokenAnnotator.cpp | 6 +-
clang/lib/Format/UnwrappedLineFormatter.cpp | 31 +++++++-
clang/unittests/Format/ConfigParseTest.cpp | 6 ++
clang/unittests/Format/FormatTest.cpp | 79 +++++++++++++++++++++
7 files changed, 150 insertions(+), 4 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 9ecac68ae72bf..e5641fa5037ae 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -1926,6 +1926,15 @@ the configuration (without a prefix: ``Auto``).
void f() {
}
+ * ``SFS_StaticInlineOnly`` (in configuration: ``StaticInlineOnly``)
+ Only merge functions defined as static inline.
+
+ .. code-block:: c++
+
+ void f5(void) {
+ }
+ static inline int f6(int a, int b) { return a + b; }
+
* ``SFS_Empty`` (in configuration: ``Empty``)
Only merge empty functions.
@@ -1949,6 +1958,14 @@ the configuration (without a prefix: ``Auto``).
}
void f() {}
+ * ``SFS_StaticInline`` (in configuration: ``StaticInline``)
+ Only merge functions defined as static inline. Implies ``empty``.
+
+ .. code-block:: c++
+
+ void f5(void) {}
+ static inline int f6(int a, int b) { return a + b; }
+
* ``SFS_All`` (in configuration: ``All``)
Merge all functions fitting on a single line.
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index fec47a248abb4..7fa9f87422867 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -844,6 +844,13 @@ struct FormatStyle {
/// }
/// \endcode
SFS_InlineOnly,
+ /// Only merge functions defined as static inline.
+ /// \code
+ /// void f5(void) {
+ /// }
+ /// static inline int f6(int a, int b) { return a + b; }
+ /// \endcode
+ SFS_StaticInlineOnly,
/// Only merge empty functions.
/// \code
/// void f() {}
@@ -863,6 +870,12 @@ struct FormatStyle {
/// void f() {}
/// \endcode
SFS_Inline,
+ /// Only merge functions defined as static inline. Implies ``empty``.
+ /// \code
+ /// void f5(void) {}
+ /// static inline int f6(int a, int b) { return a + b; }
+ /// \endcode
+ SFS_StaticInline,
/// Merge all functions fitting on a single line.
/// \code
/// class Foo {
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 28aea86139e0d..05ed4f4a2bec8 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -622,6 +622,8 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortFunctionStyle> {
IO.enumCase(Value, "Inline", FormatStyle::SFS_Inline);
IO.enumCase(Value, "InlineOnly", FormatStyle::SFS_InlineOnly);
IO.enumCase(Value, "Empty", FormatStyle::SFS_Empty);
+ IO.enumCase(Value, "StaticInlineOnly", FormatStyle::SFS_StaticInlineOnly);
+ IO.enumCase(Value, "StaticInline", FormatStyle::SFS_StaticInline);
}
};
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index d87b3a6088bd8..2b2177c542d88 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -5690,8 +5690,10 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
return Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_None ||
Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Empty ||
(Left.NestingLevel == 0 && Line.Level == 0 &&
- Style.AllowShortFunctionsOnASingleLine &
- FormatStyle::SFS_InlineOnly);
+ (Style.AllowShortFunctionsOnASingleLine ==
+ FormatStyle::SFS_InlineOnly ||
+ Style.AllowShortFunctionsOnASingleLine ==
+ FormatStyle::SFS_Inline));
}
} else if (Style.Language == FormatStyle::LK_Java) {
if (Right.is(tok::plus) && Left.is(tok::string_literal) && AfterRight &&
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 000a5105ca407..ceca9c36fff7b 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -300,8 +300,9 @@ class LineJoiner {
return true;
}
- if (Style.AllowShortFunctionsOnASingleLine &
- FormatStyle::SFS_InlineOnly) {
+ if (Style.AllowShortFunctionsOnASingleLine ==
+ FormatStyle::SFS_InlineOnly ||
+ Style.AllowShortFunctionsOnASingleLine == FormatStyle::SFS_Inline) {
// Just checking TheLine->Level != 0 is not enough, because it
// provokes treating functions inside indented namespaces as short.
if (Style.isJavaScript() && TheLine->Last->is(TT_FunctionLBrace))
@@ -335,6 +336,32 @@ class LineJoiner {
}
}
+ if (Style.AllowShortFunctionsOnASingleLine ==
+ FormatStyle::SFS_StaticInlineOnly ||
+ Style.AllowShortFunctionsOnASingleLine ==
+ FormatStyle::SFS_StaticInline) {
+ // Check if the current line belongs to a static inline function
+ const auto *FirstNonCommentToken =
+ TheLine ? TheLine->getFirstNonComment() : nullptr;
+
+ // Look for 'static' and 'inline' keywords in any order
+ bool HasStatic = false;
+ bool HasInline = false;
+ const FormatToken *Tok = FirstNonCommentToken;
+
+ while (Tok && !Tok->is(TT_FunctionLBrace)) {
+ if (Tok->is(tok::kw_static))
+ HasStatic = true;
+ if (Tok->is(tok::kw_inline))
+ HasInline = true;
+ Tok = Tok->Next;
+ }
+
+ // If we found both static and inline, allow merging
+ if (HasStatic && HasInline)
+ return true;
+ }
+
return false;
};
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 287191d04d885..d7dfe577bedae 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -620,6 +620,12 @@ TEST(ConfigParseTest, ParsesConfiguration) {
AllowShortFunctionsOnASingleLine, FormatStyle::SFS_Empty);
CHECK_PARSE("AllowShortFunctionsOnASingleLine: All",
AllowShortFunctionsOnASingleLine, FormatStyle::SFS_All);
+ CHECK_PARSE("AllowShortFunctionsOnASingleLine: StaticInlineOnly",
+ AllowShortFunctionsOnASingleLine,
+ FormatStyle::SFS_StaticInlineOnly);
+ CHECK_PARSE("AllowShortFunctionsOnASingleLine: StaticInline",
+ AllowShortFunctionsOnASingleLine, FormatStyle::SFS_StaticInline);
+
// For backward compatibility:
CHECK_PARSE("AllowShortFunctionsOnASingleLine: false",
AllowShortFunctionsOnASingleLine, FormatStyle::SFS_None);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0b90bd360b758..2fec608055ac9 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -15120,6 +15120,85 @@ TEST_F(FormatTest, PullInlineFunctionDefinitionsIntoSingleLine) {
MergeInlineOnly);
}
+TEST_F(FormatTest, PullStaticInlineFunctionDefinitionsIntoSingleLine) {
+ FormatStyle MergeStaticInlineOnly = getLLVMStyle();
+ MergeStaticInlineOnly.AllowShortFunctionsOnASingleLine =
+ FormatStyle::SFS_StaticInlineOnly;
+ verifyFormat("static inline int f() { return 42; }",
+ "static inline int f() {\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInlineOnly);
+ verifyFormat("inline static int f() { return 42; }",
+ "inline static int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInlineOnly);
+ verifyNoChange("int f() {\n"
+ " return 42;\n"
+ "}",
+ MergeStaticInlineOnly);
+
+ verifyFormat("void f1(void) {\n"
+ "}",
+ "void f1(void)\n"
+ "{\n"
+ "}",
+ MergeStaticInlineOnly);
+
+ verifyNoChange("int f2(int a, int b) {\n"
+ " return a + b;\n"
+ "}",
+ MergeStaticInlineOnly);
+
+ verifyNoChange("static void f3(void) {\n"
+ "}\n",
+ MergeStaticInlineOnly);
+
+ verifyFormat("static int f4(int a, int b) {\n"
+ " return a + b;\n"
+ "}\n",
+ MergeStaticInlineOnly);
+
+ verifyNoChange("static inline void f5(void) {}", MergeStaticInlineOnly);
+
+ verifyFormat("static inline int f6(int a, int b) { return a + b; }",
+ "static inline int f6(int a, int b) \n"
+ "{ return a + b; }",
+ MergeStaticInlineOnly);
+
+ verifyFormat("int f(int a, int b) {\n"
+ " return a + b;\n"
+ "}",
+ "int f(int a, int b) { return a + b; }", MergeStaticInlineOnly);
+
+ FormatStyle MergeStaticInline = getLLVMStyle();
+ MergeStaticInline.AllowShortFunctionsOnASingleLine =
+ FormatStyle::SFS_StaticInline;
+ verifyFormat("static inline int f() { return 42; }",
+ "static inline int f() {\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+ verifyFormat("inline static int f() { return 42; }",
+ "inline static int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+ verifyNoChange("int f() {\n"
+ " return 42;\n"
+ "}",
+ MergeStaticInline);
+
+ verifyFormat("void f1(void) {}",
+ "void f1(void)\n"
+ "{\n"
+ "}",
+ MergeStaticInline);
+}
+
TEST_F(FormatTest, PullInlineOnlyFunctionDefinitionsIntoSingleLine) {
FormatStyle MergeInlineOnly = getLLVMStyle();
MergeInlineOnly.AllowShortFunctionsOnASingleLine =
>From 3ccdaeb33d152c179bbced16356e793c203c944b Mon Sep 17 00:00:00 2001
From: Ivan Rymarchyk <>
Date: Sat, 29 Mar 2025 20:31:11 -0700
Subject: [PATCH 2/2] PR-133598: [clang-format]: Add StaticInlineOnly and
StaticInline options to ShortFunctionStyle
* fixed small PR requests such as grammar
+ added additional unit tests to test with attribute in different places
---
clang/include/clang/Format/Format.h | 2 +-
clang/lib/Format/UnwrappedLineFormatter.cpp | 7 +--
clang/unittests/Format/FormatTest.cpp | 50 +++++++++++++++++++++
3 files changed, 55 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 7fa9f87422867..e12336f577553 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -870,7 +870,7 @@ struct FormatStyle {
/// void f() {}
/// \endcode
SFS_Inline,
- /// Only merge functions defined as static inline. Implies ``empty``.
+ /// Merge functions defined as static inline, also merge empty functions.
/// \code
/// void f5(void) {}
/// static inline int f6(int a, int b) { return a + b; }
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index ceca9c36fff7b..e1c3d6caf8c22 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -344,12 +344,13 @@ class LineJoiner {
const auto *FirstNonCommentToken =
TheLine ? TheLine->getFirstNonComment() : nullptr;
- // Look for 'static' and 'inline' keywords in any order
+ // Look for 'static' and 'inline' keywords in any order.
bool HasStatic = false;
bool HasInline = false;
const FormatToken *Tok = FirstNonCommentToken;
- while (Tok && !Tok->is(TT_FunctionLBrace)) {
+ while (Tok && !Tok->is(TT_FunctionDeclarationName) &&
+ (!HasStatic || !HasInline)) {
if (Tok->is(tok::kw_static))
HasStatic = true;
if (Tok->is(tok::kw_inline))
@@ -357,7 +358,7 @@ class LineJoiner {
Tok = Tok->Next;
}
- // If we found both static and inline, allow merging
+ // If we found both static and inline, allow merging.
if (HasStatic && HasInline)
return true;
}
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 2fec608055ac9..149479e612bd1 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -15197,6 +15197,56 @@ TEST_F(FormatTest, PullStaticInlineFunctionDefinitionsIntoSingleLine) {
"{\n"
"}",
MergeStaticInline);
+
+ // additional attribute tests
+ verifyFormat("inline static __attribute__((unused)) int f() { return 42; }",
+ "inline static __attribute__((unused)) int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+ verifyFormat("__attribute__((unused)) inline static int f() { return 42; }",
+ "__attribute__((unused)) inline static int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+ verifyFormat("inline static int f() __attribute__((unused)) { return 42; }",
+ "inline static int f() \n"
+ "__attribute__((unused)) \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+ verifyFormat("__attribute__((unused)) inline static int f() { return 42; }",
+ "__attribute__((unused)) inline static int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+
+ verifyFormat("inline static const int f() { return 42; }",
+ "inline static const int f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+
+ verifyFormat("_Noreturn static inline auto f() { return 42; }",
+ "_Noreturn static inline auto f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
+
+ verifyFormat("constexpr auto f() {\n"
+ " return 42;\n"
+ "}",
+ "constexpr auto f() \n"
+ "{\n"
+ " return 42; \n"
+ "}",
+ MergeStaticInline);
}
TEST_F(FormatTest, PullInlineOnlyFunctionDefinitionsIntoSingleLine) {
More information about the cfe-commits
mailing list