[clang] [clang-format] Add AlignConsecutiveEnums (PR #194154)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 9 10:03:41 PDT 2026
https://github.com/aaronsms updated https://github.com/llvm/llvm-project/pull/194154
>From 516e15335ef6a67bd293bbdbaf648fe27fb751b0 Mon Sep 17 00:00:00 2001
From: Aaron Saw Min Sern <aaron at aaronsms.com>
Date: Sat, 9 May 2026 16:38:28 +0800
Subject: [PATCH] add option to only align enum assignments for
AlignConsecutiveAssignments
---
clang/docs/ClangFormatStyleOptions.rst | 77 ++++++++++++++++++++++
clang/include/clang/Format/Format.h | 11 ++++
clang/lib/Format/Format.cpp | 16 +++--
clang/lib/Format/FormatToken.h | 1 +
clang/lib/Format/TokenAnnotator.cpp | 17 ++++-
clang/lib/Format/TokenAnnotator.h | 2 +
clang/lib/Format/WhitespaceManager.cpp | 13 +++-
clang/unittests/Format/AlignmentTest.cpp | 23 ++++++-
clang/unittests/Format/ConfigParseTest.cpp | 72 ++++++++++----------
9 files changed, 187 insertions(+), 45 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index fe8dfa406bc32..55f5fbf8560f3 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -402,6 +402,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -560,6 +571,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -718,6 +740,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -877,6 +910,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -1155,6 +1199,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -1311,6 +1366,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
@@ -1467,6 +1533,17 @@ the configuration (without a prefix: ``Auto``).
int *p;
int (*f)();
+ * ``bool EnumAssignments`` Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ aligned.
+
+ .. code-block:: c++
+
+ enum ValueKind {
+ VK_Argument = 1,
+ VK_BasicBlock = 2,
+ VK_Segment = 8,
+ };
+
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index f89323c71713b..80589e8bbfa5e 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -236,6 +236,16 @@ struct FormatStyle {
/// int (*f)();
/// \endcode
bool AlignFunctionPointers;
+ /// Only for ``AlignConsecutiveAssignments``. Whether enum assignments are
+ /// aligned.
+ /// \code
+ /// enum ValueKind {
+ /// VK_Argument = 1,
+ /// VK_BasicBlock = 2,
+ /// VK_Segment = 8,
+ /// };
+ /// \endcode
+ bool EnumAssignments;
/// Only for ``AlignConsecutiveAssignments``. Whether short assignment
/// operators are left-padded to the same length as long ones in order to
/// put all assignment operators to the right of the left hand side.
@@ -259,6 +269,7 @@ struct FormatStyle {
return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines &&
AcrossComments == R.AcrossComments &&
AlignCompound == R.AlignCompound &&
+ EnumAssignments == R.EnumAssignments &&
AlignFunctionDeclarations == R.AlignFunctionDeclarations &&
AlignFunctionPointers == R.AlignFunctionPointers &&
PadOperators == R.PadOperators;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 8d447177a18a7..249fa1f950f9a 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -62,25 +62,29 @@ template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
{/*Enabled=*/true, /*AcrossEmptyLines=*/false,
/*AcrossComments=*/false, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+ /*AlignFunctionPointers=*/false,
+ /*EnumAssignments=*/true, /*PadOperators=*/true}));
IO.enumCase(Value, "AcrossEmptyLines",
FormatStyle::AlignConsecutiveStyle(
{/*Enabled=*/true, /*AcrossEmptyLines=*/true,
/*AcrossComments=*/false, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+ /*AlignFunctionPointers=*/false,
+ /*EnumAssignments=*/true, /*PadOperators=*/true}));
IO.enumCase(Value, "AcrossComments",
FormatStyle::AlignConsecutiveStyle(
{/*Enabled=*/true, /*AcrossEmptyLines=*/false,
/*AcrossComments=*/true, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+ /*AlignFunctionPointers=*/false,
+ /*EnumAssignments=*/true, /*PadOperators=*/true}));
IO.enumCase(Value, "AcrossEmptyLinesAndComments",
FormatStyle::AlignConsecutiveStyle(
{/*Enabled=*/true, /*AcrossEmptyLines=*/true,
/*AcrossComments=*/true, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+ /*AlignFunctionPointers=*/false,
+ /*EnumAssignments=*/true, /*PadOperators=*/true}));
// For backward compatibility.
IO.enumCase(Value, "true",
@@ -88,7 +92,8 @@ template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
{/*Enabled=*/true, /*AcrossEmptyLines=*/false,
/*AcrossComments=*/false, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+ /*AlignFunctionPointers=*/false,
+ /*EnumAssignments=*/true, /*PadOperators=*/true}));
IO.enumCase(Value, "false", FormatStyle::AlignConsecutiveStyle{});
}
@@ -100,6 +105,7 @@ template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
IO.mapOptional("AlignFunctionDeclarations",
Value.AlignFunctionDeclarations);
IO.mapOptional("AlignFunctionPointers", Value.AlignFunctionPointers);
+ IO.mapOptional("EnumAssignments", Value.EnumAssignments);
IO.mapOptional("PadOperators", Value.PadOperators);
}
};
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 68d94b087136d..1d8f0f1cfe412 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -76,6 +76,7 @@ namespace format {
TYPE(DoWhile) \
TYPE(ElseLBrace) \
TYPE(ElseRBrace) \
+ TYPE(EnumEqual) \
TYPE(EnumLBrace) \
TYPE(EnumRBrace) \
TYPE(EnumUnderlyingTypeColon) \
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 898759cb8ea1b..7a555e05ec08b 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -139,6 +139,8 @@ class AnnotatingParser {
case TT_StructLBrace:
case TT_UnionLBrace:
return ST_Class;
+ case TT_EnumLBrace:
+ return ST_Enum;
case TT_CompoundRequirementLBrace:
return ST_CompoundRequirement;
default:
@@ -1232,6 +1234,7 @@ class AnnotatingParser {
return false;
updateParameterCount(&OpeningBrace, CurrentToken);
if (CurrentToken->isOneOf(tok::colon, tok::l_brace, tok::less)) {
+ assert(!Scopes.empty());
FormatToken *Previous = CurrentToken->getPreviousNonComment();
if (Previous->is(TT_JsTypeOptionalQuestion))
Previous = Previous->getPreviousNonComment();
@@ -1239,6 +1242,7 @@ class AnnotatingParser {
(!Contexts.back().ColonIsDictLiteral || !IsCpp)) ||
Style.isProto()) {
OpeningBrace.setType(TT_DictLiteral);
+ Scopes.back() = getScopeType(OpeningBrace);
if (Previous->Tok.getIdentifierInfo() ||
Previous->is(tok::string_literal)) {
Previous->setType(TT_SelectorName);
@@ -1247,16 +1251,21 @@ class AnnotatingParser {
if (CurrentToken->is(tok::colon) && OpeningBrace.is(TT_Unknown) &&
!Style.isTableGen()) {
OpeningBrace.setType(TT_DictLiteral);
+ Scopes.back() = getScopeType(OpeningBrace);
} else if (Style.isJavaScript()) {
OpeningBrace.overwriteFixedType(TT_DictLiteral);
+ Scopes.back() = getScopeType(OpeningBrace);
}
}
bool IsBracedListComma = false;
if (CurrentToken->is(tok::comma)) {
- if (Style.isJavaScript())
+ assert(!Scopes.empty());
+ if (Style.isJavaScript()) {
OpeningBrace.overwriteFixedType(TT_DictLiteral);
- else
+ Scopes.back() = getScopeType(OpeningBrace);
+ } else {
IsBracedListComma = OpeningBrace.is(BK_BracedInit);
+ }
++CommaCount;
}
if (!consumeToken())
@@ -1835,6 +1844,10 @@ class AnnotatingParser {
// In TableGen, there must be a value after "=";
if (Style.isTableGen() && !parseTableGenValue())
return false;
+ if ((CurrentToken && CurrentToken->is(tok::numeric_constant)) &&
+ (!Scopes.empty() && Scopes.back() == ST_Enum)) {
+ Tok->setFinalizedType(TT_EnumEqual);
+ }
break;
default:
break;
diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 597dd890ee990..33c7df9d0f949 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -40,6 +40,8 @@ enum LineType {
enum ScopeType {
// Contained in class declaration/definition.
ST_Class,
+ // Contained in enum declaration/definition.
+ ST_Enum,
// Contained in compound requirement.
ST_CompoundRequirement,
// Contained in other blocks (function, lambda, loop, if/else, child, etc).
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index 74aa8a2795150..4a72abbfa9ec3 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -813,8 +813,10 @@ void WhitespaceManager::alignConsecutiveMacros() {
}
void WhitespaceManager::alignConsecutiveAssignments() {
- if (!Style.AlignConsecutiveAssignments.Enabled)
+ if (!Style.AlignConsecutiveAssignments.Enabled &&
+ !Style.AlignConsecutiveAssignments.EnumAssignments) {
return;
+ }
AlignTokens(
Style,
@@ -827,6 +829,15 @@ void WhitespaceManager::alignConsecutiveAssignments() {
if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
return false;
+ // Align enum '=' when EnumAssignments is enabled.
+ if (Style.AlignConsecutiveAssignments.EnumAssignments &&
+ C.Tok->is(TT_EnumEqual)) {
+ return true;
+ }
+
+ if (!Style.AlignConsecutiveAssignments.Enabled)
+ return false;
+
// Do not align operator= overloads.
FormatToken *Previous = C.Tok->getPreviousNonComment();
if (Previous && Previous->is(tok::kw_operator))
diff --git a/clang/unittests/Format/AlignmentTest.cpp b/clang/unittests/Format/AlignmentTest.cpp
index 2f49c5c7875a4..6109fc276319c 100644
--- a/clang/unittests/Format/AlignmentTest.cpp
+++ b/clang/unittests/Format/AlignmentTest.cpp
@@ -1135,6 +1135,25 @@ TEST_F(AlignmentTest, ConsecutiveAssignmentsAcrossEmptyLinesAndComments) {
Alignment);
}
+TEST_F(AlignmentTest, ConsecutiveEnumAssignments) {
+ FormatStyle Alignment = getLLVMStyle();
+ Alignment.AlignConsecutiveAssignments.EnumAssignments = true;
+ verifyFormat("enum ValueKind {\n"
+ " VK_Argument = 1,\n"
+ " VK_BasicBlock = 2,\n"
+ " VK_Segment = 8,\n"
+ "};",
+ Alignment);
+ Alignment.AlignConsecutiveAssignments.Enabled = true;
+ Alignment.AlignConsecutiveAssignments.EnumAssignments = false;
+ verifyFormat("enum ValueKind {\n"
+ " VK_Argument = 1,\n"
+ " VK_BasicBlock = 2,\n"
+ " VK_Segment = 8,\n"
+ "};",
+ Alignment);
+}
+
TEST_F(AlignmentTest, ConsecutiveCompoundAssignments) {
FormatStyle Alignment = getLLVMStyle();
Alignment.AlignConsecutiveAssignments.Enabled = true;
@@ -2363,14 +2382,14 @@ TEST_F(AlignmentTest, AlignWithLineBreaks) {
/*AcrossComments=*/false, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/false,
/*AlignFunctionPointers=*/false,
- /*PadOperators=*/true}));
+ /*EnumAssignments=*/false, /*PadOperators=*/true}));
EXPECT_EQ(Style.AlignConsecutiveDeclarations,
FormatStyle::AlignConsecutiveStyle(
{/*Enabled=*/false, /*AcrossEmptyLines=*/false,
/*AcrossComments=*/false, /*AlignCompound=*/false,
/*AlignFunctionDeclarations=*/true,
/*AlignFunctionPointers=*/false,
- /*PadOperators=*/false}));
+ /*EnumAssignments=*/false, /*PadOperators=*/false}));
verifyFormat("void foo() {\n"
" int myVar = 5;\n"
" double x = 3.14;\n"
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 498e44b190ef2..9c1b870796454 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -338,44 +338,44 @@ TEST(ConfigParseTest, ParsesConfiguration) {
do { \
Style.FIELD.Enabled = true; \
CHECK_PARSE(#FIELD ": None", FIELD, FormatStyle::AlignConsecutiveStyle{}); \
- CHECK_PARSE( \
- #FIELD ": Consecutive", FIELD, \
- FormatStyle::AlignConsecutiveStyle( \
- {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
- /*AcrossComments=*/false, /*AlignCompound=*/false, \
- /*AlignFunctionDeclarations=*/true, \
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); \
- CHECK_PARSE( \
- #FIELD ": AcrossEmptyLines", FIELD, \
- FormatStyle::AlignConsecutiveStyle( \
- {/*Enabled=*/true, /*AcrossEmptyLines=*/true, \
- /*AcrossComments=*/false, /*AlignCompound=*/false, \
- /*AlignFunctionDeclarations=*/true, \
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); \
- CHECK_PARSE( \
- #FIELD ": AcrossComments", FIELD, \
- FormatStyle::AlignConsecutiveStyle( \
- {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
- /*AcrossComments=*/true, /*AlignCompound=*/false, \
- /*AlignFunctionDeclarations=*/true, \
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); \
- CHECK_PARSE( \
- #FIELD ": AcrossEmptyLinesAndComments", FIELD, \
- FormatStyle::AlignConsecutiveStyle( \
- {/*Enabled=*/true, /*AcrossEmptyLines=*/true, \
- /*AcrossComments=*/true, /*AlignCompound=*/false, \
- /*AlignFunctionDeclarations=*/true, \
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); \
+ CHECK_PARSE(#FIELD ": Consecutive", FIELD, \
+ FormatStyle::AlignConsecutiveStyle( \
+ {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
+ /*AcrossComments=*/false, /*AlignCompound=*/false, \
+ /*AlignFunctionDeclarations=*/true, \
+ /*AlignFunctionPointers=*/false, \
+ /*EnumAssignments =*/true, /*PadOperators=*/true})); \
+ CHECK_PARSE(#FIELD ": AcrossEmptyLines", FIELD, \
+ FormatStyle::AlignConsecutiveStyle( \
+ {/*Enabled=*/true, /*AcrossEmptyLines=*/true, \
+ /*AcrossComments=*/false, /*AlignCompound=*/false, \
+ /*AlignFunctionDeclarations=*/true, \
+ /*AlignFunctionPointers=*/false, \
+ /*EnumAssignments =*/true, /*PadOperators=*/true})); \
+ CHECK_PARSE(#FIELD ": AcrossComments", FIELD, \
+ FormatStyle::AlignConsecutiveStyle( \
+ {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
+ /*AcrossComments=*/true, /*AlignCompound=*/false, \
+ /*AlignFunctionDeclarations=*/true, \
+ /*AlignFunctionPointers=*/false, \
+ /*EnumAssignments =*/true, /*PadOperators=*/true})); \
+ CHECK_PARSE(#FIELD ": AcrossEmptyLinesAndComments", FIELD, \
+ FormatStyle::AlignConsecutiveStyle( \
+ {/*Enabled=*/true, /*AcrossEmptyLines=*/true, \
+ /*AcrossComments=*/true, /*AlignCompound=*/false, \
+ /*AlignFunctionDeclarations=*/true, \
+ /*AlignFunctionPointers=*/false, \
+ /*EnumAssignments =*/true, /*PadOperators=*/true})); \
/* For backwards compability, false / true should still parse */ \
CHECK_PARSE(#FIELD ": false", FIELD, \
FormatStyle::AlignConsecutiveStyle{}); \
- CHECK_PARSE( \
- #FIELD ": true", FIELD, \
- FormatStyle::AlignConsecutiveStyle( \
- {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
- /*AcrossComments=*/false, /*AlignCompound=*/false, \
- /*AlignFunctionDeclarations=*/true, \
- /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); \
+ CHECK_PARSE(#FIELD ": true", FIELD, \
+ FormatStyle::AlignConsecutiveStyle( \
+ {/*Enabled=*/true, /*AcrossEmptyLines=*/false, \
+ /*AcrossComments=*/false, /*AlignCompound=*/false, \
+ /*AlignFunctionDeclarations=*/true, \
+ /*AlignFunctionPointers=*/false, \
+ /*EnumAssignments =*/true, /*PadOperators=*/true})); \
\
CHECK_PARSE_NESTED_BOOL(FIELD, Enabled); \
CHECK_PARSE_NESTED_BOOL(FIELD, AcrossEmptyLines); \
@@ -383,8 +383,10 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE_NESTED_BOOL(FIELD, AlignCompound); \
CHECK_PARSE_NESTED_BOOL(FIELD, AlignFunctionDeclarations); \
CHECK_PARSE_NESTED_BOOL(FIELD, AlignFunctionPointers); \
+ CHECK_PARSE_NESTED_BOOL(FIELD, EnumAssignments); \
CHECK_PARSE_NESTED_BOOL(FIELD, PadOperators); \
} while (false)
+ // @todo
CHECK_ALIGN_CONSECUTIVE(AlignConsecutiveAssignments);
CHECK_ALIGN_CONSECUTIVE(AlignConsecutiveBitFields);
More information about the cfe-commits
mailing list