[clang] [clang-format] Support fine-grained alignment of variable declarations (PR #160270)

James Henderson via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 23 06:38:17 PDT 2025


https://github.com/jh7370 updated https://github.com/llvm/llvm-project/pull/160270

>From f08a439bf3aa968e0d15c483ecab2f2457c78e8d Mon Sep 17 00:00:00 2001
From: James Henderson <james.henderson at sony.com>
Date: Mon, 22 Sep 2025 16:41:38 +0100
Subject: [PATCH 1/3] [clang-format] Support fine-grained alignment of variable
 declarations

This allows disabling variable alignment declarations while still having
function alignment. It also distinguishes between class member variables
and other variables (e.g. function local variables).

Fixes #143947.
---
 clang/docs/ClangFormatStyleOptions.rst     | 210 +++++++++++++++++++++
 clang/include/clang/Format/Format.h        |  31 +++
 clang/lib/Format/Format.cpp                |  81 +++++---
 clang/lib/Format/FormatToken.h             |   6 +-
 clang/lib/Format/TokenAnnotator.cpp        |   2 +
 clang/lib/Format/WhitespaceManager.cpp     |   6 +
 clang/unittests/Format/ConfigParseTest.cpp |  82 ++++----
 clang/unittests/Format/FormatTest.cpp      |  59 ++++++
 8 files changed, 411 insertions(+), 66 deletions(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index b746df5dab264..58d1fb43821b7 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -396,6 +396,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -428,6 +443,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -554,6 +584,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -586,6 +631,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -712,6 +772,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -744,6 +819,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -871,6 +961,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -903,6 +1008,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -1149,6 +1269,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -1181,6 +1316,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -1305,6 +1455,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -1337,6 +1502,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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.
@@ -1461,6 +1641,21 @@ the configuration (without a prefix: ``Auto``).
       a &= 2;
       bbb = 2;
 
+  * ``bool AlignFreeVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int v1;
+      float        v2;
+      size_t       v3;
+
+      false:
+      unsigned int v1;
+      float v2;
+      size_t v3;
+
   * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     are aligned.
 
@@ -1493,6 +1688,21 @@ the configuration (without a prefix: ``Auto``).
       int     *p;
       int (*f)();
 
+  * ``bool AlignMemberVariableDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    variable declarations are aligned.
+
+    .. code-block:: c++
+
+      true:
+      unsigned int member1;
+      float        member2;
+      size_t       member3;
+
+      false:
+      unsigned int member1;
+      float member2;
+      size_t member3;
+
   * ``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 3df5b92654094..b18524b7f67a2 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -226,6 +226,20 @@ struct FormatStyle {
     ///   bbb = 2;
     /// \endcode
     bool AlignCompound;
+    /// Only for ``AlignConsecutiveDeclarations``. Whether non-member variable
+    /// declarations are aligned.
+    /// \code
+    ///   true:
+    ///   unsigned int v1;
+    ///   float        v2;
+    ///   size_t       v3;
+    ///
+    ///   false:
+    ///   unsigned int v1;
+    ///   float v2;
+    ///   size_t v3;
+    /// \endcode
+    bool AlignFreeVariableDeclarations;
     /// Only for ``AlignConsecutiveDeclarations``. Whether function declarations
     /// are aligned.
     /// \code
@@ -256,6 +270,20 @@ struct FormatStyle {
     ///   int (*f)();
     /// \endcode
     bool AlignFunctionPointers;
+    /// Only for ``AlignConsecutiveDeclarations``. Whether class/struct member
+    /// variable declarations are aligned.
+    /// \code
+    ///   true:
+    ///   unsigned int member1;
+    ///   float        member2;
+    ///   size_t       member3;
+    ///
+    ///   false:
+    ///   unsigned int member1;
+    ///   float member2;
+    ///   size_t member3;
+    /// \endcode
+    bool AlignMemberVariableDeclarations;
     /// 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.
@@ -279,8 +307,11 @@ struct FormatStyle {
       return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines &&
              AcrossComments == R.AcrossComments &&
              AlignCompound == R.AlignCompound &&
+             AlignFreeVariableDeclarations == R.AlignFreeVariableDeclarations &&
              AlignFunctionDeclarations == R.AlignFunctionDeclarations &&
              AlignFunctionPointers == R.AlignFunctionPointers &&
+             AlignMemberVariableDeclarations ==
+                 R.AlignMemberVariableDeclarations &&
              PadOperators == R.PadOperators;
     }
     bool operator!=(const AlignConsecutiveStyle &R) const {
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index b38f2810c0a74..c374445939efe 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -47,38 +47,53 @@ struct ScalarEnumerationTraits<FormatStyle::BreakBeforeNoexceptSpecifierStyle> {
 template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
   static void enumInput(IO &IO, FormatStyle::AlignConsecutiveStyle &Value) {
     IO.enumCase(Value, "None", FormatStyle::AlignConsecutiveStyle({}));
-    IO.enumCase(Value, "Consecutive",
-                FormatStyle::AlignConsecutiveStyle(
-                    {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
-                     /*AcrossComments=*/false, /*AlignCompound=*/false,
-                     /*AlignFunctionDeclarations=*/true,
-                     /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
-    IO.enumCase(Value, "AcrossEmptyLines",
-                FormatStyle::AlignConsecutiveStyle(
-                    {/*Enabled=*/true, /*AcrossEmptyLines=*/true,
-                     /*AcrossComments=*/false, /*AlignCompound=*/false,
-                     /*AlignFunctionDeclarations=*/true,
-                     /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
-    IO.enumCase(Value, "AcrossComments",
-                FormatStyle::AlignConsecutiveStyle(
-                    {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
-                     /*AcrossComments=*/true, /*AlignCompound=*/false,
-                     /*AlignFunctionDeclarations=*/true,
-                     /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
-    IO.enumCase(Value, "AcrossEmptyLinesAndComments",
-                FormatStyle::AlignConsecutiveStyle(
-                    {/*Enabled=*/true, /*AcrossEmptyLines=*/true,
-                     /*AcrossComments=*/true, /*AlignCompound=*/false,
-                     /*AlignFunctionDeclarations=*/true,
-                     /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+    IO.enumCase(
+        Value, "Consecutive",
+        FormatStyle::AlignConsecutiveStyle(
+            {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
+             /*AcrossComments=*/false, /*AlignCompound=*/false,
+             /*AlignFreeVariableDeclarations=*/true,
+             /*AlignFunctionDeclarations=*/true,
+             /*AlignFunctionPointers=*/false,
+             /*AlignMemberVariableDeclarations=*/true, /*PadOperators=*/true}));
+    IO.enumCase(
+        Value, "AcrossEmptyLines",
+        FormatStyle::AlignConsecutiveStyle(
+            {/*Enabled=*/true, /*AcrossEmptyLines=*/true,
+             /*AcrossComments=*/false, /*AlignCompound=*/false,
+             /*AlignFreeVariableDeclarations=*/true,
+             /*AlignFunctionDeclarations=*/true,
+             /*AlignFunctionPointers=*/false,
+             /*AlignMemberVariableDeclarations=*/true, /*PadOperators=*/true}));
+    IO.enumCase(
+        Value, "AcrossComments",
+        FormatStyle::AlignConsecutiveStyle(
+            {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
+             /*AcrossComments=*/true, /*AlignCompound=*/false,
+             /*AlignFreeVariableDeclarations=*/true,
+             /*AlignFunctionDeclarations=*/true,
+             /*AlignFunctionPointers=*/false,
+             /*AlignMemberVariableDeclarations=*/true, /*PadOperators=*/true}));
+    IO.enumCase(
+        Value, "AcrossEmptyLinesAndComments",
+        FormatStyle::AlignConsecutiveStyle(
+            {/*Enabled=*/true, /*AcrossEmptyLines=*/true,
+             /*AcrossComments=*/true, /*AlignCompound=*/false,
+             /*AlignFreeVariableDeclarations=*/true,
+             /*AlignFunctionDeclarations=*/true,
+             /*AlignFunctionPointers=*/false,
+             /*AlignMemberVariableDeclarations=*/true, /*PadOperators=*/true}));
 
     // For backward compatibility.
-    IO.enumCase(Value, "true",
-                FormatStyle::AlignConsecutiveStyle(
-                    {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
-                     /*AcrossComments=*/false, /*AlignCompound=*/false,
-                     /*AlignFunctionDeclarations=*/true,
-                     /*AlignFunctionPointers=*/false, /*PadOperators=*/true}));
+    IO.enumCase(
+        Value, "true",
+        FormatStyle::AlignConsecutiveStyle(
+            {/*Enabled=*/true, /*AcrossEmptyLines=*/false,
+             /*AcrossComments=*/false, /*AlignCompound=*/false,
+             /*AlignFreeVariableDeclarations=*/true,
+             /*AlignFunctionDeclarations=*/true,
+             /*AlignFunctionPointers=*/false,
+             /*AlignMemberVariableDeclarations=*/true, /*PadOperators=*/true}));
     IO.enumCase(Value, "false", FormatStyle::AlignConsecutiveStyle({}));
   }
 
@@ -87,9 +102,13 @@ template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
     IO.mapOptional("AcrossEmptyLines", Value.AcrossEmptyLines);
     IO.mapOptional("AcrossComments", Value.AcrossComments);
     IO.mapOptional("AlignCompound", Value.AlignCompound);
+    IO.mapOptional("AlignFreeVariableDeclarations",
+                   Value.AlignFreeVariableDeclarations);
     IO.mapOptional("AlignFunctionDeclarations",
                    Value.AlignFunctionDeclarations);
     IO.mapOptional("AlignFunctionPointers", Value.AlignFunctionPointers);
+    IO.mapOptional("AlignMemberVariableDeclarations",
+                   Value.AlignMemberVariableDeclarations);
     IO.mapOptional("PadOperators", Value.PadOperators);
   }
 };
@@ -1555,7 +1574,9 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.AlignConsecutiveAssignments.PadOperators = true;
   LLVMStyle.AlignConsecutiveBitFields = {};
   LLVMStyle.AlignConsecutiveDeclarations = {};
+  LLVMStyle.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations = true;
   LLVMStyle.AlignConsecutiveDeclarations.AlignFunctionDeclarations = true;
+  LLVMStyle.AlignConsecutiveDeclarations.AlignMemberVariableDeclarations = true;
   LLVMStyle.AlignConsecutiveMacros = {};
   LLVMStyle.AlignConsecutiveShortCaseStatements = {};
   LLVMStyle.AlignConsecutiveTableGenBreakingDAGArgColons = {};
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index e04b0e7af10c0..3a94de29461d0 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -313,7 +313,7 @@ struct FormatToken {
         EndsBinaryExpression(false), PartOfMultiVariableDeclStmt(false),
         ContinuesLineCommentSection(false), Finalized(false),
         ClosesRequiresClause(false), EndsCppAttributeGroup(false),
-        BlockKind(BK_Unknown), Decision(FD_Unformatted),
+        IsInClassScope(false), BlockKind(BK_Unknown), Decision(FD_Unformatted),
         PackingKind(PPK_Inconclusive), TypeIsFinalized(false),
         Type(TT_Unknown) {}
 
@@ -391,6 +391,10 @@ struct FormatToken {
   /// \c true if this token ends a group of C++ attributes.
   unsigned EndsCppAttributeGroup : 1;
 
+  /// \c true if this token is within a class-like scope and not within a
+  /// function inside that scope.
+  unsigned IsInClassScope : 1;
+
 private:
   /// Contains the kind of block if this token is a brace.
   unsigned BlockKind : 2;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 4bfb803ebedf7..da7f50d5f7c2f 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1313,6 +1313,8 @@ class AnnotatingParser {
     }
     FormatToken *Tok = CurrentToken;
     next();
+    if (!Scopes.empty() && Scopes.back() == ST_Class)
+      Tok->IsInClassScope = true;
     // In Verilog primitives' state tables, `:`, `?`, and `-` aren't normal
     // operators.
     if (Tok->is(TT_VerilogTableItem))
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index cc3cc0f6906cc..2c4ad3d1dfbbc 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -1016,6 +1016,12 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
           return Style.AlignConsecutiveDeclarations.AlignFunctionPointers;
         if (C.Tok->is(TT_FunctionDeclarationName))
           return Style.AlignConsecutiveDeclarations.AlignFunctionDeclarations;
+        if (C.Tok->IsInClassScope && !Style.AlignConsecutiveDeclarations
+                                          .AlignMemberVariableDeclarations)
+          return false;
+        if (!C.Tok->IsInClassScope &&
+            !Style.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations)
+          return false;
         if (C.Tok->isNot(TT_StartOfName))
           return false;
         if (C.Tok->Previous &&
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index bb4d38bb741ec..1c63c3c580179 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -327,51 +327,63 @@ TEST(ConfigParseTest, ParsesConfiguration) {
     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,        \
+                     /*AlignFreeVariableDeclarations=*/true,                   \
+                     /*AlignFunctionDeclarations=*/true,                       \
+                     /*AlignFunctionPointers=*/false,                          \
+                     /*AlgnMemberVariableDeclarations=*/true,                  \
+                     /*PadOperators=*/true}));                                 \
+    CHECK_PARSE(#FIELD ": AcrossEmptyLines", FIELD,                            \
+                FormatStyle::AlignConsecutiveStyle(                            \
+                    {/*Enabled=*/true, /*AcrossEmptyLines=*/true,              \
+                     /*AcrossComments=*/false, /*AlignCompound=*/false,        \
+                     /*AlignFreeVariableDeclarations=*/true,                   \
+                     /*AlignFunctionDeclarations=*/true,                       \
+                     /*AlignFunctionPointers=*/false,                          \
+                     /*AlgnMemberVariableDeclarations=*/true,                  \
+                     /*PadOperators=*/true}));                                 \
+    CHECK_PARSE(#FIELD ": AcrossComments", FIELD,                              \
+                FormatStyle::AlignConsecutiveStyle(                            \
+                    {/*Enabled=*/true, /*AcrossEmptyLines=*/false,             \
+                     /*AcrossComments=*/true, /*AlignCompound=*/false,         \
+                     /*AlignFreeVariableDeclarations=*/true,                   \
+                     /*AlignFunctionDeclarations=*/true,                       \
+                     /*AlignFunctionPointers=*/false,                          \
+                     /*AlgnMemberVariableDeclarations=*/true,                  \
+                     /*PadOperators=*/true}));                                 \
+    CHECK_PARSE(#FIELD ": AcrossEmptyLinesAndComments", FIELD,                 \
+                FormatStyle::AlignConsecutiveStyle(                            \
+                    {/*Enabled=*/true, /*AcrossEmptyLines=*/true,              \
+                     /*AcrossComments=*/true, /*AlignCompound=*/false,         \
+                     /*AlignFreeVariableDeclarations=*/true,                   \
+                     /*AlignFunctionDeclarations=*/true,                       \
+                     /*AlignFunctionPointers=*/false,                          \
+                     /*AlgnMemberVariableDeclarations=*/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,        \
+                     /*AlignFreeVariableDeclarations=*/true,                   \
+                     /*AlignFunctionDeclarations=*/true,                       \
+                     /*AlignFunctionPointers=*/false,                          \
+                     /*AlgnMemberVariableDeclarations=*/true,                  \
+                     /*PadOperators=*/true}));                                 \
                                                                                \
     CHECK_PARSE_NESTED_BOOL(FIELD, Enabled);                                   \
     CHECK_PARSE_NESTED_BOOL(FIELD, AcrossEmptyLines);                          \
     CHECK_PARSE_NESTED_BOOL(FIELD, AcrossComments);                            \
     CHECK_PARSE_NESTED_BOOL(FIELD, AlignCompound);                             \
+    CHECK_PARSE_NESTED_BOOL(FIELD, AlignFreeVariableDeclarations);             \
     CHECK_PARSE_NESTED_BOOL(FIELD, AlignFunctionDeclarations);                 \
     CHECK_PARSE_NESTED_BOOL(FIELD, AlignFunctionPointers);                     \
+    CHECK_PARSE_NESTED_BOOL(FIELD, AlignMemberVariableDeclarations);           \
     CHECK_PARSE_NESTED_BOOL(FIELD, PadOperators);                              \
   } while (false)
 
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 7d550143be5df..1b4fa57bd0dfb 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -20323,6 +20323,61 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) {
                "void f2(void);\n"
                "size_t f3(void);",
                Alignment);
+
+  Alignment.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations = false;
+  verifyFormat("int func() { //\n"
+               "  int local1;\n"
+               "  unsigned local2;\n"
+               "}\n"
+               "class Z {\n"
+               "  int      member1;\n"
+               "  unsigned member2;\n"
+               "  int funcInClass() {\n"
+               "    int localInClass1;\n"
+               "    unsigned localInClass2;\n"
+               "  }\n"
+               "};\n"
+               "int global1;\n"
+               "unsigned global2;",
+               Alignment);
+
+  Alignment.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations = true;
+  Alignment.AlignConsecutiveDeclarations.AlignMemberVariableDeclarations =
+      false;
+  verifyFormat("int func() { //\n"
+               "  int      local1;\n"
+               "  unsigned local2;\n"
+               "}\n"
+               "class Z {\n"
+               "  int member1;\n"
+               "  unsigned member2;\n"
+               "  int funcInClass() {\n"
+               "    int      localInClass1;\n"
+               "    unsigned localInClass2;\n"
+               "  }\n"
+               "};\n"
+               "int      global1;\n"
+               "unsigned global2;",
+               Alignment);
+
+  Alignment.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations = false;
+  Alignment.AlignConsecutiveDeclarations.AlignMemberVariableDeclarations =
+      false;
+  verifyFormat("int func() { //\n"
+               "  int local1;\n"
+               "  unsigned local2;\n"
+               "}\n"
+               "class Z {\n"
+               "  int member1;\n"
+               "  unsigned member2;\n"
+               "  int funcInClass() {\n"
+               "    int localInClass1;\n"
+               "    unsigned localInClass2;\n"
+               "  }\n"
+               "};\n"
+               "int global1;\n"
+               "unsigned global2;",
+               Alignment);
 }
 
 TEST_F(FormatTest, AlignConsecutiveShortCaseStatements) {
@@ -20566,15 +20621,19 @@ TEST_F(FormatTest, AlignWithLineBreaks) {
             FormatStyle::AlignConsecutiveStyle(
                 {/*Enabled=*/false, /*AcrossEmptyLines=*/false,
                  /*AcrossComments=*/false, /*AlignCompound=*/false,
+                 /*AlignFreeVariableDeclarations=*/false,
                  /*AlignFunctionDeclarations=*/false,
                  /*AlignFunctionPointers=*/false,
+                 /*AlignMemberVariableDeclarations=*/false,
                  /*PadOperators=*/true}));
   EXPECT_EQ(Style.AlignConsecutiveDeclarations,
             FormatStyle::AlignConsecutiveStyle(
                 {/*Enabled=*/false, /*AcrossEmptyLines=*/false,
                  /*AcrossComments=*/false, /*AlignCompound=*/false,
+                 /*AlignFreeVariableDeclarations=*/true,
                  /*AlignFunctionDeclarations=*/true,
                  /*AlignFunctionPointers=*/false,
+                 /*AlignMemberVariableDeclarations=*/true,
                  /*PadOperators=*/false}));
   verifyFormat("void foo() {\n"
                "  int myVar = 5;\n"

>From e1a1a400ccb53f5493d5079b1cb5f0beba07076c Mon Sep 17 00:00:00 2001
From: James Henderson <james.henderson at sony.com>
Date: Tue, 23 Sep 2025 11:53:46 +0100
Subject: [PATCH 2/3] Format

---
 clang/lib/Format/WhitespaceManager.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index 2c4ad3d1dfbbc..e9b79ae0854b0 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -1017,11 +1017,13 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
         if (C.Tok->is(TT_FunctionDeclarationName))
           return Style.AlignConsecutiveDeclarations.AlignFunctionDeclarations;
         if (C.Tok->IsInClassScope && !Style.AlignConsecutiveDeclarations
-                                          .AlignMemberVariableDeclarations)
+                                          .AlignMemberVariableDeclarations) {
           return false;
+        }
         if (!C.Tok->IsInClassScope &&
-            !Style.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations)
+            !Style.AlignConsecutiveDeclarations.AlignFreeVariableDeclarations) {
           return false;
+        }
         if (C.Tok->isNot(TT_StartOfName))
           return false;
         if (C.Tok->Previous &&

>From c665910cd40c3e2af4957b2e92554305a8abbb4f Mon Sep 17 00:00:00 2001
From: James Henderson <james.henderson at sony.com>
Date: Tue, 23 Sep 2025 14:37:59 +0100
Subject: [PATCH 3/3] Add release note

---
 clang/docs/ReleaseNotes.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fb429a675476d..79371fc773ec9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -532,6 +532,8 @@ clang-format
   literals.
 - Add ``Leave`` suboption to ``IndentPPDirectives``.
 - Add ``AllowBreakBeforeQtProperty`` option.
+- Add ``AlignFreeVariableDeclarations`` and ``AlignMemberVariableDeclarations``
+  to ``AlignConsecutiveDeclarations``.
 
 libclang
 --------



More information about the cfe-commits mailing list