1 //===--- IntegerLiteralSeparatorFixer.cpp -----------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// This file implements IntegerLiteralSeparatorFixer that fixes C++ integer
11 /// literal separators.
13 //===----------------------------------------------------------------------===//
15 #include "IntegerLiteralSeparatorFixer.h"
20 enum class Base
{ Binary
, Decimal
, Hex
, Other
};
22 static Base
getBase(const StringRef IntegerLiteral
) {
23 assert(IntegerLiteral
.size() > 1);
25 if (IntegerLiteral
[0] > '0') {
26 assert(IntegerLiteral
[0] <= '9');
30 assert(IntegerLiteral
[0] == '0');
32 switch (IntegerLiteral
[1]) {
44 std::pair
<tooling::Replacements
, unsigned>
45 IntegerLiteralSeparatorFixer::process(const Environment
&Env
,
46 const FormatStyle
&Style
) {
47 switch (Style
.Language
) {
48 case FormatStyle::LK_Cpp
:
49 case FormatStyle::LK_ObjC
:
52 case FormatStyle::LK_CSharp
:
53 case FormatStyle::LK_Java
:
54 case FormatStyle::LK_JavaScript
:
61 const auto &Option
= Style
.IntegerLiteralSeparator
;
62 const auto Binary
= Option
.Binary
;
63 const auto Decimal
= Option
.Decimal
;
64 const auto Hex
= Option
.Hex
;
65 const bool SkipBinary
= Binary
== 0;
66 const bool SkipDecimal
= Decimal
== 0;
67 const bool SkipHex
= Hex
== 0;
69 if (SkipBinary
&& SkipDecimal
&& SkipHex
)
72 const auto BinaryMinDigits
=
73 std::max((int)Option
.BinaryMinDigits
, Binary
+ 1);
74 const auto DecimalMinDigits
=
75 std::max((int)Option
.DecimalMinDigits
, Decimal
+ 1);
76 const auto HexMinDigits
= std::max((int)Option
.HexMinDigits
, Hex
+ 1);
78 const auto &SourceMgr
= Env
.getSourceManager();
79 AffectedRangeManager
AffectedRangeMgr(SourceMgr
, Env
.getCharRanges());
81 const auto ID
= Env
.getFileID();
82 const auto LangOpts
= getFormattingLangOpts(Style
);
83 Lexer
Lex(ID
, SourceMgr
.getBufferOrFake(ID
), SourceMgr
, LangOpts
);
84 Lex
.SetCommentRetentionState(true);
87 tooling::Replacements Result
;
89 for (bool Skip
= false; !Lex
.LexFromRawLexer(Tok
);) {
90 auto Length
= Tok
.getLength();
93 auto Location
= Tok
.getLocation();
94 auto Text
= StringRef(SourceMgr
.getCharacterData(Location
), Length
);
95 if (Tok
.is(tok::comment
)) {
96 if (isClangFormatOff(Text
))
98 else if (isClangFormatOn(Text
))
102 if (Skip
|| Tok
.isNot(tok::numeric_constant
) || Text
[0] == '.' ||
103 !AffectedRangeMgr
.affectsCharSourceRange(
104 CharSourceRange::getCharRange(Location
, Tok
.getEndLoc()))) {
107 const auto B
= getBase(Text
);
108 const bool IsBase2
= B
== Base::Binary
;
109 const bool IsBase10
= B
== Base::Decimal
;
110 const bool IsBase16
= B
== Base::Hex
;
111 if ((IsBase2
&& SkipBinary
) || (IsBase10
&& SkipDecimal
) ||
112 (IsBase16
&& SkipHex
) || B
== Base::Other
) {
116 // Hex alpha digits a-f/A-F must be at the end of the string literal.
117 StringRef Suffixes
= "_himnsuyd";
119 Text
.find_first_of(IsBase16
? Suffixes
.drop_back() : Suffixes
);
120 Pos
!= StringRef::npos
) {
121 Text
= Text
.substr(0, Pos
);
125 if ((IsBase10
&& Text
.find_last_of(".eEfFdDmM") != StringRef::npos
) ||
126 (IsBase16
&& Text
.find_last_of(".pP") != StringRef::npos
)) {
129 const auto Start
= Text
[0] == '0' ? 2 : 0;
130 auto End
= Text
.find_first_of("uUlLzZn", Start
);
131 if (End
== StringRef::npos
)
133 if (Start
> 0 || End
< Length
) {
134 Length
= End
- Start
;
135 Text
= Text
.substr(Start
, Length
);
137 auto DigitsPerGroup
= Decimal
;
138 auto MinDigits
= DecimalMinDigits
;
140 DigitsPerGroup
= Binary
;
141 MinDigits
= BinaryMinDigits
;
142 } else if (IsBase16
) {
143 DigitsPerGroup
= Hex
;
144 MinDigits
= HexMinDigits
;
146 const auto SeparatorCount
= Text
.count(Separator
);
147 const int DigitCount
= Length
- SeparatorCount
;
148 const bool RemoveSeparator
= DigitsPerGroup
< 0 || DigitCount
< MinDigits
;
149 if (RemoveSeparator
&& SeparatorCount
== 0)
151 if (!RemoveSeparator
&& SeparatorCount
> 0 &&
152 checkSeparator(Text
, DigitsPerGroup
)) {
155 const auto &Formatted
=
156 format(Text
, DigitsPerGroup
, DigitCount
, RemoveSeparator
);
157 assert(Formatted
!= Text
);
159 Location
= Location
.getLocWithOffset(Start
);
161 tooling::Replacement(SourceMgr
, Location
, Length
, Formatted
)));
167 bool IntegerLiteralSeparatorFixer::checkSeparator(
168 const StringRef IntegerLiteral
, int DigitsPerGroup
) const {
169 assert(DigitsPerGroup
> 0);
172 for (auto C
: llvm::reverse(IntegerLiteral
)) {
173 if (C
== Separator
) {
174 if (I
< DigitsPerGroup
)
178 if (I
== DigitsPerGroup
)
187 std::string
IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral
,
190 bool RemoveSeparator
) const {
191 assert(DigitsPerGroup
!= 0);
193 std::string Formatted
;
195 if (RemoveSeparator
) {
196 for (auto C
: IntegerLiteral
)
198 Formatted
.push_back(C
);
202 int Remainder
= DigitCount
% DigitsPerGroup
;
205 for (auto C
: IntegerLiteral
) {
208 if (I
== (Remainder
> 0 ? Remainder
: DigitsPerGroup
)) {
209 Formatted
.push_back(Separator
);
213 Formatted
.push_back(C
);
220 } // namespace format