diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3375,6 +3375,10 @@ Decimal: 3 Hex: -1 + 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. + * ``int8_t Binary`` Format separators in binary literals. .. code-block:: text @@ -3384,6 +3388,15 @@ /* 3: */ b = 0b100'111'101'101; /* 4: */ b = 0b1001'1110'1101; + * ``int8_t BinaryMinDigits`` Format separators in binary literals with a minimum number of digits. + + .. code-block:: text + + // Binary: 3 + // BinaryMinDigits: 7 + b1 = 0b101101; + b2 = 0b1'101'101; + * ``int8_t Decimal`` Format separators in decimal literals. .. code-block:: text @@ -3392,6 +3405,15 @@ /* 0: */ d = 184467'440737'0'95505'92ull; /* 3: */ d = 18'446'744'073'709'550'592ull; + * ``int8_t DecimalMinDigits`` Format separators in decimal literals with a minimum number of digits. + + .. code-block:: text + + // Decimal: 3 + // DecimalMinDigits: 5 + d1 = 2023; + d2 = 10'000; + * ``int8_t Hex`` Format separators in hexadecimal literals. .. code-block:: text @@ -3400,6 +3422,16 @@ /* 0: */ h = 0xDEAD'BEEF'DE'AD'BEE'Fuz; /* 2: */ h = 0xDE'AD'BE'EF'DE'AD'BE'EFuz; + * ``int8_t HexMinDigits`` Format separators in hexadecimal literals with a minimum number of + digits. + + .. code-block:: text + + // Hex: 2 + // HexMinDigits: 6 + h1 = 0xABCDE; + h2 = 0xAB'CD'EF; + .. _JavaImportGroups: diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2500,6 +2500,10 @@ /// Decimal: 3 /// Hex: -1 /// \endcode + /// + /// 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. struct IntegerLiteralSeparatorStyle { /// Format separators in binary literals. /// \code{.text} @@ -2509,6 +2513,14 @@ /// /* 4: */ b = 0b1001'1110'1101; /// \endcode int8_t Binary; + /// Format separators in binary literals with a minimum number of digits. + /// \code{.text} + /// // Binary: 3 + /// // BinaryMinDigits: 7 + /// b1 = 0b101101; + /// b2 = 0b1'101'101; + /// \endcode + int8_t BinaryMinDigits; /// Format separators in decimal literals. /// \code{.text} /// /* -1: */ d = 18446744073709550592ull; @@ -2516,6 +2528,14 @@ /// /* 3: */ d = 18'446'744'073'709'550'592ull; /// \endcode int8_t Decimal; + /// Format separators in decimal literals with a minimum number of digits. + /// \code{.text} + /// // Decimal: 3 + /// // DecimalMinDigits: 5 + /// d1 = 2023; + /// d2 = 10'000; + /// \endcode + int8_t DecimalMinDigits; /// Format separators in hexadecimal literals. /// \code{.text} /// /* -1: */ h = 0xDEADBEEFDEADBEEFuz; @@ -2523,6 +2543,20 @@ /// /* 2: */ h = 0xDE'AD'BE'EF'DE'AD'BE'EFuz; /// \endcode int8_t Hex; + /// Format separators in hexadecimal literals with a minimum number of + /// digits. + /// \code{.text} + /// // Hex: 2 + /// // HexMinDigits: 6 + /// h1 = 0xABCDE; + /// h2 = 0xAB'CD'EF; + /// \endcode + int8_t HexMinDigits; + bool operator==(const IntegerLiteralSeparatorStyle &R) const { + return Binary == R.Binary && BinaryMinDigits == R.BinaryMinDigits && + Decimal == R.Decimal && DecimalMinDigits == R.DecimalMinDigits && + Hex == R.Hex && HexMinDigits == R.HexMinDigits; + } }; /// Format integer literal separators (``'`` for C++ and ``_`` for C#, Java, @@ -4277,10 +4311,7 @@ IndentWrappedFunctionNames == R.IndentWrappedFunctionNames && InsertBraces == R.InsertBraces && InsertNewlineAtEOF == R.InsertNewlineAtEOF && - IntegerLiteralSeparator.Binary == R.IntegerLiteralSeparator.Binary && - IntegerLiteralSeparator.Decimal == - R.IntegerLiteralSeparator.Decimal && - IntegerLiteralSeparator.Hex == R.IntegerLiteralSeparator.Hex && + IntegerLiteralSeparator == R.IntegerLiteralSeparator && JavaImportGroups == R.JavaImportGroups && JavaScriptQuotes == R.JavaScriptQuotes && JavaScriptWrapImports == R.JavaScriptWrapImports && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -348,8 +348,11 @@ template <> struct MappingTraits { static void mapping(IO &IO, FormatStyle::IntegerLiteralSeparatorStyle &Base) { IO.mapOptional("Binary", Base.Binary); + IO.mapOptional("BinaryMinDigits", Base.BinaryMinDigits); IO.mapOptional("Decimal", Base.Decimal); + IO.mapOptional("DecimalMinDigits", Base.DecimalMinDigits); IO.mapOptional("Hex", Base.Hex); + IO.mapOptional("HexMinDigits", Base.HexMinDigits); } }; @@ -1395,7 +1398,10 @@ LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; - LLVMStyle.IntegerLiteralSeparator = {/*Binary=*/0, /*Decimal=*/0, /*Hex=*/0}; + LLVMStyle.IntegerLiteralSeparator = { + /*Binary=*/0, /*BinaryMinDigits=*/0, + /*Decimal=*/0, /*DecimalMinDigits=*/0, + /*Hex=*/0, /*HexMinDigits=*/0}; LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; LLVMStyle.JavaScriptWrapImports = true; LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.h b/clang/lib/Format/IntegerLiteralSeparatorFixer.h --- a/clang/lib/Format/IntegerLiteralSeparatorFixer.h +++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.h @@ -27,7 +27,8 @@ private: bool checkSeparator(const StringRef IntegerLiteral, int DigitsPerGroup) const; - std::string format(const StringRef IntegerLiteral, int DigitsPerGroup) const; + std::string format(const StringRef IntegerLiteral, int DigitsPerGroup, + int DigitCount, bool RemoveSeparator) const; char Separator; }; diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp --- a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp +++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp @@ -69,6 +69,12 @@ 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); + const auto &SourceMgr = Env.getSourceManager(); AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges()); @@ -116,11 +122,6 @@ (IsBase16 && Text.find_last_of(".pP") != StringRef::npos)) { continue; } - if (((IsBase2 && Binary < 0) || (IsBase10 && Decimal < 0) || - (IsBase16 && Hex < 0)) && - Text.find(Separator) == StringRef::npos) { - continue; - } const auto Start = Text[0] == '0' ? 2 : 0; auto End = Text.find_first_of("uUlLzZn", Start); if (End == StringRef::npos) @@ -130,13 +131,25 @@ Text = Text.substr(Start, Length); } auto DigitsPerGroup = Decimal; - if (IsBase2) + auto MinDigits = DecimalMinDigits; + if (IsBase2) { DigitsPerGroup = Binary; - else if (IsBase16) + MinDigits = BinaryMinDigits; + } else if (IsBase16) { DigitsPerGroup = Hex; - if (DigitsPerGroup > 0 && checkSeparator(Text, DigitsPerGroup)) + MinDigits = HexMinDigits; + } + const auto SeparatorCount = Text.count(Separator); + const int DigitCount = Length - SeparatorCount; + const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits; + if (RemoveSeparator && SeparatorCount == 0) continue; - const auto &Formatted = format(Text, DigitsPerGroup); + if (!RemoveSeparator && SeparatorCount > 0 && + checkSeparator(Text, DigitsPerGroup)) { + continue; + } + const auto &Formatted = + format(Text, DigitsPerGroup, DigitCount, RemoveSeparator); assert(Formatted != Text); if (Start > 0) Location = Location.getLocWithOffset(Start); @@ -168,23 +181,20 @@ } std::string IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral, - int DigitsPerGroup) const { + int DigitsPerGroup, + int DigitCount, + bool RemoveSeparator) const { assert(DigitsPerGroup != 0); std::string Formatted; - if (DigitsPerGroup < 0) { + if (RemoveSeparator) { for (auto C : IntegerLiteral) if (C != Separator) Formatted.push_back(C); return Formatted; } - int DigitCount = 0; - for (auto C : IntegerLiteral) - if (C != Separator) - ++DigitCount; - int Remainder = DigitCount % DigitsPerGroup; int I = 0; diff --git a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp --- a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp +++ b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp @@ -116,6 +116,41 @@ verifyFormat("o = 0o400000000000000003n;", Style); } +TEST_F(IntegerLiteralSeparatorTest, MinDigits) { + FormatStyle Style = getLLVMStyle(); + Style.IntegerLiteralSeparator.Binary = 3; + Style.IntegerLiteralSeparator.Decimal = 3; + Style.IntegerLiteralSeparator.Hex = 2; + + Style.IntegerLiteralSeparator.BinaryMinDigits = 7; + verifyFormat("b1 = 0b101101;\n" + "b2 = 0b1'101'101;", + "b1 = 0b101'101;\n" + "b2 = 0b1101101;", + Style); + + Style.IntegerLiteralSeparator.DecimalMinDigits = 5; + verifyFormat("d1 = 2023;\n" + "d2 = 10'000;", + "d1 = 2'023;\n" + "d2 = 100'00;", + Style); + + Style.IntegerLiteralSeparator.DecimalMinDigits = 3; + verifyFormat("d1 = 123;\n" + "d2 = 1'234;", + "d1 = 12'3;\n" + "d2 = 12'34;", + Style); + + Style.IntegerLiteralSeparator.HexMinDigits = 6; + verifyFormat("h1 = 0xABCDE;\n" + "h2 = 0xAB'CD'EF;", + "h1 = 0xA'BC'DE;\n" + "h2 = 0xABC'DEF;", + Style); +} + TEST_F(IntegerLiteralSeparatorTest, FixRanges) { FormatStyle Style = getLLVMStyle(); Style.IntegerLiteralSeparator.Decimal = 3;