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
