]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/misc.rs
Update Clippy for MethodCall changes
[rust.git] / clippy_lints / src / misc.rs
index 6618876b3751a80ff8fe7ece7f67254705dd6f13..a0947608e60776dc0db4ab3a43addc0871f72f10 100644 (file)
@@ -1,6 +1,4 @@
 use if_chain::if_chain;
-use matches::matches;
-use rustc::ty;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -9,13 +7,15 @@
     TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{ExpnKind, Span};
 
 use crate::consts::{constant, Constant};
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    get_item_name, get_parent_expr, implements_trait, in_constant, is_integer_const, iter_input_pats,
+    get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
     last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
     span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
 };
     /// dereferences, e.g., changing `*x` to `x` within the function.
     ///
     /// **Example:**
-    /// ```rust
+    /// ```rust,ignore
+    /// // Bad
     /// fn foo(ref x: u8) -> bool {
     ///     true
     /// }
+    ///
+    /// // Good
+    /// fn foo(x: &u8) -> bool {
+    ///     true
+    /// }
     /// ```
     pub TOPLEVEL_REF_ARG,
     style,
     ///
     /// **Example:**
     /// ```rust
-    /// # use core::f32::NAN;
     /// # let x = 1.0;
     ///
-    /// if x == NAN { }
+    /// // Bad
+    /// if x == f32::NAN { }
+    ///
+    /// // Good
+    /// if x.is_nan() { }
     /// ```
     pub CMP_NAN,
     correctness,
     /// ```rust
     /// let x = 1.2331f64;
     /// let y = 1.2332f64;
+    ///
+    /// // Bad
     /// if y == 1.23f64 { }
     /// if y != x {} // where both are floats
+    ///
+    /// // Good
+    /// let error = 0.01f64; // Use an epsilon for comparison
+    /// if (y - 1.23f64).abs() < error { }
+    /// if (y - x).abs() > error { }
     /// ```
     pub FLOAT_CMP,
     correctness,
     /// **Example:**
     ///
     /// ```rust
+    /// // Bad
     /// let a = 0 as *const u32;
+    ///
+    /// // Good
+    /// let a = std::ptr::null::<u32>();
     /// ```
     pub ZERO_PTR,
     style,
     /// ```rust
     /// let x: f64 = 1.0;
     /// const ONE: f64 = 1.00;
-    /// x == ONE;  // where both are floats
+    ///
+    /// // Bad
+    /// if x == ONE { }  // where both are floats
+    ///
+    /// // Good
+    /// let error = 0.1f64; // Use an epsilon for comparison
+    /// if (x - ONE).abs() < error { }
     /// ```
     pub FLOAT_CMP_CONST,
     restriction,
@@ -249,17 +275,14 @@ fn check_fn(
             return;
         }
         for arg in iter_input_pats(decl, body) {
-            match arg.pat.kind {
-                PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => {
-                    span_lint(
-                        cx,
-                        TOPLEVEL_REF_ARG,
-                        arg.pat.span,
-                        "`ref` directly on a function argument is ignored. Consider using a reference type \
-                         instead.",
-                    );
-                },
-                _ => {},
+            if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
+                span_lint(
+                    cx,
+                    TOPLEVEL_REF_ARG,
+                    arg.pat.span,
+                    "`ref` directly on a function argument is ignored. \
+                    Consider using a reference type instead.",
+                );
             }
         }
     }
@@ -269,6 +292,7 @@ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
             if let StmtKind::Local(ref local) = stmt.kind;
             if let PatKind::Binding(an, .., name, None) = local.pat.kind;
             if let Some(ref init) = local.init;
