From 2f8247ada5bc1d88653605373e953e8e8dd814fd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 5 Aug 2016 18:50:02 +0200 Subject: [PATCH] Lint literal suffixes not separated by underscores (see #703) --- CHANGELOG.md | 1 + README.md | 3 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/misc_early.rs | 70 +++++++++++++++++++++++----- tests/compile-fail/doc.rs | 0 tests/compile-fail/entry.rs | 0 tests/compile-fail/filter_methods.rs | 6 +-- tests/compile-fail/if_not_else.rs | 0 tests/compile-fail/literals.rs | 10 ++++ tests/compile-fail/shadow.rs | 2 +- 10 files changed, 77 insertions(+), 16 deletions(-) mode change 100755 => 100644 tests/compile-fail/doc.rs mode change 100755 => 100644 tests/compile-fail/entry.rs mode change 100755 => 100644 tests/compile-fail/if_not_else.rs mode change 100755 => 100644 tests/compile-fail/literals.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4081d3d25c0..d83611f1fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -281,6 +281,7 @@ All notable changes to this project will be documented in this file. [`unnecessary_operation`]: https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation [`unneeded_field_pattern`]: https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern [`unsafe_removed_from_name`]: https://github.com/Manishearth/rust-clippy/wiki#unsafe_removed_from_name +[`unseparated_literal_suffix`]: https://github.com/Manishearth/rust-clippy/wiki#unseparated_literal_suffix [`unstable_as_mut_slice`]: https://github.com/Manishearth/rust-clippy/wiki#unstable_as_mut_slice [`unstable_as_slice`]: https://github.com/Manishearth/rust-clippy/wiki#unstable_as_slice [`unused_collect`]: https://github.com/Manishearth/rust-clippy/wiki#unused_collect diff --git a/README.md b/README.md index 7edb751fb44..0c8e3911219 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Table of contents: ## Lints -There are 161 lints included in this crate: +There are 162 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -166,6 +166,7 @@ name [unnecessary_operation](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation) | warn | outer expressions with no effect [unneeded_field_pattern](https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern) | warn | Struct fields are bound to a wildcard instead of using `..` [unsafe_removed_from_name](https://github.com/Manishearth/rust-clippy/wiki#unsafe_removed_from_name) | warn | unsafe removed from name +[unseparated_literal_suffix](https://github.com/Manishearth/rust-clippy/wiki#unseparated_literal_suffix) | allow | literal suffixes should be separated with an underscore [unused_collect](https://github.com/Manishearth/rust-clippy/wiki#unused_collect) | warn | `collect()`ing an iterator without using the result; this is usually better written as a for loop [unused_label](https://github.com/Manishearth/rust-clippy/wiki#unused_label) | warn | unused label [unused_lifetimes](https://github.com/Manishearth/rust-clippy/wiki#unused_lifetimes) | warn | unused lifetimes in function definitions diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index eaac42c5c17..08f5e6b242d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -277,6 +277,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { methods::RESULT_UNWRAP_USED, methods::WRONG_PUB_SELF_CONVENTION, misc::USED_UNDERSCORE_BINDING, + misc_early::UNSEPARATED_LITERAL_SUFFIX, mut_mut::MUT_MUT, mutex_atomic::MUTEX_INTEGER, non_expressive_names::SIMILAR_NAMES, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 2d6b0a357da..2c058f3bcc2 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,5 +1,6 @@ use rustc::lint::*; use std::collections::HashMap; +use std::char; use syntax::ast::*; use syntax::codemap::Span; use syntax::visit::FnKind; @@ -79,6 +80,21 @@ declare_lint! { "letter digits in hex literals should be either completely upper- or lowercased" } +/// **What it does:** Warns if literal suffixes are not separated by an underscore. +/// +/// **Why is this bad?** It is much less readable. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let y = 123832i32; +/// ``` +declare_lint! { + pub UNSEPARATED_LITERAL_SUFFIX, Allow, + "literal suffixes should be separated with an underscore" +} + #[derive(Copy, Clone)] pub struct MiscEarly; @@ -86,7 +102,7 @@ pub struct MiscEarly; impl LintPass for MiscEarly { fn get_lints(&self) -> LintArray { lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL, - DOUBLE_NEG, MIXED_CASE_HEX_LITERALS) + DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX) } } @@ -196,20 +212,52 @@ impl EarlyLintPass for MiscEarly { if_let_chain! {[ let LitKind::Int(..) = lit.node, let Some(src) = snippet_opt(cx, lit.span), - src.starts_with("0x") + let Some(firstch) = src.chars().next(), + char::to_digit(firstch, 10).is_some() ], { - let mut seen = (false, false); + let mut prev = '\0'; for ch in src.chars() { - match ch { - 'a' ... 'f' => seen.0 = true, - 'A' ... 'F' => seen.1 = true, - 'i' | 'u' => break, // start of suffix already - _ => () + if ch == 'i' || ch == 'u' { + if prev != '_' { + span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, + "integer type suffix should be separated by an underscore"); + } + break; + } + prev = ch; + } + if src.starts_with("0x") { + let mut seen = (false, false); + for ch in src.chars() { + match ch { + 'a' ... 'f' => seen.0 = true, + 'A' ... 'F' => seen.1 = true, + 'i' | 'u' => break, // start of suffix already + _ => () + } + } + if seen.0 && seen.1 { + span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span, + "inconsistent casing in hexadecimal literal"); } } - if seen.0 && seen.1 { - span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span, - "inconsistent casing in hexadecimal literal"); + }} + if_let_chain! {[ + let LitKind::Float(..) = lit.node, + let Some(src) = snippet_opt(cx, lit.span), + let Some(firstch) = src.chars().next(), + char::to_digit(firstch, 10).is_some() + ], { + let mut prev = '\0'; + for ch in src.chars() { + if ch == 'f' { + if prev != '_' { + span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, + "float type suffix should be separated by an underscore"); + } + break; + } + prev = ch; } }} } diff --git a/tests/compile-fail/doc.rs b/tests/compile-fail/doc.rs old mode 100755 new mode 100644 diff --git a/tests/compile-fail/entry.rs b/tests/compile-fail/entry.rs old mode 100755 new mode 100644 diff --git a/tests/compile-fail/filter_methods.rs b/tests/compile-fail/filter_methods.rs index 743c3c15aeb..bee9688f581 100644 --- a/tests/compile-fail/filter_methods.rs +++ b/tests/compile-fail/filter_methods.rs @@ -8,17 +8,17 @@ fn main() { .map(|x| x * 2) .collect(); - let _: Vec<_> = vec![5i8; 6].into_iter() //~ERROR called `filter(p).flat_map(q)` on an `Iterator` + let _: Vec<_> = vec![5_i8; 6].into_iter() //~ERROR called `filter(p).flat_map(q)` on an `Iterator` .filter(|&x| x == 0) .flat_map(|x| x.checked_mul(2)) .collect(); - let _: Vec<_> = vec![5i8; 6].into_iter() //~ERROR called `filter_map(p).flat_map(q)` on an `Iterator` + let _: Vec<_> = vec![5_i8; 6].into_iter() //~ERROR called `filter_map(p).flat_map(q)` on an `Iterator` .filter_map(|x| x.checked_mul(2)) .flat_map(|x| x.checked_mul(2)) .collect(); - let _: Vec<_> = vec![5i8; 6].into_iter() //~ERROR called `filter_map(p).map(q)` on an `Iterator` + let _: Vec<_> = vec![5_i8; 6].into_iter() //~ERROR called `filter_map(p).map(q)` on an `Iterator` .filter_map(|x| x.checked_mul(2)) .map(|x| x.checked_mul(2)) .collect(); diff --git a/tests/compile-fail/if_not_else.rs b/tests/compile-fail/if_not_else.rs old mode 100755 new mode 100644 diff --git a/tests/compile-fail/literals.rs b/tests/compile-fail/literals.rs old mode 100755 new mode 100644 index fb031ea7c96..7645fb56e20 --- a/tests/compile-fail/literals.rs +++ b/tests/compile-fail/literals.rs @@ -1,6 +1,7 @@ #![feature(plugin)] #![plugin(clippy)] #![deny(mixed_case_hex_literals)] +#![deny(unseparated_literal_suffix)] #![allow(dead_code)] fn main() { @@ -12,4 +13,13 @@ fn main() { let fail1 = 0xabCD; //~ERROR inconsistent casing in hexadecimal literal let fail2 = 0xabCD_u32; //~ERROR inconsistent casing in hexadecimal literal let fail2 = 0xabCD_isize; //~ERROR inconsistent casing in hexadecimal literal + + let ok6 = 1234_i32; + let ok7 = 1234_f32; + let ok8 = 1234_isize; + let fail3 = 1234i32; //~ERROR integer type suffix should be separated + let fail4 = 1234u32; //~ERROR integer type suffix should be separated + let fail5 = 1234isize; //~ERROR integer type suffix should be separated + let fail6 = 1234usize; //~ERROR integer type suffix should be separated + let fail7 = 1.5f32; //~ERROR float type suffix should be separated } diff --git a/tests/compile-fail/shadow.rs b/tests/compile-fail/shadow.rs index 1cfcff74a44..1200e25cdbc 100644 --- a/tests/compile-fail/shadow.rs +++ b/tests/compile-fail/shadow.rs @@ -20,7 +20,7 @@ fn main() { let y = 1; let x = y; //~ERROR `x` is shadowed by `y` - let o = Some(1u8); + let o = Some(1_u8); if let Some(p) = o { assert_eq!(1, p); } match o {