From ff0d44e45a27e688b5ee5447dc9175d5e72b37c9 Mon Sep 17 00:00:00 2001 From: Krishna Sai Veera Reddy Date: Sun, 23 Feb 2020 21:06:55 -0800 Subject: [PATCH] Add `imprecise_flops` lint Add lint to detect floating point operations that can be computed more accurately at the cost of performance. `cbrt`, `ln_1p` and `exp_m1` library functions call their equivalent cmath implementations which is slower but more accurate so moving checks for these under this new lint. --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 55 +++++++++++++++---- clippy_lints/src/lib.rs | 2 + src/lintlist/mod.rs | 9 ++- tests/ui/floating_point_exp.fixed | 2 +- tests/ui/floating_point_exp.rs | 2 +- tests/ui/floating_point_exp.stderr | 2 +- tests/ui/floating_point_log.fixed | 2 +- tests/ui/floating_point_log.rs | 2 +- tests/ui/floating_point_log.stderr | 2 + tests/ui/floating_point_powf.fixed | 2 +- tests/ui/floating_point_powf.rs | 2 +- tests/ui/floating_point_powf.stderr | 2 + 14 files changed, 67 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce696aa8550..99e84ea5193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1169,6 +1169,7 @@ Released 2018-09-13 [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask diff --git a/README.md b/README.md index 3103f960839..1300c5ad47b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 357 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 358 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 542ea5132de..eed4f58cf90 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -16,6 +16,39 @@ use std::f64::consts as f64_consts; use sugg::{format_numeric_literal, Sugg}; use syntax::ast; +declare_clippy_lint! { + /// **What it does:** Looks for floating-point expressions that + /// can be expressed using built-in methods to improve accuracy + /// at the cost of performance. + /// + /// **Why is this bad?** Negatively impacts accuracy. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// + /// let a = 3f32; + /// let _ = a.powf(1.0 / 3.0); + /// let _ = (1.0 + a).ln(); + /// let _ = a.exp() - 1.0; + /// ``` + /// + /// is better expressed as + /// + /// ```rust + /// + /// let a = 3f32; + /// let _ = a.cbrt(); + /// let _ = a.ln_1p(); + /// let _ = a.exp_m1(); + /// ``` + pub IMPRECISE_FLOPS, + nursery, + "usage of imprecise floating point operations" +} + declare_clippy_lint! { /// **What it does:** Looks for floating-point expressions that /// can be expressed using built-in methods to improve both @@ -34,12 +67,9 @@ declare_clippy_lint! { /// let _ = (2f32).powf(a); /// let _ = E.powf(a); /// let _ = a.powf(1.0 / 2.0); - /// let _ = a.powf(1.0 / 3.0); /// let _ = a.log(2.0); /// let _ = a.log(10.0); /// let _ = a.log(E); - /// let _ = (1.0 + a).ln(); - /// let _ = a.exp() - 1.0; /// let _ = a.powf(2.0); /// let _ = a * 2.0 + 4.0; /// ``` @@ -53,12 +83,9 @@ declare_clippy_lint! { /// let _ = a.exp2(); /// let _ = a.exp(); /// let _ = a.sqrt(); - /// let _ = a.cbrt(); /// let _ = a.log2(); /// let _ = a.log10(); /// let _ = a.ln(); - /// let _ = a.ln_1p(); - /// let _ = a.exp_m1(); /// let _ = a.powi(2); /// let _ = a.mul_add(2.0, 4.0); /// ``` @@ -67,7 +94,10 @@ declare_clippy_lint! { "usage of sub-optimal floating point operations" } -declare_lint_pass!(FloatingPointArithmetic => [SUBOPTIMAL_FLOPS]); +declare_lint_pass!(FloatingPointArithmetic => [ + IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS +]); // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e @@ -156,7 +186,7 @@ fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { span_lint_and_sugg( cx, - SUBOPTIMAL_FLOPS, + IMPRECISE_FLOPS, expr.span, "ln(1 + x) can be computed more accurately", "consider using", @@ -215,18 +245,21 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { - let (help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { + let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( + SUBOPTIMAL_FLOPS, "square-root of a number can be computed more efficiently and accurately", format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")), ) } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value { ( + IMPRECISE_FLOPS, "cube-root of a number can be computed more accurately", format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")), ) } else if let Some(exponent) = get_integer_from_float_constant(&value) { ( + SUBOPTIMAL_FLOPS, "exponentiation with integer powers can be computed more efficiently", format!( "{}.powi({})", @@ -240,7 +273,7 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { span_lint_and_sugg( cx, - SUBOPTIMAL_FLOPS, + lint, expr.span, help, "consider using", @@ -264,7 +297,7 @@ fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { then { span_lint_and_sugg( cx, - SUBOPTIMAL_FLOPS, + IMPRECISE_FLOPS, expr.span, "(e.pow(x) - 1) can be computed more accurately", "consider using", diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 503eb9ec10d..c732657b2e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -538,6 +538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &fallible_impl_from::FALLIBLE_IMPL_FROM, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, + &floating_point_arithmetic::IMPRECISE_FLOPS, &floating_point_arithmetic::SUBOPTIMAL_FLOPS, &format::USELESS_FORMAT, &formatting::POSSIBLE_MISSING_COMMA, @@ -1648,6 +1649,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), + LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5b4aad347af..15e6a4b6036 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 357] = [ +pub const ALL_LINTS: [Lint; 358] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -749,6 +749,13 @@ pub const ALL_LINTS: [Lint; 357] = [ deprecation: None, module: "implicit_return", }, + Lint { + name: "imprecise_flops", + group: "nursery", + desc: "usage of imprecise floating point operations", + deprecation: None, + module: "floating_point_arithmetic", + }, Lint { name: "inconsistent_digit_grouping", group: "style", diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed index 1f534e3705d..ae7805fdf01 100644 --- a/tests/ui/floating_point_exp.fixed +++ b/tests/ui/floating_point_exp.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs index bed8d312140..27e0b9bcbc9 100644 --- a/tests/ui/floating_point_exp.rs +++ b/tests/ui/floating_point_exp.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr index 7882b2c24e3..5cd999ad47c 100644 --- a/tests/ui/floating_point_exp.stderr +++ b/tests/ui/floating_point_exp.stderr @@ -4,7 +4,7 @@ error: (e.pow(x) - 1) can be computed more accurately LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: (e.pow(x) - 1) can be computed more accurately --> $DIR/floating_point_exp.rs:7:13 diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index afe72a8dd11..42c5e5d2bae 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code, clippy::double_parens)] -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; const E: f32 = std::f32::consts::E; diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 785b5a3bc48..8be0d9ad56f 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code, clippy::double_parens)] -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; const E: f32 = std::f32::consts::E; diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index cb0bb6d652a..943fbdb0b83 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -53,6 +53,8 @@ error: ln(1 + x) can be computed more accurately | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:25:13 diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index c5d900de329..78a9d44829b 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index cc75e230f7d..dbc1cac5cb4 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index 8f0544d4b58..ad5163f0079 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -47,6 +47,8 @@ error: cube-root of a number can be computed more accurately | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:14:13