https://github.com/misopeth created 
https://github.com/llvm/llvm-project/pull/188748

None

>From 4e0ffa564ff943e36219ae8083460a23ceaaaa25 Mon Sep 17 00:00:00 2001
From: Francesco Galizzi <[email protected]>
Date: Thu, 26 Mar 2026 14:50:28 +0100
Subject: [PATCH] Add the style option `SpaceBeforeEnumColon`

---
 clang/docs/ClangFormatStyleOptions.rst        | 12 ++++
 clang/include/clang/Format/Format.h           | 13 +++++
 clang/lib/Format/Format.cpp                   |  3 +
 clang/lib/Format/FormatToken.h                |  1 +
 clang/lib/Format/TokenAnnotator.cpp           | 23 ++++++++
 clang/unittests/Format/ConfigParseTest.cpp    |  1 +
 clang/unittests/Format/FormatTest.cpp         | 58 +++++++++++++++++++
 clang/unittests/Format/TokenAnnotatorTest.cpp |  2 +-
 8 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index f637b81bb75bc..fc375251dc6c0 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6841,6 +6841,18 @@ the configuration (without a prefix: ``Auto``).
      true:                                  false:
      class Foo : Bar {}             vs.     class Foo: Bar {}
 
+.. _SpaceBeforeEnumColon:
+
+**_SpaceBeforeEnumColon** (``Boolean``) :versionbadge:`clang-format 24` 
:ref:`¶ <_SpaceBeforeEnumColon>`
+  If ``false``, spaces will be removed before the colon specifying the
+  underlying type of an enum.
+
+  .. code-block:: c++
+
+     true:                                  false:
+     enum Foo : int {}           vs.        enum Foo: int {}
+     enum class Bar : unsigned {}           enum class Bar: unsigned {}
+
 .. _SpaceBeforeJsonColon:
 
 **SpaceBeforeJsonColon** (``Boolean``) :versionbadge:`clang-format 17` :ref:`¶ 
<SpaceBeforeJsonColon>`
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 8c90cc2e98121..9242306bbcdbd 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5129,6 +5129,18 @@ struct FormatStyle {
   /// \version 7
   bool SpaceBeforeInheritanceColon;
 
+  /// If ``false``, spaces will be removed before the colon specifying the
+  /// underlying type of an enum.
+  /// \code
+  ///   true:                                  false:
+  ///   enum Foo : int {}           vs.        enum Foo: int {}
+  ///   enum class Bar : unsigned {}           enum class Bar: unsigned {}
+  ///   enum struct Baz : char {};             enum struct Baz: char {};
+  ///   enum Qux : short;                      enum Qux: short;
+  /// \endcode
+  /// \version 24
+  bool SpaceBeforeEnumColon;
+
   /// If ``true``, a space will be added before a JSON colon. For other
   /// languages, e.g. JavaScript, use ``SpacesInContainerLiterals`` instead.
   /// \code
@@ -6077,6 +6089,7 @@ struct FormatStyle {
            SpaceBeforeCtorInitializerColon ==
                R.SpaceBeforeCtorInitializerColon &&
            SpaceBeforeInheritanceColon == R.SpaceBeforeInheritanceColon &&
+           SpaceBeforeEnumColon == R.SpaceBeforeEnumColon &&
            SpaceBeforeJsonColon == R.SpaceBeforeJsonColon &&
            SpaceBeforeParens == R.SpaceBeforeParens &&
            SpaceBeforeParensOptions == R.SpaceBeforeParensOptions &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2b0aa1735c895..56f447a94a6c6 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1400,6 +1400,8 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.SpaceBeforeCtorInitializerColon);
     IO.mapOptional("SpaceBeforeInheritanceColon",
                    Style.SpaceBeforeInheritanceColon);
+    IO.mapOptional("SpaceBeforeEnumColon",
+                   Style.SpaceBeforeEnumColon);
     IO.mapOptional("SpaceBeforeJsonColon", Style.SpaceBeforeJsonColon);
     IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens);
     IO.mapOptional("SpaceBeforeParensOptions", Style.SpaceBeforeParensOptions);