+            if !higher::is_from_for_desugar(local);
             then {
                 if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
                     let sugg_init = if init.span.from_expansion() {
@@ -292,8 +316,8 @@ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
                         init.hir_id,
                         local.pat.span,
                         "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
-                        |db| {
-                            db.span_suggestion(
+                        |diag| {
+                            diag.span_suggestion(
                                 stmt.span,
                                 "try",
                                 format!(
@@ -319,9 +343,9 @@ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
                     SHORT_CIRCUIT_STATEMENT,
                     stmt.span,
                     "boolean short circuit operator in statement may be clearer using an explicit test",
-                    |db| {
+                    |diag| {
                         let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
-                        db.span_suggestion(
+                        diag.span_suggestion(
                             stmt.span,
                             "replace it with",
                             format!(
@@ -371,26 +395,28 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
                             return;
                         }
                     }
-                    let (lint, msg) = if is_named_constant(cx, left) || is_named_constant(cx, right) {
-                        (FLOAT_CMP_CONST, "strict comparison of `f32` or `f64` constant")
-                    } else {
-                        (FLOAT_CMP, "strict comparison of `f32` or `f64`")
-                    };
-                    span_lint_and_then(cx, lint, expr.span, msg, |db| {
+                    let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
+                    let (lint, msg) = get_lint_and_message(
+                        is_named_constant(cx, left) || is_named_constant(cx, right),
+                        is_comparing_arrays,
+                    );
+                    span_lint_and_then(cx, lint, expr.span, msg, |diag| {
                         let lhs = Sugg::hir(cx, left, "..");
                         let rhs = Sugg::hir(cx, right, "..");
 
-                        db.span_suggestion(
-                            expr.span,
-                            "consider comparing them within some error",
-                            format!(
-                                "({}).abs() {} error",
-                                lhs - rhs,
-                                if op == BinOpKind::Eq { '<' } else { '>' }
-                            ),
-                            Applicability::HasPlaceholders, // snippet
-                        );
-                        db.span_note(expr.span, "`std::f32::EPSILON` and `std::f64::EPSILON` are available.");
+                        if !is_comparing_arrays {
+                            diag.span_suggestion(
+                                expr.span,
+                                "consider comparing them within some error",
+                                format!(
+                                    "({}).abs() {} error",
+                                    lhs - rhs,
+                                    if op == BinOpKind::Eq { '<' } else { '>' }
+                                ),
+                                Applicability::HasPlaceholders, // snippet
+                            );
+                        }
+                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
                     });
                 } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
                     span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
@@ -398,8 +424,8 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
             },
             _ => {},
         }
-        if in_attributes_expansion(expr) {
-            // Don't lint things expanded by #[derive(...)], etc
+        if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
+            // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
             return;
         }
         let binding = match expr.kind {
@@ -442,6 +468,31 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
+fn get_lint_and_message(
+    is_comparing_constants: bool,
+    is_comparing_arrays: bool,
+) -> (&'static rustc_lint::Lint, &'static str) {
+    if is_comparing_constants {
+        (
+            FLOAT_CMP_CONST,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` constant arrays"
+            } else {
+                "strict comparison of `f32` or `f64` constant"
+            },
+        )
+    } else {
+        (
+            FLOAT_CMP,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` arrays"
+            } else {
+                "strict comparison of `f32` or `f64`"
+            },
+        )
+    }
+}
+
 fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
     if_chain! {
         if !in_constant(cx, cmp_expr.hir_id);
@@ -458,7 +509,7 @@ fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
                     cx,
                     CMP_NAN,
                     cmp_expr.span,
-                    "doomed comparison with `NAN`, use `std::{f32,f64}::is_nan()` instead",
+                    "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
                 );
             }
         }
@@ -477,6 +528,11 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> boo
     match constant(cx, cx.tables, expr) {
         Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
         Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
+        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
+            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
+            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
+            _ => false,
+        }),
         _ => false,
     }
 }
@@ -489,7 +545,7 @@ fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
     }
 
     if_chain! {
-        if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.kind;
+        if let ExprKind::MethodCall(ref method_name, _, ref expressions, _) = expr.kind;
         if sym!(signum) == method_name.ident.name;
         // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
         // the method call)
@@ -501,12 +557,22 @@ fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_float(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
-    matches!(walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Float(_))
+    let value = &walk_ptrs_ty(cx.tables.expr_ty(expr)).kind;
+
+    if let ty::Array(arr_ty, _) = value {
+        return matches!(arr_ty.kind, ty::Float(_));
+    };
+
+    matches!(value, ty::Float(_))
+}
+
+fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+    matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _))
 }
 
 fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
     let (arg_ty, snip) = match expr.kind {
-        ExprKind::MethodCall(.., ref args) if args.len() == 1 => {
+        ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
             if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
                 (cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
             } else {
@@ -561,10 +627,10 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
         CMP_OWNED,
         lint_span,
         "this creates an owned instance just for comparison",
-        |db| {
+        |diag| {
             // This also catches `PartialEq` implementations that call `to_owned`.
             if other_gets_derefed {
-                db.span_label(lint_span, "try implementing the comparison without allocating");
+                diag.span_label(lint_span, "try implementing the comparison without allocating");
                 return;
             }
 
@@ -576,7 +642,7 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
                 snip.to_string()
             };
 
-            db.span_suggestion(
+            diag.span_suggestion(
                 lint_span,
                 "try",
                 try_hint,