Skip to content

Commit 7e417d4

Browse files
bors[bot]Zaechus
andcommitted
Merge #3353
3353: float support added for mistyped_literal_suffixes lint r=mikerite a=Maxgy I implemented the mistyped_literal_suffixes lint for float literals. ``` #![allow(unused_variables)] fn main() { let x = 1E2_32; let x = 75.22_64; } ``` Given the above, the additional check suggests the variables to be written as `let x = 1E2_f32` and `let x = 75.22_f64`. Fixes #3167 Co-authored-by: Maxwell Anderson <[email protected]>
2 parents d8b4269 + 3e0de17 commit 7e417d4

File tree

3 files changed

+192
-120
lines changed

3 files changed

+192
-120
lines changed

clippy_lints/src/literal_representation.rs

+94-62
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,21 +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 && (d == 'f' || d == 'e' || d == 'E') ||
177-
!float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len)) {
178-
let (digits, suffix) = sans_prefix.split_at(suffix_start);
179-
return Self {
180-
digits,
181-
radix,
182-
prefix,
183-
suffix: Some(suffix),
184-
float,
185-
};
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+
};
186185
}
187186
last_d = d
188187
}
@@ -224,18 +223,44 @@ impl<'a> DigitInfo<'a> {
224223
.map(|chunk| chunk.iter().collect())
225224
.collect::<Vec<String>>()
226225
.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.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.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+
};
227254
format!(
228-
"{}.{}{}",
229-
int_part_hint,
230-
frac_part_hint,
231-
self.suffix.unwrap_or("")
255+
"{}{}{}{}{}",
256+
self.prefix.unwrap_or(""),
257+
before_e_hint,
258+
which_e,
259+
after_e_hint,
260+
suffix_hint
232261
)
233262
} else {
234-
let filtered_digits_vec = self.digits
235-
.chars()
236-
.filter(|&c| c != '_')
237-
.rev()
238-
.collect::<Vec<_>>();
263+
let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
239264
let mut hint = filtered_digits_vec
240265
.chunks(group_size)
241266
.map(|chunk| chunk.iter().rev().collect())
@@ -248,18 +273,11 @@ impl<'a> DigitInfo<'a> {
248273
hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
249274
}
250275
let suffix_hint = match self.suffix {
251-
Some(suffix) if is_mistyped_suffix(suffix) => {
252-
format!("_i{}", &suffix[1..])
253-
},
276+
Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]),
254277
Some(suffix) => suffix.to_string(),
255-
None => String::new()
278+
None => String::new(),
256279
};
257-
format!(
258-
"{}{}{}",
259-
self.prefix.unwrap_or(""),
260-
hint,
261-
suffix_hint
262-
)
280+
format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint)
263281
}
264282
}
265283
}
@@ -269,22 +287,20 @@ enum WarningType {
269287
InconsistentDigitGrouping,
270288
LargeDigitGroups,
271289
DecimalRepresentation,
272-
MistypedLiteralSuffix
290+
MistypedLiteralSuffix,
273291
}
274292

275293
impl WarningType {
276294
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
277295
match self {
278-
WarningType::MistypedLiteralSuffix => {
279-
span_lint_and_sugg(
280-
cx,
281-
MISTYPED_LITERAL_SUFFIXES,
282-
span,
283-
"mistyped literal suffix",
284-
"did you mean to write",
285-
grouping_hint.to_string()
286-
)
287-
},
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+
),
288304
WarningType::UnreadableLiteral => span_lint_and_sugg(
289305
cx,
290306
UNREADABLE_LITERAL,
@@ -380,7 +396,7 @@ impl LiteralDigitGrouping {
380396

381397
// Lint integral and fractional parts separately, and then check consistency of digit
382398
// groups if both pass.
383-
let _ = Self::do_lint(parts[0], None)
399+
let _ = Self::do_lint(parts[0], digit_info.suffix)
384400
.map(|integral_group_size| {
385401
if parts.len() > 1 {
386402
// Lint the fractional part of literal just like integral part, but reversed.
@@ -391,11 +407,11 @@ impl LiteralDigitGrouping {
391407
fractional_group_size,
392408
parts[0].len(),
393409
parts[1].len());
394-
if !consistent {
395-
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
396-
cx,
397-
lit.span);
398-
}
410+
if !consistent {
411+
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
412+
cx,
413+
lit.span);
414+
}
399415
})
400416
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
401417
cx,
@@ -494,9 +510,7 @@ impl EarlyLintPass for LiteralRepresentation {
494510

495511
impl LiteralRepresentation {
496512
pub fn new(threshold: u64) -> Self {
497-
Self {
498-
threshold,
499-
}
513+
Self { threshold }
500514
}
501515
fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
502516
// Lint integral literals.
@@ -529,7 +543,12 @@ impl LiteralRepresentation {
529543
fn do_lint(digits: &str) -> Result<(), WarningType> {
530544
if digits.len() == 1 {
531545
// Lint for 1 digit literals, if someone really sets the threshold that low
532-
if digits == "1" || digits == "2" || digits == "4" || digits == "8" || digits == "3" || digits == "7"
546+
if digits == "1"
547+
|| digits == "2"
548+
|| digits == "4"
549+
|| digits == "8"
550+
|| digits == "3"
551+
|| digits == "7"
533552
|| digits == "F"
534553
{
535554
return Err(WarningType::DecimalRepresentation);
@@ -538,6 +557,7 @@ impl LiteralRepresentation {
538557
// Lint for Literals with a hex-representation of 2 or 3 digits
539558
let f = &digits[0..1]; // first digit
540559
let s = &digits[1..]; // suffix
560+
541561
// Powers of 2
542562
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
543563
// Powers of 2 minus 1
@@ -550,6 +570,7 @@ impl LiteralRepresentation {
550570
let f = &digits[0..1]; // first digit
551571
let m = &digits[1..digits.len() - 1]; // middle digits, except last
552572
let s = &digits[1..]; // suffix
573+
553574
// Powers of 2 with a margin of +15/-16
554575
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
555576
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
@@ -570,6 +591,17 @@ fn is_mistyped_suffix(suffix: &str) -> bool {
570591
}
571592

572593
fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
573-
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) &&
574-
is_mistyped_suffix(lit.split_at(idx).1)
594+
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1)
595+
}
596+
597+
fn is_mistyped_float_suffix(suffix: &str) -> bool {
598+
["_32", "_64"].contains(&suffix)
599+
}
600+
601+
fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
602+
(len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1)
603+
}
604+
605+
fn has_possible_float_suffix(lit: &str) -> bool {
606+
lit.ends_with("_32") || lit.ends_with("_64")
575607
}

tests/ui/literals.rs

+7-3
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)]
@@ -64,4 +61,11 @@ fn main() {
6461
let fail21 = 4___16;
6562
let fail22 = 3__4___23;
6663
let fail23 = 3__16___23;
64+
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;
6771
}

0 commit comments

Comments
 (0)