about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-11-05 03:58:15 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-11-05 03:58:15 +0000
commit7e417d4cbd4c254792b6fb7bb0c13896f9ff42e7 (patch)
tree583ae2a30c49bed0a19b79dad0bab01aede98795
parentd8b426901a75b1eb975f52b4537f2736f2b94436 (diff)
parent3e0de1745d643d78dcc7e10ec81720dcae1ba4fe (diff)
downloadrust-7e417d4cbd4c254792b6fb7bb0c13896f9ff42e7.tar.gz
rust-7e417d4cbd4c254792b6fb7bb0c13896f9ff42e7.zip
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 <maxwell.brayden.anderson@gmail.com>
-rw-r--r--clippy_lints/src/literal_representation.rs156
-rw-r--r--tests/ui/literals.rs10
-rw-r--r--tests/ui/literals.stderr146
3 files changed, 192 insertions, 120 deletions
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index f029dd65b39..bd54c068486 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -7,16 +7,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
 //! Lints concerned with the grouping of digits with underscores in integral or
 //! floating-point literal expressions.
 
-use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass, in_external_macro, LintContext};
+use crate::rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
 use crate::rustc::{declare_tool_lint, lint_array};
-use if_chain::if_chain;
 use crate::syntax::ast::*;
 use crate::syntax_pos;
 use crate::utils::{snippet_opt, span_lint_and_sugg};
+use if_chain::if_chain;
 
 /// **What it does:** Warns if a long integral or floating-point constant does
 /// not contain underscores.
@@ -41,9 +40,9 @@ declare_clippy_lint! {
 /// **Why is this bad?** This is most probably a typo
 ///
 /// **Known problems:**
-///		- Recommends a signed suffix, even though the number might be too big and an unsigned
+/// 		- Recommends a signed suffix, even though the number might be too big and an unsigned
 ///		suffix is required
-///		- Does not match on `_128` since that is a valid grouping for decimal and octal numbers
+/// 		- Does not match on `_128` since that is a valid grouping for decimal and octal numbers
 ///
 /// **Example:**
 ///
@@ -168,21 +167,21 @@ impl<'a> DigitInfo<'a> {
         let len = sans_prefix.len();
         let mut last_d = '\0';
         for (d_idx, d) in sans_prefix.char_indices() {
-            let suffix_start = if last_d == '_' {
-                d_idx - 1
-            } else {
-                d_idx
-            };
-            if float && (d == 'f' || d == 'e' || d == 'E') ||
-                !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len)) {
-                    let (digits, suffix) = sans_prefix.split_at(suffix_start);
-                    return Self {
-                        digits,
-                        radix,
-                        prefix,
-                        suffix: Some(suffix),
-                        float,
-                    };
+            let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
+            if float
+                && (d == 'f'
+                    || is_possible_float_suffix_index(&sans_prefix, suffix_start, len)
+                    || ((d == 'E' || d == 'e') && !has_possible_float_suffix(&sans_prefix)))
+                || !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len))
+            {
+                let (digits, suffix) = sans_prefix.split_at(suffix_start);
+                return Self {
+                    digits,
+                    radix,
+                    prefix,
+                    suffix: Some(suffix),
+                    float,
+                };
             }
             last_d = d
         }
