From 03b6ccc976d42847d06adaeb4174b2ca9b7cb393 Mon Sep 17 00:00:00 2001 From: Klaus Gerlicher Date: Sat, 20 Sep 2025 15:13:10 +0200 Subject: [PATCH] [clang-format] Add SpaceBetweenUnderscoreParens option for GNU gettext macro This adds a new configuration option SpaceBetweenUnderscoreParens 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 SpaceBetweenUnderscoreParens=false: printf(_("Hello")); // No space before '_(' my_func (arg); // Space before other functions preserved LLVM style with SpaceBetweenUnderscoreParens=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 | 5 +++++ clang/unittests/Format/FormatTest.cpp | 31 ++++++++++++++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 9413b9a348b76..aa95eee801d21 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]; +.. _SpaceBetweenUnderscoreParens: + +**SpaceBetweenUnderscoreParens** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ ` + 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:`¶ ` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 342fefcfc408c..9b9aa3c713798 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4896,6 +4896,16 @@ struct FormatStyle { /// \version 10 // bool SpaceInEmptyBlock; + /// 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 SpaceBetweenUnderscoreParens; + /// Style of when to insert a space in empty braces. enum SpaceInEmptyBracesStyle : int8_t { /// Always insert a space in empty braces. diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 24a36d97a6fa9..99cdf8d2d3ff5 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1219,6 +1219,8 @@ template <> struct MappingTraits { Style.SpaceBeforeCtorInitializerColon); IO.mapOptional("SpaceBeforeInheritanceColon", Style.SpaceBeforeInheritanceColon); + IO.mapOptional("SpaceBetweenUnderscoreParens", + Style.SpaceBetweenUnderscoreParens); 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.SpaceBetweenUnderscoreParens = 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.SpaceBetweenUnderscoreParens = false; return Style; } diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d97f56751ea69..142177fdbce94 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4901,6 +4901,11 @@ 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.SpaceBetweenUnderscoreParens && + Left.TokenText == "_") { + return false; + } return spaceRequiredBeforeParens(Right); } return false; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index d9db06667d802..e21c3956e65cc 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -17852,6 +17852,37 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { Spaces); } +TEST_F(FormatTest, SpaceBetweenUnderscoreParens) { + FormatStyle Style = getLLVMStyle(); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + + EXPECT_TRUE(Style.SpaceBetweenUnderscoreParens); + verifyFormat("func (arg);", Style); + verifyFormat("my_func (arg);", Style); + verifyFormat("_ (message);", Style); + verifyFormat("underscore_ (param);", Style); + + Style.SpaceBetweenUnderscoreParens = false; + verifyFormat("func (arg);", Style); + verifyFormat("my_func (arg);", Style); + verifyFormat("_(message);", Style); + verifyFormat("underscore_ (param);", Style); + verifyFormat("_private_func (data);", Style); + + FormatStyle GNUStyle = getGNUStyle(); + EXPECT_FALSE(GNUStyle.SpaceBetweenUnderscoreParens); + EXPECT_EQ(GNUStyle.SpaceBeforeParens, FormatStyle::SBPO_Always); + verifyFormat("func (arg);", GNUStyle); + verifyFormat("my_func (arg);", GNUStyle); + verifyFormat("_(message);", GNUStyle); + verifyFormat("_private_func (data);", GNUStyle); + + verifyFormat("printf (_(\"Hello\"));\n" + "func (arg);\n" + "_(\"World\");", + GNUStyle); +} + TEST_F(FormatTest, ConfigurableSpacesInSquareBrackets) { verifyFormat("int a[5];"); verifyFormat("a[3] += 42;");