[clang] [clang-format] Add xxxMaxDigitsNoSeparator (PR #164286)
Björn Schäpers via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 28 14:34:35 PDT 2025
https://github.com/HazardyKnusperkeks updated https://github.com/llvm/llvm-project/pull/164286
>From f4be7c77489ddb427a019eefdae8817501132ef5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= <bjoern at hazardy.de>
Date: Mon, 20 Oct 2025 01:31:04 +0200
Subject: [PATCH 1/3] [clang-format] Add xxxMaxDigitsNoSeparator
This basically adds a Leave option for a specific range of literals.
---
clang/docs/ClangFormatStyleOptions.rst | 44 +++++++++++++++++-
clang/include/clang/Format/Format.h | 46 ++++++++++++++++++-
clang/lib/Format/Format.cpp | 18 ++++++--
.../Format/IntegerLiteralSeparatorFixer.cpp | 38 ++++++++++++---
.../Format/IntegerLiteralSeparatorTest.cpp | 16 +++++++
5 files changed, 148 insertions(+), 14 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 570cab262c115..25160b137535e 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -4651,7 +4651,12 @@ the configuration (without a prefix: ``Auto``).
You can also specify a minimum number of digits (``BinaryMinDigits``,
``DecimalMinDigits``, and ``HexMinDigits``) the integer literal must
- have in order for the separators to be inserted.
+ have in order for the separators to be inserted, and a maximum number of
+ digits (``BinaryMaxDigitsNoSeparator``, ``DecimalMaxDigitsNoSeparator``,
+ and ``HexMaxDigitsNoSeparator``) until the separators are removed. This
+ divides the literals in 3 regions, always without separator (up until
+ including ``xxxMaxDigitsNoSeparator``), maybe with, or without separators
+ (up until excluding ``xxxMinDigits``), and finally always with separators.
* ``int8_t Binary`` Format separators in binary literals.
@@ -4671,6 +4676,18 @@ the configuration (without a prefix: ``Auto``).
b1 = 0b101101;
b2 = 0b1'101'101;
+ * ``int8_t BinaryMaxDigitsNoSeparator`` Remove separators in binary literals with a maximum number of digits.
+
+ .. code-block:: text
+
+ // Binary: 3
+ // BinaryMinDigits: 7
+ // BinaryMaxDigitsNoSeparator: 4
+ b0 = 0b1011; // Always removed.
+ b1 = 0b101101; // Not added.
+ b2 = 0b101'101; // Not removed.
+ b3 = 0b1'101'101; // Always added.
+
* ``int8_t Decimal`` Format separators in decimal literals.
.. code-block:: text
@@ -4688,6 +4705,18 @@ the configuration (without a prefix: ``Auto``).
d1 = 2023;
d2 = 10'000;
+ * ``int8_t DecimalMaxDigitsNoSeparator`` Remove separators in decimal literals with a maximum number of digits.
+
+ .. code-block:: text
+
+ // Decimal: 3
+ // DecimalMinDigits: 7
+ // DecimalMaxDigitsNoSeparator: 4
+ d0 = 2023; // Always removed.
+ d1 = 123456; // Not added.
+ d2 = 123'456; // Not removed.
+ d3 = 5'000'000; // Always added.
+
* ``int8_t Hex`` Format separators in hexadecimal literals.
.. code-block:: text
@@ -4706,6 +4735,19 @@ the configuration (without a prefix: ``Auto``).
h1 = 0xABCDE;
h2 = 0xAB'CD'EF;
+ * ``int8_t HexMaxDigitsNoSeparator`` Remove separators in hexadecimal literals with a maximum number of
+ digits.
+
+ .. code-block:: text
+
+ // Hex: 2
+ // HexMinDigits: 6
+ // HexMaxDigitsNoSeparator: 4
+ h0 = 0xAFFE; // Always removed.
+ h1 = 0xABCDE; // Not added.
+ h2 = 0xA'BC'DE; // Not removed.
+ h3 = 0xAB'CD'EF; // Always added.
+
.. _JavaImportGroups:
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 2852c4a2916a4..4e1974b8f9460 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3174,7 +3174,12 @@ struct FormatStyle {
///
/// You can also specify a minimum number of digits (``BinaryMinDigits``,
/// ``DecimalMinDigits``, and ``HexMinDigits``) the integer literal must
- /// have in order for the separators to be inserted.
+ /// have in order for the separators to be inserted, and a maximum number of
+ /// digits (``BinaryMaxDigitsNoSeparator``, ``DecimalMaxDigitsNoSeparator``,
+ /// and ``HexMaxDigitsNoSeparator``) until the separators are removed. This
+ /// divides the literals in 3 regions, always without separator (up until
+ /// including ``xxxMaxDigitsNoSeparator``), maybe with, or without separators
+ /// (up until excluding ``xxxMinDigits``), and finally always with separators.
struct IntegerLiteralSeparatorStyle {
/// Format separators in binary literals.
/// \code{.text}
@@ -3192,6 +3197,17 @@ struct FormatStyle {
/// b2 = 0b1'101'101;
/// \endcode
int8_t BinaryMinDigits;
+ /// Remove separators in binary literals with a maximum number of digits.
+ /// \code{.text}
+ /// // Binary: 3
+ /// // BinaryMinDigits: 7
+ /// // BinaryMaxDigitsNoSeparator: 4
+ /// b0 = 0b1011; // Always removed.
+ /// b1 = 0b101101; // Not added.
+ /// b2 = 0b101'101; // Not removed.
+ /// b3 = 0b1'101'101; // Always added.
+ /// \endcode
+ int8_t BinaryMaxDigitsNoSeparator;
/// Format separators in decimal literals.
/// \code{.text}
/// /* -1: */ d = 18446744073709550592ull;
@@ -3207,6 +3223,17 @@ struct FormatStyle {
/// d2 = 10'000;
/// \endcode
int8_t DecimalMinDigits;
+ /// Remove separators in decimal literals with a maximum number of digits.
+ /// \code{.text}
+ /// // Decimal: 3
+ /// // DecimalMinDigits: 7
+ /// // DecimalMaxDigitsNoSeparator: 4
+ /// d0 = 2023; // Always removed.
+ /// d1 = 123456; // Not added.
+ /// d2 = 123'456; // Not removed.
+ /// d3 = 5'000'000; // Always added.
+ /// \endcode
+ int8_t DecimalMaxDigitsNoSeparator;
/// Format separators in hexadecimal literals.
/// \code{.text}
/// /* -1: */ h = 0xDEADBEEFDEADBEEFuz;
@@ -3223,10 +3250,25 @@ struct FormatStyle {
/// h2 = 0xAB'CD'EF;
/// \endcode
int8_t HexMinDigits;
+ /// Remove separators in hexadecimal literals with a maximum number of
+ /// digits.
+ /// \code{.text}
+ /// // Hex: 2
+ /// // HexMinDigits: 6
+ /// // HexMaxDigitsNoSeparator: 4
+ /// h0 = 0xAFFE; // Always removed.
+ /// h1 = 0xABCDE; // Not added.
+ /// h2 = 0xA'BC'DE; // Not removed.
+ /// h3 = 0xAB'CD'EF; // Always added.
+ /// \endcode
+ int8_t HexMaxDigitsNoSeparator;
bool operator==(const IntegerLiteralSeparatorStyle &R) const {
return Binary == R.Binary && BinaryMinDigits == R.BinaryMinDigits &&
+ BinaryMaxDigitsNoSeparator == R.BinaryMaxDigitsNoSeparator &&
Decimal == R.Decimal && DecimalMinDigits == R.DecimalMinDigits &&
- Hex == R.Hex && HexMinDigits == R.HexMinDigits;
+ DecimalMaxDigitsNoSeparator == R.DecimalMaxDigitsNoSeparator &&
+ Hex == R.Hex && HexMinDigits == R.HexMinDigits &&
+ HexMaxDigitsNoSeparator == R.HexMaxDigitsNoSeparator;
}
};
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index edd126c7724b8..59495467c01b4 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -399,10 +399,15 @@ template <> struct MappingTraits<FormatStyle::IntegerLiteralSeparatorStyle> {
static void mapping(IO &IO, FormatStyle::IntegerLiteralSeparatorStyle &Base) {
IO.mapOptional("Binary", Base.Binary);
IO.mapOptional("BinaryMinDigits", Base.BinaryMinDigits);
+ IO.mapOptional("BinaryMaxDigitsNoSeparator",
+ Base.BinaryMaxDigitsNoSeparator);
IO.mapOptional("Decimal", Base.Decimal);
IO.mapOptional("DecimalMinDigits", Base.DecimalMinDigits);
+ IO.mapOptional("DecimalMaxDigitsNoSeparator",
+ Base.DecimalMaxDigitsNoSeparator);
IO.mapOptional("Hex", Base.Hex);
IO.mapOptional("HexMinDigits", Base.HexMinDigits);
+ IO.mapOptional("HexMaxDigitsNoSeparator", Base.HexMaxDigitsNoSeparator);
}
};
@@ -1673,10 +1678,15 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.InsertBraces = false;
LLVMStyle.InsertNewlineAtEOF = false;
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;
- LLVMStyle.IntegerLiteralSeparator = {
- /*Binary=*/0, /*BinaryMinDigits=*/0,
- /*Decimal=*/0, /*DecimalMinDigits=*/0,
- /*Hex=*/0, /*HexMinDigits=*/0};
+ LLVMStyle.IntegerLiteralSeparator = {/*Binary=*/0,
+ /*BinaryMinDigits=*/0,
+ /*BinaryMaxDigitsNoSeparator=*/-1,
+ /*Decimal=*/0,
+ /*DecimalMinDigits=*/0,
+ /*DecimalMaxDigitsNoSeparator=*/-1,
+ /*Hex=*/0,
+ /*HexMinDigits=*/0,
+ /*HexMaxDigitsNoSeparator=*/-1};
LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
LLVMStyle.JavaScriptWrapImports = true;
LLVMStyle.KeepEmptyLines = {
diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
index b51991bfeff4b..ae3b886d7e491 100644
--- a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
+++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
@@ -72,11 +72,25 @@ IntegerLiteralSeparatorFixer::process(const Environment &Env,
if (SkipBinary && SkipDecimal && SkipHex)
return {};
- const auto BinaryMinDigits =
- std::max((int)Option.BinaryMinDigits, Binary + 1);
- const auto DecimalMinDigits =
- std::max((int)Option.DecimalMinDigits, Decimal + 1);
- const auto HexMinDigits = std::max((int)Option.HexMinDigits, Hex + 1);
+ auto CalcMinAndMax = [](int8_t Digits, int8_t MinDigits,
+ int8_t MaxDigitsNoSeparator) {
+ std::pair<int, int> Ret;
+ Ret.first = std::max<int>(MinDigits, Digits + 1);
+ if (Ret.first == 0)
+ Ret.second = 0;
+ else if (MaxDigitsNoSeparator < 0)
+ Ret.second = Ret.first - 1;
+ else
+ Ret.second = std::min<int>(MaxDigitsNoSeparator, Ret.first - 1);
+ return Ret;
+ };
+
+ const auto [BinaryMinDigits, BinaryMaxDigitsNoSeparator] = CalcMinAndMax(
+ Binary, Option.BinaryMinDigits, Option.BinaryMaxDigitsNoSeparator);
+ const auto [DecimalMinDigits, DecimalMaxDigitsNoSeparator] = CalcMinAndMax(
+ Decimal, Option.DecimalMinDigits, Option.DecimalMaxDigitsNoSeparator);
+ const auto [HexMinDigits, HexMaxDigitsNoSeparator] =
+ CalcMinAndMax(Hex, Option.HexMinDigits, Option.HexMaxDigitsNoSeparator);
const auto &SourceMgr = Env.getSourceManager();
AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());
@@ -139,19 +153,29 @@ IntegerLiteralSeparatorFixer::process(const Environment &Env,
}
auto DigitsPerGroup = Decimal;
auto MinDigits = DecimalMinDigits;
+ auto MaxDigitsNoSeparator = DecimalMaxDigitsNoSeparator;
if (IsBase2) {
DigitsPerGroup = Binary;
MinDigits = BinaryMinDigits;
+ MaxDigitsNoSeparator = BinaryMaxDigitsNoSeparator;
} else if (IsBase16) {
DigitsPerGroup = Hex;
MinDigits = HexMinDigits;
+ MaxDigitsNoSeparator = HexMaxDigitsNoSeparator;
}
const auto SeparatorCount = Text.count(Separator);
const int DigitCount = Length - SeparatorCount;
- const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits;
+ const bool RemoveSeparator =
+ DigitsPerGroup < 0 || DigitCount <= MaxDigitsNoSeparator;
+ const bool AddSeparator =
+ DigitsPerGroup > 0 &&
+ (DigitCount >= MinDigits ||
+ (DigitCount > MaxDigitsNoSeparator && SeparatorCount > 0));
+ if (!RemoveSeparator && !AddSeparator)
+ continue;
if (RemoveSeparator && SeparatorCount == 0)
continue;
- if (!RemoveSeparator && SeparatorCount > 0 &&
+ if (AddSeparator && SeparatorCount > 0 &&
checkSeparator(Text, DigitsPerGroup)) {
continue;
}
diff --git a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
index 53b6dd8efadff..d66de86be88b0 100644
--- a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
+++ b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
@@ -243,6 +243,22 @@ TEST_F(IntegerLiteralSeparatorTest, FloatingPoint) {
Style);
}
+TEST_F(IntegerLiteralSeparatorTest, MaxDigitsNoSeparator) {
+ auto Style = getLLVMStyle();
+ Style.IntegerLiteralSeparator.Decimal = 3;
+ Style.IntegerLiteralSeparator.DecimalMaxDigitsNoSeparator = 4;
+ Style.IntegerLiteralSeparator.DecimalMinDigits = 7;
+ verifyFormat("d0 = 2023;\n"
+ "d1 = 123456;\n"
+ "d2 = 123'456;\n"
+ "d3 = 5'000'000;",
+ "d0 = 20'2'3;\n"
+ "d1 = 123456;\n"
+ "d2 = 1234'56;\n"
+ "d3 = 5000000;",
+ Style);
+}
+
} // namespace
} // namespace test
} // namespace format
>From cd21d8443f59f4257098b11781ed556e7834b577 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= <bjoern at hazardy.de>
Date: Tue, 21 Oct 2025 00:00:14 +0200
Subject: [PATCH 2/3] Add config parse test
---
clang/include/clang/Format/Format.h | 3 +++
clang/unittests/Format/ConfigParseTest.cpp | 22 ++++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 4e1974b8f9460..64069dac04e2b 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3270,6 +3270,9 @@ struct FormatStyle {
Hex == R.Hex && HexMinDigits == R.HexMinDigits &&
HexMaxDigitsNoSeparator == R.HexMaxDigitsNoSeparator;
}
+ bool operator!=(const IntegerLiteralSeparatorStyle &R) const {
+ return !operator==(R);
+ }
};
/// Format integer literal separators (``'`` for C++ and ``_`` for C#, Java,
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 6488e38badee7..3906d5783bdb3 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -1150,6 +1150,28 @@ TEST(ConfigParseTest, ParsesConfiguration) {
FormatStyle::BLS_Block);
CHECK_PARSE("Cpp11BracedListStyle: true", Cpp11BracedListStyle,
FormatStyle::BLS_AlignFirstComment);
+
+ const FormatStyle::IntegerLiteralSeparatorStyle
+ ExpectedIntegerLiteralSeparatorStyle{/*Binary=*/2,
+ /*BinaryMinDigit=*/5,
+ /*BinaryMaxDigitNoSeparator=*/2,
+ /*Decimal=*/6,
+ /*DecimalMinDigit=*/6,
+ /*DecimalMaxDigitNoSeparator=*/3,
+ /*Hex=*/4,
+ /*HexMinDigit=*/2,
+ /*HexMaxDigitNoSeparator=*/1};
+ CHECK_PARSE("IntegerLiteralSeparator:\n"
+ " Binary: 2\n"
+ " BinaryMinDigits: 5\n"
+ " BinaryMaxDigitsNoSeparator: 2\n"
+ " Decimal: 6\n"
+ " DecimalMinDigits: 6\n"
+ " DecimalMaxDigitsNoSeparator: 3\n"
+ " Hex: 4\n"
+ " HexMinDigits: 2\n"
+ " HexMaxDigitsNoSeparator: 1",
+ IntegerLiteralSeparator, ExpectedIntegerLiteralSeparatorStyle);
}
TEST(ConfigParseTest, ParsesConfigurationWithLanguages) {
>From a3b63280d9e4eff4db94e7f8df045ba15b1225f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= <bjoern at hazardy.de>
Date: Tue, 28 Oct 2025 22:34:25 +0100
Subject: [PATCH 3/3] Update doc and add release note
---
clang/docs/ClangFormatStyleOptions.rst | 3 +++
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Format/Format.h | 3 +++
3 files changed, 7 insertions(+)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 25160b137535e..bae0f588ce0e8 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -4687,6 +4687,7 @@ the configuration (without a prefix: ``Auto``).
b1 = 0b101101; // Not added.
b2 = 0b101'101; // Not removed.
b3 = 0b1'101'101; // Always added.
+ b4 = 0b10'1101; // Corrected to 0b101'101.
* ``int8_t Decimal`` Format separators in decimal literals.
@@ -4716,6 +4717,7 @@ the configuration (without a prefix: ``Auto``).
d1 = 123456; // Not added.
d2 = 123'456; // Not removed.
d3 = 5'000'000; // Always added.
+ d4 = 1'23'45; // Corrected to 12'345.
* ``int8_t Hex`` Format separators in hexadecimal literals.
@@ -4747,6 +4749,7 @@ the configuration (without a prefix: ``Auto``).
h1 = 0xABCDE; // Not added.
h2 = 0xA'BC'DE; // Not removed.
h3 = 0xAB'CD'EF; // Always added.
+ h4 = 0xABCD'E; // Corrected to 0xA'BC'DE.
.. _JavaImportGroups:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fe77f917bb801..aa077f51d9ce5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -609,6 +609,7 @@ clang-format
literals.
- Add ``Leave`` suboption to ``IndentPPDirectives``.
- Add ``AllowBreakBeforeQtProperty`` option.
+- Add ``(Binary|Decimal|Hex)MaxDigitsSeparator`` suboptions to ``IntegerLiteralSeparator``.
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 64069dac04e2b..3fd4cd3ce95dd 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3206,6 +3206,7 @@ struct FormatStyle {
/// b1 = 0b101101; // Not added.
/// b2 = 0b101'101; // Not removed.
/// b3 = 0b1'101'101; // Always added.
+ /// b4 = 0b10'1101; // Corrected to 0b101'101.
/// \endcode
int8_t BinaryMaxDigitsNoSeparator;
/// Format separators in decimal literals.
@@ -3232,6 +3233,7 @@ struct FormatStyle {
/// d1 = 123456; // Not added.
/// d2 = 123'456; // Not removed.
/// d3 = 5'000'000; // Always added.
+ /// d4 = 1'23'45; // Corrected to 12'345.
/// \endcode
int8_t DecimalMaxDigitsNoSeparator;
/// Format separators in hexadecimal literals.
@@ -3260,6 +3262,7 @@ struct FormatStyle {
/// h1 = 0xABCDE; // Not added.
/// h2 = 0xA'BC'DE; // Not removed.
/// h3 = 0xAB'CD'EF; // Always added.
+ /// h4 = 0xABCD'E; // Corrected to 0xA'BC'DE.
/// \endcode
int8_t HexMaxDigitsNoSeparator;
bool operator==(const IntegerLiteralSeparatorStyle &R) const {
More information about the cfe-commits
mailing list