Skip to content

Commit 2d244d3

Browse files
author
Michael Wright
committed
literal representation restructure 4
Simplify `grouping_hint` by splitting digits into parts and handling one at a time. Fixes #4762
1 parent 2e8946a commit 2d244d3

7 files changed

+136
-66
lines changed

clippy_lints/src/literal_representation.rs

Lines changed: 86 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -190,89 +190,111 @@ impl<'a> DigitInfo<'a> {
190190
}
191191
}
192192

193+
fn split_digit_parts(&self) -> (&str, Option<&str>, Option<(char, &str)>) {
194+
let digits = self.digits;
195+
196+
let mut integer = digits;
197+
let mut fraction = None;
198+
let mut exponent = None;
199+
200+
if self.float {
201+
for (i, c) in digits.char_indices() {
202+
match c {
203+
'.' => {
204+
integer = &digits[..i];
205+
fraction = Some(&digits[i + 1..]);
206+
},
207+
'e' | 'E' => {
208+
if integer.len() > i {
209+
integer = &digits[..i];
210+
} else {
211+
fraction = Some(&digits[integer.len() + 1..i]);
212+
};
213+
exponent = Some((c, &digits[i + 1..]));
214+
break;
215+
},
216+
_ => {},
217+
}
218+
}
219+
}
220+
221+
(integer, fraction, exponent)
222+
}
223+
193224
/// Returns literal formatted in a sensible way.
194225
crate fn grouping_hint(&self) -> String {
226+
let mut output = String::new();
227+
228+
if let Some(prefix) = self.prefix {
229+
output.push_str(prefix);
230+
}
231+
195232
let group_size = self.radix.suggest_grouping();
196-
if self.digits.contains('.') {
197-
let mut parts = self.digits.split('.');
198-
let int_part_hint = parts
199-
.next()
200-
.expect("split always returns at least one element")
233+
234+
let (integer, fraction, exponent) = &self.split_digit_parts();
235+
236+
let int_digits: Vec<_> = integer.chars().rev().filter(|&c| c != '_').collect();
237+
let int_part_hint = int_digits
238+
.chunks(group_size)
239+
.map(|chunk| chunk.iter().rev().collect())
240+
.rev()
241+
.collect::<Vec<String>>()
242+
.join("_");
243+
244+
// Pad leading hexidecimal group with zeros
245+
if self.radix == Radix::Hexadecimal {
246+
debug_assert!(group_size > 0);
247+
let first_group_size = (int_digits.len() + group_size - 1) % group_size + 1;
248+
for _ in 0..group_size - first_group_size {
249+
output.push('0');
250+
}
251+
}
252+
253+
output.push_str(&int_part_hint);
254+
255+
if let Some(fraction) = fraction {
256+
let frac_part_hint = fraction
201257
.chars()
202-
.rev()
203258
.filter(|&c| c != '_')
204259
.collect::<Vec<_>>()
205260
.chunks(group_size)
206-
.map(|chunk| chunk.iter().rev().collect())
207-
.rev()
261+
.map(|chunk| chunk.iter().collect())
208262
.collect::<Vec<String>>()
209263
.join("_");
210-
let frac_part_hint = parts
211-
.next()
212-
.expect("already checked that there is a `.`")
264+
265+
output.push('.');
266+
output.push_str(&frac_part_hint);
267+
}
268+
269+
if let Some((separator, exponent)) = exponent {
270+
let after_e_hint = exponent
213271
.chars()
272+
.rev()
214273
.filter(|&c| c != '_')
215274
.collect::<Vec<_>>()
216-
.chunks(group_size)
217-
.map(|chunk| chunk.iter().collect())
218-
.collect::<Vec<String>>()
219-
.join("_");
220-
let suffix_hint = match self.suffix {
221-
Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
222-
Some(suffix) => suffix.to_string(),
223-
None => String::new(),
224-
};
225-
format!("{}.{}{}", int_part_hint, frac_part_hint, suffix_hint)
226-
} else if self.float && (self.digits.contains('E') || self.digits.contains('e')) {
227-
let which_e = if self.digits.contains('E') { 'E' } else { 'e' };
228-
let parts: Vec<&str> = self.digits.split(which_e).collect();
229-
let filtered_digits_vec_0 = parts[0].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
230-
let filtered_digits_vec_1 = parts[1].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
231-
let before_e_hint = filtered_digits_vec_0
232-
.chunks(group_size)
233-
.map(|chunk| chunk.iter().rev().collect())
234-
.rev()
235-
.collect::<Vec<String>>()
236-
.join("_");
237-
let after_e_hint = filtered_digits_vec_1
238275
.chunks(group_size)
239276
.map(|chunk| chunk.iter().rev().collect())
240277
.rev()
241278
.collect::<Vec<String>>()
242279
.join("_");
243-
let suffix_hint = match self.suffix {
244-
Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
245-
Some(suffix) => suffix.to_string(),
246-
None => String::new(),
247-
};
248-
format!(
249-
"{}{}{}{}{}",
250-
self.prefix.unwrap_or(""),
251-
before_e_hint,
252-
which_e,
253-
after_e_hint,
254-
suffix_hint
255-
)
256-
} else {
257-
let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
258-
let mut hint = filtered_digits_vec
259-
.chunks(group_size)
260-
.map(|chunk| chunk.iter().rev().collect())
261-
.rev()
262-
.collect::<Vec<String>>()
263-
.join("_");
264-
// Forces hexadecimal values to be grouped by 4 being filled with zeroes (e.g 0x00ab_cdef)
265-
let nb_digits_to_fill = filtered_digits_vec.len() % 4;
266-
if self.radix == Radix::Hexadecimal && nb_digits_to_fill != 0 {
267-
hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
280+
281+
output.push(*separator);
282+
output.push_str(&after_e_hint);
283+
}
284+
285+
if let Some(suffix) = self.suffix {
286+
if self.float && is_mistyped_float_suffix(suffix) {
287+
output.push_str("_f");
288+
output.push_str(&suffix[1..]);
289+
} else if is_mistyped_suffix(suffix) {
290+
output.push_str("_i");
291+
output.push_str(&suffix[1..]);
292+
} else {
293+
output.push_str(suffix);
268294
}
269-
let suffix_hint = match self.suffix {
270-
Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]),
271-
Some(suffix) => suffix.to_string(),
272-
None => String::new(),
273-
};
274-
format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint)
275295
}
296+
297+
output
276298
}
277299
}
278300

