Skip to content

Commit 6a695ff

Browse files
committed
added float support for mistyped literal lints
1 parent 83ca8d4 commit 6a695ff

File tree

3 files changed

+185
-136
lines changed

3 files changed

+185
-136
lines changed

clippy_lints/src/literal_representation.rs

Lines changed: 88 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@
77
// option. This file may not be copied, modified, or distributed
88
// except according to those terms.
99

10-
1110
//! Lints concerned with the grouping of digits with underscores in integral or
1211
//! floating-point literal expressions.
1312
14-
use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass, in_external_macro, LintContext};
13+
use crate::rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
1514
use crate::rustc::{declare_tool_lint, lint_array};
16-
use if_chain::if_chain;
1715
use crate::syntax::ast::*;
1816
use crate::syntax_pos;
1917
use crate::utils::{snippet_opt, span_lint_and_sugg};
18+
use if_chain::if_chain;
2019

2120
/// **What it does:** Warns if a long integral or floating-point constant does
2221
/// not contain underscores.
@@ -41,9 +40,9 @@ declare_clippy_lint! {
4140
/// **Why is this bad?** This is most probably a typo
4241
///
4342
/// **Known problems:**
44-
/// - Recommends a signed suffix, even though the number might be too big and an unsigned
43+
/// - Recommends a signed suffix, even though the number might be too big and an unsigned
4544
/// suffix is required
46-
/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
45+
/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
4746
///
4847
/// **Example:**
4948
///
@@ -168,23 +167,21 @@ impl<'a> DigitInfo<'a> {
168167
let len = sans_prefix.len();
169168
let mut last_d = '\0';
170169
for (d_idx, d) in sans_prefix.char_indices() {
171-
let suffix_start = if last_d == '_' {
172-
d_idx - 1
173-
} else {
174-
d_idx
175-
};
176-
if float && ((is_possible_float_suffix_index(&sans_prefix, suffix_start, len)) ||
177-
(d == 'f' || d == 'e' || d == 'E')) ||
178-
!float && (d == 'i' || d == 'u' ||
179-
is_possible_suffix_index(&sans_prefix, suffix_start, len)) {
180-
let (digits, suffix) = sans_prefix.split_at(suffix_start);
181-
return Self {
182-
digits,
183-
radix,
184-
prefix,
185-
suffix: Some(suffix),
186-
float,
187-
};
170+
let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
171+
if float
172+
&& (d == 'f'
173+
|| is_possible_float_suffix_index(&sans_prefix, suffix_start, len)
174+
|| ((d == 'E' || d == 'e') && !has_possible_float_suffix(&sans_prefix)))
175+
|| !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len))
176+
{
177+
let (digits, suffix) = sans_prefix.split_at(suffix_start);
178+
return Self {
179+
digits,
180+
radix,
181+
prefix,
182+
suffix: Some(suffix),
183+
float,
184+
};
188185
}
189186
last_d = d
190187
}
@@ -226,18 +223,44 @@ impl<'a> DigitInfo<'a> {
226223
.map(|chunk| chunk.into_iter().collect())
227224
.collect::<Vec<String>>()
228225
.join("_");
226+
let suffix_hint = match self.suffix {
227+
Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
228+
Some(suffix) => suffix.to_string(),
229+
None => String::new(),
230+
};
231+
format!("{}.{}{}", int_part_hint, frac_part_hint, suffix_hint)
232+
} else if self.float && (self.digits.contains("E") || self.digits.contains("E")) {
233+
let which_e = if self.digits.contains("E") { "E" } else { "e" };
234+
let parts: Vec<&str> = self.digits.split(which_e).collect();
235+
let filtered_digits_vec_0 = parts[0].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
236+
let filtered_digits_vec_1 = parts[1].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
237+
let before_e_hint = filtered_digits_vec_0
238+
.chunks(group_size)
239+
.map(|chunk| chunk.into_iter().rev().collect())
240+
.rev()
241+
.collect::<Vec<String>>()
242+
.join("_");
243+
let after_e_hint = filtered_digits_vec_1
244+
.chunks(group_size)
245+
.map(|chunk| chunk.into_iter().rev().collect())
246+
.rev()
247+
.collect::<Vec<String>>()
248+
.join("_");
249+
let suffix_hint = match self.suffix {
250+
Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
251+
Some(suffix) => suffix.to_string(),
252+
None => String::new(),
253+
};
229254
format!(
230-
"{}.{}{}",
231-
int_part_hint,
232-
frac_part_hint,
233-
self.suffix.unwrap_or("")
255+
"{}{}{}{}{}",
256+
self.prefix.unwrap_or(""),
257+
before_e_hint,
258+
which_e,
259+
after_e_hint,
260+
suffix_hint
234261
)
235262
} else {
236-
let filtered_digits_vec = self.digits
237-
.chars()
238-
.filter(|&c| c != '_')
239-
.rev()
240-
.collect::<Vec<_>>();
263+
let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
241264
let mut hint = filtered_digits_vec
242265
.chunks(group_size)
243266
.map(|chunk| chunk.into_iter().rev().collect())
@@ -250,21 +273,11 @@ impl<'a> DigitInfo<'a> {
250273
hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
251274
}
252275
let suffix_hint = match self.suffix {
253-
Some(suffix) if is_mistyped_float_suffix(suffix) && self.digits.contains(".") => {
254-
format!("_f{}", &suffix[1..])
255-
},
256-
Some(suffix) if is_mistyped_suffix(suffix) => {
257-
format!("_i{}", &suffix[1..])
258-
},
276+
Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]),
259277
Some(suffix) => suffix.to_string(),
260-
None => String::new()
278+
None => String::new(),
261279
};
262-
format!(
263-
"{}{}{}",
264-
self.prefix.unwrap_or(""),
265-
hint,
266-
suffix_hint
267-
)
280+
format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint)
268281
}
269282
}
270283
}
@@ -274,22 +287,20 @@ enum WarningType {
274287
InconsistentDigitGrouping,
275288
LargeDigitGroups,
276289
DecimalRepresentation,
277-
MistypedLiteralSuffix
290+
MistypedLiteralSuffix,
278291
}
279292

