about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-07-13 14:21:20 +0000
committerbors <bors@rust-lang.org>2020-07-13 14:21:20 +0000
commit4b8700879c59d1673dc36ea16aff3020af23dcc5 (patch)
treee49ddb022fa5dacc464ceb901e0ce9a30f592f07
parentfa4a737fba9b9f32ebe0c54e3734e864af6ea093 (diff)
parent314b068e2dfda364b7da178c374510fbd58129b8 (diff)
downloadrust-4b8700879c59d1673dc36ea16aff3020af23dcc5.tar.gz
rust-4b8700879c59d1673dc36ea16aff3020af23dcc5.zip
Auto merge of #5792 - flip1995:rollup-torc1we, r=flip1995
Rollup of 5 pull requests

Successful merges:

 - #5443 (Some accuracy lints for floating point operations)
 - #5752 (Move range_minus_one to pedantic)
 - #5756 (unnecessary_sort_by: avoid linting if key borrows)
 - #5784 (Fix out of bounds access by checking length equality BEFORE accessing by index.)
 - #5786 (fix phrase in new_lint issue template)

Failed merges:

r? @ghost

changelog: rollup
-rw-r--r--.github/ISSUE_TEMPLATE/new_lint.md2
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs227
-rw-r--r--clippy_lints/src/let_and_return.rs21
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--clippy_lints/src/ranges.rs12
-rw-r--r--clippy_lints/src/unnecessary_sort_by.rs48
-rw-r--r--clippy_lints/src/unnested_or_patterns.rs4
-rw-r--r--clippy_lints/src/utils/mod.rs15
-rw-r--r--src/lintlist/mod.rs2
-rw-r--r--tests/ui/floating_point_hypot.fixed14
-rw-r--r--tests/ui/floating_point_hypot.rs14
-rw-r--r--tests/ui/floating_point_hypot.stderr22
-rw-r--r--tests/ui/floating_point_log.fixed10
-rw-r--r--tests/ui/floating_point_log.rs10
-rw-r--r--tests/ui/floating_point_log.stderr20
-rw-r--r--tests/ui/floating_point_logbase.fixed16
-rw-r--r--tests/ui/floating_point_logbase.rs16
-rw-r--r--tests/ui/floating_point_logbase.stderr28
-rw-r--r--tests/ui/floating_point_mul_add.fixed5
-rw-r--r--tests/ui/floating_point_mul_add.rs5
-rw-r--r--tests/ui/floating_point_mul_add.stderr8
-rw-r--r--tests/ui/floating_point_powf.fixed4
-rw-r--r--tests/ui/floating_point_powf.rs4
-rw-r--r--tests/ui/floating_point_powf.stderr8
-rw-r--r--tests/ui/floating_point_powi.fixed19
-rw-r--r--tests/ui/floating_point_powi.rs19
-rw-r--r--tests/ui/floating_point_powi.stderr40
-rw-r--r--tests/ui/floating_point_rad.fixed13
-rw-r--r--tests/ui/floating_point_rad.rs13
-rw-r--r--tests/ui/floating_point_rad.stderr16
-rw-r--r--tests/ui/range_plus_minus_one.fixed1
-rw-r--r--tests/ui/range_plus_minus_one.rs1
-rw-r--r--tests/ui/range_plus_minus_one.stderr18
-rw-r--r--tests/ui/unnecessary_sort_by.fixed46
-rw-r--r--tests/ui/unnecessary_sort_by.rs46
35 files changed, 656 insertions, 94 deletions
diff --git a/.github/ISSUE_TEMPLATE/new_lint.md b/.github/ISSUE_TEMPLATE/new_lint.md
index 70445d7ef25..98fd0df685f 100644
--- a/.github/ISSUE_TEMPLATE/new_lint.md
+++ b/.github/ISSUE_TEMPLATE/new_lint.md
@@ -12,7 +12,7 @@ labels: L-lint
 
 - Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
 
-*What benefit of this lint over old code?*
+*What is the advantage of the recommended code over the original code*
 
 For example:
 - Remove bounce checking inserted by ...
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 4efd0689267..3087d6a940a 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,11 +1,11 @@
 use crate::consts::{
     constant, constant_simple, Constant,
-    Constant::{F32, F64},
+    Constant::{Int, F32, F64},
 };
-use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
+use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     }
 }
 
+fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) {
+        if value == Int(2) {
+            if let Some(parent) = get_parent_expr(cx, expr) {
+                if let Some(grandparent) = get_parent_expr(cx, parent) {
+                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind {
+                        if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                            return;
+                        }
+                    }
+                }
+
+                if let ExprKind::Binary(
+                    Spanned {
+                        node: BinOpKind::Add, ..
+                    },
+                    ref lhs,
+                    ref rhs,
+                ) = parent.kind
+                {
+                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
+
+                    span_lint_and_sugg(
+                        cx,
+                        SUBOPTIMAL_FLOPS,
+                        parent.span,
+                        "square can be computed more efficiently",
+                        "consider using",
+                        format!(
+                            "{}.mul_add({}, {})",
+                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &other_addend, ".."),
+                        ),
+                        Applicability::MachineApplicable,
+                    );
+
+                    return;
+                }
+            }
+
+            span_lint_and_sugg(
+                cx,
+                SUBOPTIMAL_FLOPS,
+                expr.span,
+                "square can be computed more efficiently",
+                "consider using",
+                format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
+    if let ExprKind::Binary(
+        Spanned {
+            node: BinOpKind::Add, ..
+        },
+        ref add_lhs,
+        ref add_rhs,
+    ) = args[0].kind
+    {
+        // check if expression of the form x * x + y * y
+        if_chain! {
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
+            if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
+            if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
+            }
+        }
+
+        // check if expression of the form x.powi(2) + y.powi(2)
+        if_chain! {
+            if let ExprKind::MethodCall(
+                PathSegment { ident: lmethod_name, .. },
+                ref _lspan,
+                ref largs,
+                _
+            ) = add_lhs.kind;
+            if let ExprKind::MethodCall(
+                PathSegment { ident: rmethod_name, .. },
+                ref _rspan,
+                ref rargs,
+                _
+            ) = add_rhs.kind;
+            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
+            if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]);
+            if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]);
+            if Int(2) == lvalue && Int(2) == rvalue;
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
+            }
+        }
+    }
+
+    None
+}
+
+fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some(message) = detect_hypot(cx, args) {
+        span_lint_and_sugg(
+            cx,
+            IMPRECISE_FLOPS,
+            expr.span,
+            "hypotenuse can be computed more accurately",
+            "consider using",
+            message,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
 // and suggest usage of `x.exp_m1() - (y - 1)` instead
 fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
         rhs,
     ) = &expr.kind
     {
+        if let Some(parent) = get_parent_expr(cx, expr) {
+            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
+                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                    return;
+                }
+            }
+        }
+
         let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
             (inner_lhs, inner_rhs, rhs)
         } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
@@ -479,6 +602,100 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
+fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
+    if_chain! {
+        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind;
+        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind;
+        then {
+            return method_name_a.as_str() == method_name_b.as_str() &&
+                args_a.len() == args_b.len() &&
+                (
+                    ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
+                    method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
+                );
+        }
+    }
+
+    false
+}
+
+fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    // check if expression of the form x.logN() / y.logN()
+    if_chain! {
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Div, ..
+            },
+            lhs,
+            rhs,
+        ) = &expr.kind;
+        if are_same_base_logs(cx, lhs, rhs);
+        if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind;
+        if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind;
+        then {
+            span_lint_and_sugg(
+                cx,
+                SUBOPTIMAL_FLOPS,
+                expr.span,
+                "log base can be expressed more clearly",
+                "consider using",
+                format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if_chain! {
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Div, ..
+            },
+            div_lhs,
+            div_rhs,
+        ) = &expr.kind;
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Mul, ..
+            },
+            mul_lhs,
+            mul_rhs,
+        ) = &div_lhs.kind;
+        if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs);
+        if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs);
+        then {
+            // TODO: also check for constant values near PI/180 or 180/PI
+            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
+               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    SUBOPTIMAL_FLOPS,
+                    expr.span,
+                    "conversion to degrees can be done more accurately",
+                    "consider using",
+                    format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")),
+                    Applicability::MachineApplicable,
+                );
+            } else if
+                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
+                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    SUBOPTIMAL_FLOPS,
+                    expr.span,
+                    "conversion to radians can be done more accurately",
+                    "consider using",
+                    format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind {
@@ -489,6 +706,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
                     "ln" => check_ln1p(cx, expr, args),
                     "log" => check_log_base(cx, expr, args),
                     "powf" => check_powf(cx, expr, args),
+                    "powi" => check_powi(cx, expr, args),
+                    "sqrt" => check_hypot(cx, expr, args),
                     _ => {},
                 }
             }
