https://github.com/kgerlich updated https://github.com/llvm/llvm-project/pull/159925
>From fc3192bda41076b4dbb63c4bcbdad101da8a3e1a Mon Sep 17 00:00:00 2001 From: Klaus Gerlicher <[email protected]> Date: Sat, 20 Sep 2025 15:13:10 +0200 Subject: [PATCH] [clang-format] Add SpaceBeforeUnderscoreParens option for GNU gettext macro This adds a new configuration option SpaceBeforeUnderscoreParens to control spacing between underscore and opening parenthesis. This is specifically designed for the gettext internationalization macro '_()' commonly used in GNU projects like GDB. The option: - Defaults to true for LLVM style (preserving existing behavior) - Defaults to false for GNU style (removes space before '_(') - Only affects single underscore tokens '_', not other identifiers - Leaves all other spacing rules unchanged Examples: GNU style with SpaceBeforeUnderscoreParens=false: printf(_("Hello")); // No space before '_(' my_func (arg); // Space before other functions preserved LLVM style with SpaceBeforeUnderscoreParens=true: printf(_("Hello")); // Standard spacing rules apply This addresses the common pattern in GNU software where gettext messages use '_()' without spaces, improving consistency with GNU coding standards. --- clang/docs/ClangFormatStyleOptions.rst | 12 ++++++ clang/include/clang/Format/Format.h | 10 +++++ clang/lib/Format/Format.cpp | 4 ++ clang/lib/Format/TokenAnnotator.cpp | 6 +++ clang/unittests/Format/FormatTest.cpp | 53 ++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 9413b9a348b76..e26505d644197 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -6605,6 +6605,18 @@ the configuration (without a prefix: ``Auto``). int a [5]; vs. int a[5]; int a [5][5]; vs. int a[5][5]; +.. _SpaceBeforeUnderscoreParens: + +**SpaceBeforeUnderscoreParens** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ <SpaceBeforeUnderscoreParens>` + If ``false``, spaces will be removed between underscore and an opening + parenthesis. This is specifically for the gettext macro ``_()`` commonly + used in GNU projects. + + .. code-block:: c++ + + true: false: + _ (message); vs. _(message); + .. _SpaceInEmptyBlock: **SpaceInEmptyBlock** (``Boolean``) :versionbadge:`clang-format 10` :ref:`¶ <SpaceInEmptyBlock>` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 342fefcfc408c..1643c575fcbba 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4667,6 +4667,16 @@ struct FormatStyle { /// \version 7 bool SpaceBeforeInheritanceColon; + /// If ``false``, spaces will be removed between underscore and an opening + /// parenthesis. This is specifically for the gettext macro ``_()`` commonly + /// used in GNU projects. + /// \code + /// true: false: + /// _ (message); vs. _(message); + /// \endcode + /// \version 19 + bool SpaceBeforeUnderscoreParens; + /// If ``true``, a space will be added before a JSON colon. For other /// languages, e.g. JavaScript, use ``SpacesInContainerLiterals`` instead. /// \code diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 24a36d97a6fa9..9181e9a9ecd26 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1219,6 +1219,8 @@ template <> struct MappingTraits<FormatStyle> { Style.SpaceBeforeCtorInitializerColon); IO.mapOptional("SpaceBeforeInheritanceColon", Style.SpaceBeforeInheritanceColon); + IO.mapOptional("SpaceBeforeUnderscoreParens", + Style.SpaceBeforeUnderscoreParens); IO.mapOptional("SpaceBeforeJsonColon", Style.SpaceBeforeJsonColon); IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens); IO.mapOptional("SpaceBeforeParensOptions", Style.SpaceBeforeParensOptions); @@ -1713,6 +1715,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.SpaceBeforeCpp11BracedList = false; LLVMStyle.SpaceBeforeCtorInitializerColon = true; LLVMStyle.SpaceBeforeInheritanceColon = true; + LLVMStyle.SpaceBeforeUnderscoreParens = true; LLVMStyle.SpaceBeforeJsonColon = false; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; LLVMStyle.SpaceBeforeParensOptions = {}; @@ -2044,6 +2047,7 @@ FormatStyle getGNUStyle() { Style.FixNamespaceComments = false; Style.KeepFormFeed = true; Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + Style.SpaceBeforeUnderscoreParens = false; return Style; } diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d97f56751ea69..9af90184d157e 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4901,6 +4901,12 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, // Handle builtins like identifiers. if (Line.Type != LT_PreprocessorDirective && (Left.Tok.getIdentifierInfo() || Left.is(tok::r_paren))) { + // Check for special case: single underscore token (gettext macro) + if (Left.Tok.getIdentifierInfo() && !Style.SpaceBeforeUnderscoreParens) { + StringRef TokenText = Left.TokenText; + if (TokenText == "_") + return false; + } return spaceRequiredBeforeParens(Right); } return false; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index d9db06667d802..4fd73ebed7fed 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -17852,6 +17852,59 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { Spaces); } +TEST_F(FormatTest, SpaceBeforeUnderscoreParens) { + // Test with SpaceBeforeParens = Always to clearly show the difference + FormatStyle Style = getLLVMStyle(); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + + // Default LLVM style should have SpaceBeforeUnderscoreParens = true + EXPECT_TRUE(Style.SpaceBeforeUnderscoreParens); + verifyFormat("func (arg);", Style); // All functions should have space + verifyFormat("my_func (arg);", Style); // All functions should have space + verifyFormat("_ (message);", Style); // Single underscore should have space + // when SpaceBeforeUnderscoreParens=true + verifyFormat("underscore_ (param);", + Style); // Other underscores should have space + + // Now test with SpaceBeforeUnderscoreParens = false but SpaceBeforeParens = + // Always + Style.SpaceBeforeUnderscoreParens = false; + verifyFormat("func (arg);", + Style); // Non-underscore functions should still have space + verifyFormat("my_func (arg);", + Style); // Functions with underscores should still have space + verifyFormat( + "_(message);", + Style); // Single underscore (gettext macro) should NOT have space + verifyFormat("underscore_ (param);", + Style); // Other underscores should still have space + verifyFormat("_private_func (data);", + Style); // Functions starting with underscore but not single _ + // should have space + + // Test GNU style (should have SpaceBeforeUnderscoreParens = false by default) + FormatStyle GNUStyle = getGNUStyle(); + EXPECT_FALSE(GNUStyle.SpaceBeforeUnderscoreParens); + EXPECT_EQ(GNUStyle.SpaceBeforeParens, + FormatStyle::SBPO_Always); // GNU style should have + // SpaceBeforeParens = Always + verifyFormat("func (arg);", + GNUStyle); // GNU style has SpaceBeforeParens = Always + verifyFormat("my_func (arg);", + GNUStyle); // Functions with underscores should have space + verifyFormat( + "_(message);", + GNUStyle); // Single underscore (gettext macro) should NOT have space + verifyFormat("_private_func (data);", + GNUStyle); // Other functions should have space + + // Test mixed scenarios with GNU style + verifyFormat("printf (_(\"Hello\"));\n" + "func (arg);\n" + "_(\"World\");", + GNUStyle); +} + TEST_F(FormatTest, ConfigurableSpacesInSquareBrackets) { verifyFormat("int a[5];"); verifyFormat("a[3] += 42;"); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
