diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 9ecac68ae72bf..4d7267a65d76b 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``) + Merge functions defined as static inline, also merge empty functions. + + .. 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..e12336f577553 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, + /// 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; } + /// \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 { 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..e1c3d6caf8c22 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,33 @@ 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_FunctionDeclarationName) && + (!HasStatic || !HasInline)) { + 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..149479e612bd1 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15120,6 +15120,135 @@ 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); + + // 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) { FormatStyle MergeInlineOnly = getLLVMStyle(); MergeInlineOnly.AllowShortFunctionsOnASingleLine =