@@ -224,18 +223,44 @@ impl<'a> DigitInfo<'a> {
                 .map(|chunk| chunk.iter().collect())
                 .collect::<Vec<String>>()
                 .join("_");
+            let suffix_hint = match self.suffix {
+                Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
+                Some(suffix) => suffix.to_string(),
+                None => String::new(),
+            };
+            format!("{}.{}{}", int_part_hint, frac_part_hint, suffix_hint)
+        } else if self.float && (self.digits.contains('E') || self.digits.contains('e')) {
+            let which_e = if self.digits.contains('E') { 'E' } else { 'e' };
+            let parts: Vec<&str> = self.digits.split(which_e).collect();
+            let filtered_digits_vec_0 = parts[0].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
+            let filtered_digits_vec_1 = parts[1].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
+            let before_e_hint = filtered_digits_vec_0
+                .chunks(group_size)
+                .map(|chunk| chunk.iter().rev().collect())
+                .rev()
+                .collect::<Vec<String>>()
+                .join("_");
+            let after_e_hint = filtered_digits_vec_1
+                .chunks(group_size)
+                .map(|chunk| chunk.iter().rev().collect())
+                .rev()
+                .collect::<Vec<String>>()
+                .join("_");
+            let suffix_hint = match self.suffix {
+                Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
+                Some(suffix) => suffix.to_string(),
+                None => String::new(),
+            };
             format!(
-                "{}.{}{}",
-                int_part_hint,
-                frac_part_hint,
-                self.suffix.unwrap_or("")
+                "{}{}{}{}{}",
+                self.prefix.unwrap_or(""),
+                before_e_hint,
+                which_e,
+                after_e_hint,
+                suffix_hint
             )
         } else {
-            let filtered_digits_vec = self.digits
-                .chars()
-                .filter(|&c| c != '_')
-                .rev()
-                .collect::<Vec<_>>();
+            let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
             let mut hint = filtered_digits_vec
                 .chunks(group_size)
                 .map(|chunk| chunk.iter().rev().collect())
@@ -248,18 +273,11 @@ impl<'a> DigitInfo<'a> {
                 hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
             }
             let suffix_hint = match self.suffix {
-                Some(suffix) if is_mistyped_suffix(suffix) => {
-                    format!("_i{}", &suffix[1..])
-                },
+                Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]),
                 Some(suffix) => suffix.to_string(),
-                None => String::new()
+                None => String::new(),
             };
-            format!(
-                "{}{}{}",
-                self.prefix.unwrap_or(""),
-                hint,
-                suffix_hint
-            )
+            format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint)
         }
     }
 }