@@ -496,6 +715,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
             check_expm1(cx, expr);
             check_mul_add(cx, expr);
             check_custom_abs(cx, expr);
+            check_log_division(cx, expr);
+            check_radians(cx, expr);
         }
     }
 }
diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs
index ddc41f89f8d..fa560ffb980 100644
--- a/clippy_lints/src/let_and_return.rs
+++ b/clippy_lints/src/let_and_return.rs
@@ -1,6 +1,5 @@
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
+use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `let`-bindings, which are subsequently
@@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> {
     borrows: bool,
 }
 
-impl BorrowVisitor<'_, '_> {
-    fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
-        match &expr.kind {
-            ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id),
-            ExprKind::Call(
-                Expr {
-                    kind: ExprKind::Path(qpath),
-                    ..
-                },
-                ..,
-            ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
-            _ => None,
-        }
-    }
-}
-
 impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
     type Map = Map<'tcx>;
 
@@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
             return;
         }
 
-        if let Some(def_id) = self.fn_def_id(expr) {
+        if let Some(def_id) = fn_def_id(self.cx, expr) {
             self.borrows = self
                 .cx
                 .tcx
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index c4f1af8f4e4..aa911cf59ee 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -1161,6 +1161,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
         LintId::of(&non_expressive_names::SIMILAR_NAMES),
         LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
+        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_PLUS_ONE),
         LintId::of(&shadow::SHADOW_UNRELATED),
         LintId::of(&strings::STRING_ADD_ASSIGN),
@@ -1383,7 +1384,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ptr::PTR_ARG),
         LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
         LintId::of(&question_mark::QUESTION_MARK),
-        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
         LintId::of(&ranges::REVERSED_EMPTY_RANGES),
         LintId::of(&redundant_clone::REDUNDANT_CLONE),
@@ -1599,7 +1599,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
         LintId::of(&precedence::PRECEDENCE),
         LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
-        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
         LintId::of(&reference::DEREF_ADDROF),
         LintId::of(&reference::REF_IN_DEREF),
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index c164ec9aaf1..dd608de5723 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -52,6 +52,11 @@ declare_clippy_lint! {
     /// exclusive ranges, because they essentially add an extra branch that
     /// LLVM may fail to hoist out of the loop.
     ///
+    /// This will cause a warning that cannot be fixed if the consumer of the
+    /// range only accepts a specific range type, instead of the generic
+    /// `RangeBounds` trait
+    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
+    ///
     /// **Example:**
     /// ```rust,ignore
     /// for x..(y+1) { .. }
@@ -72,7 +77,10 @@ declare_clippy_lint! {
     /// **Why is this bad?** The code is more readable with an exclusive range
     /// like `x..y`.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** This will cause a warning that cannot be fixed if
+    /// the consumer of the range only accepts a specific range type, instead of
+    /// the generic `RangeBounds` trait
+    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
     ///
     /// **Example:**
     /// ```rust,ignore
@@ -83,7 +91,7 @@ declare_clippy_lint! {
     /// for x..y { .. }
     /// ```
     pub RANGE_MINUS_ONE,
-    complexity,
+    pedantic,
     "`x..=(y-1)` reads better as `x..y`"
 }
 
diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs
index d940776817c..91c1789a2ff 100644
--- a/clippy_lints/src/unnecessary_sort_by.rs
+++ b/clippy_lints/src/unnecessary_sort_by.rs
@@ -5,24 +5,23 @@ use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, subst::GenericArgKind};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// **What it does:**
-    /// Detects when people use `Vec::sort_by` and pass in a function
+    /// Detects uses of `Vec::sort_by` passing in a closure
     /// which compares the two arguments, either directly or indirectly.
     ///
     /// **Why is this bad?**
     /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
-    /// possible) than to use `Vec::sort_by` and and a more complicated
+    /// possible) than to use `Vec::sort_by` and a more complicated
     /// closure.
     ///
     /// **Known problems:**
-    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't
-    /// imported by a use statement in the current frame, then a `use`
-    /// statement that imports it will need to be added (which this lint
-    /// can't do).
+    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
+    /// imported by a use statement, then it will need to be added manually.
     ///
     /// **Example:**
     ///
@@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
             };
             let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
             let unstable = name == "sort_unstable_by";
