Skip to content

Commit 8b94bb6

Browse files
committed
fixup! [clang-format] Add an option to format integer literal case
1 parent d1839bb commit 8b94bb6

File tree

2 files changed

+54
-207
lines changed

2 files changed

+54
-207
lines changed

clang/lib/Format/NumericLiteralCaseFixer.cpp

Lines changed: 47 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "NumericLiteralCaseFixer.h"
16+
#include "NumericLiteralInfo.h"
1617

1718
#include "llvm/ADT/StringExtras.h"
1819

@@ -21,69 +22,17 @@
2122
namespace clang {
2223
namespace format {
2324

24-
using CharTransformFn = char (*)(char C);
25-
namespace {
26-
27-
/// @brief Collection of std::transform predicates for each part of a numeric
28-
/// literal.
29-
struct FormatParameters {
30-
FormatParameters(FormatStyle::LanguageKind Language,
31-
const FormatStyle::NumericLiteralCaseStyle &CaseStyle);
32-
33-
CharTransformFn Prefix;
34-
CharTransformFn HexDigit;
35-
CharTransformFn FloatExponentSeparator;
36-
CharTransformFn Suffix;
37-
38-
char Separator;
39-
};
40-
41-
/// @brief Parse a single numeric constant from text into ranges that are
42-
/// appropriate for applying NumericLiteralCaseStyle rules.
43-
class QuickNumericalConstantParser {
44-
public:
45-
QuickNumericalConstantParser(const StringRef &IntegerLiteral,
46-
const FormatParameters &Transforms);
47-
48-
/// @brief Reformats the numeric constant if needed.
49-
/// Calling this method invalidates the object's state.
50-
/// @return std::nullopt if no reformatting is required. std::optional<>
51-
/// containing the reformatted string otherwise.
52-
std::optional<std::string> formatIfNeeded() &&;
53-
54-
private:
55-
const StringRef &IntegerLiteral;
56-
const FormatParameters &Transforms;
57-
58-
std::string Formatted;
59-
60-
std::string::iterator PrefixBegin;
61-
std::string::iterator PrefixEnd;
62-
std::string::iterator HexDigitBegin;
63-
std::string::iterator HexDigitEnd;
64-
std::string::iterator FloatExponentSeparatorBegin;
65-
std::string::iterator FloatExponentSeparatorEnd;
66-
std::string::iterator SuffixBegin;
67-
std::string::iterator SuffixEnd;
68-
69-
void parse();
70-
void applyFormatting();
71-
};
72-
73-
} // namespace
74-
75-
static char noOpTransform(char C) { return C; }
76-
77-
static CharTransformFn
78-
getTransform(FormatStyle::NumericLiteralComponentStyle ConfigValue) {
25+
static std::string
26+
transformComponent(StringRef Component,
27+
FormatStyle::NumericLiteralComponentStyle ConfigValue) {
7928
switch (ConfigValue) {
8029
case FormatStyle::NLCS_Upper:
81-
return llvm::toUpper;
30+
return Component.upper();
8231
case FormatStyle::NLCS_Lower:
83-
return llvm::toLower;
32+
return Component.lower();
8433
case FormatStyle::NLCS_Leave:
8534
default:
86-
return noOpTransform;
35+
return Component.str();
8736
}
8837
}
8938

@@ -104,14 +53,10 @@ static bool matchesReservedSuffix(StringRef Suffix) {
10453
return *entry == Suffix;
10554
}
10655

107-
FormatParameters::FormatParameters(
108-
FormatStyle::LanguageKind Language,
109-
const FormatStyle::NumericLiteralCaseStyle &CaseStyle)
110-
: Prefix(getTransform(CaseStyle.Prefix)),
111-
HexDigit(getTransform(CaseStyle.HexDigit)),
112-
FloatExponentSeparator(getTransform(CaseStyle.ExponentLetter)),
113-
Suffix(getTransform(CaseStyle.Suffix)) {
114-
switch (Language) {
56+
static std::optional<std::string> formatIfNeeded(StringRef IntegerLiteral,
57+
const FormatStyle &Style) {
58+
char Separator;
59+
switch (Style.Language) {
11560
case FormatStyle::LK_CSharp:
11661
case FormatStyle::LK_Java:
11762
case FormatStyle::LK_JavaScript:
@@ -123,161 +68,57 @@ FormatParameters::FormatParameters(
12368
default:
12469
Separator = '\'';
12570
}
126-
}
127-
128-
QuickNumericalConstantParser::QuickNumericalConstantParser(
129-
const StringRef &IntegerLiteral, const FormatParameters &Transforms)
130-
: IntegerLiteral(IntegerLiteral), Transforms(Transforms),
131-
Formatted(IntegerLiteral), PrefixBegin(Formatted.begin()),
132-
PrefixEnd(Formatted.begin()), HexDigitBegin(Formatted.begin()),
133-
HexDigitEnd(Formatted.begin()),
134-
FloatExponentSeparatorBegin(Formatted.begin()),
135-
FloatExponentSeparatorEnd(Formatted.begin()),
136-
SuffixBegin(Formatted.begin()), SuffixEnd(Formatted.begin()) {}
137-
138-
void QuickNumericalConstantParser::parse() {
139-
auto Cur = Formatted.begin();
140-
const auto End = Formatted.end();
141-
142-
bool IsHex = false;
143-
bool IsFloat = false;
144-
145-
// Find the range that contains the prefix.
146-
PrefixBegin = Cur;
147-
if (Cur != End && *Cur == '0') {
148-
++Cur;
149-
if (Cur != End) {
150-
const char C = *Cur;
151-
switch (C) {
152-
case 'x':
153-
case 'X':
154-
IsHex = true;
155-
++Cur;
156-
break;
157-
case 'b':
158-
case 'B':
159-
++Cur;
160-
break;
161-
case 'o':
162-
case 'O':
163-
// Javascript uses 0o as octal prefix.
164-
++Cur;
165-
break;
166-
default:
167-
break;
168-
}
169-
}
170-
}
171-
PrefixEnd = Cur;
71+
const NumericLiteralInfo N{IntegerLiteral, Separator};
17272

173-
// Find the range that contains hex digits.
174-
HexDigitBegin = Cur;
175-
if (IsHex) {
176-
Cur = std::find_if_not(Cur, End, [this, &IsFloat](char C) {
177-
if (C == '.') {
178-
IsFloat = true;
179-
return true;
180-
}
181-
return C == Transforms.Separator || llvm::isHexDigit(C);
182-
});
183-
}
184-
HexDigitEnd = Cur;
73+
std::string Formatted{""};
18574

186-
// Find the range that contains a floating point exponent separator.
187-
// Hex digits have already been scanned through the decimal point.
188-
// Decimal/octal/binary literals must fast forward through the decimal first.
189-
if (!IsHex) {
190-
Cur = std::find_if_not(Cur, End, [this, &IsFloat](char C) {
191-
if (C == '.') {
192-
IsFloat = true;
193-
return true;
194-
}
195-
return C == Transforms.Separator || llvm::isDigit(C);
196-
});
75+
if (N.BaseLetterPos != llvm::StringRef::npos) {
76+
Formatted +=
77+
transformComponent(IntegerLiteral.take_front(1 + N.BaseLetterPos),
78+
Style.NumericLiteralCase.Prefix);
19779
}
198-
// The next character of a floating point literal will either be the
199-
// separator, or the start of a suffix.
200-
FloatExponentSeparatorBegin = Cur;
201-
if (IsFloat) {
202-
const char LSep = IsHex ? 'p' : 'e';
203-
const char USep = IsHex ? 'P' : 'E';
204-
Cur = std::find_if_not(
205-
Cur, End, [LSep, USep](char C) { return C == LSep || C == USep; });
80+
// reformat this slice as HexDigit whether or not the digit has hexadecimal
81+
// characters because binary/decimal/octal digits are unchanged
82+
Formatted += transformComponent(
83+
IntegerLiteral.slice(
84+
N.BaseLetterPos == llvm::StringRef::npos ? 0 : 1 + N.BaseLetterPos,
85+
N.ExponentLetterPos == llvm::StringRef::npos
86+
? N.SuffixPos == llvm::StringRef::npos ? IntegerLiteral.size()
87+
: N.SuffixPos
88+
: N.ExponentLetterPos),
89+
Style.NumericLiteralCase.HexDigit);
90+
91+
if (N.ExponentLetterPos != llvm::StringRef::npos) {
92+
Formatted += transformComponent(
93+
IntegerLiteral.slice(N.ExponentLetterPos,
94+
N.SuffixPos == llvm::StringRef::npos
95+
? IntegerLiteral.size()
96+
: N.SuffixPos),
97+
Style.NumericLiteralCase.ExponentLetter);
20698
}
207-
FloatExponentSeparatorEnd = Cur;
20899

209-
// Fast forward through the exponent part of a floating point literal.
210-
if (IsFloat && FloatExponentSeparatorBegin != FloatExponentSeparatorEnd) {
211-
Cur = std::find_if_not(Cur, End, [](char C) {
212-
return llvm::isDigit(C) || C == '+' || C == '-';
213-
});
100+
if (N.SuffixPos != llvm::StringRef::npos) {
101+
StringRef Suffix = IntegerLiteral.drop_front(N.SuffixPos);
102+
if (matchesReservedSuffix(Suffix) || Suffix.front() == '_') {
103+
// In C++, it is idiomatic, but NOT standardized to define user-defined
104+
// literals with a leading '_'. Omit user defined literals and standard
105+
// reserved suffixes from transformation.
106+
Formatted += Suffix.str();
107+
} else {
108+
Formatted += transformComponent(Suffix, Style.NumericLiteralCase.Suffix);
109+
}
214110
}
215111

216-
// Find the range containing a suffix if any.
217-
SuffixBegin = Cur;
218-
size_t const SuffixLen = End - Cur;
219-
StringRef suffix(&(*SuffixBegin), SuffixLen);
220-
if (!matchesReservedSuffix(suffix)) {
221-
Cur = std::find_if_not(Cur, End, [](char C) {
222-
// In C++, it is idiomatic, but NOT standard to define user-defined
223-
// literals with a leading '_'. Omit user defined literals from
224-
// transformation.
225-
return C != '_';
226-
});
227-
}
228-
SuffixEnd = Cur;
229-
}
230-
231-
void QuickNumericalConstantParser::applyFormatting() {
232-
233-
auto Start = Formatted.cbegin();
234-
auto End = Formatted.cend();
235-
236-
assert(Start <= PrefixBegin && End >= PrefixBegin &&
237-
"PrefixBegin is out of bounds");
238-
assert(Start <= PrefixEnd && End >= PrefixEnd &&
239-
"PrefixEnd is out of bounds");
240-
assert(Start <= HexDigitBegin && End >= HexDigitBegin &&
241-
"HexDigitBegin is out of bounds");
242-
assert(Start <= HexDigitEnd && End >= HexDigitEnd &&
243-
"HexDigitEnd is out of bounds");
244-
assert(Start <= FloatExponentSeparatorBegin &&
245-
End >= FloatExponentSeparatorBegin &&
246-
"FloatExponentSeparatorBegin is out of bounds");
247-
assert(Start <= FloatExponentSeparatorEnd &&
248-
End >= FloatExponentSeparatorEnd &&
249-
"FloatExponentSeparatorEnd is out of bounds");
250-
assert(Start <= SuffixBegin && End >= SuffixBegin &&
251-
"SuffixBegin is out of bounds");
252-
assert(Start <= SuffixEnd && End >= SuffixEnd &&
253-
"SuffixEnd is out of bounds");
254-
255-
std::transform(PrefixBegin, PrefixEnd, PrefixBegin, Transforms.Prefix);
256-
std::transform(HexDigitBegin, HexDigitEnd, HexDigitBegin,
257-
Transforms.HexDigit);
258-
std::transform(FloatExponentSeparatorBegin, FloatExponentSeparatorEnd,
259-
FloatExponentSeparatorBegin,
260-
Transforms.FloatExponentSeparator);
261-
std::transform(SuffixBegin, SuffixEnd, SuffixBegin, Transforms.Suffix);
262-
}
263-
264-
std::optional<std::string> QuickNumericalConstantParser::formatIfNeeded() && {
265-
parse();
266-
applyFormatting();
267-
268112
if (Formatted == IntegerLiteral)
269113
return std::nullopt;
270114
else
271-
return std::move(Formatted);
115+
return Formatted;
272116
}
273117

274118
std::pair<tooling::Replacements, unsigned>
275119
NumericLiteralCaseFixer::process(const Environment &Env,
276120
const FormatStyle &Style) {
277121

278-
const auto &CaseStyle = Style.NumericLiteralCase;
279-
const FormatParameters Transforms{Style.Language, CaseStyle};
280-
281122
const auto &SourceMgr = Env.getSourceManager();
282123
AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());
283124

@@ -315,8 +156,7 @@ NumericLiteralCaseFixer::process(const Environment &Env,
315156
continue;
316157
}
317158

318-
const auto Formatted =
319-
QuickNumericalConstantParser(Text, Transforms).formatIfNeeded();
159+
const auto Formatted = formatIfNeeded(Text, Style);
320160
if (Formatted) {
321161
assert(*Formatted != Text && "QuickNumericalConstantParser returned an "
322162
"unchanged value instead of nullopt");

clang/unittests/Format/NumericLiteralInfoTest.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,21 @@ TEST_F(NumericLiteralInfoTest, IntegerLiteral) {
4747
// Hexadecimal.
4848
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("0xF"), 1));
4949
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("0XfZ"), 1, npos, npos, 3));
50+
51+
// C++ suffixes.
52+
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("1if"), npos, npos, npos, 1));
53+
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("123_felt_pen"), npos, npos, npos, 3));
5054
}
5155

5256
TEST_F(NumericLiteralInfoTest, FloatingPointLiteral) {
5357
// Decimal.
5458
EXPECT_TRUE(verifyInfo(NumericLiteralInfo(".9"), npos, 0));
5559
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("9."), npos, 1));
60+
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("0."), npos, 1));
61+
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("0.f"), npos, 1, npos, 2));
5662
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("9.F"), npos, 1, npos, 2));
5763
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("9e9"), npos, npos, 1));
64+
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("0.e9f"), npos, 1, 2, 4));
5865
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("9E-9f"), npos, npos, 1, 4));
5966
EXPECT_TRUE(verifyInfo(NumericLiteralInfo("9.9e+9bf16"), npos, 1, 3, 6));
6067

0 commit comments

Comments
 (0)