@@ -1919,6 +1921,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind 
Language) {
   LLVMStyle.SpaceBeforeCpp11BracedList = false;
   LLVMStyle.SpaceBeforeCtorInitializerColon = true;
   LLVMStyle.SpaceBeforeInheritanceColon = true;
+  LLVMStyle.SpaceBeforeEnumColon = true;
   LLVMStyle.SpaceBeforeJsonColon = false;
   LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements;
   LLVMStyle.SpaceBeforeParensOptions = {};
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index ba9a95440f0c2..e75d1affaa862 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -78,6 +78,7 @@ namespace format {
   TYPE(ElseRBrace)                                                             
\
   TYPE(EnumLBrace)                                                             
\
   TYPE(EnumRBrace)                                                             
\
+  TYPE(EnumUnderlyingTypeColon)                                                
\
   TYPE(FatArrow)                                                               
\
   TYPE(ForEachMacro)                                                           
\
   TYPE(FunctionAnnotationRParen)                                               
\
diff --git a/clang/lib/Format/TokenAnnotator.cpp 
b/clang/lib/Format/TokenAnnotator.cpp
index d2cdc28a7da7b..b4de3efd2316a 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1347,6 +1347,27 @@ class AnnotatingParser {
         return false;
       // Goto labels and case labels are already identified in
       // UnwrappedLineParser.
+      // Identify colons specifying the underlying type of an enum.
+      // e.g. enum Foo : int { ... }
+      //      enum class Bar : unsigned { ... }
+      //      enum Qux : short;
+      if (Style.isCpp() && (Line.First->is(tok::kw_enum) ||
+          Line.startsWith(tok::kw_typedef, tok::kw_enum))) {
+        // Make sure we're not inside the enum body (past the opening brace).
+        bool InsideBraces = false;
+        for (auto *T = Tok->Previous; T; T = T->Previous) {
+          if (T->is(tok::l_brace)) {
+            InsideBraces = true;
+            break;
+          }
+          if (T->is(tok::r_brace))
+            break;
+        }
+        if (!InsideBraces) {
+          Tok->setType(TT_EnumUnderlyingTypeColon);
+          break;
+        }
+      }
       if (Tok->isTypeFinalized())
         break;
       // Colons from ?: are handled in parseConditional().
@@ -5551,6 +5572,8 @@ bool TokenAnnotator::spaceRequiredBefore(const 
AnnotatedLine &Line,
     return Style.SpaceBeforeCtorInitializerColon;
   if (Right.is(TT_InheritanceColon) && !Style.SpaceBeforeInheritanceColon)
     return false;
+  if (Right.is(TT_EnumUnderlyingTypeColon) && !Style.SpaceBeforeEnumColon)
+    return false;
   if (Right.is(TT_RangeBasedForLoopColon) &&
       !Style.SpaceBeforeRangeBasedForLoopColon) {
     return false;
diff --git a/clang/unittests/Format/ConfigParseTest.cpp 
b/clang/unittests/Format/ConfigParseTest.cpp
index 953f57da26cd9..d961461d66c89 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -220,6 +220,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(SpaceBeforeCpp11BracedList);
   CHECK_PARSE_BOOL(SpaceBeforeCtorInitializerColon);
   CHECK_PARSE_BOOL(SpaceBeforeInheritanceColon);
+  CHECK_PARSE_BOOL(SpaceBeforeEnumColon);
   CHECK_PARSE_BOOL(SpaceBeforeJsonColon);
   CHECK_PARSE_BOOL(SpaceBeforeRangeBasedForLoopColon);
   CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets);
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 2701a7fca7346..3b898d54ca5e8 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -18632,6 +18632,64 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeColon) {
                InvertedSpaceStyle);
 }
 
+TEST_F(FormatTest, SpaceBeforeEnumColon) {
+  FormatStyle Style = getLLVMStyle();
+
+  // Default: SpaceBeforeEnumColon is true
+  EXPECT_TRUE(Style.SpaceBeforeEnumColon);
+  verifyFormat("enum Foo : int {};", Style);
+  verifyFormat("enum class Foo : int {};", Style);
+  verifyFormat("enum struct Foo : int {};", Style);
+  verifyFormat("enum Foo : int;", Style);
+  verifyFormat("enum Foo : unsigned int { A, B, C };", Style);
+  verifyFormat("enum class Foo : unsigned char { A, B, C };", Style);
+
+  // SpaceBeforeEnumColon set to false
+  Style.SpaceBeforeEnumColon = false;
+  verifyFormat("enum Foo: int {};", Style);
+  verifyFormat("enum class Foo: int {};", Style);
+  verifyFormat("enum struct Foo: int {};", Style);
+  verifyFormat("enum Foo: int;", Style);
+  verifyFormat("enum Foo: unsigned int { A, B, C };", Style);
+  verifyFormat("enum class Foo: unsigned char { A, B, C };", Style);
+
+  // Ensure typedef enum also works
+  verifyFormat("typedef enum Foo: int {} Foo_t;", Style);
+
+  // Ensure other colons are not affected
+  verifyFormat("class Foo : Bar {};", Style);
+  Style.SpaceBeforeInheritanceColon = true;
+  verifyFormat("class Foo : Bar {};", Style);
+
+  // Ensure ternary and case colons are not affected
+  verifyFormat("int x = a ? b : c;", Style);
+  verifyFormat("switch (x) {\n"
+               "case 1:\n"
+               "default:\n"
+               "}",
+               Style);
+
+  // Ensure ctor initializer colons are not affected
+  Style.SpaceBeforeCtorInitializerColon = true;
+  verifyFormat("Foo::Foo() : a(a) {}", Style);
+
+  // Ensure range-based for colons are not affected
+  Style.SpaceBeforeRangeBasedForLoopColon = true;
+  verifyFormat("for (auto a : b) {\n}", Style);
+
+  // Combined: all colon spaces off
+  FormatStyle NoSpaceStyle = getLLVMStyle();
+  NoSpaceStyle.SpaceBeforeEnumColon = false;
+  NoSpaceStyle.SpaceBeforeCtorInitializerColon = false;
+  NoSpaceStyle.SpaceBeforeInheritanceColon = false;
+  NoSpaceStyle.SpaceBeforeRangeBasedForLoopColon = false;
+  verifyFormat("enum Foo: int {};", NoSpaceStyle);
+  verifyFormat("class Foo: Bar {};", NoSpaceStyle);
+  verifyFormat("Foo::Foo(): foo(1) {}", NoSpaceStyle);
+  verifyFormat("for (auto a: b) {\n}", NoSpaceStyle);
+  verifyFormat("int x = a ? b : c;", NoSpaceStyle);
+}
+
 TEST_F(FormatTest, ConfigurableSpaceAroundPointerQualifiers) {
   FormatStyle Style = getLLVMStyle();
 
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp 
b/clang/unittests/Format/TokenAnnotatorTest.cpp
index efb361387bb1e..18681e6923dbf 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -4342,7 +4342,7 @@ TEST_F(TokenAnnotatorTest, UserDefinedLiteral) {
 TEST_F(TokenAnnotatorTest, EnumColonInTypedef) {
   auto Tokens = annotate("typedef enum : int {} foo;");
   ASSERT_EQ(Tokens.size(), 9u) << Tokens;
-  EXPECT_TOKEN(Tokens[2], tok::colon, TT_Unknown); // Not TT_InheritanceColon.
+  EXPECT_TOKEN(Tokens[2], tok::colon, TT_EnumUnderlyingTypeColon);
 }
 
 TEST_F(TokenAnnotatorTest, BitFieldColon) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to