+
             if_chain! {
                 if let ExprKind::Path(QPath::Resolved(_, Path {
                     segments: [PathSegment { ident: left_name, .. }], ..
                 })) = &left_expr.kind;
                 if left_name == left_ident;
                 then {
-                    Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
-                }
-                else {
-                    Some(LintTrigger::SortByKey(SortByKeyDetection {
-                        vec_name,
-                        unstable,
-                        closure_arg,
-                        closure_body,
-                        reverse
-                    }))
+                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
+                } else {
+                    if !key_returns_borrow(cx, left_expr) {
+                        return Some(LintTrigger::SortByKey(SortByKeyDetection {
+                            vec_name,
+                            unstable,
+                            closure_arg,
+                            closure_body,
+                            reverse
+                        }))
+                    }
                 }
             }
-        } else {
-            None
         }
     }
+
+    None
+}
+
+fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(def_id) = utils::fn_def_id(cx, expr) {
+        let output = cx.tcx.fn_sig(def_id).output();
+        let ty = output.skip_binder();
+        return matches!(ty.kind, ty::Ref(..))
+            || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
+    }
+
+    false
 }
 
 impl LateLintPass<'_> for UnnecessarySortBy {
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index 169a486d1eb..502bf0c4279 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -400,8 +400,8 @@ fn extend_with_matching(
 
 /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
 fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
-    ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
-        && ps1.len() == ps2.len()
+    ps1.len() == ps2.len()
+        && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
         && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
         && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
 }
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index 0b4cba3fc42..4b7a1c2b537 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -1356,6 +1356,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
     )
 }
 
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+    match &expr.kind {
+        ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(qpath),
+                ..
+            },
+            ..,
+        ) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
+        _ => None,
+    }
+}
+
 pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
     lints.iter().any(|lint| {
         matches!(
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 888b4755484..0c77e07c872 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -1790,7 +1790,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
     },
     Lint {
         name: "range_minus_one",
-        group: "complexity",
+        group: "pedantic",
         desc: "`x..=(y-1)` reads better as `x..y`",
         deprecation: None,
         module: "ranges",
diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed
new file mode 100644
index 00000000000..bbe411b3f48
--- /dev/null
+++ b/tests/ui/floating_point_hypot.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = x.hypot(y);
+    let _ = (x + 1f32).hypot(y);
+    let _ = x.hypot(y);
+    // Cases where the lint shouldn't be applied
+    // TODO: linting this adds some complexity, but could be done
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs
new file mode 100644
index 00000000000..586fd170ea1
--- /dev/null
+++ b/tests/ui/floating_point_hypot.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = (x * x + y * y).sqrt();
+    let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+    // Cases where the lint shouldn't be applied
+    // TODO: linting this adds some complexity, but could be done
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr
new file mode 100644
index 00000000000..42069d9ee9e
--- /dev/null
+++ b/tests/ui/floating_point_hypot.stderr
@@ -0,0 +1,22 @@
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:7:13
+   |
+LL |     let _ = (x * x + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:8:13
+   |
+LL |     let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:9:13
+   |
+LL |     let _ = (x.powi(2) + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed
index 42c5e5d2bae..7dc7ee94aff 100644
--- a/tests/ui/floating_point_log.fixed
+++ b/tests/ui/floating_point_log.fixed
@@ -25,11 +25,11 @@ fn check_ln1p() {
     let _ = 2.0f32.ln_1p();
     let _ = x.ln_1p();
     let _ = (x / 2.0).ln_1p();
-    let _ = x.powi(2).ln_1p();
-    let _ = (x.powi(2) / 2.0).ln_1p();
+    let _ = x.powi(3).ln_1p();
+    let _ = (x.powi(3) / 2.0).ln_1p();
     let _ = ((std::f32::consts::E - 1.0)).ln_1p();
     let _ = x.ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = (x + 2.0).ln_1p();
     let _ = (x / 2.0).ln_1p();
     // Cases where the lint shouldn't be applied
@@ -43,9 +43,9 @@ fn check_ln1p() {
     let _ = 2.0f64.ln_1p();
     let _ = x.ln_1p();
     let _ = (x / 2.0).ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = x.ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = (x + 2.0).ln_1p();
     let _ = (x / 2.0).ln_1p();
     // Cases where the lint shouldn't be applied
diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs
index 8be0d9ad56f..01181484e7d 100644
--- a/tests/ui/floating_point_log.rs
+++ b/tests/ui/floating_point_log.rs
@@ -25,11 +25,11 @@ fn check_ln1p() {
     let _ = (1f32 + 2.0).ln();
     let _ = (1.0 + x).ln();
     let _ = (1.0 + x / 2.0).ln();
-    let _ = (1.0 + x.powi(2)).ln();
-    let _ = (1.0 + x.powi(2) / 2.0).ln();
+    let _ = (1.0 + x.powi(3)).ln();
+    let _ = (1.0 + x.powi(3) / 2.0).ln();
     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
     let _ = (x + 1.0).ln();
-    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x.powi(3) + 1.0).ln();
     let _ = (x + 2.0 + 1.0).ln();
     let _ = (x / 2.0 + 1.0).ln();
     // Cases where the lint shouldn't be applied
@@ -43,9 +43,9 @@ fn check_ln1p() {
     let _ = (1f64 + 2.0).ln();
     let _ = (1.0 + x).ln();
     let _ = (1.0 + x / 2.0).ln();
-    let _ = (1.0 + x.powi(2)).ln();
+    let _ = (1.0 + x.powi(3)).ln();
     let _ = (x + 1.0).ln();
-    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x.powi(3) + 1.0).ln();
     let _ = (x + 2.0 + 1.0).ln();
     let _ = (x / 2.0 + 1.0).ln();
     // Cases where the lint shouldn't be applied
diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr
index 943fbdb0b83..900dc2b7933 100644
--- a/tests/ui/floating_point_log.stderr
+++ b/tests/ui/floating_point_log.stderr
@@ -77,14 +77,14 @@ LL |     let _ = (1.0 + x / 2.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:28:13
    |
-LL |     let _ = (1.0 + x.powi(2)).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:29:13
    |
-LL |     let _ = (1.0 + x.powi(2) / 2.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3) / 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:30:13
@@ -101,8 +101,8 @@ LL |     let _ = (x + 1.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:32:13
    |
-LL |     let _ = (x.powi(2) + 1.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (x.powi(3) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:33:13
@@ -143,8 +143,8 @@ LL |     let _ = (1.0 + x / 2.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:46:13
    |
-LL |     let _ = (1.0 + x.powi(2)).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:47:13
@@ -155,8 +155,8 @@ LL |     let _ = (x + 1.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:48:13
    |
-LL |     let _ = (x.powi(2) + 1.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (x.powi(3) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:49:13
diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed
new file mode 100644
index 00000000000..13962a272d4
--- /dev/null
+++ b/tests/ui/floating_point_logbase.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 5f32;
+    let _ = x.log(y);
+    let _ = x.log(y);
+    let _ = x.log(y);
+    let _ = x.log(y);
+    // Cases where the lint shouldn't be applied
+    let _ = x.ln() / y.powf(3.2);
+    let _ = x.powf(3.2) / y.powf(3.2);
+    let _ = x.powf(3.2) / y.ln();
+    let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs
new file mode 100644
index 00000000000..26bc20d5370
--- /dev/null
+++ b/tests/ui/floating_point_logbase.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 5f32;
+    let _ = x.ln() / y.ln();
+    let _ = x.log2() / y.log2();
+    let _ = x.log10() / y.log10();
+    let _ = x.log(5f32) / y.log(5f32);
+    // Cases where the lint shouldn't be applied
+    let _ = x.ln() / y.powf(3.2);
+    let _ = x.powf(3.2) / y.powf(3.2);
+    let _ = x.powf(3.2) / y.ln();
+    let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr
new file mode 100644
index 00000000000..78354c2f62d
--- /dev/null
+++ b/tests/ui/floating_point_logbase.stderr
@@ -0,0 +1,28 @@
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:7:13
+   |
+LL |     let _ = x.ln() / y.ln();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:8:13
+   |
+LL |     let _ = x.log2() / y.log2();
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:9:13
+   |
+LL |     let _ = x.log10() / y.log10();
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:10:13
+   |
+LL |     let _ = x.log(5f32) / y.log(5f32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed
index e343c37740d..911700bab00 100644
--- a/tests/ui/floating_point_mul_add.fixed
+++ b/tests/ui/floating_point_mul_add.fixed
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
     let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
+
+    let _ = a.mul_add(a, b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs
index 810f929c856..d202385fc8a 100644
--- a/tests/ui/floating_point_mul_add.rs
+++ b/tests/ui/floating_point_mul_add.rs
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+
+    let _ = (a * a + b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr
index 2dfbf562d15..ac8d0c0cae0 100644
--- a/tests/ui/floating_point_mul_add.stderr
+++ b/tests/ui/floating_point_mul_add.stderr
@@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
-error: aborting due to 9 previous errors
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:22:13
+   |
+LL |     let _ = (a * a + b).sqrt();
+   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
+
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed
index 78a9d44829b..b0641a100cd 100644
--- a/tests/ui/floating_point_powf.fixed
+++ b/tests/ui/floating_point_powf.fixed
@@ -11,7 +11,7 @@ fn main() {
     let _ = (-3.1f32).exp();
     let _ = x.sqrt();
     let _ = x.cbrt();
-    let _ = x.powi(2);
+    let _ = x.powi(3);
     let _ = x.powi(-2);
     let _ = x.powi(16_777_215);
     let _ = x.powi(-16_777_215);
@@ -30,7 +30,7 @@ fn main() {
     let _ = (-3.1f64).exp();
     let _ = x.sqrt();
     let _ = x.cbrt();
-    let _ = x.powi(2);
+    let _ = x.powi(3);
     let _ = x.powi(-2);
     let _ = x.powi(-2_147_483_648);
     let _ = x.powi(2_147_483_647);
diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs
index dbc1cac5cb4..a0a2c973900 100644
--- a/tests/ui/floating_point_powf.rs
+++ b/tests/ui/floating_point_powf.rs
@@ -11,7 +11,7 @@ fn main() {
     let _ = std::f32::consts::E.powf(-3.1);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
-    let _ = x.powf(2.0);
+    let _ = x.powf(3.0);
     let _ = x.powf(-2.0);
     let _ = x.powf(16_777_215.0);
     let _ = x.powf(-16_777_215.0);
@@ -30,7 +30,7 @@ fn main() {
     let _ = std::f64::consts::E.powf(-3.1);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
-    let _ = x.powf(2.0);
+    let _ = x.powf(3.0);
     let _ = x.powf(-2.0);
     let _ = x.powf(-2_147_483_648.0);
     let _ = x.powf(2_147_483_647.0);
diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr
index ad5163f0079..2422eb911e9 100644
--- a/tests/ui/floating_point_powf.stderr
+++ b/tests/ui/floating_point_powf.stderr
@@ -53,8 +53,8 @@ LL |     let _ = x.powf(1.0 / 3.0);
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:14:13
    |
-LL |     let _ = x.powf(2.0);
-   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+LL |     let _ = x.powf(3.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:15:13
@@ -125,8 +125,8 @@ LL |     let _ = x.powf(1.0 / 3.0);
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:33:13
    |
-LL |     let _ = x.powf(2.0);
-   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+LL |     let _ = x.powf(3.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:34:13
diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed
new file mode 100644
index 00000000000..56762400593
--- /dev/null
+++ b/tests/ui/floating_point_powi.fixed
@@ -0,0 +1,19 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let one = 1;
+    let x = 3f32;
+    let _ = x * x;
+    let _ = x * x;
+
+    let y = 4f32;
+    let _ = x.mul_add(x, y);
+    let _ = y.mul_add(y, x);
+    let _ = x.mul_add(x, y).sqrt();
+    let _ = y.mul_add(y, x).sqrt();
+    // Cases where the lint shouldn't be applied
+    let _ = x.powi(3);
+    let _ = x.powi(one + 1);
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs
new file mode 100644
index 00000000000..1f800e4628d
--- /dev/null
+++ b/tests/ui/floating_point_powi.rs
@@ -0,0 +1,19 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let one = 1;
+    let x = 3f32;
+    let _ = x.powi(2);
+    let _ = x.powi(1 + 1);
+
+    let y = 4f32;
+    let _ = x.powi(2) + y;
+    let _ = x + y.powi(2);
+    let _ = (x.powi(2) + y).sqrt();
+    let _ = (x + y.powi(2)).sqrt();
+    // Cases where the lint shouldn't be applied
+    let _ = x.powi(3);
+    let _ = x.powi(one + 1);
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr
new file mode 100644
index 00000000000..d5a5f1bcca1
--- /dev/null
+++ b/tests/ui/floating_point_powi.stderr
@@ -0,0 +1,40 @@
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:7:13
+   |
+LL |     let _ = x.powi(2);
+   |             ^^^^^^^^^ help: consider using: `x * x`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:8:13
+   |
+LL |     let _ = x.powi(1 + 1);
+   |             ^^^^^^^^^^^^^ help: consider using: `x * x`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:11:13
+   |
+LL |     let _ = x.powi(2) + y;
+   |             ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:12:13
+   |
+LL |     let _ = x + y.powi(2);
+   |             ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:13:13
+   |
+LL |     let _ = (x.powi(2) + y).sqrt();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:14:13
+   |
+LL |     let _ = (x + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed
new file mode 100644
index 00000000000..92480c5db8b
--- /dev/null
+++ b/tests/ui/floating_point_rad.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = x.to_degrees();
+    let _ = x.to_radians();
+    // Cases where the lint shouldn't be applied
+    let _ = x * 90f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 90f32;
+    let _ = x * 180f32 / std::f32::consts::E;
+    let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs
new file mode 100644
index 00000000000..062e7c3fdc1
--- /dev/null
+++ b/tests/ui/floating_point_rad.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 180f32;
+    // Cases where the lint shouldn't be applied
+    let _ = x * 90f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 90f32;
+    let _ = x * 180f32 / std::f32::consts::E;
+    let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr
new file mode 100644
index 00000000000..a6ffdca64ee
--- /dev/null
+++ b/tests/ui/floating_point_rad.stderr
@@ -0,0 +1,16 @@
+error: conversion to degrees can be done more accurately
+  --> $DIR/floating_point_rad.rs:6:13
+   |
+LL |     let _ = x * 180f32 / std::f32::consts::PI;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: conversion to radians can be done more accurately
+  --> $DIR/floating_point_rad.rs:7:13
+   |
+LL |     let _ = x * std::f32::consts::PI / 180f32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/range_plus_minus_one.fixed b/tests/ui/range_plus_minus_one.fixed
index 6b402114099..19b253b0fe2 100644
--- a/tests/ui/range_plus_minus_one.fixed
+++ b/tests/ui/range_plus_minus_one.fixed
@@ -7,6 +7,7 @@ fn f() -> usize {
 }
 
 #[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
 fn main() {
     for _ in 0..2 {}
     for _ in 0..=2 {}
diff --git a/tests/ui/range_plus_minus_one.rs b/tests/ui/range_plus_minus_one.rs
index 3cfed4125b3..7d034117547 100644
--- a/tests/ui/range_plus_minus_one.rs
+++ b/tests/ui/range_plus_minus_one.rs
@@ -7,6 +7,7 @@ fn f() -> usize {
 }
 
 #[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
 fn main() {
     for _ in 0..2 {}
     for _ in 0..=2 {}
diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr
index f72943a04f2..fb4f1658597 100644
--- a/tests/ui/range_plus_minus_one.stderr
+++ b/tests/ui/range_plus_minus_one.stderr
@@ -1,5 +1,5 @@
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:14:14
+  --> $DIR/range_plus_minus_one.rs:15:14
    |
 LL |     for _ in 0..3 + 1 {}
    |              ^^^^^^^^ help: use: `0..=3`
@@ -7,25 +7,25 @@ LL |     for _ in 0..3 + 1 {}
    = note: `-D clippy::range-plus-one` implied by `-D warnings`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:17:14
+  --> $DIR/range_plus_minus_one.rs:18:14
    |
 LL |     for _ in 0..1 + 5 {}
    |              ^^^^^^^^ help: use: `0..=5`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:20:14
+  --> $DIR/range_plus_minus_one.rs:21:14
    |
 LL |     for _ in 1..1 + 1 {}
    |              ^^^^^^^^ help: use: `1..=1`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:26:14
+  --> $DIR/range_plus_minus_one.rs:27:14
    |
 LL |     for _ in 0..(1 + f()) {}
    |              ^^^^^^^^^^^^ help: use: `0..=f()`
 
 error: an exclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:30:13
+  --> $DIR/range_plus_minus_one.rs:31:13
    |
 LL |     let _ = ..=11 - 1;
    |             ^^^^^^^^^ help: use: `..11`
@@ -33,25 +33,25 @@ LL |     let _ = ..=11 - 1;
    = note: `-D clippy::range-minus-one` implied by `-D warnings`
 
 error: an exclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:31:13
+  --> $DIR/range_plus_minus_one.rs:32:13
    |
 LL |     let _ = ..=(11 - 1);
    |             ^^^^^^^^^^^ help: use: `..11`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:32:13
+  --> $DIR/range_plus_minus_one.rs:33:13
    |
 LL |     let _ = (1..11 + 1);
    |             ^^^^^^^^^^^ help: use: `(1..=11)`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:33:13
+  --> $DIR/range_plus_minus_one.rs:34:13
    |
 LL |     let _ = (f() + 1)..(f() + 1);
    |             ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:37:14
+  --> $DIR/range_plus_minus_one.rs:38:14
    |
 LL |     for _ in 1..ONE + ONE {}
    |              ^^^^^^^^^^^^ help: use: `1..=ONE`
diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed
index 779fd57707a..c017d1cf9a4 100644
--- a/tests/ui/unnecessary_sort_by.fixed
+++ b/tests/ui/unnecessary_sort_by.fixed
@@ -2,11 +2,11 @@
 
 use std::cmp::Reverse;
 
-fn id(x: isize) -> isize {
-    x
-}
+fn unnecessary_sort_by() {
+    fn id(x: isize) -> isize {
+        x
+    }
 
-fn main() {
     let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
     // Forward examples
     vec.sort();
@@ -24,3 +24,41 @@ fn main() {
     vec.sort_by(|_, b| b.cmp(c));
     vec.sort_unstable_by(|a, _| a.cmp(c));
 }
+
+// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+mod issue_5754 {
+    struct Test(String);
+
+    #[derive(PartialOrd, Ord, PartialEq, Eq)]
+    struct Wrapper<'a>(&'a str);
+
+    impl Test {
+        fn name(&self) -> &str {
+            &self.0
+        }
+
+        fn wrapped(&self) -> Wrapper<'_> {
+            Wrapper(&self.0)
+        }
+    }
+
+    pub fn test() {
+        let mut args: Vec<Test> = vec![];
+
+        // Forward
+        args.sort_by(|a, b| a.name().cmp(b.name()));
+        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        // Reverse
+        args.sort_by(|a, b| b.name().cmp(a.name()));
+        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+    }
+}
+
+fn main() {
+    unnecessary_sort_by();
+    issue_5754::test();
+}
diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs
index 0485a5630af..1929c72b2f2 100644
--- a/tests/ui/unnecessary_sort_by.rs
+++ b/tests/ui/unnecessary_sort_by.rs
@@ -2,11 +2,11 @@
 
 use std::cmp::Reverse;
 
-fn id(x: isize) -> isize {
-    x
-}
+fn unnecessary_sort_by() {
+    fn id(x: isize) -> isize {
+        x
+    }
 
-fn main() {
     let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
     // Forward examples
     vec.sort_by(|a, b| a.cmp(b));
@@ -24,3 +24,41 @@ fn main() {
     vec.sort_by(|_, b| b.cmp(c));
     vec.sort_unstable_by(|a, _| a.cmp(c));
 }
+
+// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+mod issue_5754 {
+    struct Test(String);
+
+    #[derive(PartialOrd, Ord, PartialEq, Eq)]
+    struct Wrapper<'a>(&'a str);
+
+    impl Test {
+        fn name(&self) -> &str {
+            &self.0
+        }
+
+        fn wrapped(&self) -> Wrapper<'_> {
+            Wrapper(&self.0)
+        }
+    }
+
+    pub fn test() {
+        let mut args: Vec<Test> = vec![];
+
+        // Forward
+        args.sort_by(|a, b| a.name().cmp(b.name()));
+        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        // Reverse
+        args.sort_by(|a, b| b.name().cmp(a.name()));
+        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+    }
+}
+
+fn main() {
+    unnecessary_sort_by();
+    issue_5754::test();
+}