280293
impl WarningType {
281294
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
282295
match self {
283-
WarningType::MistypedLiteralSuffix => {
284-
span_lint_and_sugg(
285-
cx,
286-
MISTYPED_LITERAL_SUFFIXES,
287-
span,
288-
"mistyped literal suffix",
289-
"did you mean to write",
290-
grouping_hint.to_string()
291-
)
292-
},
296+
WarningType::MistypedLiteralSuffix => span_lint_and_sugg(
297+
cx,
298+
MISTYPED_LITERAL_SUFFIXES,
299+
span,
300+
"mistyped literal suffix",
301+
"did you mean to write",
302+
grouping_hint.to_string(),
303+
),
293304
WarningType::UnreadableLiteral => span_lint_and_sugg(
294305
cx,
295306
UNREADABLE_LITERAL,
@@ -331,11 +342,7 @@ pub struct LiteralDigitGrouping;
331342

332343
impl LintPass for LiteralDigitGrouping {
333344
fn get_lints(&self) -> LintArray {
334-
lint_array!(
335-
UNREADABLE_LITERAL,
336-
INCONSISTENT_DIGIT_GROUPING,
337-
LARGE_DIGIT_GROUPS
338-
)
345+
lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
339346
}
340347
}
341348

@@ -384,7 +391,7 @@ impl LiteralDigitGrouping {
384391

385392
// Lint integral and fractional parts separately, and then check consistency of digit
386393
// groups if both pass.
387-
let _ = Self::do_lint(parts[0], None)
394+
let _ = Self::do_lint(parts[0], digit_info.suffix)
388395
.map(|integral_group_size| {
389396
if parts.len() > 1 {
390397
// Lint the fractional part of literal just like integral part, but reversed.
@@ -395,11 +402,11 @@ impl LiteralDigitGrouping {
395402
fractional_group_size,
396403
parts[0].len(),
397404
parts[1].len());
398-
if !consistent {
399-
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
400-
cx,
401-
lit.span);
402-
}
405+
if !consistent {
406+
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
407+
cx,
408+
lit.span);
409+
}
403410
})
404411
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
405412
cx,
@@ -498,9 +505,7 @@ impl EarlyLintPass for LiteralRepresentation {
498505

499506
impl LiteralRepresentation {
500507
pub fn new(threshold: u64) -> Self {
501-
Self {
502-
threshold,
503-
}
508+
Self { threshold }
504509
}
505510
fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
506511
// Lint integral literals.
@@ -533,7 +538,12 @@ impl LiteralRepresentation {
533538
fn do_lint(digits: &str) -> Result<(), WarningType> {
534539
if digits.len() == 1 {
535540
// Lint for 1 digit literals, if someone really sets the threshold that low
536-
if digits == "1" || digits == "2" || digits == "4" || digits == "8" || digits == "3" || digits == "7"
541+
if digits == "1"
542+
|| digits == "2"
543+
|| digits == "4"
544+
|| digits == "8"
545+
|| digits == "3"
546+
|| digits == "7"
537547
|| digits == "F"
538548
{
539549
return Err(WarningType::DecimalRepresentation);
@@ -542,7 +552,7 @@ impl LiteralRepresentation {
542552
// Lint for Literals with a hex-representation of 2 or 3 digits
543553
let f = &digits[0..1]; // first digit
544554
let s = &digits[1..]; // suffix
545-
// Powers of 2
555+
// Powers of 2
546556
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
547557
// Powers of 2 minus 1
548558
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
@@ -554,7 +564,7 @@ impl LiteralRepresentation {
554564
let f = &digits[0..1]; // first digit
555565
let m = &digits[1..digits.len() - 1]; // middle digits, except last
556566
let s = &digits[1..]; // suffix
557-
// Powers of 2 with a margin of +15/-16
567+
// Powers of 2 with a margin of +15/-16
558568
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
559569
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
560570
// Lint for representations with only 0s and Fs, while allowing 7 as the first
@@ -574,15 +584,17 @@ fn is_mistyped_suffix(suffix: &str) -> bool {
574584
}
575585

576586
fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
577-
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) &&
578-
is_mistyped_suffix(lit.split_at(idx).1)
587+
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1)
579588
}
580589

581590
fn is_mistyped_float_suffix(suffix: &str) -> bool {
582591
["_32", "_64"].contains(&suffix)
583592
}
584593

585594
fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
586-
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) &&
587-
is_mistyped_float_suffix(lit.split_at(idx).1)
595+
(len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1)
596+
}
597+
598+
fn has_possible_float_suffix(lit: &str) -> bool {
599+
lit.ends_with("_32") || lit.ends_with("_64")
588600
}

tests/ui/literals.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
// option. This file may not be copied, modified, or distributed
88
// except according to those terms.
99

10-
11-
12-
1310
#![warn(clippy::mixed_case_hex_literals)]
1411
#![warn(clippy::unseparated_literal_suffix)]
1512
#![warn(clippy::zero_prefixed_literal)]
@@ -65,6 +62,10 @@ fn main() {
6562
let fail22 = 3__4___23;
6663
let fail23 = 3__16___23;
6764

68-
//let fail24 = 1E2_32;
69-
let fail25 = 1.2_32;
65+
let fail24 = 12.34_64;
66+
let fail25 = 1E2_32;
67+
let fail26 = 43E7_64;
68+
let fail27 = 243E17_32;
69+
let fail28 = 241251235E723_64;
70+
let fail29 = 42279.911_32;
7071
}

0 commit comments

Comments
 (0)