@@ -269,22 +287,20 @@ enum WarningType {
     InconsistentDigitGrouping,
     LargeDigitGroups,
     DecimalRepresentation,
-    MistypedLiteralSuffix
+    MistypedLiteralSuffix,
 }
 
 impl WarningType {
     crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
         match self {
-            WarningType::MistypedLiteralSuffix => {
-                span_lint_and_sugg(
-                    cx,
-                    MISTYPED_LITERAL_SUFFIXES,
-                    span,
-                    "mistyped literal suffix",
-                    "did you mean to write",
-                    grouping_hint.to_string()
-                )
-            },
+            WarningType::MistypedLiteralSuffix => span_lint_and_sugg(
+                cx,
+                MISTYPED_LITERAL_SUFFIXES,
+                span,
+                "mistyped literal suffix",
+                "did you mean to write",
+                grouping_hint.to_string(),
+            ),
             WarningType::UnreadableLiteral => span_lint_and_sugg(
                 cx,
                 UNREADABLE_LITERAL,
@@ -380,7 +396,7 @@ impl LiteralDigitGrouping {
 
                         // Lint integral and fractional parts separately, and then check consistency of digit
                         // groups if both pass.
-                        let _ = Self::do_lint(parts[0], None)
+                        let _ = Self::do_lint(parts[0], digit_info.suffix)
                             .map(|integral_group_size| {
                                 if parts.len() > 1 {
                                     // Lint the fractional part of literal just like integral part, but reversed.
@@ -391,11 +407,11 @@ impl LiteralDigitGrouping {
                                                                                     fractional_group_size,
                                                                                     parts[0].len(),
                                                                                     parts[1].len());
-                                            if !consistent {
-                                                WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
-                                                cx,
-                                                lit.span);
-                                            }
+                                                if !consistent {
+                                                    WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
+                                                    cx,
+                                                    lit.span);
+                                                }
                                         })
                                     .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
                                     cx,
@@ -494,9 +510,7 @@ impl EarlyLintPass for LiteralRepresentation {
 
 impl LiteralRepresentation {
     pub fn new(threshold: u64) -> Self {
-        Self {
-            threshold,
-        }
+        Self { threshold }
     }
     fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
         // Lint integral literals.
@@ -529,7 +543,12 @@ impl LiteralRepresentation {
     fn do_lint(digits: &str) -> Result<(), WarningType> {
         if digits.len() == 1 {
             // Lint for 1 digit literals, if someone really sets the threshold that low
-            if digits == "1" || digits == "2" || digits == "4" || digits == "8" || digits == "3" || digits == "7"
+            if digits == "1"
+                || digits == "2"
+                || digits == "4"
+                || digits == "8"
+                || digits == "3"
+                || digits == "7"
                 || digits == "F"
             {
                 return Err(WarningType::DecimalRepresentation);
@@ -538,6 +557,7 @@ impl LiteralRepresentation {
             // Lint for Literals with a hex-representation of 2 or 3 digits
             let f = &digits[0..1]; // first digit
             let s = &digits[1..]; // suffix
+
             // Powers of 2
             if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
                 // Powers of 2 minus 1
@@ -550,6 +570,7 @@ impl LiteralRepresentation {
             let f = &digits[0..1]; // first digit
             let m = &digits[1..digits.len() - 1]; // middle digits, except last
             let s = &digits[1..]; // suffix
+
             // Powers of 2 with a margin of +15/-16
             if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
                 || ((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 {
 }
 
 fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
-    ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) &&
-        is_mistyped_suffix(lit.split_at(idx).1)
+    ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1)
+}
+
+fn is_mistyped_float_suffix(suffix: &str) -> bool {
+    ["_32", "_64"].contains(&suffix)
+}
+
+fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
+    (len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1)
+}
+
+fn has_possible_float_suffix(lit: &str) -> bool {
+    lit.ends_with("_32") || lit.ends_with("_64")
 }
diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs
index 4db7ce95712..c08c4b693b8 100644
--- a/tests/ui/literals.rs
+++ b/tests/ui/literals.rs
@@ -7,9 +7,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
-
-
 #![warn(clippy::mixed_case_hex_literals)]
 #![warn(clippy::unseparated_literal_suffix)]
 #![warn(clippy::zero_prefixed_literal)]
@@ -64,4 +61,11 @@ fn main() {
     let fail21 = 4___16;
     let fail22 = 3__4___23;
     let fail23 = 3__16___23;
+
+    let fail24 = 12.34_64;
+    let fail25 = 1E2_32;
+    let fail26 = 43E7_64;
+    let fail27 = 243E17_32;
+    let fail28 = 241251235E723_64;
+    let fail29 = 42279.911_32;
 }
diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr
index 4e26b9dd321..d2a50e2ded5 100644
--- a/tests/ui/literals.stderr
+++ b/tests/ui/literals.stderr
@@ -1,182 +1,218 @@
 error: inconsistent casing in hexadecimal literal
-  --> $DIR/literals.rs:24:17
+  --> $DIR/literals.rs:21:17
    |
-24 |     let fail1 = 0xabCD;
+21 |     let fail1 = 0xabCD;
    |                 ^^^^^^
    |
    = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
 
 error: inconsistent casing in hexadecimal literal
-  --> $DIR/literals.rs:25:17
+  --> $DIR/literals.rs:22:17
    |
-25 |     let fail2 = 0xabCD_u32;
+22 |     let fail2 = 0xabCD_u32;
    |                 ^^^^^^^^^^
 
 error: inconsistent casing in hexadecimal literal
-  --> $DIR/literals.rs:26:17
+  --> $DIR/literals.rs:23:17
    |
-26 |     let fail2 = 0xabCD_isize;
+23 |     let fail2 = 0xabCD_isize;
    |                 ^^^^^^^^^^^^
 
 error: integer type suffix should be separated by an underscore
-  --> $DIR/literals.rs:27:27
+  --> $DIR/literals.rs:24:27
    |
-27 |     let fail_multi_zero = 000_123usize;
+24 |     let fail_multi_zero = 000_123usize;
    |                           ^^^^^^^^^^^^
    |
    = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
 
 error: this is a decimal constant
-  --> $DIR/literals.rs:27:27
+  --> $DIR/literals.rs:24:27
    |
-27 |     let fail_multi_zero = 000_123usize;
+24 |     let fail_multi_zero = 000_123usize;
    |                           ^^^^^^^^^^^^
    |
    = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
 help: if you mean to use a decimal constant, remove the `0` to remove confusion
    |
-27 |     let fail_multi_zero = 123usize;
+24 |     let fail_multi_zero = 123usize;
    |                           ^^^^^^^^
 help: if you mean to use an octal constant, use `0o`
    |
-27 |     let fail_multi_zero = 0o123usize;
+24 |     let fail_multi_zero = 0o123usize;
    |                           ^^^^^^^^^^
 
 error: integer type suffix should be separated by an underscore
-  --> $DIR/literals.rs:32:17
+  --> $DIR/literals.rs:29:17
    |
-32 |     let fail3 = 1234i32;
+29 |     let fail3 = 1234i32;
    |                 ^^^^^^^
 
 error: integer type suffix should be separated by an underscore
-  --> $DIR/literals.rs:33:17
+  --> $DIR/literals.rs:30:17
    |
-33 |     let fail4 = 1234u32;
+30 |     let fail4 = 1234u32;
    |                 ^^^^^^^
 
 error: integer type suffix should be separated by an underscore
-  --> $DIR/literals.rs:34:17
+  --> $DIR/literals.rs:31:17
    |
-34 |     let fail5 = 1234isize;
+31 |     let fail5 = 1234isize;
    |                 ^^^^^^^^^
 
 error: integer type suffix should be separated by an underscore
-  --> $DIR/literals.rs:35:17
+  --> $DIR/literals.rs:32:17
    |
-35 |     let fail6 = 1234usize;
+32 |     let fail6 = 1234usize;
    |                 ^^^^^^^^^
 
 error: float type suffix should be separated by an underscore
-  --> $DIR/literals.rs:36:17
+  --> $DIR/literals.rs:33:17
    |
-36 |     let fail7 = 1.5f32;
+33 |     let fail7 = 1.5f32;
    |                 ^^^^^^
 
 error: this is a decimal constant
-  --> $DIR/literals.rs:40:17
+  --> $DIR/literals.rs:37:17
    |
-40 |     let fail8 = 0123;
+37 |     let fail8 = 0123;
    |                 ^^^^
 help: if you mean to use a decimal constant, remove the `0` to remove confusion
    |
-40 |     let fail8 = 123;
+37 |     let fail8 = 123;
    |                 ^^^
 help: if you mean to use an octal constant, use `0o`
    |
-40 |     let fail8 = 0o123;
+37 |     let fail8 = 0o123;
    |                 ^^^^^
 
 error: long literal lacking separators
-  --> $DIR/literals.rs:51:17
+  --> $DIR/literals.rs:48:17
    |
-51 |     let fail9 = 0xabcdef;
+48 |     let fail9 = 0xabcdef;
    |                 ^^^^^^^^ help: consider: `0x00ab_cdef`
    |
    = note: `-D clippy::unreadable-literal` implied by `-D warnings`
 
 error: long literal lacking separators
-  --> $DIR/literals.rs:52:18
+  --> $DIR/literals.rs:49:18
    |
-52 |     let fail10 = 0xBAFEBAFE;
+49 |     let fail10 = 0xBAFEBAFE;
    |                  ^^^^^^^^^^ help: consider: `0xBAFE_BAFE`
 
 error: long literal lacking separators
-  --> $DIR/literals.rs:53:18
+  --> $DIR/literals.rs:50:18
    |
-53 |     let fail11 = 0xabcdeff;
+50 |     let fail11 = 0xabcdeff;
    |                  ^^^^^^^^^ help: consider: `0x0abc_deff`
 
 error: long literal lacking separators
-  --> $DIR/literals.rs:54:18
+  --> $DIR/literals.rs:51:18
    |
-54 |     let fail12 = 0xabcabcabcabcabcabc;
+51 |     let fail12 = 0xabcabcabcabcabcabc;
    |                  ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
 
 error: digit groups should be smaller
-  --> $DIR/literals.rs:55:18
+  --> $DIR/literals.rs:52:18
    |
-55 |     let fail13 = 0x1_23456_78901_usize;
+52 |     let fail13 = 0x1_23456_78901_usize;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider: `0x0123_4567_8901_usize`
    |
    = note: `-D clippy::large-digit-groups` implied by `-D warnings`
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:57:18
+  --> $DIR/literals.rs:54:18
    |
-57 |     let fail14 = 2_32;
+54 |     let fail14 = 2_32;
    |                  ^^^^ help: did you mean to write: `2_i32`
    |
    = note: #[deny(clippy::mistyped_literal_suffixes)] on by default
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:58:18
+  --> $DIR/literals.rs:55:18
    |
-58 |     let fail15 = 4_64;
+55 |     let fail15 = 4_64;
    |                  ^^^^ help: did you mean to write: `4_i64`
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:59:18
+  --> $DIR/literals.rs:56:18
    |
-59 |     let fail16 = 7_8;
+56 |     let fail16 = 7_8;
    |                  ^^^ help: did you mean to write: `7_i8`
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:60:18
+  --> $DIR/literals.rs:57:18
    |
-60 |     let fail17 = 23_16;
+57 |     let fail17 = 23_16;
    |                  ^^^^^ help: did you mean to write: `23_i16`
 
 error: digits grouped inconsistently by underscores
-  --> $DIR/literals.rs:62:18
+  --> $DIR/literals.rs:59:18
    |
-62 |     let fail19 = 12_3456_21;
+59 |     let fail19 = 12_3456_21;
    |                  ^^^^^^^^^^ help: consider: `12_345_621`
    |
    = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:63:18
+  --> $DIR/literals.rs:60:18
    |
-63 |     let fail20 = 2__8;
+60 |     let fail20 = 2__8;
    |                  ^^^^ help: did you mean to write: `2_i8`
 
 error: mistyped literal suffix
-  --> $DIR/literals.rs:64:18
+  --> $DIR/literals.rs:61:18
    |
-64 |     let fail21 = 4___16;
+61 |     let fail21 = 4___16;
    |                  ^^^^^^ help: did you mean to write: `4_i16`
 
 error: digits grouped inconsistently by underscores
-  --> $DIR/literals.rs:65:18
+  --> $DIR/literals.rs:62:18
    |
-65 |     let fail22 = 3__4___23;
+62 |     let fail22 = 3__4___23;
    |                  ^^^^^^^^^ help: consider: `3_423`
 
 error: digits grouped inconsistently by underscores
-  --> $DIR/literals.rs:66:18
+  --> $DIR/literals.rs:63:18
    |
-66 |     let fail23 = 3__16___23;
+63 |     let fail23 = 3__16___23;
    |                  ^^^^^^^^^^ help: consider: `31_623`
 
-error: aborting due to 25 previous errors
+error: mistyped literal suffix
+  --> $DIR/literals.rs:65:18
+   |
+65 |     let fail24 = 12.34_64;
+   |                  ^^^^^^^^ help: did you mean to write: `12.34_f64`
+
+error: mistyped literal suffix
+  --> $DIR/literals.rs:66:18
+   |
+66 |     let fail25 = 1E2_32;
+   |                  ^^^^^^ help: did you mean to write: `1E2_f32`
+
+error: mistyped literal suffix
+  --> $DIR/literals.rs:67:18
+   |
+67 |     let fail26 = 43E7_64;
+   |                  ^^^^^^^ help: did you mean to write: `43E7_f64`
+
+error: mistyped literal suffix
+  --> $DIR/literals.rs:68:18
+   |
+68 |     let fail27 = 243E17_32;
+   |                  ^^^^^^^^^ help: did you mean to write: `243E17_f32`
+
+error: mistyped literal suffix
+  --> $DIR/literals.rs:69:18
+   |
+69 |     let fail28 = 241251235E723_64;
+   |                  ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
+
+error: mistyped literal suffix
+  --> $DIR/literals.rs:70:18
+   |
+70 |     let fail29 = 42279.911_32;
+   |                  ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32`
+
+error: aborting due to 31 previous errors