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/3] [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/3] 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) {

>From 1e03eac2190477873821809960b41be1e285d86c Mon Sep 17 00:00:00 2001
From: Ivan Rymarchyk <>
Date: Sun, 30 Mar 2025 15:27:47 -0700
Subject: [PATCH 3/3] PR-133598: fixed documentation which led to build failure

---
 clang/docs/ClangFormatStyleOptions.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index e5641fa5037ae..4d7267a65d76b 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -1959,7 +1959,7 @@ the configuration (without a prefix: ``Auto``).
       void f() {}
 
   * ``SFS_StaticInline`` (in configuration: ``StaticInline``)
-    Only merge functions defined as static inline. Implies ``empty``.
+    Merge functions defined as static inline, also merge empty functions.
 
     .. code-block:: c++
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to