diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6295a45f3..137fd793e75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3821,6 +3821,7 @@ Released 2018-09-13 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 570d736518b..02171a0909e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -546,6 +546,7 @@ store.register_lints(&[ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, + unused_rounding::UNUSED_ROUNDING, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, unwrap::PANICKING_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f..10808af363d 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -32,5 +32,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(unused_rounding::UNUSED_ROUNDING), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5f636e5114b..09435b06d0a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -402,6 +402,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; +mod unused_rounding; mod unused_self; mod unused_unit; mod unwrap; @@ -906,6 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); store.register_late_pass(|| Box::new(get_first::GetFirst)); + store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs new file mode 100644 index 00000000000..306afe44148 --- /dev/null +++ b/clippy_lints/src/unused_rounding.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases where a whole-number literal float is being rounded, using + /// the `floor`, `ceil`, or `round` methods. + /// + /// ### Why is this bad? + /// + /// This is unnecessary and confusing to the reader. Doing this is probably a mistake. + /// + /// ### Example + /// ```rust + /// let x = 1f32.ceil(); + /// ``` + /// Use instead: + /// ```rust + /// let x = 1f32; + /// ``` + #[clippy::version = "1.62.0"] + pub UNUSED_ROUNDING, + nursery, + "Uselessly rounding a whole number floating-point literal" +} +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); + +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { + if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind + && let method_name = name_ident.ident.name.as_str() + && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && !args.is_empty() + && let ExprKind::Lit(spanned) = &args[0].kind + && let LitKind::Float(symbol, ty) = spanned.kind { + let f = symbol.as_str().parse::().unwrap(); + let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + ty.name_str() + } else { + "" + }; + if f.fract() == 0.0 { + Some((method_name, f_str)) + } else { + None + } + } else { + None + } +} + +impl EarlyLintPass for UnusedRounding { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let Some((method_name, float)) = is_useless_rounding(expr) { + span_lint_and_sugg( + cx, + UNUSED_ROUNDING, + expr.span, + &format!("used the `{}` method with a whole number float", method_name), + &format!("remove the `{}` method call", method_name), + float, + Applicability::MachineApplicable, + ); + } + } +} diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed new file mode 100644 index 00000000000..54f85806ac3 --- /dev/null +++ b/tests/ui/unused_rounding.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32; + let _ = 1.0f64; + let _ = 1.00f32; + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs new file mode 100644 index 00000000000..8d007bc4a1d --- /dev/null +++ b/tests/ui/unused_rounding.rs @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32.ceil(); + let _ = 1.0f64.floor(); + let _ = 1.00f32.round(); + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr new file mode 100644 index 00000000000..6cfb02e0402 --- /dev/null +++ b/tests/ui/unused_rounding.stderr @@ -0,0 +1,22 @@ +error: used the `ceil` method with a whole number float + --> $DIR/unused_rounding.rs:5:13 + | +LL | let _ = 1f32.ceil(); + | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32` + | + = note: `-D clippy::unused-rounding` implied by `-D warnings` + +error: used the `floor` method with a whole number float + --> $DIR/unused_rounding.rs:6:13 + | +LL | let _ = 1.0f64.floor(); + | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64` + +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:7:13 + | +LL | let _ = 1.00f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` + +error: aborting due to 3 previous errors +