[clang] [clang-format] Support fine-grained alignment of variable declarations (PR #160270)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 23 03:28:32 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-format
Author: James Henderson (jh7370)
<details>
<summary>Changes</summary>
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.
---
Patch is 31.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/160270.diff
8 Files Affected:
- (modified) clang/docs/ClangFormatStyleOptions.rst (+210)
- (modified) clang/include/clang/Format/Format.h (+31)
- (modified) clang/lib/Format/Format.cpp (+51-30)
- (modified) clang/lib/Format/FormatToken.h (+5-1)
- (modified) clang/lib/Format/TokenAnnotator.cpp (+2)
- (modified) clang/lib/Format/WhitespaceManager.cpp (+6)
- (modified) clang/unittests/Format/ConfigParseTest.cpp (+47-35)
- (modified) clang/unittests/Format/FormatTest.cpp (+59)
``````````diff
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
+++ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/160270
More information about the cfe-commits
mailing list