X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=clippy_lints%2Fsrc%2Fmisc.rs;h=a0947608e60776dc0db4ab3a43addc0871f72f10;hb=6b3ee8f6000ea0fe88d1d19bceac9441a43d8694;hp=0dc55b7f7beb55dcf1ea9e095c03790a6165dc27;hpb=aff57e0f43855925eeb747963f6875335eba596b;p=rust.git diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 0dc55b7f7be..a0947608e60 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -9,12 +9,13 @@ 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, }; @@ -37,10 +38,16 @@ /// 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, @@ -57,10 +64,13 @@ /// /// **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, @@ -83,8 +93,15 @@ /// ```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, @@ -191,7 +208,11 @@ /// **Example:** /// /// ```rust + /// // Bad /// let a = 0 as *const u32; + /// + /// // Good + /// let a = std::ptr::null::(); /// ``` pub ZERO_PTR, style, @@ -214,7 +235,13 @@ /// ```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, @@ -248,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.", + ); } } } @@ -268,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() { @@ -291,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!( @@ -318,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!( @@ -370,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"); @@ -397,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 { @@ -441,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); @@ -457,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", ); } } @@ -476,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, } } @@ -488,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) @@ -500,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 { @@ -560,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; } @@ -575,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,