tests/ui/inconsistent_digit_grouping.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ fn main() {
1212
1.123_456_7_f32,
1313
);
1414
let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32);
15+
16+
// Test padding
17+
let _ = 0x0010_0000;
18+
let _ = 0x0100_0000;
19+
let _ = 0x1000_0000;
20+
let _ = 0x0001_0000_0000_u64;
1521
}

tests/ui/inconsistent_digit_grouping.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ fn main() {
1212
1.123_456_7_f32,
1313
);
1414
let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
15+
16+
// Test padding
17+
let _ = 0x100000;
18+
let _ = 0x1000000;
19+
let _ = 0x10000000;
20+
let _ = 0x100000000_u64;
1521
}

tests/ui/inconsistent_digit_grouping.stderr

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,31 @@ error: digits grouped inconsistently by underscores
3030
LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
3131
| ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32`
3232

33-
error: aborting due to 5 previous errors
33+
error: long literal lacking separators
34+
--> $DIR/inconsistent_digit_grouping.rs:17:13
35+
|
36+
LL | let _ = 0x100000;
37+
| ^^^^^^^^ help: consider: `0x0010_0000`
38+
|
39+
= note: `-D clippy::unreadable-literal` implied by `-D warnings`
40+
41+
error: long literal lacking separators
42+
--> $DIR/inconsistent_digit_grouping.rs:18:13
43+
|
44+
LL | let _ = 0x1000000;
45+
| ^^^^^^^^^ help: consider: `0x0100_0000`
46+
47+
error: long literal lacking separators
48+
--> $DIR/inconsistent_digit_grouping.rs:19:13
49+
|
50+
LL | let _ = 0x10000000;
51+
| ^^^^^^^^^^ help: consider: `0x1000_0000`
52+
53+
error: long literal lacking separators
54+
--> $DIR/inconsistent_digit_grouping.rs:20:13
55+
|
56+
LL | let _ = 0x100000000_u64;
57+
| ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64`
58+
59+
error: aborting due to 9 previous errors
3460

tests/ui/mistyped_literal_suffix.fixed

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ fn main() {
1919
#[allow(overflowing_literals)]
2020
let fail28 = 241_251_235E723_f64;
2121
let fail29 = 42_279.911_f32;
22+
23+
let _ = 1.123_45E1_f32;
2224
}

tests/ui/mistyped_literal_suffix.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ fn main() {
1919
#[allow(overflowing_literals)]
2020
let fail28 = 241251235E723_64;
2121
let fail29 = 42279.911_32;
22+
23+
let _ = 1.12345E1_32;
2224
}

tests/ui/mistyped_literal_suffix.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,11 @@ error: mistyped literal suffix
7272
LL | let fail29 = 42279.911_32;
7373
| ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32`
7474

75-
error: aborting due to 12 previous errors
75+
error: mistyped literal suffix
76+
--> $DIR/mistyped_literal_suffix.rs:23:13
77+
|
78+
LL | let _ = 1.12345E1_32;
79+
| ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
80+
81+
error: aborting due to 13 previous errors
7682

0 commit comments

Comments
 (0)