From 648d1ae8e08ab8617d17c4c815a067dd7d7e550f Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Wed, 14 Jun 2023 03:44:05 +0200 Subject: [PATCH 01/90] lint on matches and pattern matching --- clippy_lints/src/methods/filter_map.rs | 322 ++++++++++++++++++++----- clippy_utils/src/higher.rs | 1 + tests/ui/manual_filter_map.fixed | 24 ++ tests/ui/manual_filter_map.rs | 28 +++ tests/ui/manual_filter_map.stderr | 73 +++++- tests/ui/manual_find_map.stderr | 53 ++++ 6 files changed, 441 insertions(+), 60 deletions(-) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index fc80f2eeae0..4271f2041fe 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,7 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; +use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; +use hir::{Body, HirId, MatchSource, Pat}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,7 +12,7 @@ use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::source_map::Span; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use std::borrow::Cow; use super::MANUAL_FILTER_MAP; @@ -50,6 +52,214 @@ fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_ar is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } +#[derive(Debug, Copy, Clone)] +enum OffendingFilterExpr<'tcx> { + /// `.filter(|opt| opt.is_some())` + IsSome { + /// The receiver expression + receiver: &'tcx Expr<'tcx>, + /// If `Some`, then this contains the span of an expression that possibly contains side + /// effects: `.filter(|opt| side_effect(opt).is_some())` + /// ^^^^^^^^^^^^^^^^ + /// + /// We will use this later for warning the user that the suggested fix may change + /// the behavior. + side_effect_expr_span: Option, + }, + /// `.filter(|res| res.is_ok())` + IsOk { + /// The receiver expression + receiver: &'tcx Expr<'tcx>, + /// See `IsSome` + side_effect_expr_span: Option, + }, + /// `.filter(|enum| matches!(enum, Enum::A(_)))` + Matches { + /// The DefId of the variant being matched + variant_def_id: hir::def_id::DefId, + }, +} + +#[derive(Debug)] +enum CalledMethod { + OptionIsSome, + ResultIsOk, +} + +/// The result of checking a `map` call, returned by `OffendingFilterExpr::check_map_call` +#[derive(Debug)] +enum CheckResult<'tcx> { + Method { + map_arg: &'tcx Expr<'tcx>, + /// The method that was called inside of `filter` + method: CalledMethod, + /// See `OffendingFilterExpr::IsSome` + side_effect_expr_span: Option, + }, + PatternMatching { + /// The span of the variant being matched + /// if let Some(s) = enum + /// ^^^^^^^ + variant_span: Span, + /// if let Some(s) = enum + /// ^ + variant_ident: Ident, + }, +} + +impl<'tcx> OffendingFilterExpr<'tcx> { + pub fn check_map_call( + &mut self, + cx: &LateContext<'tcx>, + map_body: &'tcx Body<'tcx>, + map_param_id: HirId, + filter_param_id: HirId, + is_filter_param_ref: bool, + ) -> Option> { + match *self { + OffendingFilterExpr::IsSome { + receiver, + side_effect_expr_span, + } + | OffendingFilterExpr::IsOk { + receiver, + side_effect_expr_span, + } => { + // check if closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind + && matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or) + // .map(|y| f(y).copied().unwrap()) + // ~~~~ + && let map_arg_peeled = match map_arg.kind { + ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { + original_arg + }, + _ => map_arg, + } + // .map(|y| y[.acceptable_method()].unwrap()) + && let simple_equal = (path_to_local_id(receiver, filter_param_id) + && path_to_local_id(map_arg_peeled, map_param_id)) + && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + path_to_local_id(a_path, filter_param_id) + && path_to_local_id(b, map_param_id) + && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + }) + && (simple_equal + || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled)) + { + Some(CheckResult::Method { + map_arg, + side_effect_expr_span, + method: match self { + OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome, + OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk, + OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"), + } + }) + } else { + None + } + }, + OffendingFilterExpr::Matches { variant_def_id } => { + let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| { + if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind + && let PatKind::Binding(_, local_id, ident, _) = subpat.kind + && path_to_local_id(expr.peel_blocks(), local_id) + && let Some(local_variant_def_id) = path.res.opt_def_id() + && local_variant_def_id == variant_def_id + { + Some((ident, pat.span)) + } else { + None + } + }; + + // look for: + // `if let Variant (v) = enum { v } else { unreachable!() }` + // ^^^^^^^ ^ ^^^^ ^^^^^^^^^^^^^^^^^^ + // variant_span variant_ident scrutinee else_ (blocks peeled later) + // OR + // `match enum { Variant (v) => v, _ => unreachable!() }` + // ^^^^ ^^^^^^^ ^ ^^^^^^^^^^^^^^ + // scrutinee variant_span variant_ident else_ + let (scrutinee, else_, variant_ident, variant_span) = + match higher::IfLetOrMatch::parse(cx, map_body.value) { + // For `if let` we want to check that the variant matching arm references the local created by its pattern + Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_))) + if let Some((ident, span)) = expr_uses_local(pat, then) => + { + (sc, else_, ident, span) + }, + // For `match` we want to check that the "else" arm is the wildcard (`_`) pattern + // and that the variant matching arm references the local created by its pattern + Some(higher::IfLetOrMatch::Match(sc, [arm, wild_arm], MatchSource::Normal)) + if let PatKind::Wild = wild_arm.pat.kind + && let Some((ident, span)) = expr_uses_local(arm.pat, arm.body.peel_blocks()) => + { + (sc, wild_arm.body, ident, span) + }, + _ => return None, + }; + + if path_to_local_id(scrutinee, map_param_id) + // else branch should be a `panic!` or `unreachable!` macro call + && let Some(mac) = root_macro_call(else_.peel_blocks().span) + && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable)) + { + Some(CheckResult::PatternMatching { variant_span, variant_ident }) + } else { + None + } + }, + } + } + + fn hir(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, filter_param_id: HirId) -> Option { + if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind + && let Some(recv_ty) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() + { + // we still want to lint if the expression possibly contains side effects, + // *but* it can't be machine-applicable then, because that can change the behavior of the program: + // .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap()) + // vs. + // .filter_map(|x| effect(x)) + // + // the latter only calls `effect` once + let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); + + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) + && path.ident.name == sym!(is_some) + { + Some(Self::IsSome { receiver, side_effect_expr_span }) + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) + && path.ident.name == sym!(is_ok) + { + Some(Self::IsOk { receiver, side_effect_expr_span }) + } else { + None + } + } else if let Some(macro_call) = root_macro_call(expr.span) + && cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro) + // we know for a fact that the wildcard pattern is the second arm + && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind + && path_to_local_id(scrutinee, filter_param_id) + && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind + && let Some(variant_def_id) = path.res.opt_def_id() + { + Some(OffendingFilterExpr::Matches { variant_def_id }) + } else { + None + } + } +} + /// is `filter(|x| x.is_some()).map(|x| x.unwrap())` fn is_filter_some_map_unwrap( cx: &LateContext<'_>, @@ -104,55 +314,18 @@ pub(super) fn check( } else { (filter_param.pat, false) }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { - Some(true) - } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - // ...map(|x| ...unwrap()) + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id); + if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; let map_body = cx.tcx.hir().body(map_body_id); if let [map_param] = map_body.params; if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - // .filter(..).map(|y| f(y).copied().unwrap()) - // ~~~~ - let map_arg_peeled = match map_arg.kind { - ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { - original_arg - }, - _ => map_arg, - }; + if let Some(check_result) = + offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref); - // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) - let simple_equal = path_to_local_id(filter_arg, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) - && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) - }; - - if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); then { let span = filter_span.with_hi(expr.span.hi()); let (filter_name, lint) = if is_find { @@ -161,22 +334,53 @@ pub(super) fn check( ("filter", MANUAL_FILTER_MAP) }; let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - let (to_opt, deref) = if is_result { - (".ok()", String::new()) - } else { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); - ("", "*".repeat(derefs)) + let (sugg, note_and_span, applicability) = match check_result { + CheckResult::Method { map_arg, method, side_effect_expr_span } => { + let (to_opt, deref) = match method { + CalledMethod::ResultIsOk => (".ok()", String::new()), + CalledMethod::OptionIsSome => { + let derefs = cx.typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + + ("", "*".repeat(derefs)) + } + }; + + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span { + let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \ + because this expression potentially contains side effects and will only execute once"; + + (Some((note, span)), Applicability::MaybeIncorrect) + } else { + (None, Applicability::MachineApplicable) + }; + + (sugg, note_and_span, applicability) + } + CheckResult::PatternMatching { variant_span, variant_ident } => { + let pat = snippet(cx, variant_span, ""); + + (format!("{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \ + {pat} => Some({variant_ident}), \ + _ => None \ + }})"), None, Applicability::MachineApplicable) + } }; - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + span_lint_and_then(cx, lint, span, &msg, |diag| { + diag.span_suggestion(span, "try", sugg, applicability); + + if let Some((note, span)) = note_and_span { + diag.span_note(span, note); + } + }); } } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index a61e4c38088..e6dc81127dd 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -138,6 +138,7 @@ impl<'hir> IfLet<'hir> { } /// An `if let` or `match` expression. Useful for lints that trigger on one or the other. +#[derive(Debug)] pub enum IfLetOrMatch<'hir> { /// Any `match` expression Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed index 9dd376df2b4..35872a39a71 100644 --- a/tests/ui/manual_filter_map.fixed +++ b/tests/ui/manual_filter_map.fixed @@ -120,3 +120,27 @@ fn issue_8920() { .iter() .filter_map(|f| f.result_field.to_owned().ok()); } + +fn issue8010() { + #[derive(Clone)] + enum Enum { + A(i32), + B, + } + + let iter = [Enum::A(123), Enum::B].into_iter(); + + let _x = iter.clone().filter_map(|x| match x { Enum::A(s) => Some(s), _ => None }); + let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x { + Enum::A(s) => s, + _ => unreachable!(), + }); + let _x = iter + .clone() + .filter_map(|x| match x { Enum::A(s) => Some(s), _ => None }); + #[allow(clippy::unused_unit)] + let _x = iter + .clone() + .filter(|x| matches!(x, Enum::B)) + .map(|x| if let Enum::B = x { () } else { unreachable!() }); +} diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs index 6dd1e066aeb..50d8d2722c2 100644 --- a/tests/ui/manual_filter_map.rs +++ b/tests/ui/manual_filter_map.rs @@ -133,3 +133,31 @@ fn issue_8920() { .filter(|f| f.result_field.is_ok()) .map(|f| f.result_field.to_owned().unwrap()); } + +fn issue8010() { + #[derive(Clone)] + enum Enum { + A(i32), + B, + } + + let iter = [Enum::A(123), Enum::B].into_iter(); + + let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x { + Enum::A(s) => s, + _ => unreachable!(), + }); + let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x { + Enum::A(s) => s, + _ => unreachable!(), + }); + let _x = iter + .clone() + .filter(|x| matches!(x, Enum::A(_))) + .map(|x| if let Enum::A(s) = x { s } else { unreachable!() }); + #[allow(clippy::unused_unit)] + let _x = iter + .clone() + .filter(|x| matches!(x, Enum::B)) + .map(|x| if let Enum::B = x { () } else { unreachable!() }); +} diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr index 882468b0f5f..0e8672c0293 100644 --- a/tests/ui/manual_filter_map.stderr +++ b/tests/ui/manual_filter_map.stderr @@ -4,6 +4,11 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:9:30 + | +LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^ = note: `-D clippy::manual-filter-map` implied by `-D warnings` error: `filter(..).map(..)` can be simplified as `filter_map(..)` @@ -11,12 +16,24 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` | LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:12:31 + | +LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:15:19 | LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:15:31 + | +LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:18:10 @@ -25,6 +42,12 @@ LL | .filter(|&x| to_ref(to_opt(x)).is_some()) | __________^ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:18:22 + | +LL | .filter(|&x| to_ref(to_opt(x)).is_some()) + | ^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:21:10 @@ -33,6 +56,12 @@ LL | .filter(|x| to_ref(to_opt(*x)).is_some()) | __________^ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:21:21 + | +LL | .filter(|x| to_ref(to_opt(*x)).is_some()) + | ^^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:25:10 @@ -41,6 +70,12 @@ LL | .filter(|&x| to_ref(to_res(x)).is_ok()) | __________^ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:25:22 + | +LL | .filter(|&x| to_ref(to_res(x)).is_ok()) + | ^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:28:10 @@ -49,6 +84,12 @@ LL | .filter(|x| to_ref(to_res(*x)).is_ok()) | __________^ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:28:21 + | +LL | .filter(|x| to_ref(to_res(*x)).is_ok()) + | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_filter_map.rs:34:27 @@ -75,6 +116,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` | LL | iter::>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:37:41 + | +LL | iter::>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); + | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_filter_map.rs:39:30 @@ -117,6 +164,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` | LL | iter::>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_filter_map.rs:45:45 + | +LL | iter::>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); + | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` --> $DIR/manual_filter_map.rs:93:10 @@ -190,5 +243,23 @@ LL | .filter(|f| f.result_field.is_ok()) LL | | .map(|f| f.result_field.to_owned().unwrap()); | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())` -error: aborting due to 27 previous errors +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:146:27 + | +LL | let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x { + | ___________________________^ +LL | | Enum::A(s) => s, +LL | | _ => unreachable!(), +LL | | }); + | |______^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:156:10 + | +LL | .filter(|x| matches!(x, Enum::A(_))) + | __________^ +LL | | .map(|x| if let Enum::A(s) = x { s } else { unreachable!() }); + | |_____________________________________________________________________^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })` + +error: aborting due to 29 previous errors diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr index 693a06bb559..4e52b5efacf 100644 --- a/tests/ui/manual_find_map.stderr +++ b/tests/ui/manual_find_map.stderr @@ -4,6 +4,11 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:9:28 + | +LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^ = note: `-D clippy::manual-find-map` implied by `-D warnings` error: `find(..).map(..)` can be simplified as `find_map(..)` @@ -11,12 +16,24 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` | LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:12:29 + | +LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:15:19 | LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:15:29 + | +LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:18:10 @@ -25,6 +42,12 @@ LL | .find(|&x| to_ref(to_opt(x)).is_some()) | __________^ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:18:20 + | +LL | .find(|&x| to_ref(to_opt(x)).is_some()) + | ^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:21:10 @@ -33,6 +56,12 @@ LL | .find(|x| to_ref(to_opt(*x)).is_some()) | __________^ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:21:19 + | +LL | .find(|x| to_ref(to_opt(*x)).is_some()) + | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:25:10 @@ -41,6 +70,12 @@ LL | .find(|&x| to_ref(to_res(x)).is_ok()) | __________^ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:25:20 + | +LL | .find(|&x| to_ref(to_res(x)).is_ok()) + | ^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:28:10 @@ -49,6 +84,12 @@ LL | .find(|x| to_ref(to_res(*x)).is_ok()) | __________^ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:28:19 + | +LL | .find(|x| to_ref(to_res(*x)).is_ok()) + | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:34:26 @@ -91,6 +132,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` | LL | iter::>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:40:41 + | +LL | iter::>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); + | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:42:30 @@ -133,6 +180,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` | LL | iter::>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())` + | +note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once + --> $DIR/manual_find_map.rs:48:45 + | +LL | iter::>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); + | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` --> $DIR/manual_find_map.rs:96:10 From 885ce610fe2b257d2263e28954ab11a92e0372cf Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Wed, 12 Jul 2023 03:19:22 -0500 Subject: [PATCH 02/90] New lint [`four_forward_slashes`] Make it trim the contents --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/four_forward_slashes.rs | 82 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/four_forward_slashes.fixed | 44 +++++++++++++ tests/ui/four_forward_slashes.rs | 44 +++++++++++++ tests/ui/four_forward_slashes.stderr | 75 ++++++++++++++++++++++ 7 files changed, 249 insertions(+) create mode 100644 clippy_lints/src/four_forward_slashes.rs create mode 100644 tests/ui/four_forward_slashes.fixed create mode 100644 tests/ui/four_forward_slashes.rs create mode 100644 tests/ui/four_forward_slashes.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b6e3b865f..ec62d399aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4867,6 +4867,7 @@ Released 2018-09-13 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string +[`four_forward_slashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#four_forward_slashes [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3723de7299b..d8b14a9f967 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -184,6 +184,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, + crate::four_forward_slashes::FOUR_FORWARD_SLASHES_INFO, crate::from_over_into::FROM_OVER_INTO_INFO, crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs new file mode 100644 index 00000000000..7efa8c1b31f --- /dev/null +++ b/clippy_lints/src/four_forward_slashes.rs @@ -0,0 +1,82 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; +use rustc_errors::Applicability; +use rustc_hir::Item; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for outer doc comments written with 4 forward slashes (`////`). + /// + /// ### Why is this bad? + /// This is (probably) a typo, and results in it not being a doc comment; just a regular + /// comment. + /// + /// ### Example + /// ```rust + /// //// My amazing data structure + /// pub struct Foo { + /// // ... + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// /// My amazing data structure + /// pub struct Foo { + /// // ... + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub FOUR_FORWARD_SLASHES, + suspicious, + "comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)" +} +declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]); + +impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if item.span.from_expansion() { + return; + } + let src = cx.sess().source_map(); + let item_and_attrs_span = cx + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span)); + let (Some(file), _, _, end_line, _) = src.span_to_location_info(item_and_attrs_span) else { + return; + }; + for line in (0..end_line.saturating_sub(1)).rev() { + let Some(contents) = file.get_line(line) else { + continue; + }; + let contents = contents.trim(); + if contents.is_empty() { + break; + } + if contents.starts_with("////") { + let bounds = file.line_bounds(line); + let span = Span::new(bounds.start, bounds.end, SyntaxContext::root(), None); + + if snippet_opt(cx, span).is_some_and(|s| s.trim().starts_with("////")) { + span_lint_and_sugg( + cx, + FOUR_FORWARD_SLASHES, + span, + "comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't", + "make this a doc comment by removing one `/`", + // It's a little unfortunate but the span includes the `\n` yet the contents + // do not, so we must add it back. If some codebase uses `\r\n` instead they + // will need normalization but it should be fine + contents.replacen("////", "///", 1) + "\n", + Applicability::MachineApplicable, + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cebe2e44c0f..607c718b562 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -137,6 +137,7 @@ mod format_args; mod format_impl; mod format_push_string; mod formatting; +mod four_forward_slashes; mod from_over_into; mod from_raw_with_void_ptr; mod from_str_radix_10; @@ -1081,6 +1082,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(visibility::Visibility)); store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); + store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/four_forward_slashes.fixed b/tests/ui/four_forward_slashes.fixed new file mode 100644 index 00000000000..4f9b1f4b7c8 --- /dev/null +++ b/tests/ui/four_forward_slashes.fixed @@ -0,0 +1,44 @@ +//// first line borked doc comment. doesn't combust! +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![feature(custom_inner_attributes)] +#![allow(unused)] +#![warn(clippy::four_forward_slashes)] +#![no_main] +#![rustfmt::skip] + +#[macro_use] +extern crate proc_macros; + +/// whoops +fn a() {} + +/// whoops +#[allow(dead_code)] +fn b() {} + +/// whoops +/// two borked comments! +#[track_caller] +fn c() {} + +fn d() {} + +#[test] +/// between attributes +#[allow(dead_code)] +fn g() {} + +/// not very start of contents +fn h() {} + +external! { + //// don't lint me bozo + fn e() {} +} + +with_span! { + span + //// don't lint me bozo + fn f() {} +} diff --git a/tests/ui/four_forward_slashes.rs b/tests/ui/four_forward_slashes.rs new file mode 100644 index 00000000000..02f2084c17c --- /dev/null +++ b/tests/ui/four_forward_slashes.rs @@ -0,0 +1,44 @@ +//// first line borked doc comment. doesn't combust! +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![feature(custom_inner_attributes)] +#![allow(unused)] +#![warn(clippy::four_forward_slashes)] +#![no_main] +#![rustfmt::skip] + +#[macro_use] +extern crate proc_macros; + +//// whoops +fn a() {} + +//// whoops +#[allow(dead_code)] +fn b() {} + +//// whoops +//// two borked comments! +#[track_caller] +fn c() {} + +fn d() {} + +#[test] +//// between attributes +#[allow(dead_code)] +fn g() {} + + //// not very start of contents +fn h() {} + +external! { + //// don't lint me bozo + fn e() {} +} + +with_span! { + span + //// don't lint me bozo + fn f() {} +} diff --git a/tests/ui/four_forward_slashes.stderr b/tests/ui/four_forward_slashes.stderr new file mode 100644 index 00000000000..1640b241bf3 --- /dev/null +++ b/tests/ui/four_forward_slashes.stderr @@ -0,0 +1,75 @@ +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:13:1 + | +LL | / //// whoops +LL | | fn a() {} + | |_ + | + = note: `-D clippy::four-forward-slashes` implied by `-D warnings` +help: make this a doc comment by removing one `/` + | +LL + /// whoops + | + +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:16:1 + | +LL | / //// whoops +LL | | #[allow(dead_code)] + | |_ + | +help: make this a doc comment by removing one `/` + | +LL + /// whoops + | + +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:21:1 + | +LL | / //// two borked comments! +LL | | #[track_caller] + | |_ + | +help: make this a doc comment by removing one `/` + | +LL + /// two borked comments! + | + +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:20:1 + | +LL | / //// whoops +LL | | //// two borked comments! + | |_ + | +help: make this a doc comment by removing one `/` + | +LL + /// whoops + | + +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:28:1 + | +LL | / //// between attributes +LL | | #[allow(dead_code)] + | |_ + | +help: make this a doc comment by removing one `/` + | +LL + /// between attributes + | + +error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't + --> $DIR/four_forward_slashes.rs:32:1 + | +LL | / //// not very start of contents +LL | | fn h() {} + | |_ + | +help: make this a doc comment by removing one `/` + | +LL + /// not very start of contents + | + +error: aborting due to 6 previous errors + From 2e43d0c91717ed68cbfafc34d89dc95bca771c63 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Wed, 12 Jul 2023 19:39:19 -0500 Subject: [PATCH 03/90] Improve suggestion and add more tests --- clippy_lints/src/four_forward_slashes.rs | 67 ++++++++++++------- tests/ui/four_forward_slashes.fixed | 6 +- tests/ui/four_forward_slashes.rs | 6 +- tests/ui/four_forward_slashes.stderr | 41 +++++------- .../ui/four_forward_slashes_first_line.fixed | 7 ++ tests/ui/four_forward_slashes_first_line.rs | 7 ++ .../ui/four_forward_slashes_first_line.stderr | 15 +++++ 7 files changed, 98 insertions(+), 51 deletions(-) create mode 100644 tests/ui/four_forward_slashes_first_line.fixed create mode 100644 tests/ui/four_forward_slashes_first_line.rs create mode 100644 tests/ui/four_forward_slashes_first_line.stderr diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index 7efa8c1b31f..419c7734344 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -1,9 +1,9 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::Item; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{Span, SyntaxContext}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -40,43 +40,60 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { if item.span.from_expansion() { return; } - let src = cx.sess().source_map(); - let item_and_attrs_span = cx + let sm = cx.sess().source_map(); + let mut span = cx .tcx .hir() .attrs(item.hir_id()) .iter() .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span)); - let (Some(file), _, _, end_line, _) = src.span_to_location_info(item_and_attrs_span) else { + let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { return; }; + let mut bad_comments = vec![]; for line in (0..end_line.saturating_sub(1)).rev() { - let Some(contents) = file.get_line(line) else { - continue; + let Some(contents) = file.get_line(line).map(|c| c.trim().to_owned()) else { + return; }; - let contents = contents.trim(); - if contents.is_empty() { + // Keep searching until we find the next item + if !contents.is_empty() && !contents.starts_with("//") && !contents.starts_with("#[") { break; } - if contents.starts_with("////") { - let bounds = file.line_bounds(line); - let span = Span::new(bounds.start, bounds.end, SyntaxContext::root(), None); - if snippet_opt(cx, span).is_some_and(|s| s.trim().starts_with("////")) { - span_lint_and_sugg( - cx, - FOUR_FORWARD_SLASHES, - span, - "comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't", - "make this a doc comment by removing one `/`", - // It's a little unfortunate but the span includes the `\n` yet the contents - // do not, so we must add it back. If some codebase uses `\r\n` instead they - // will need normalization but it should be fine - contents.replacen("////", "///", 1) + "\n", + if contents.starts_with("////") && !matches!(contents.chars().nth(4), Some('/' | '!')) { + let bounds = file.line_bounds(line); + let line_span = Span::with_root_ctxt(bounds.start, bounds.end); + span = line_span.to(span); + bad_comments.push((line_span, contents)); + } + } + + if !bad_comments.is_empty() { + span_lint_and_then( + cx, + FOUR_FORWARD_SLASHES, + span, + "this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't", + |diag| { + let msg = if bad_comments.len() == 1 { + "make this a doc comment by removing one `/`" + } else { + "turn these into doc comments by removing one `/`" + }; + + diag.multipart_suggestion( + msg, + bad_comments + .into_iter() + // It's a little unfortunate but the span includes the `\n` yet the contents + // do not, so we must add it back. If some codebase uses `\r\n` instead they + // will need normalization but it should be fine + .map(|(span, c)| (span, c.replacen("////", "///", 1) + "\n")) + .collect(), Applicability::MachineApplicable, ); - } - } + }, + ); } } } diff --git a/tests/ui/four_forward_slashes.fixed b/tests/ui/four_forward_slashes.fixed index 4f9b1f4b7c8..54b2c414b62 100644 --- a/tests/ui/four_forward_slashes.fixed +++ b/tests/ui/four_forward_slashes.fixed @@ -1,4 +1,3 @@ -//// first line borked doc comment. doesn't combust! //@run-rustfix //@aux-build:proc_macros.rs:proc-macro #![feature(custom_inner_attributes)] @@ -32,6 +31,11 @@ fn g() {} /// not very start of contents fn h() {} +fn i() { + //// don't lint me bozo + todo!() +} + external! { //// don't lint me bozo fn e() {} diff --git a/tests/ui/four_forward_slashes.rs b/tests/ui/four_forward_slashes.rs index 02f2084c17c..facdc8cb17d 100644 --- a/tests/ui/four_forward_slashes.rs +++ b/tests/ui/four_forward_slashes.rs @@ -1,4 +1,3 @@ -//// first line borked doc comment. doesn't combust! //@run-rustfix //@aux-build:proc_macros.rs:proc-macro #![feature(custom_inner_attributes)] @@ -32,6 +31,11 @@ fn g() {} //// not very start of contents fn h() {} +fn i() { + //// don't lint me bozo + todo!() +} + external! { //// don't lint me bozo fn e() {} diff --git a/tests/ui/four_forward_slashes.stderr b/tests/ui/four_forward_slashes.stderr index 1640b241bf3..89162e6b010 100644 --- a/tests/ui/four_forward_slashes.stderr +++ b/tests/ui/four_forward_slashes.stderr @@ -1,5 +1,5 @@ -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:13:1 +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes.rs:12:1 | LL | / //// whoops LL | | fn a() {} @@ -11,11 +11,12 @@ help: make this a doc comment by removing one `/` LL + /// whoops | -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:16:1 +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes.rs:15:1 | LL | / //// whoops LL | | #[allow(dead_code)] +LL | | fn b() {} | |_ | help: make this a doc comment by removing one `/` @@ -23,35 +24,27 @@ help: make this a doc comment by removing one `/` LL + /// whoops | -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:21:1 - | -LL | / //// two borked comments! -LL | | #[track_caller] - | |_ - | -help: make this a doc comment by removing one `/` - | -LL + /// two borked comments! - | - -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:20:1 +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes.rs:19:1 | LL | / //// whoops LL | | //// two borked comments! +LL | | #[track_caller] +LL | | fn c() {} | |_ | -help: make this a doc comment by removing one `/` +help: turn these into doc comments by removing one `/` | LL + /// whoops +LL ~ /// two borked comments! | -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:28:1 +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes.rs:27:1 | LL | / //// between attributes LL | | #[allow(dead_code)] +LL | | fn g() {} | |_ | help: make this a doc comment by removing one `/` @@ -59,8 +52,8 @@ help: make this a doc comment by removing one `/` LL + /// between attributes | -error: comment with 4 forward slashes (`////`). This looks like a doc comment, but it isn't - --> $DIR/four_forward_slashes.rs:32:1 +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes.rs:31:1 | LL | / //// not very start of contents LL | | fn h() {} @@ -71,5 +64,5 @@ help: make this a doc comment by removing one `/` LL + /// not very start of contents | -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/four_forward_slashes_first_line.fixed b/tests/ui/four_forward_slashes_first_line.fixed new file mode 100644 index 00000000000..ce272b4c6cd --- /dev/null +++ b/tests/ui/four_forward_slashes_first_line.fixed @@ -0,0 +1,7 @@ +/// borked doc comment on the first line. doesn't combust! +fn a() {} + +//@run-rustfix +// This test's entire purpose is to make sure we don't panic if the comment with four slashes +// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an +// ICE. diff --git a/tests/ui/four_forward_slashes_first_line.rs b/tests/ui/four_forward_slashes_first_line.rs new file mode 100644 index 00000000000..d8f82d4410b --- /dev/null +++ b/tests/ui/four_forward_slashes_first_line.rs @@ -0,0 +1,7 @@ +//// borked doc comment on the first line. doesn't combust! +fn a() {} + +//@run-rustfix +// This test's entire purpose is to make sure we don't panic if the comment with four slashes +// extends to the first line of the file. This is likely pretty rare in production, but an ICE is an +// ICE. diff --git a/tests/ui/four_forward_slashes_first_line.stderr b/tests/ui/four_forward_slashes_first_line.stderr new file mode 100644 index 00000000000..7944da14feb --- /dev/null +++ b/tests/ui/four_forward_slashes_first_line.stderr @@ -0,0 +1,15 @@ +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> $DIR/four_forward_slashes_first_line.rs:1:1 + | +LL | / //// borked doc comment on the first line. doesn't combust! +LL | | fn a() {} + | |_ + | + = note: `-D clippy::four-forward-slashes` implied by `-D warnings` +help: make this a doc comment by removing one `/` + | +LL + /// borked doc comment on the first line. doesn't combust! + | + +error: aborting due to previous error + From c2aaa622eb1b8ce390dc5766199ed7c63d4900d7 Mon Sep 17 00:00:00 2001 From: syvb Date: Wed, 5 Jul 2023 22:13:37 -0400 Subject: [PATCH 04/90] Fix ICE on None.unwrap_or_default() --- .../src/methods/unnecessary_literal_unwrap.rs | 12 ++ tests/ui/unnecessary_literal_unwrap.fixed | 2 + tests/ui/unnecessary_literal_unwrap.rs | 2 + tests/ui/unnecessary_literal_unwrap.stderr | 88 +++++---- .../unnecessary_literal_unwrap_unfixable.rs | 2 + ...nnecessary_literal_unwrap_unfixable.stderr | 182 ++++++++++-------- 6 files changed, 165 insertions(+), 123 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 111bcaaecec..6d9a0451073 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -3,6 +3,8 @@ use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_middle::ty::print::with_forced_trimmed_paths; use super::UNNECESSARY_LITERAL_UNWRAP; @@ -84,6 +86,16 @@ pub(super) fn check( } Some(suggs) }, + ("None", "unwrap_or_default", _) => { + let ty = cx.typeck_results().expr_ty(expr); + let default_ty_string = if let ty::Adt(def, ..) = ty.kind() { + with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did()))) + } else { + "Default".to_string() + }; + Some(vec![(expr.span, format!("{default_ty_string}::default()"))]) + }, + _ if call_args.is_empty() => None, (_, _, Some(_)) => None, ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ ( diff --git a/tests/ui/unnecessary_literal_unwrap.fixed b/tests/ui/unnecessary_literal_unwrap.fixed index 276cd800b89..5eaa117b8e8 100644 --- a/tests/ui/unnecessary_literal_unwrap.fixed +++ b/tests/ui/unnecessary_literal_unwrap.fixed @@ -19,9 +19,11 @@ fn unwrap_option_some() { fn unwrap_option_none() { let _val = panic!(); let _val = panic!("this always happens"); + let _val: String = String::default(); panic!(); panic!("this always happens"); + String::default(); } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap.rs b/tests/ui/unnecessary_literal_unwrap.rs index 3065778d779..6408851b42a 100644 --- a/tests/ui/unnecessary_literal_unwrap.rs +++ b/tests/ui/unnecessary_literal_unwrap.rs @@ -19,9 +19,11 @@ fn unwrap_option_some() { fn unwrap_option_none() { let _val = None::<()>.unwrap(); let _val = None::<()>.expect("this always happens"); + let _val: String = None.unwrap_or_default(); None::<()>.unwrap(); None::<()>.expect("this always happens"); + None::.unwrap_or_default(); } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap.stderr b/tests/ui/unnecessary_literal_unwrap.stderr index 5823313b736..acaf37b13a5 100644 --- a/tests/ui/unnecessary_literal_unwrap.stderr +++ b/tests/ui/unnecessary_literal_unwrap.stderr @@ -64,14 +64,20 @@ help: remove the `None` and `expect()` LL | let _val = panic!("this always happens"); | ~~~~~~~ ~ +error: used `unwrap_or_default()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:22:24 + | +LL | let _val: String = None.unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()` + error: used `unwrap()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:23:5 + --> $DIR/unnecessary_literal_unwrap.rs:24:5 | LL | None::<()>.unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap()`: `panic!()` error: used `expect()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:24:5 + --> $DIR/unnecessary_literal_unwrap.rs:25:5 | LL | None::<()>.expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,8 +87,14 @@ help: remove the `None` and `expect()` LL | panic!("this always happens"); | ~~~~~~~ ~ +error: used `unwrap_or_default()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:26:5 + | +LL | None::.unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()` + error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:28:16 + --> $DIR/unnecessary_literal_unwrap.rs:30:16 | LL | let _val = Ok::<_, ()>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +106,7 @@ LL + let _val = 1; | error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:29:16 + --> $DIR/unnecessary_literal_unwrap.rs:31:16 | LL | let _val = Ok::<_, ()>(1).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +118,7 @@ LL + let _val = 1; | error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:30:16 + --> $DIR/unnecessary_literal_unwrap.rs:32:16 | LL | let _val = Ok::<_, ()>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,7 +129,7 @@ LL | let _val = panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:31:16 + --> $DIR/unnecessary_literal_unwrap.rs:33:16 | LL | let _val = Ok::<_, ()>(1).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,7 +140,7 @@ LL | let _val = panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:33:5 + --> $DIR/unnecessary_literal_unwrap.rs:35:5 | LL | Ok::<_, ()>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -140,7 +152,7 @@ LL + 1; | error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:34:5 + --> $DIR/unnecessary_literal_unwrap.rs:36:5 | LL | Ok::<_, ()>(1).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,7 +164,7 @@ LL + 1; | error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:35:5 + --> $DIR/unnecessary_literal_unwrap.rs:37:5 | LL | Ok::<_, ()>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +175,7 @@ LL | panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:36:5 + --> $DIR/unnecessary_literal_unwrap.rs:38:5 | LL | Ok::<_, ()>(1).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,7 +186,7 @@ LL | panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:40:16 + --> $DIR/unnecessary_literal_unwrap.rs:42:16 | LL | let _val = Err::<(), _>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -186,7 +198,7 @@ LL + let _val = 1; | error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:41:16 + --> $DIR/unnecessary_literal_unwrap.rs:43:16 | LL | let _val = Err::<(), _>(1).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +210,7 @@ LL + let _val = 1; | error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:42:16 + --> $DIR/unnecessary_literal_unwrap.rs:44:16 | LL | let _val = Err::<(), _>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +221,7 @@ LL | let _val = panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:43:16 + --> $DIR/unnecessary_literal_unwrap.rs:45:16 | LL | let _val = Err::<(), _>(1).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +232,7 @@ LL | let _val = panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:45:5 + --> $DIR/unnecessary_literal_unwrap.rs:47:5 | LL | Err::<(), _>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,7 +244,7 @@ LL + 1; | error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:46:5 + --> $DIR/unnecessary_literal_unwrap.rs:48:5 | LL | Err::<(), _>(1).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +256,7 @@ LL + 1; | error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:47:5 + --> $DIR/unnecessary_literal_unwrap.rs:49:5 | LL | Err::<(), _>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -255,7 +267,7 @@ LL | panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:48:5 + --> $DIR/unnecessary_literal_unwrap.rs:50:5 | LL | Err::<(), _>(1).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -266,7 +278,7 @@ LL | panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:52:16 + --> $DIR/unnecessary_literal_unwrap.rs:54:16 | LL | let _val = Some(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^ @@ -278,7 +290,7 @@ LL + let _val = 1; | error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:53:16 + --> $DIR/unnecessary_literal_unwrap.rs:55:16 | LL | let _val = Some(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -290,7 +302,7 @@ LL + let _val = 1; | error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:54:16 + --> $DIR/unnecessary_literal_unwrap.rs:56:16 | LL | let _val = Some(1).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -302,7 +314,7 @@ LL + let _val = 1; | error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:56:5 + --> $DIR/unnecessary_literal_unwrap.rs:58:5 | LL | Some(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +326,7 @@ LL + 1; | error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:57:5 + --> $DIR/unnecessary_literal_unwrap.rs:59:5 | LL | Some(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -326,7 +338,7 @@ LL + 1; | error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:58:5 + --> $DIR/unnecessary_literal_unwrap.rs:60:5 | LL | Some(1).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -338,7 +350,7 @@ LL + 1; | error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:62:16 + --> $DIR/unnecessary_literal_unwrap.rs:64:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +362,7 @@ LL + let _val = 1; | error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:63:16 + --> $DIR/unnecessary_literal_unwrap.rs:65:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -362,7 +374,7 @@ LL + let _val = 1; | error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:64:16 + --> $DIR/unnecessary_literal_unwrap.rs:66:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -374,7 +386,7 @@ LL + let _val = 1; | error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:66:5 + --> $DIR/unnecessary_literal_unwrap.rs:68:5 | LL | Ok::<_, ()>(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,7 +398,7 @@ LL + 1; | error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:67:5 + --> $DIR/unnecessary_literal_unwrap.rs:69:5 | LL | Ok::<_, ()>(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -398,7 +410,7 @@ LL + 1; | error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:68:5 + --> $DIR/unnecessary_literal_unwrap.rs:70:5 | LL | Ok::<_, ()>(1).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -410,7 +422,7 @@ LL + 1; | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:82:22 + --> $DIR/unnecessary_literal_unwrap.rs:84:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -422,7 +434,7 @@ LL + let _ = 1; | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:83:22 + --> $DIR/unnecessary_literal_unwrap.rs:85:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() + *(&1 as *const i32) }; // needs to keep the unsafe block | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -434,7 +446,7 @@ LL + let _ = unsafe { 1 + *(&1 as *const i32) }; // needs to keep the unsafe | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:84:22 + --> $DIR/unnecessary_literal_unwrap.rs:86:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() } + 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -446,7 +458,7 @@ LL + let _ = 1 + 1; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:85:22 + --> $DIR/unnecessary_literal_unwrap.rs:87:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -458,7 +470,7 @@ LL + let _ = 1; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:86:22 + --> $DIR/unnecessary_literal_unwrap.rs:88:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() + *(&1 as *const i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -470,7 +482,7 @@ LL + let _ = unsafe { 1 + *(&1 as *const i32) }; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:87:22 + --> $DIR/unnecessary_literal_unwrap.rs:89:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() } + 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -482,7 +494,7 @@ LL + let _ = 1 + 1; | error: used `unwrap_err_unchecked()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:88:22 + --> $DIR/unnecessary_literal_unwrap.rs:90:22 | LL | let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -493,5 +505,5 @@ LL - let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; LL + let _ = 123; | -error: aborting due to 43 previous errors +error: aborting due to 45 previous errors diff --git a/tests/ui/unnecessary_literal_unwrap_unfixable.rs b/tests/ui/unnecessary_literal_unwrap_unfixable.rs index 711fdce3962..41300aceb41 100644 --- a/tests/ui/unnecessary_literal_unwrap_unfixable.rs +++ b/tests/ui/unnecessary_literal_unwrap_unfixable.rs @@ -21,6 +21,8 @@ fn unwrap_option_none() { let val = None::<()>; let _val2 = val.unwrap(); let _val2 = val.expect("this always happens"); + let _val3: u8 = None.unwrap_or_default(); + None::<()>.unwrap_or_default(); } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap_unfixable.stderr b/tests/ui/unnecessary_literal_unwrap_unfixable.stderr index feb9325b77a..2d1270d4717 100644 --- a/tests/ui/unnecessary_literal_unwrap_unfixable.stderr +++ b/tests/ui/unnecessary_literal_unwrap_unfixable.stderr @@ -95,509 +95,521 @@ help: remove the `None` and `expect()` LL | let val = None::<()>; | ^^^^^^^^^^ +error: used `unwrap_or_default()` on `None` value + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:24:21 + | +LL | let _val3: u8 = None.unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `Default::default()` + +error: used `unwrap_or_default()` on `None` value + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:25:5 + | +LL | None::<()>.unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `Default::default()` + error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:28:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:30:17 | LL | let _val2 = val.unwrap(); | ^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:27:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:31:17 | LL | let _val2 = val.expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:27:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:30:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:32:17 | LL | let _val2 = val.unwrap_err(); | ^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:27:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:31:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:33:17 | LL | let _val2 = val.expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:27:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:29:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:35:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:35:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:36:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:36:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:39:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:37:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:39:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:38:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:41:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:43:17 | LL | let _val2 = val.unwrap(); | ^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:44:17 | LL | let _val2 = val.expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:43:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:45:17 | LL | let _val2 = val.unwrap_err(); | ^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:44:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:46:17 | LL | let _val2 = val.expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:40:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:42:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:49:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:51:17 | LL | let _val2 = val.unwrap_err(); | ^^^^^^^^^^^^^^^^ | help: remove the `Err` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:48:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15 | LL | let val = Err::<(), _>(1); | ^^^^^^^^^^^^^^^ error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:52:17 | LL | let _val2 = val.expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:48:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15 | LL | let val = Err::<(), _>(1); | ^^^^^^^^^^^^^^^ error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:51:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:53:17 | LL | let _val2 = val.unwrap(); | ^^^^^^^^^^^^ | help: remove the `Err` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:48:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15 | LL | let val = Err::<(), _>(1); | ^^^^^^^^^^^^^^^ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:52:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:54:17 | LL | let _val2 = val.expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:48:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:50:15 | LL | let val = Err::<(), _>(1); | ^^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:56:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:56:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:57:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:57:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:60:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:58:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:60:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:59:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:16 | LL | let _val = Err::<(), usize>([1, 2, 3].iter().sum()).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:62:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:64:17 | LL | let _val2 = val.unwrap_err(); | ^^^^^^^^^^^^^^^^ | help: remove the `Err` and `unwrap_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15 | LL | let val = Err::<(), usize>([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:65:17 | LL | let _val2 = val.expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect_err()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15 | LL | let val = Err::<(), usize>([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:64:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:66:17 | LL | let _val2 = val.unwrap(); | ^^^^^^^^^^^^ | help: remove the `Err` and `unwrap()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15 | LL | let val = Err::<(), usize>([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:65:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:67:17 | LL | let _val2 = val.expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Err` and `expect()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:61:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:63:15 | LL | let val = Err::<(), usize>([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:70:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:72:17 | LL | let _val2 = val.unwrap_or(2); | ^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:69:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15 | LL | let val = Some(1); | ^^^^^^^ error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:73:17 | LL | let _val2 = val.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:69:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15 | LL | let val = Some(1); | ^^^^^^^ error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:72:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:74:17 | LL | let _val2 = val.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:69:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:71:15 | LL | let val = Some(1); | ^^^^^^^ error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:76:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:76:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:77:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:79:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:77:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:79:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:78:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:16 | LL | let _val = Some::([1, 2, 3].iter().sum()).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:81:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:83:17 | LL | let _val2 = val.unwrap_or(2); | ^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15 | LL | let val = Some::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:84:17 | LL | let _val2 = val.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15 | LL | let val = Some::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:83:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:85:17 | LL | let _val2 = val.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Some` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:80:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:82:15 | LL | let val = Some::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:88:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:90:17 | LL | let _val2 = val.unwrap_or(2); | ^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:87:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:91:17 | LL | let _val2 = val.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:87:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:90:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:92:17 | LL | let _val2 = val.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:87:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:89:15 | LL | let val = Ok::<_, ()>(1); | ^^^^^^^^^^^^^^ error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:94:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:94:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:95:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:97:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:95:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:97:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:96:16 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:16 | LL | let _val = Ok::([1, 2, 3].iter().sum()).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:99:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:101:17 | LL | let _val2 = val.unwrap_or(2); | ^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:102:17 | LL | let _val2 = val.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_default()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:101:17 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:103:17 | LL | let _val2 = val.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `Ok` and `unwrap_or_else()` - --> $DIR/unnecessary_literal_unwrap_unfixable.rs:98:15 + --> $DIR/unnecessary_literal_unwrap_unfixable.rs:100:15 | LL | let val = Ok::([1, 2, 3].iter().sum()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 50 previous errors +error: aborting due to 52 previous errors From 8d258c15080f23b13b5268ac757c8f71adbe38b6 Mon Sep 17 00:00:00 2001 From: syvb Date: Wed, 5 Jul 2023 23:24:20 -0400 Subject: [PATCH 05/90] Add handling for None.unwrap_or(_else) --- .../src/methods/unnecessary_literal_unwrap.rs | 19 ++ tests/ui/unnecessary_literal_unwrap.fixed | 9 + tests/ui/unnecessary_literal_unwrap.rs | 9 + tests/ui/unnecessary_literal_unwrap.stderr | 180 ++++++++++++++---- 4 files changed, 175 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 6d9a0451073..937aac8d25e 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -24,6 +24,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) - } } +#[expect(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -95,6 +96,24 @@ pub(super) fn check( }; Some(vec![(expr.span, format!("{default_ty_string}::default()"))]) }, + ("None", "unwrap_or", _) => Some(vec![ + (expr.span.with_hi(args[0].span.lo()), String::new()), + (expr.span.with_lo(args[0].span.hi()), String::new()), + ]), + ("None", "unwrap_or_else", _) => match args[0].kind { + hir::ExprKind::Closure(hir::Closure { + fn_decl: + hir::FnDecl { + output: hir::FnRetTy::DefaultReturn(span) | hir::FnRetTy::Return(hir::Ty { span, .. }), + .. + }, + .. + }) => Some(vec![ + (expr.span.with_hi(span.hi()), String::new()), + (expr.span.with_lo(args[0].span.hi()), String::new()), + ]), + _ => None, + }, _ if call_args.is_empty() => None, (_, _, Some(_)) => None, ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ diff --git a/tests/ui/unnecessary_literal_unwrap.fixed b/tests/ui/unnecessary_literal_unwrap.fixed index 5eaa117b8e8..72d52c62355 100644 --- a/tests/ui/unnecessary_literal_unwrap.fixed +++ b/tests/ui/unnecessary_literal_unwrap.fixed @@ -16,14 +16,23 @@ fn unwrap_option_some() { 1; } +#[rustfmt::skip] // force rustfmt not to remove braces in `|| { 234 }` fn unwrap_option_none() { let _val = panic!(); let _val = panic!("this always happens"); let _val: String = String::default(); + let _val: u16 = 234; + let _val: u16 = 234; + let _val: u16 = { 234 }; + let _val: u16 = { 234 }; panic!(); panic!("this always happens"); String::default(); + 234; + 234; + { 234 }; + { 234 }; } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap.rs b/tests/ui/unnecessary_literal_unwrap.rs index 6408851b42a..7d713ea205f 100644 --- a/tests/ui/unnecessary_literal_unwrap.rs +++ b/tests/ui/unnecessary_literal_unwrap.rs @@ -16,14 +16,23 @@ fn unwrap_option_some() { Some(1).expect("this never happens"); } +#[rustfmt::skip] // force rustfmt not to remove braces in `|| { 234 }` fn unwrap_option_none() { let _val = None::<()>.unwrap(); let _val = None::<()>.expect("this always happens"); let _val: String = None.unwrap_or_default(); + let _val: u16 = None.unwrap_or(234); + let _val: u16 = None.unwrap_or_else(|| 234); + let _val: u16 = None.unwrap_or_else(|| { 234 }); + let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 }); None::<()>.unwrap(); None::<()>.expect("this always happens"); None::.unwrap_or_default(); + None::.unwrap_or(234); + None::.unwrap_or_else(|| 234); + None::.unwrap_or_else(|| { 234 }); + None::.unwrap_or_else(|| -> u16 { 234 }); } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap.stderr b/tests/ui/unnecessary_literal_unwrap.stderr index acaf37b13a5..7f603d6ef58 100644 --- a/tests/ui/unnecessary_literal_unwrap.stderr +++ b/tests/ui/unnecessary_literal_unwrap.stderr @@ -48,13 +48,13 @@ LL + 1; | error: used `unwrap()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:20:16 + --> $DIR/unnecessary_literal_unwrap.rs:21:16 | LL | let _val = None::<()>.unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap()`: `panic!()` error: used `expect()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:21:16 + --> $DIR/unnecessary_literal_unwrap.rs:22:16 | LL | let _val = None::<()>.expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,19 +65,67 @@ LL | let _val = panic!("this always happens"); | ~~~~~~~ ~ error: used `unwrap_or_default()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:22:24 + --> $DIR/unnecessary_literal_unwrap.rs:23:24 | LL | let _val: String = None.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()` +error: used `unwrap_or()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:24:21 + | +LL | let _val: u16 = None.unwrap_or(234); + | ^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or()` + | +LL - let _val: u16 = None.unwrap_or(234); +LL + let _val: u16 = 234; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:25:21 + | +LL | let _val: u16 = None.unwrap_or_else(|| 234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - let _val: u16 = None.unwrap_or_else(|| 234); +LL + let _val: u16 = 234; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:26:21 + | +LL | let _val: u16 = None.unwrap_or_else(|| { 234 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - let _val: u16 = None.unwrap_or_else(|| { 234 }); +LL + let _val: u16 = { 234 }; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:27:21 + | +LL | let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 }); +LL + let _val: u16 = { 234 }; + | + error: used `unwrap()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:24:5 + --> $DIR/unnecessary_literal_unwrap.rs:29:5 | LL | None::<()>.unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap()`: `panic!()` error: used `expect()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:25:5 + --> $DIR/unnecessary_literal_unwrap.rs:30:5 | LL | None::<()>.expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,13 +136,61 @@ LL | panic!("this always happens"); | ~~~~~~~ ~ error: used `unwrap_or_default()` on `None` value - --> $DIR/unnecessary_literal_unwrap.rs:26:5 + --> $DIR/unnecessary_literal_unwrap.rs:31:5 | LL | None::.unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the `None` and `unwrap_or_default()`: `String::default()` +error: used `unwrap_or()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:32:5 + | +LL | None::.unwrap_or(234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or()` + | +LL - None::.unwrap_or(234); +LL + 234; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:33:5 + | +LL | None::.unwrap_or_else(|| 234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - None::.unwrap_or_else(|| 234); +LL + 234; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:34:5 + | +LL | None::.unwrap_or_else(|| { 234 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - None::.unwrap_or_else(|| { 234 }); +LL + { 234 }; + | + +error: used `unwrap_or_else()` on `None` value + --> $DIR/unnecessary_literal_unwrap.rs:35:5 + | +LL | None::.unwrap_or_else(|| -> u16 { 234 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `None` and `unwrap_or_else()` + | +LL - None::.unwrap_or_else(|| -> u16 { 234 }); +LL + { 234 }; + | + error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:30:16 + --> $DIR/unnecessary_literal_unwrap.rs:39:16 | LL | let _val = Ok::<_, ()>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +202,7 @@ LL + let _val = 1; | error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:31:16 + --> $DIR/unnecessary_literal_unwrap.rs:40:16 | LL | let _val = Ok::<_, ()>(1).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +214,7 @@ LL + let _val = 1; | error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:32:16 + --> $DIR/unnecessary_literal_unwrap.rs:41:16 | LL | let _val = Ok::<_, ()>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +225,7 @@ LL | let _val = panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:33:16 + --> $DIR/unnecessary_literal_unwrap.rs:42:16 | LL | let _val = Ok::<_, ()>(1).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -140,7 +236,7 @@ LL | let _val = panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:35:5 + --> $DIR/unnecessary_literal_unwrap.rs:44:5 | LL | Ok::<_, ()>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,7 +248,7 @@ LL + 1; | error: used `expect()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:36:5 + --> $DIR/unnecessary_literal_unwrap.rs:45:5 | LL | Ok::<_, ()>(1).expect("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -164,7 +260,7 @@ LL + 1; | error: used `unwrap_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:37:5 + --> $DIR/unnecessary_literal_unwrap.rs:46:5 | LL | Ok::<_, ()>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -175,7 +271,7 @@ LL | panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect_err()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:38:5 + --> $DIR/unnecessary_literal_unwrap.rs:47:5 | LL | Ok::<_, ()>(1).expect_err("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -186,7 +282,7 @@ LL | panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:42:16 + --> $DIR/unnecessary_literal_unwrap.rs:51:16 | LL | let _val = Err::<(), _>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +294,7 @@ LL + let _val = 1; | error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:43:16 + --> $DIR/unnecessary_literal_unwrap.rs:52:16 | LL | let _val = Err::<(), _>(1).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +306,7 @@ LL + let _val = 1; | error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:44:16 + --> $DIR/unnecessary_literal_unwrap.rs:53:16 | LL | let _val = Err::<(), _>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +317,7 @@ LL | let _val = panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:45:16 + --> $DIR/unnecessary_literal_unwrap.rs:54:16 | LL | let _val = Err::<(), _>(1).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,7 +328,7 @@ LL | let _val = panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:47:5 + --> $DIR/unnecessary_literal_unwrap.rs:56:5 | LL | Err::<(), _>(1).unwrap_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +340,7 @@ LL + 1; | error: used `expect_err()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:48:5 + --> $DIR/unnecessary_literal_unwrap.rs:57:5 | LL | Err::<(), _>(1).expect_err("this never happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +352,7 @@ LL + 1; | error: used `unwrap()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:49:5 + --> $DIR/unnecessary_literal_unwrap.rs:58:5 | LL | Err::<(), _>(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -267,7 +363,7 @@ LL | panic!("{:?}", 1); | ~~~~~~~~~~~~~~ ~ error: used `expect()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:50:5 + --> $DIR/unnecessary_literal_unwrap.rs:59:5 | LL | Err::<(), _>(1).expect("this always happens"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -278,7 +374,7 @@ LL | panic!("{1}: {:?}", 1, "this always happens"); | ~~~~~~~~~~~~~~~~~~~ ~ error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:54:16 + --> $DIR/unnecessary_literal_unwrap.rs:63:16 | LL | let _val = Some(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^ @@ -290,7 +386,7 @@ LL + let _val = 1; | error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:55:16 + --> $DIR/unnecessary_literal_unwrap.rs:64:16 | LL | let _val = Some(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -302,7 +398,7 @@ LL + let _val = 1; | error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:56:16 + --> $DIR/unnecessary_literal_unwrap.rs:65:16 | LL | let _val = Some(1).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +410,7 @@ LL + let _val = 1; | error: used `unwrap_or()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:58:5 + --> $DIR/unnecessary_literal_unwrap.rs:67:5 | LL | Some(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^ @@ -326,7 +422,7 @@ LL + 1; | error: used `unwrap_or_default()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:59:5 + --> $DIR/unnecessary_literal_unwrap.rs:68:5 | LL | Some(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -338,7 +434,7 @@ LL + 1; | error: used `unwrap_or_else()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:60:5 + --> $DIR/unnecessary_literal_unwrap.rs:69:5 | LL | Some(1).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +446,7 @@ LL + 1; | error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:64:16 + --> $DIR/unnecessary_literal_unwrap.rs:73:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -362,7 +458,7 @@ LL + let _val = 1; | error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:65:16 + --> $DIR/unnecessary_literal_unwrap.rs:74:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -374,7 +470,7 @@ LL + let _val = 1; | error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:66:16 + --> $DIR/unnecessary_literal_unwrap.rs:75:16 | LL | let _val = Ok::<_, ()>(1).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,7 +482,7 @@ LL + let _val = 1; | error: used `unwrap_or()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:68:5 + --> $DIR/unnecessary_literal_unwrap.rs:77:5 | LL | Ok::<_, ()>(1).unwrap_or(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -398,7 +494,7 @@ LL + 1; | error: used `unwrap_or_default()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:69:5 + --> $DIR/unnecessary_literal_unwrap.rs:78:5 | LL | Ok::<_, ()>(1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -410,7 +506,7 @@ LL + 1; | error: used `unwrap_or_else()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:70:5 + --> $DIR/unnecessary_literal_unwrap.rs:79:5 | LL | Ok::<_, ()>(1).unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -422,7 +518,7 @@ LL + 1; | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:84:22 + --> $DIR/unnecessary_literal_unwrap.rs:93:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -434,7 +530,7 @@ LL + let _ = 1; | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:85:22 + --> $DIR/unnecessary_literal_unwrap.rs:94:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() + *(&1 as *const i32) }; // needs to keep the unsafe block | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -446,7 +542,7 @@ LL + let _ = unsafe { 1 + *(&1 as *const i32) }; // needs to keep the unsafe | error: used `unwrap_unchecked()` on `Some` value - --> $DIR/unnecessary_literal_unwrap.rs:86:22 + --> $DIR/unnecessary_literal_unwrap.rs:95:22 | LL | let _ = unsafe { Some(1).unwrap_unchecked() } + 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -458,7 +554,7 @@ LL + let _ = 1 + 1; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:87:22 + --> $DIR/unnecessary_literal_unwrap.rs:96:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -470,7 +566,7 @@ LL + let _ = 1; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:88:22 + --> $DIR/unnecessary_literal_unwrap.rs:97:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() + *(&1 as *const i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -482,7 +578,7 @@ LL + let _ = unsafe { 1 + *(&1 as *const i32) }; | error: used `unwrap_unchecked()` on `Ok` value - --> $DIR/unnecessary_literal_unwrap.rs:89:22 + --> $DIR/unnecessary_literal_unwrap.rs:98:22 | LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() } + 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -494,7 +590,7 @@ LL + let _ = 1 + 1; | error: used `unwrap_err_unchecked()` on `Err` value - --> $DIR/unnecessary_literal_unwrap.rs:90:22 + --> $DIR/unnecessary_literal_unwrap.rs:99:22 | LL | let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -505,5 +601,5 @@ LL - let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; LL + let _ = 123; | -error: aborting due to 45 previous errors +error: aborting due to 53 previous errors From fdb2e363d33e04aa306041e2c084e13c6ce489e1 Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Tue, 11 Jul 2023 22:35:29 +0100 Subject: [PATCH 06/90] refactor(rustc_middle): Substs -> GenericArg --- .../src/assertions_on_result_states.rs | 6 +- clippy_lints/src/bool_assert_comparison.rs | 2 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 2 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/copy_iterator.rs | 2 +- clippy_lints/src/default.rs | 8 +- clippy_lints/src/default_numeric_fallback.rs | 8 +- clippy_lints/src/dereference.rs | 46 +++++----- clippy_lints/src/derivable_impls.rs | 14 ++-- clippy_lints/src/derive.rs | 12 +-- clippy_lints/src/empty_enum.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/eta_reduction.rs | 28 +++---- clippy_lints/src/from_over_into.rs | 4 +- clippy_lints/src/functions/must_use.rs | 6 +- clippy_lints/src/functions/result.rs | 6 +- clippy_lints/src/future_not_send.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 4 +- clippy_lints/src/inherent_impl.rs | 2 +- .../src/iter_not_returning_iterator.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 6 +- clippy_lints/src/len_zero.rs | 4 +- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/missing_spin_loop.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/manual_bits.rs | 2 +- .../src/manual_slice_size_calculation.rs | 2 +- clippy_lints/src/map_unit_fn.rs | 2 +- clippy_lints/src/matches/match_as_ref.rs | 8 +- .../src/matches/redundant_pattern_match.rs | 2 +- .../matches/rest_pat_in_fully_bound_struct.rs | 2 +- .../matches/significant_drop_in_scrutinee.rs | 2 +- .../src/methods/bytes_count_to_len.rs | 2 +- ...se_sensitive_file_extension_comparisons.rs | 2 +- clippy_lints/src/methods/err_expect.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 4 +- clippy_lints/src/methods/flat_map_option.rs | 2 +- clippy_lints/src/methods/get_first.rs | 2 +- clippy_lints/src/methods/implicit_clone.rs | 2 +- .../src/methods/inefficient_to_string.rs | 8 +- clippy_lints/src/methods/manual_ok_or.rs | 2 +- clippy_lints/src/methods/map_clone.rs | 2 +- .../src/methods/map_collect_result_unit.rs | 4 +- clippy_lints/src/methods/map_err_ignore.rs | 2 +- clippy_lints/src/methods/map_flatten.rs | 2 +- clippy_lints/src/methods/mod.rs | 8 +- clippy_lints/src/methods/mut_mutex_lock.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 14 ++-- clippy_lints/src/methods/ok_expect.rs | 2 +- clippy_lints/src/methods/open_options.rs | 2 +- .../src/methods/path_buf_push_overwrite.rs | 2 +- .../src/methods/stable_sort_primitive.rs | 2 +- clippy_lints/src/methods/suspicious_splitn.rs | 2 +- .../src/methods/unnecessary_sort_by.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 38 ++++----- clippy_lints/src/methods/utils.rs | 2 +- .../src/methods/vec_resize_to_zero.rs | 2 +- clippy_lints/src/mut_key.rs | 6 +- clippy_lints/src/mut_reference.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 4 +- clippy_lints/src/new_without_default.rs | 6 +- clippy_lints/src/non_copy_const.rs | 20 ++--- .../src/non_send_fields_in_send_ty.rs | 14 ++-- clippy_lints/src/only_used_in_recursion.rs | 18 ++-- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/ptr.rs | 14 ++-- clippy_lints/src/redundant_slicing.rs | 4 +- clippy_lints/src/returns.rs | 4 +- clippy_lints/src/self_named_constructors.rs | 2 +- .../src/significant_drop_tightening.rs | 2 +- clippy_lints/src/size_of_in_element_count.rs | 4 +- .../src/transmute/transmute_undefined_repr.rs | 8 +- .../transmute/unsound_collection_transmute.rs | 6 +- clippy_lints/src/uninit_vec.rs | 4 +- clippy_lints/src/unit_return_expecting_ord.rs | 8 +- clippy_lints/src/unit_types/let_unit_value.rs | 2 +- clippy_lints/src/unnamed_address.rs | 2 +- clippy_lints/src/use_self.rs | 10 +-- clippy_lints/src/useless_conversion.rs | 8 +- .../interning_defined_symbol.rs | 2 +- .../utils/internal_lints/msrv_attr_impl.rs | 4 +- .../internal_lints/unnecessary_def_path.rs | 4 +- clippy_lints/src/vec.rs | 4 +- clippy_lints/src/zero_sized_map_values.rs | 4 +- clippy_utils/src/consts.rs | 16 ++-- clippy_utils/src/eager_or_lazy.rs | 6 +- clippy_utils/src/lib.rs | 10 +-- clippy_utils/src/qualify_min_const_fn.rs | 4 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 84 +++++++++---------- tests/ui/crashes/ice-6256.rs | 2 +- tests/ui/eta.fixed | 2 +- tests/ui/eta.rs | 2 +- 94 files changed, 307 insertions(+), 307 deletions(-) diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index f6d6c23bb6e..2980c9d6db3 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() && is_type_diagnostic_item(cx, result_type, sym::Result) - && let ty::Adt(_, substs) = result_type.kind() + && let ty::Adt(_, args) = result_type.kind() { if !is_copy(cx, result_type) { if result_type_with_refs != result_type { @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""}; let mut app = Applicability::MachineApplicable; match method_segment.ident.as_str() { - "is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => { + "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { span_lint_and_sugg( cx, ASSERTIONS_ON_RESULT_STATES, @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { app, ); } - "is_err" if type_suitable_to_unwrap(cx, substs.type_at(0)) => { + "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { span_lint_and_sugg( cx, ASSERTIONS_ON_RESULT_STATES, diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index e8775b08144..d984fddc57a 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -61,7 +61,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - ) }) .map_or(false, |assoc_item| { - let proj = Ty::new_projection(cx.tcx,assoc_item.def_id, cx.tcx.mk_substs_trait(ty, [])); + let proj = Ty::new_projection(cx.tcx,assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs index 1633ffd589c..fa1550a0ef9 100644 --- a/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind && method_name.ident.name == rustc_span::sym::as_ptr && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) - && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).subst_identity() + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() && let Some(recv) = snippet_opt(cx, receiver.span) diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 6c8ee296c75..5bf467efa0f 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -66,7 +66,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned") && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && let Some(def_id) = cx.tcx.impl_of_method(def_id) - && cx.tcx.type_of(def_id).subst_identity().is_unsafe_ptr() + && cx.tcx.type_of(def_id).instantiate_identity().is_unsafe_ptr() { true } else { diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 0fc11523298..5d04ad0112d 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator { of_trait: Some(ref trait_ref), .. }) = item.kind; - let ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if is_copy(cx, ty); if let Some(trait_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 80c22742ba4..763ad0264ad 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .fields .iter() .all(|field| { - is_copy(cx, cx.tcx.type_of(field.did).subst_identity()) + is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) }); if !has_drop(cx, binding_type) || all_fields_are_copy; then { @@ -219,11 +219,11 @@ impl<'tcx> LateLintPass<'tcx> for Default { // give correct suggestion if generics are involved (see #6944) let binding_type = if_chain! { - if let ty::Adt(adt_def, substs) = binding_type.kind(); - if !substs.is_empty(); + if let ty::Adt(adt_def, args) = binding_type.kind(); + if !args.is_empty(); then { let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); - let generic_args = substs.iter().collect::>(); + let generic_args = args.iter().collect::>(); let tys_str = generic_args .iter() .map(ToString::to_string) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index e53a9877b20..9217edcef07 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -141,7 +141,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::MethodCall(_, receiver, args, _) => { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { - let fn_sig = self.cx.tcx.fn_sig(def_id).subst_identity().skip_binder(); + let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { self.ty_bounds.push((*bound).into()); self.visit_expr(expr); @@ -167,7 +167,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { .iter() .find_map(|f_def| { if f_def.ident(self.cx.tcx) == field.ident - { Some(self.cx.tcx.type_of(f_def.did).subst_identity()) } + { Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) } else { None } }); self.ty_bounds.push(bound.into()); @@ -213,9 +213,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option> { let node_ty = cx.typeck_results().node_type_opt(hir_id)?; - // We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs. + // We can't use `Ty::fn_sig` because it automatically performs args, this may result in FNs. match node_ty.kind() { - ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).subst_identity()), + ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).instantiate_identity()), ty::FnPtr(fn_sig) => Some(*fn_sig), _ => None, } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 73556c1053e..0e7efd53390 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -739,7 +739,7 @@ fn walk_parents<'tcx>( span, .. }) if span.ctxt() == ctxt => { - let ty = cx.tcx.type_of(owner_id.def_id).subst_identity(); + let ty = cx.tcx.type_of(owner_id.def_id).instantiate_identity(); Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx)) }, @@ -763,7 +763,7 @@ fn walk_parents<'tcx>( }) if span.ctxt() == ctxt => { let output = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); + .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).instantiate_identity().output()); Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)) }, @@ -785,7 +785,7 @@ fn walk_parents<'tcx>( cx.tcx, // Use the param_env of the target type. cx.tcx.param_env(adt.did()), - cx.tcx.type_of(field_def.did).subst_identity(), + cx.tcx.type_of(field_def.did).instantiate_identity(), precedence, ) .position_for_arg() @@ -808,7 +808,7 @@ fn walk_parents<'tcx>( } else { let output = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); + .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).instantiate_identity().output()); ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx) }, ) @@ -879,9 +879,9 @@ fn walk_parents<'tcx>( && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let subs = cx .typeck_results() - .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() + .node_args_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() && let impl_ty = if cx.tcx.fn_sig(fn_id) - .subst_identity() + .instantiate_identity() .skip_binder() .inputs()[0].is_ref() { @@ -905,7 +905,7 @@ fn walk_parents<'tcx>( return Some(Position::MethodReceiver); } args.iter().position(|arg| arg.hir_id == child_id).map(|i| { - let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1); + let ty = cx.tcx.fn_sig(fn_id).instantiate_identity().input(i + 1); // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 if e.hir_id == child_id @@ -1124,10 +1124,10 @@ fn needless_borrow_impl_arg_position<'tcx>( let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) }; - let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder(); - let substs_with_expr_ty = cx + let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); + let args_with_expr_ty = cx .typeck_results() - .node_substs(if let ExprKind::Call(callee, _) = parent.kind { + .node_args(if let ExprKind::Call(callee, _) = parent.kind { callee.hir_id } else { parent.hir_id @@ -1181,9 +1181,9 @@ fn needless_borrow_impl_arg_position<'tcx>( return Position::Other(precedence); } - // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same + // `args_with_referent_ty` can be constructed outside of `check_referent` because the same // elements are modified each time `check_referent` is called. - let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); + let mut args_with_referent_ty = args_with_expr_ty.to_vec(); let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); @@ -1207,7 +1207,7 @@ fn needless_borrow_impl_arg_position<'tcx>( fn_sig, arg_index, &projection_predicates, - &mut substs_with_referent_ty, + &mut args_with_referent_ty, ) { return false; } @@ -1216,14 +1216,14 @@ fn needless_borrow_impl_arg_position<'tcx>( if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() + && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() && ty.is_array() && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return false; } - let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, &substs_with_referent_ty); + let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(); infcx.predicate_must_hold_modulo_regions(&obligation) @@ -1252,7 +1252,7 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { .in_definition_order() .any(|assoc_item| { if assoc_item.fn_has_self_parameter { - let self_ty = cx.tcx.fn_sig(assoc_item.def_id).subst_identity().skip_binder().inputs()[0]; + let self_ty = cx.tcx.fn_sig(assoc_item.def_id).instantiate_identity().skip_binder().inputs()[0]; matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) } else { false @@ -1323,7 +1323,7 @@ fn referent_used_exactly_once<'tcx>( } } -// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting +// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. // See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 @@ -1334,11 +1334,11 @@ fn replace_types<'tcx>( fn_sig: FnSig<'tcx>, arg_index: usize, projection_predicates: &[ProjectionPredicate<'tcx>], - substs: &mut [ty::GenericArg<'tcx>], + args: &mut [ty::GenericArg<'tcx>], ) -> bool { - let mut replaced = BitSet::new_empty(substs.len()); + let mut replaced = BitSet::new_empty(args.len()); - let mut deque = VecDeque::with_capacity(substs.len()); + let mut deque = VecDeque::with_capacity(args.len()); deque.push_back((param_ty, new_ty)); while let Some((param_ty, new_ty)) = deque.pop_front() { @@ -1352,7 +1352,7 @@ fn replace_types<'tcx>( return false; } - substs[param_ty.index as usize] = ty::GenericArg::from(new_ty); + args[param_ty.index as usize] = ty::GenericArg::from(new_ty); // The `replaced.insert(...)` check provides some protection against infinite loops. if replaced.insert(param_ty.index) { @@ -1367,7 +1367,7 @@ fn replace_types<'tcx>( )); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) - && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) { deque.push_back((*term_param_ty, projected_ty)); } @@ -1442,7 +1442,7 @@ fn ty_auto_deref_stability<'tcx>( ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { Position::ReborrowStable(precedence).into() }, - ty::Adt(_, substs) if substs.has_non_region_param() => { + ty::Adt(_, args) if args.has_non_region_param() => { TyPosition::new_deref_stable_for_result(precedence, ty) }, ty::Bool diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 020ffe7f8fa..71b5104bed8 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -10,7 +10,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; -use rustc_middle::ty::{self, Adt, AdtDef, SubstsRef, Ty, TypeckResults}; +use rustc_middle::ty::{self, Adt, AdtDef, GenericArgsRef, Ty, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -80,7 +80,7 @@ fn is_path_self(e: &Expr<'_>) -> bool { fn contains_trait_object(ty: Ty<'_>) -> bool { match ty.kind() { ty::Ref(_, ty, _) => contains_trait_object(*ty), - ty::Adt(def, substs) => def.is_box() && substs[0].as_type().map_or(false, contains_trait_object), + ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object), ty::Dynamic(..) => true, _ => false, } @@ -92,18 +92,18 @@ fn check_struct<'tcx>( self_ty: &hir::Ty<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>, - substs: SubstsRef<'_>, + ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { if let Some(PathSegment { args, .. }) = p.segments.last() { let args = args.map(|a| a.args).unwrap_or(&[]); - // substs contains the generic parameters of the type declaration, while args contains the arguments + // ty_args contains the generic parameters of the type declaration, while args contains the arguments // used at instantiation time. If both len are not equal, it means that some parameters were not // provided (which means that the default values were used); in this case we will not risk // suggesting too broad a rewrite. We won't either if any argument is a type or a const. - if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { return; } } @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind(); + if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { then { if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, substs, cx.tcx.typeck_body(*b)); + check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { check_enum(cx, item, func_expr, adt_def); } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index a005a360e9c..78e7f93e2bf 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -211,7 +211,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { .. }) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -252,7 +252,7 @@ fn check_hash_peq<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.subst_identity().substs.type_at(1) == ty { + if trait_ref.instantiate_identity().args.type_at(1) == ty { span_lint_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, @@ -300,7 +300,7 @@ fn check_ord_partial_ord<'tcx>( // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.subst_identity().substs.type_at(1) == ty { + if trait_ref.instantiate_identity().args.type_at(1) == ty { let mess = if partial_ord_is_automatically_derived { "you are implementing `Ord` explicitly but have derived `PartialOrd`" } else { @@ -347,7 +347,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { impls .iter() - .any(|&id| matches!(cx.tcx.type_of(id).subst_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did())) + .any(|&id| matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did())) }); if !has_copy_impl { return; @@ -464,7 +464,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { - if let ty::Adt(adt, substs) = ty.kind(); + if let ty::Adt(adt, args) = ty.kind(); if cx.tcx.visibility(adt.did()).is_public(); if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); if let Some(def_id) = trait_ref.trait_def_id(); @@ -474,7 +474,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r // If all of our fields implement `Eq`, we can implement `Eq` too if adt .all_fields() - .map(|f| f.ty(cx.tcx, substs)) + .map(|f| f.ty(cx.tcx, args)) .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, [])); then { span_lint_and_sugg( diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index d94664daa56..1701d061128 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for EmptyEnum { } if let ItemKind::Enum(..) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); if adt.variants().is_empty() { span_lint_and_help( diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index d85650712db..96c5c7fc509 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { for var in def.variants { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body); - let mut ty = cx.tcx.type_of(def_id.to_def_id()).subst_identity(); + let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); let constant = cx .tcx .const_eval_poly(def_id.to_def_id()) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 58e62d1f3d3..22e10accd35 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -108,18 +108,18 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_inputs(cx, body.params, None, args); let callee_ty = cx.typeck_results().expr_ty_adjusted(callee); let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id) - .map_or(callee_ty, |id| cx.tcx.type_of(id).subst_identity()); + .map_or(callee_ty, |id| cx.tcx.type_of(id).instantiate_identity()); if check_sig(cx, closure_ty, call_ty); - let substs = cx.typeck_results().node_substs(callee.hir_id); + let args = cx.typeck_results().node_args(callee.hir_id); // This fixes some false positives that I don't entirely understand - if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions(); + if args.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions(); // A type param function ref like `T::f` is not 'static, however // it is if cast like `T::f as fn()`. This seems like a rustc bug. - if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); + if !args.types().any(|t| matches!(t.kind(), ty::Param(_))); let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); - if let ty::Closure(_, substs) = *closure_ty.kind(); + if let ty::Closure(_, args) = *closure_ty.kind(); // Don't lint if this is an inclusive range expression. // They desugar to a call to `RangeInclusiveNew` which would have odd suggestions. (#10684) if !matches!(higher::Range::hir(body.value), Some(higher::Range { @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() - && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs() + && let args = cx.tcx.erase_late_bound_regions(args.as_closure().sig()).inputs() && implements_trait( cx, callee_ty.peel_refs(), @@ -160,12 +160,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind; if check_inputs(cx, body.params, Some(receiver), args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); - let substs = cx.typeck_results().node_substs(body.value.hir_id); - let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs); + let args = cx.typeck_results().node_args(body.value.hir_id); + let call_ty = cx.tcx.type_of(method_def_id).instantiate(cx.tcx, args); if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { - let name = get_ufcs_type_name(cx, method_def_id, substs); + let name = get_ufcs_type_name(cx, method_def_id, args); diag.span_suggestion( expr.span, "replace the closure with the method itself", @@ -228,14 +228,14 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc if !closure_ty.has_late_bound_regions() { return true; } - let ty::Closure(_, substs) = closure_ty.kind() else { + let ty::Closure(_, args) = closure_ty.kind() else { return false; }; - let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal); + let closure_sig = cx.tcx.signature_unclosure(args.as_closure().sig(), Unsafety::Normal); cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) } -fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String { +fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: GenericArgsRef<'tcx>) -> String { let assoc_item = cx.tcx.associated_item(method_def_id); let def_id = assoc_item.container_id(cx.tcx); match assoc_item.container { @@ -251,7 +251,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs | ty::Ref(..) | ty::Slice(_) | ty::Tuple(_) => { - format!("<{}>", EarlyBinder::bind(ty).subst(cx.tcx, substs)) + format!("<{}>", EarlyBinder::bind(ty).instantiate(cx.tcx, args)) }, _ => ty.to_string(), } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 92d67ef359d..35061fc64c9 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -76,9 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args - && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity) + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::instantiate_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) - && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _)) + && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d0ad2628264..1b173de856b 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -198,14 +198,14 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(adt, substs) => { + ty::Adt(adt, args) => { tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env) || KNOWN_WRAPPER_TYS .iter() .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) - && substs.types().any(|ty| is_mutable_ty(cx, ty, tys)) + && args.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, - ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)), + ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index fa2a9b30c05..90fc0d4f662 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -21,11 +21,11 @@ fn result_err_ty<'tcx>( ) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> { if !in_external_macro(cx.sess(), item_span) && let hir::FnRetTy::Return(hir_ty) = decl.output - && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().output()) + && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output()) && is_type_diagnostic_item(cx, ty, sym::Result) - && let ty::Adt(_, substs) = ty.kind() + && let ty::Adt(_, args) = ty.kind() { - let err_ty = substs.type_at(1); + let err_ty = args.type_at(1); Some((hir_ty, err_ty)) } else { None diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 818ebd1134d..e54429aee8e 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -63,10 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(fn_def_id).expect_owner()); - if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() { + if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; - for (p, _span) in preds.subst_iter_copied(cx.tcx, substs) { + for (p, _span) in preds.arg_iter_copied(cx.tcx, args) { if let Some(trait_pred) = p.as_trait_clause() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 1e99b6faa6c..b99d4544681 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(const_id); if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).subst_identity().is_integral(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); then { print_lint_and_sugg(cx, var_name, expr) } @@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(func_id); if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).subst_identity().is_integral(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); then { print_lint_and_sugg(cx, var_name, expr) } diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 7c41699f307..3ac40401e89 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { ) }) { for impl_id in impl_ids.iter().map(|id| id.expect_local()) { - let impl_ty = cx.tcx.type_of(impl_id).subst_identity(); + let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity(); match type_map.entry(impl_ty) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index c924d7361ce..bb100ec632e 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -68,7 +68,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI if sig.decl.implicit_self.has_implicit_self() { let ret_ty = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); + .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).instantiate_identity().output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 1c99bd2f3d0..693218f8a9c 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { return; } if let ItemKind::Enum(ref def, _) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let Adt(adt, subst) = ty.kind() else { panic!("already checked whether this is an enum") }; @@ -169,8 +169,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if let Adt(_def, substs) = ty.kind() - && substs.types().next().is_some() + if let Adt(_def, args) = ty.kind() + && args.types().next().is_some() && let Some(copy_trait) = cx.tcx.lang_items().copy_trait() { return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some(); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 17bd89efaee..83fa25c795a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let Some(local_id) = ty_id.as_local(); let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); - if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder()); + if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()); then { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), @@ -425,7 +425,7 @@ fn check_for_is_empty( if !(is_empty.fn_has_self_parameter && check_is_empty_sig( cx, - cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), + cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), self_kind, output, )) => diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index e6614180920..b7a47002080 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_must_use_func_call, paths}; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::IsSuggestable; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{BytePos, Span}; diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 5c5a4cfce88..a84a0a6eeb8 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -125,7 +125,7 @@ fn is_ref_iterable<'tcx>( } let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty) - .subst(cx.tcx, typeck.node_substs(call_expr.hir_id))); + .instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 8412875b11b..0e49f08d898 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind; if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind; if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name); - if let ty::Adt(def, _substs) = cx.typeck_results().expr_ty(callee).kind(); + if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind(); if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()); then { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index cb446567506..2c20e9e8693 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -370,7 +370,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { ExprKind::MethodCall(_, receiver, args, _) => { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in iter::zip( - self.cx.tcx.fn_sig(def_id).subst_identity().inputs().skip_binder(), + self.cx.tcx.fn_sig(def_id).instantiate_identity().inputs().skip_binder(), std::iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 4629b22d171..6c7c57ba1d6 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -110,7 +110,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id); then { - cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty)) + cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty)) } else { None } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 703a6b25840..f97600b53e4 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -92,7 +92,7 @@ fn simplify_half<'tcx>( && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) - && let Some(ty2) = cx.typeck_results().node_substs(func.hir_id).types().next() + && let Some(ty2) = cx.typeck_results().node_args(func.hir_id).types().next() // T1 == T2? && *ty1 == ty2 { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index edcab6968cb..75605fb3091 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -104,7 +104,7 @@ fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); if let ty::FnDef(id, _) = *ty.kind() { - if let Some(fn_type) = cx.tcx.fn_sig(id).subst_identity().no_bound_vars() { + if let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() { return is_unit_type(fn_type.output()); } } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 2818f030b7a..29c6e11134f 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -27,10 +27,10 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let input_ty = cx.typeck_results().expr_ty(ex); let cast = if_chain! { - if let ty::Adt(_, substs) = input_ty.kind(); - let input_ty = substs.type_at(0); - if let ty::Adt(_, substs) = output_ty.kind(); - let output_ty = substs.type_at(0); + if let ty::Adt(_, args) = input_ty.kind(); + let input_ty = args.type_at(0); + if let ty::Adt(_, args) = output_ty.kind(); + let output_ty = args.type_at(0); if let ty::Ref(_, output_ty, _) = *output_ty.kind(); if input_ty != output_ty; then { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 479cfd83512..039c2134cf8 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; +use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{sym, Symbol}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index d06bcdaa27f..4efe93d4b54 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -10,7 +10,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion(); if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); - let ty = cx.tcx.type_of(def_id).subst_identity(); + let ty = cx.tcx.type_of(def_id).instantiate_identity(); if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 37528d9f7f2..02500fac293 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -6,7 +6,7 @@ use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{Ty, TypeAndMut}; use rustc_span::Span; diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index 46a20ad412b..649fc46e4ad 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); - if cx.tcx.type_of(impl_id).subst_identity().is_str(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); then { diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 7711aa78b23..fb10f782f7a 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).subst_identity().is_str(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind; if (2..=6).contains(&ext_literal.as_str().len()); let ext_str = ext_literal.as_str(); diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index ae03da0d3f9..4ab06bf2ae8 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -47,7 +47,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(), + ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(), _ => None, } } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 92d21bb8932..9af11f08c26 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -70,7 +70,7 @@ pub(super) fn check<'tcx>( if let hir::ExprKind::Path(ref p) = fun.kind { match cx.qpath_res(p, fun.hir_id) { hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( - cx.tcx.fn_sig(def_id).subst_identity().output().skip_binder().kind(), + cx.tcx.fn_sig(def_id).instantiate_identity().output().skip_binder().kind(), ty::Ref(re, ..) if re.is_static(), ), _ => false, @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( .type_dependent_def_id(arg.hir_id) .map_or(false, |method_id| { matches!( - cx.tcx.fn_sig(method_id).subst_identity().output().skip_binder().kind(), + cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(), ty::Ref(re, ..) if re.is_static() ) }) diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 615bde94133..4737a101345 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg } let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); let sig = match arg_ty.kind() { - ty::Closure(_, substs) => substs.as_closure().sig(), + ty::Closure(_, args) => args.as_closure().sig(), _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), _ => return, }; diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 945bbf53bcf..ee063adac64 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).subst_identity().is_slice(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_slice(); if let Some(_) = is_slice_of_primitives(cx, recv); if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind; then { diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 5a78a416877..043425300d8 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -54,7 +54,7 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir .tcx .impl_of_method(method_def_id) .filter(|&impl_did| { - cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() + cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() }) .is_some(), _ => false, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 424482859ee..37da52dc733 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -23,9 +23,9 @@ pub fn check( if args.is_empty() && method_name == sym::to_string; if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); - if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); + if let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id); let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver); - let self_ty = substs.type_at(0); + let self_ty = args.type_at(0); let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); if deref_count >= 1; if specializes_tostring(cx, deref_self_ty); @@ -64,8 +64,8 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if let ty::Adt(adt, substs) = ty.kind() { - cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str() + if let ty::Adt(adt, args) = ty.kind() { + cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && args.type_at(1).is_str() } else { false } diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index b9a0ec77996..3031193e531 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Option); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option); if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); if is_ok_wrapping(cx, map_expr); diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 2b26ef01410..880efe60c1a 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); if cx.tcx.impl_of_method(method_id) - .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).subst_identity(), sym::Option)) + .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option)) || is_diag_trait_item(cx, method_id, sym::Iterator); if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind; then { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index a0300d27870..042bab805b8 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -15,8 +15,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); - if let ty::Adt(_, substs) = collect_ret_ty.kind(); - if let Some(result_t) = substs.types().next(); + if let ty::Adt(_, args) = collect_ret_ty.kind(); + if let Some(result_t) = args.types().next(); if result_t.is_unit(); // get parts for snippet then { diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index a5beb291f32..fbb83c8ce56 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -9,7 +9,7 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Result) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, body, diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 361ffcb5ef3..950902d455b 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -59,7 +59,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { match map_closure_ty.kind() { ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { let map_closure_sig = match map_closure_ty.kind() { - ty::Closure(_, substs) => substs.as_closure().sig(), + ty::Closure(_, args) => args.as_closure().sig(), _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 24dbe8c1d75..9ab78c6f8e0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3508,11 +3508,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let name = impl_item.ident.name.as_str(); let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { - let method_sig = cx.tcx.fn_sig(impl_item.owner_id).subst_identity(); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); // if this impl block implements a trait, lint in trait definition instead @@ -4113,8 +4113,8 @@ impl SelfKind { } else if ty.is_box() { ty.boxed_ty() == parent_ty } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, substs) = ty.kind() { - substs.types().next().map_or(false, |t| t == parent_ty) + if let ty::Adt(_, args) = ty.kind() { + args.types().next().map_or(false, |t| t == parent_ty) } else { false } diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index d0aa39d0627..fe024ee8dff 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind(); if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Mutex); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 8ca7af8107f..f34f1cf2704 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -163,7 +163,7 @@ fn check_collect_into_intoiterator<'tcx>( // that contains `collect_expr` let inputs = cx .tcx - .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).subst_identity()) + .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).instantiate_identity()) .inputs(); // map IntoIterator generic bounds to their signature @@ -201,7 +201,7 @@ fn check_collect_into_intoiterator<'tcx>( /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { - let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder(); + let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder(); sig.inputs().len() == 1 && sig.output().is_bool() }) } @@ -215,7 +215,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, - Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.substs) + Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.args) ) { iter_item_ty == into_iter_item_ty @@ -229,7 +229,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { let typeck = cx.typeck_results(); if let Some(id) = typeck.type_dependent_def_id(call_id) - && let sig = cx.tcx.fn_sig(id).subst_identity() + && let sig = cx.tcx.fn_sig(id).instantiate_identity() && sig.skip_binder().output().is_bool() && let [_, search_ty] = *sig.skip_binder().inputs() && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() @@ -237,11 +237,11 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - && let Some(iter_item) = cx.tcx .associated_items(iter_trait) .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) - && let substs = cx.tcx.mk_substs(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) - && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, substs) + && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) + && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, args) && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) { - item_ty == EarlyBinder::bind(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id)) } else { false } diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index 646fc4a7bcf..f2ef42933df 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().nth(1), + ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index bd625a6914c..1c664e76d74 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -11,7 +11,7 @@ use super::NONSENSICAL_OPEN_OPTIONS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) - && match_type(cx, cx.tcx.type_of(impl_id).subst_identity(), &paths::OPEN_OPTIONS) + && match_type(cx, cx.tcx.type_of(impl_id).instantiate_identity(), &paths::OPEN_OPTIONS) { let mut options = Vec::new(); get_open_options(cx, recv, &mut options); diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs index 0284d9dea30..1c07d2a3a59 100644 --- a/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::PathBuf); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf); if let ExprKind::Lit(lit) = arg.kind; if let LitKind::Str(ref path_lit, _) = lit.node; if let pushed_path = Path::new(path_lit.as_str()); diff --git a/clippy_lints/src/methods/stable_sort_primitive.rs b/clippy_lints/src/methods/stable_sort_primitive.rs index b5fd0ad8ce5..0f4c97022db 100644 --- a/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/clippy_lints/src/methods/stable_sort_primitive.rs @@ -10,7 +10,7 @@ use super::STABLE_SORT_PRIMITIVE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) - && cx.tcx.type_of(impl_id).subst_identity().is_slice() + && cx.tcx.type_of(impl_id).instantiate_identity().is_slice() && let Some(slice_type) = is_slice_of_primitives(cx, recv) { span_lint_and_then( diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 90ca66bd70c..3cb2719e4a0 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); if cx.tcx.impl_trait_ref(impl_id).is_none(); - let self_ty = cx.tcx.type_of(impl_id).subst_identity(); + let self_ty = cx.tcx.type_of(impl_id).instantiate_identity(); if self_ty.is_slice() || self_ty.is_str(); then { // Ignore empty slice and string literals when used with a literal count. diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 67618f7038a..e62a65a2712 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_middle::ty::{self, GenericArgKind}; use rustc_span::sym; use rustc_span::symbol::Ident; use std::iter; @@ -118,7 +118,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).subst_identity().is_slice(); + if cx.tcx.type_of(impl_id).instantiate_identity().is_slice(); if let ExprKind::Closure(&Closure { body, .. }) = arg.kind; if let closure_body = cx.tcx.hir().body(body); if let &[ diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6bd5e9e88c8..21e2638e5b9 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_middle::ty::{self, ClauseKind, EarlyBinder, ParamTy, ProjectionPredicate, TraitPredicate, Ty}; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; @@ -250,8 +250,8 @@ fn check_other_call_arg<'tcx>( ) -> bool { if_chain! { if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr); - if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call); - let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder(); + if let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call); + let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id); if let Some(input) = fn_sig.inputs().get(i); let (input, n_refs) = peel_mid_ty_refs(*input); @@ -315,26 +315,26 @@ fn skip_addr_of_ancestors<'tcx>( } /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, -/// `Substs`, and arguments. -fn get_callee_substs_and_args<'tcx>( +/// `GenericArgs`, and arguments. +fn get_callee_generic_args_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> { +) -> Option<(DefId, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> { if_chain! { if let ExprKind::Call(callee, args) = expr.kind; let callee_ty = cx.typeck_results().expr_ty(callee); if let ty::FnDef(callee_def_id, _) = callee_ty.kind(); then { - let substs = cx.typeck_results().node_substs(callee.hir_id); - return Some((*callee_def_id, substs, None, args)); + let generic_args = cx.typeck_results().node_args(callee.hir_id); + return Some((*callee_def_id, generic_args, None, args)); } } if_chain! { if let ExprKind::MethodCall(_, recv, args, _) = expr.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); then { - let substs = cx.typeck_results().node_substs(expr.hir_id); - return Some((method_def_id, substs, Some(recv), args)); + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, generic_args, Some(recv), args)); } } None @@ -388,17 +388,17 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< } } Node::Expr(parent_expr) => { - if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) + if let Some((callee_def_id, call_generic_args, recv, call_args)) = get_callee_generic_args_and_args(cx, parent_expr) { - // FIXME: the `subst_identity()` below seems incorrect, since we eventually + // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually // call `tcx.try_subst_and_normalize_erasing_regions` further down // (i.e., we are explicitly not in the identity context). - let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder(); + let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) && let Some(param_ty) = fn_sig.inputs().get(arg_index) && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 - && (*param_index as usize) < call_substs.len() + && (*param_index as usize) < call_generic_args.len() { if fn_sig .inputs() @@ -422,8 +422,8 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< } }); - let new_subst = cx.tcx.mk_substs_from_iter( - call_substs.iter() + let new_subst = cx.tcx.mk_args_from_iter( + call_generic_args.iter() .enumerate() .map(|(i, t)| if i == (*param_index as usize) { @@ -433,7 +433,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< })); if trait_predicates.any(|predicate| { - let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, new_subst); + let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { @@ -500,8 +500,8 @@ fn is_to_string_on_string_like<'a>( return false; } - if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id) - && let [generic_arg] = substs.as_slice() + if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id) + && let [generic_arg] = args.as_slice() && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index c96d6922697..9f1f73e6021 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -143,7 +143,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { if_chain! { if args.iter().all(|arg| !self.is_binding(arg)); if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id); - let method_ty = self.cx.tcx.type_of(method_def_id).subst_identity(); + let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity(); let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder(); if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)); then { diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index b0cfc163fd0..73072718678 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Vec); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec); if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind; if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind; then { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 309f67521a3..5878f899541 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -139,7 +139,7 @@ impl MutableKeyType { } fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) { - let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity(); + let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { self.check_ty_(cx, hir_ty.span, *ty); } @@ -150,7 +150,7 @@ impl MutableKeyType { // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { let ty = ty.peel_refs(); - if let Adt(def, substs) = ty.kind() { + if let Adt(def, args) = ty.kind() { let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] .iter() .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); @@ -158,7 +158,7 @@ impl MutableKeyType { return; } - let subst_ty = substs.type_at(0); + let subst_ty = args.type_at(0); // Determines if a type contains interior mutability which would affect its implementation of // [`Hash`] or [`Ord`]. if is_interior_mut_ty(cx, subst_ty) diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index e91aac41bc4..4b20aecad4a 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -51,8 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { }, ExprKind::MethodCall(path, receiver, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); - let substs = cx.typeck_results().node_substs(e.hir_id); - let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); + let args = cx.typeck_results().node_args(e.hir_id); + let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args); check_arguments( cx, std::iter::once(receiver).chain(arguments.iter()).collect(), diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index f11d5773d04..55b6e1606ee 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ctx }; - let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity(); + let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig); for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() { @@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { !preds.is_empty() && { let ty_empty_region = Ty::new_imm_ref(cx.tcx,cx.tcx.lifetimes.re_erased, ty); preds.iter().all(|t| { - let ty_params = t.trait_ref.substs.iter().skip(1).collect::>(); + let ty_params = t.trait_ref.args.iter().skip(1).collect::>(); implements_trait(cx, ty_empty_region, t.def_id(), &ty_params) }) }, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 653b1a8a05f..cf7cd671dca 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -98,14 +98,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { if name == sym::new; if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id); let self_def_id = cx.tcx.hir().get_parent_item(id.into()); - let self_ty = cx.tcx.type_of(self_def_id).subst_identity(); + let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity(); if self_ty == return_ty(cx, id); if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default); then { if self.impling_types.is_none() { let mut impls = HirIdSet::default(); cx.tcx.for_each_impl(default_trait_id, |d| { - let ty = cx.tcx.type_of(d).subst_identity(); + let ty = cx.tcx.type_of(d).instantiate_identity(); if let Some(ty_def) = ty.ty_adt_def() { if let Some(local_def_id) = ty_def.did().as_local() { impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // generics if_chain! { if let Some(ref impling_types) = self.impling_types; - let self_def = cx.tcx.type_of(self_def_id).subst_identity(); + let self_def = cx.tcx.type_of(self_def_id).instantiate_identity(); if let Some(self_def) = self_def.ty_adt_def(); if let Some(self_local_did) = self_def.did().as_local(); let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 75f1e95276a..12d4f33d428 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -158,7 +158,7 @@ fn is_value_unfrozen_raw<'tcx>( val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)) }, ty::Adt(def, _) if def.is_union() => false, - ty::Adt(def, substs) if def.is_enum() => { + ty::Adt(def, args) if def.is_enum() => { let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); @@ -166,10 +166,10 @@ fn is_value_unfrozen_raw<'tcx>( def.variants()[variant_index] .fields .iter() - .map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, field, ty)) + .map(|field| field.ty(cx.tcx, args))).any(|(field, ty)| inner(cx, field, ty)) } - ty::Adt(def, substs) => { - val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, *field, ty)) + ty::Adt(def, args) => { + val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))).any(|(field, ty)| inner(cx, *field, ty)) } ty::Tuple(tys) => val.unwrap_branch().iter().zip(tys).any(|(field, ty)| inner(cx, *field, ty)), _ => false, @@ -206,8 +206,8 @@ fn is_value_unfrozen_raw<'tcx>( fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { let def_id = body_id.hir_id.owner.to_def_id(); - let substs = ty::InternalSubsts::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new(def_id, substs); + let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); + let instance = ty::Instance::new(def_id, args); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); @@ -215,9 +215,9 @@ fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty< } fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let substs = cx.typeck_results().node_substs(hir_id); + let args = cx.typeck_results().node_args(hir_id); - let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, substs), None); + let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None); is_value_unfrozen_raw(cx, result, ty) } @@ -228,7 +228,7 @@ pub fn const_eval_resolve<'tcx>( ct: ty::UnevaluatedConst<'tcx>, span: Option, ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::resolve(tcx, param_env, ct.def, ct.substs) { + match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: None }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) @@ -347,7 +347,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // and, in that case, the definition is *not* generic. cx.tcx.normalize_erasing_regions( cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item).subst_identity(), + cx.tcx.type_of(of_assoc_item).instantiate_identity(), ), )) .is_err(); diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index 7eaa7db78a4..c5e777c2070 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{FieldDef, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; +use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -90,8 +90,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { if send_trait == trait_id; if hir_impl.polarity == ImplPolarity::Positive; if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if let self_ty = ty_trait_ref.subst_identity().self_ty(); - if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); + if let self_ty = ty_trait_ref.instantiate_identity().self_ty(); + if let ty::Adt(adt_def, impl_trait_args) = self_ty.kind(); then { let mut non_send_fields = Vec::new(); @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { .as_local() .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id); - if let field_ty = field.ty(cx.tcx, impl_trait_substs); + if let field_ty = field.ty(cx.tcx, impl_trait_args); if !ty_allowed_in_send(cx, field_ty, send_trait); if let Node::Field(field_def) = hir_map.get(field_hir_id); then { @@ -206,10 +206,10 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t .iter() .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, *ty, send_trait), - ty::Adt(_, substs) => { + ty::Adt(_, args) => { if contains_pointer_like(cx, ty) { // descends only if ADT contains any raw pointers - substs.iter().all(|generic_arg| match generic_arg.unpack() { + args.iter().all(|generic_arg| match generic_arg.unpack() { GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), // Lifetimes and const generics are not solid part of ADT and ignored GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, @@ -224,7 +224,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t } } -/// Checks if the type contains any pointer-like types in substs (including nested ones) +/// Checks if the type contains any pointer-like types in args (including nested ones) fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { for ty_node in target_ty.walk() { if let GenericArgKind::Type(inner_ty) = ty_node.unpack() { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 8b77a5c99f7..3615c7ec9bf 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::{EarlyBinder, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{EarlyBinder, GenericArgKind, GenericArgsRef}; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Ident}; @@ -90,7 +90,7 @@ impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); enum FnKind { Fn, TraitFn, - // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`. + // This is a hack. Ideally we would store a `GenericArgsRef<'tcx>` type here, but a lint pass must be `'static`. // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for // equality. ImplTraitFn(usize), @@ -244,12 +244,12 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { })) => { #[allow(trivial_casts)] if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into()) - && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::subst_identity) + && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity) && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id { ( trait_item_id, - FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize), + FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.args) as *const _ as usize), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { @@ -289,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { ExprKind::Call(callee, args) if path_def_id(cx, callee).map_or(false, |id| { id == param.fn_id - && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id)) + && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) { @@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { ExprKind::MethodCall(_, receiver, args, _) if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| { id == param.fn_id - && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id)) + && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id)) }) => { if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) { @@ -381,15 +381,15 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } } -fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool { +fn has_matching_args(kind: FnKind, args: GenericArgsRef<'_>) -> bool { match kind { FnKind::Fn => true, - FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() { + FnKind::TraitFn => args.iter().enumerate().all(|(idx, subst)| match subst.unpack() { GenericArgKind::Lifetime(_) => true, GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx), GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx), }), #[allow(trivial_casts)] - FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs, + FnKind::ImplTraitFn(expected_args) => args as *const _ as usize == expected_args, } } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index eab725de17f..7b4812e98d8 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -143,7 +143,7 @@ impl<'tcx> PassByRefOrValue { return; } - let fn_sig = cx.tcx.fn_sig(def_id).subst_identity(); + let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity(); let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id)); // Gather all the lifetimes found in the output type which may affect whether diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 32213718b27..eb7c008c7a4 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, - cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(), + cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder().inputs(), sig.decl.inputs, &sig.decl.output, &[], @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { check_mut_from_ref(cx, sig, Some(body)); let decl = sig.decl; - let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder(); + let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) .collect(); @@ -423,7 +423,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( .enumerate() .filter_map(move |(i, (ty, hir_ty))| { if let ty::Ref(_, ty, mutability) = *ty.kind() - && let ty::Adt(adt, substs) = *ty.kind() + && let ty::Adt(adt, args) = *ty.kind() && let TyKind::Ref(lt, ref ty) = hir_ty.kind && let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind // Check that the name as typed matches the actual name of the type. @@ -443,7 +443,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } else { None }), - substs.type_at(0), + args.type_at(0), ), ), _ if Some(adt.did()) == cx.tcx.lang_items().string() => ( @@ -496,7 +496,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } let ty_name = - snippet_opt(cx, ty.span()).unwrap_or_else(|| substs.type_at(1).to_string()); + snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string()); span_lint_hir_and_then( cx, @@ -659,7 +659,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: return; }; - match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i] + match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i] .peel_refs() .kind() { @@ -725,7 +725,7 @@ fn matches_preds<'tcx>( let infcx = cx.tcx.infer_ctxt().build(); preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { ExistentialPredicate::Trait(p) => infcx - .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.substs.iter()), cx.param_env) + .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.args.iter()), cx.param_env) .must_apply_modulo_regions(), ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( cx.tcx, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index c70ce83a9c4..2b65c08d25d 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -9,7 +9,7 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::Ty; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::GenericArg; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, - Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_substs(&[GenericArg::from(indexed_ty)])), + Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), ) { if deref_ty == expr_ty { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 958351ad81b..4977df6c83f 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -9,7 +9,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; +use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -333,7 +333,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) && cx .tcx .fn_sig(def_id) - .subst_identity() + .instantiate_identity() .skip_binder() .output() .walk() diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index beca203c868..b92014f68b3 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).subst_identity(); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let ret_ty = return_ty(cx, impl_item.owner_id); // Do not check trait impls diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index fffa8a380c2..1493ad44ee5 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -10,7 +10,7 @@ use rustc_hir::{ intravisit::{walk_expr, Visitor}, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut}; +use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span, DUMMY_SP}; use std::borrow::Cow; diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index ac4e29e9dfd..80c834066bb 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -47,7 +47,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val)); then { - cx.typeck_results().node_substs(count_func.hir_id).types().next() + cx.typeck_results().node_args(count_func.hir_id).types().next() } else { None } @@ -101,7 +101,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + if let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next(); then { return Some((pointee_ty, count)); } diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 5e24213d07f..9e5d7662426 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::SubstsRef; +use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; #[expect(clippy::too_many_lines)] @@ -268,12 +268,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } ReducedTy::UnorderedFields(ty) }, - ty::Adt(def, substs) if def.is_struct() => { + ty::Adt(def, args) if def.is_struct() => { let mut iter = def .non_enum_variant() .fields .iter() - .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs)); + .map(|f| cx.tcx.type_of(f.did).instantiate(cx.tcx, args)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::TypeErasure { raw_ptr_only: false }; }; @@ -322,7 +322,7 @@ fn is_size_pair(ty: Ty<'_>) -> bool { } } -fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> bool { +fn same_except_params<'tcx>(subs1: GenericArgsRef<'tcx>, subs2: GenericArgsRef<'tcx>) -> bool { // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as // `Array<6>` for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index b1445311b71..891fefc17a6 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -10,7 +10,7 @@ use rustc_span::symbol::sym; /// Returns `true` if it's triggered, otherwise returns `false`. pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => { + (ty::Adt(from_adt, from_args), ty::Adt(to_adt, to_args)) => { if from_adt.did() != to_adt.did() { return false; } @@ -28,9 +28,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty ) { return false; } - if from_substs + if from_args .types() - .zip(to_substs.types()) + .zip(to_args.types()) .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) { span_lint( diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 1ab0162a881..6756df8e716 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -88,7 +88,7 @@ fn handle_uninit_vec_pair<'tcx>( if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); if vec.location.eq_expr(cx, set_len_self); if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); - if let ty::Adt(_, substs) = vec_ty.kind(); + if let ty::Adt(_, args) = vec_ty.kind(); // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); then { @@ -96,7 +96,7 @@ fn handle_uninit_vec_pair<'tcx>( // with_capacity / reserve -> set_len // Check T of Vec - if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { + if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { // FIXME: #7698, false positive of the internal lints #[expect(clippy::collapsible_span_lint_calls)] span_lint_and_then( diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 99a1d197678..dd829ded0d0 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -65,7 +65,7 @@ fn get_projection_pred<'tcx>( generics.predicates.iter().find_map(|(proj_pred, _)| { if let ClauseKind::Projection(pred) = proj_pred.kind().skip_binder() { let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred)); - if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs { + if projection_pred.projection_ty.args == trait_pred.trait_ref.args { return Some(projection_pred); } } @@ -76,7 +76,7 @@ fn get_projection_pred<'tcx>( fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { let mut args_to_check = Vec::new(); if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - let fn_sig = cx.tcx.fn_sig(def_id).subst_identity(); + let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity(); let generics = cx.tcx.predicates_of(def_id); let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); @@ -120,8 +120,8 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { if_chain! { if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind; - if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind(); - let ret_ty = substs.as_closure().sig().output(); + if let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind(); + let ret_ty = args.as_closure().sig().output(); let ty = cx.tcx.erase_late_bound_regions(ret_ty); if ty.is_unit(); then { diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index cc7c2b039f2..704d7abd7e5 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -161,7 +161,7 @@ fn needs_inferred_result_ty( }, _ => return false, }; - let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder(); + let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder(); if let ty::Param(output_ty) = *sig.output().kind() { let args: Vec<&Expr<'_>> = if let Some(receiver) = receiver { std::iter::once(receiver).chain(args.iter()).collect() diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 0f5cdb6aaea..dea8a1e35bb 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for UnnamedAddress { if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PTR_EQ); - let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0); + let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0); if ty_param.is_trait(); then { span_lint_and_help( diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 5a02987453c..81dc426b389 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { then { // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. - let self_ty = impl_trait_ref.subst_identity().self_ty(); + let self_ty = impl_trait_ref.instantiate_identity().self_ty(); // `trait_method_sig` is the signature of the function, how it is declared in the // trait, not in the impl of the trait. @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { .associated_item(impl_item.owner_id) .trait_item_def_id .expect("impl method matches a trait method"); - let trait_method_sig = cx.tcx.fn_sig(trait_method).subst_identity(); + let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the @@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } else { hir_ty_to_ty(cx.tcx, hir_ty) }; - if same_type_and_consts(ty, cx.tcx.type_of(impl_id).subst_identity()); + if same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()); then { span_lint(cx, hir_ty.span); } @@ -238,7 +238,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !expr.span.from_expansion(); if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).subst_identity(); + if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity(); then {} else { return; } } match expr.kind { @@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let PatKind::Path(QPath::Resolved(_, path)) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; - if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).subst_identity(); + if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity(); then { check_path(cx, path); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index b6c11cfb355..98f7bfb4ba1 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -227,8 +227,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, substs) = a.kind(); - if let Some(a_type) = substs.types().next(); + if let ty::Adt(_, args) = a.kind(); + if let Some(a_type) = args.types().next(); if same_type_and_consts(a_type, b); then { @@ -255,8 +255,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, substs) = a.kind(); - if let Some(a_type) = substs.types().next(); + if let ty::Adt(_, args) = a.kind(); + if let Some(a_type) = args.types().next(); if same_type_and_consts(a_type, b); then { diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index dced9fcf9ab..da8654d9388 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { for item in cx.tcx.module_children(def_id) { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id).subst_identity(); + let ty = cx.tcx.type_of(item_def_id).instantiate_identity(); if match_type(cx, ty, &paths::SYMBOL); if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); if let Ok(value) = value.to_u32(); diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 09f0f0d0adb..bf835f89cfc 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_middle::ty::{self, GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -39,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { if self_ty_def.all_fields().any(|f| { cx.tcx .type_of(f.did) - .subst_identity() + .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 00842376628..f66f33fee16 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -229,11 +229,11 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option read_mir_alloc_def_path( cx, cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id).subst_identity(), + cx.tcx.type_of(def_id).instantiate_identity(), ), Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).subst_identity()) + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) }, _ => None, }, diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 2a594e750b9..d1bf292d711 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -230,8 +230,8 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { /// Returns the item type of the vector (i.e., the `T` in `Vec`). fn vec_type(ty: Ty<'_>) -> Ty<'_> { - if let ty::Adt(_, substs) = ty.kind() { - substs.type_at(0) + if let ty::Adt(_, args) = ty.kind() { + args.type_at(0) } else { panic!("The type of `vec!` is a not a struct?"); } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 93e4b023c5c..002304f8840 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -51,8 +51,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if !in_trait_impl(cx, hir_ty.hir_id); let ty = ty_from_hir_ty(cx, hir_ty); if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); - if let Adt(_, substs) = ty.kind(); - let ty = substs.type_at(1); + if let Adt(_, args) = ty.kind(); + let ty = args.type_at(1); // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 if !ty.has_escaping_bound_vars(); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index d1cfdc49658..4832a38eceb 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -12,7 +12,7 @@ use rustc_lint::LateContext; use rustc_middle::mir; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; -use rustc_middle::ty::{List, SubstsRef}; +use rustc_middle::ty::{List, GenericArgsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; @@ -327,7 +327,7 @@ pub struct ConstEvalLateContext<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, source: ConstantSource, - substs: SubstsRef<'tcx>, + args: GenericArgsRef<'tcx>, } impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { @@ -337,7 +337,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { typeck_results, param_env: lcx.param_env, source: ConstantSource::Local, - substs: List::empty(), + args: List::empty(), } } @@ -473,16 +473,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { return None; } - let substs = self.typeck_results.node_substs(id); - let substs = if self.substs.is_empty() { - substs + let args = self.typeck_results.node_args(id); + let args = if self.args.is_empty() { + args } else { - EarlyBinder::bind(substs).subst(self.lcx.tcx, self.substs) + EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args) }; let result = self .lcx .tcx - .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None) + .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?; let result = miri_to_const(self.lcx, result)?; diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 4a845ca63b4..94b9006ed50 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -51,7 +51,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: let name = name.as_str(); let ty = match cx.tcx.impl_of_method(fn_id) { - Some(id) => cx.tcx.type_of(id).subst_identity(), + Some(id) => cx.tcx.type_of(id).instantiate_identity(), None => return Lazy, }; @@ -72,7 +72,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: .variants() .iter() .flat_map(|v| v.fields.iter()) - .any(|x| matches!(cx.tcx.type_of(x.did).subst_identity().peel_refs().kind(), ty::Param(_))) + .any(|x| matches!(cx.tcx.type_of(x.did).instantiate_identity().peel_refs().kind(), ty::Param(_))) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, @@ -80,7 +80,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx.tcx.fn_sig(fn_id).subst_identity().skip_binder().inputs_and_output { + match &**cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs_and_output { [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange, _ => Lazy, } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 727b59f1f43..5eed5ddf9e3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -305,7 +305,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).subst_identity().ty_adt_def() { + if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { return cx.tcx.is_diagnostic_item(diag_item, adt.did()); } } @@ -812,7 +812,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if let QPath::TypeRelative(_, method) = path { if method.ident.name == sym::new { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).subst_identity().ty_adt_def() { + if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { return std_types_symbols.iter().any(|&symbol| { cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() }); @@ -1377,7 +1377,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( .chain(args.iter()) .position(|arg| arg.hir_id == id)?; let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?; - let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i]; + let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]; ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(()) }, _ => None, @@ -1639,13 +1639,13 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option { /// Convenience function to get the return type of a function. pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId) -> Ty<'tcx> { - let ret_ty = cx.tcx.fn_sig(fn_def_id).subst_identity().output(); + let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output(); cx.tcx.erase_late_bound_regions(ret_ty) } /// Convenience function to get the nth argument type of a function. pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId, nth: usize) -> Ty<'tcx> { - let arg = cx.tcx.fn_sig(fn_def_id).subst_identity().input(nth); + let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth); cx.tcx.erase_late_bound_regions(arg) } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index fbf4ab2722e..515f80e0edf 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -15,7 +15,7 @@ use rustc_middle::mir::{ Terminator, TerminatorKind, }; use rustc_middle::traits::{ImplSource, ObligationCause}; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; use rustc_middle::ty::{BoundConstness, TraitRef}; use rustc_semver::RustcVersion; @@ -35,7 +35,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) // impl trait is gone in MIR, so check the return type manually check_ty( tcx, - tcx.fn_sig(def_id).subst_identity().output().skip_binder(), + tcx.fn_sig(def_id).instantiate_identity().output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, )?; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index cf781e18cd6..3953a7d8fa7 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -877,7 +877,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { .cx .typeck_results() .type_dependent_def_id(parent_expr.hir_id) - .map(|did| self.cx.tcx.fn_sig(did).subst_identity().skip_binder()) + .map(|did| self.cx.tcx.fn_sig(did).instantiate_identity().skip_binder()) { std::iter::once(receiver) .chain(call_args.iter()) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index d650cbe0b13..7687d361923 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, - Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + Region, RegionKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; @@ -90,14 +90,14 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() { + for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. ty::ClauseKind::Trait(trait_predicate) => { if trait_predicate .trait_ref - .substs + .args .types() .skip(1) // Skip the implicit `Self` generic parameter .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) @@ -237,7 +237,7 @@ pub fn implements_trait_with_env<'tcx>( kind: TypeVariableOriginKind::MiscVariable, span: DUMMY_SP, }; - let ty_params = tcx.mk_substs_from_iter( + let ty_params = tcx.mk_args_from_iter( ty_params .into_iter() .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())), @@ -265,7 +265,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) }, - ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), + ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { @@ -314,11 +314,11 @@ fn is_normalizable_helper<'tcx>( let cause = rustc_middle::traits::ObligationCause::dummy(); let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { match ty.kind() { - ty::Adt(def, substs) => def.variants().iter().all(|variant| { + ty::Adt(def, args) => def.variants().iter().all(|variant| { variant .fields .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache)) }), _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { GenericArgKind::Type(inner_ty) if inner_ty != ty => { @@ -517,14 +517,14 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { /// otherwise returns `false` pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } - substs_a + args_a .iter() - .zip(substs_b.iter()) + .zip(args_b.iter()) .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { @@ -643,7 +643,7 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { - Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst_identity(), Some(id))) + Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) } @@ -661,11 +661,11 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => sig_from_bounds( + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate(cx.tcx, subs), Some(id))), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => sig_from_bounds( cx, ty, - cx.tcx.item_bounds(def_id).subst_iter(cx.tcx, substs), + cx.tcx.item_bounds(def_id).arg_iter(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), @@ -681,7 +681,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } @@ -713,7 +713,7 @@ fn sig_from_bounds<'tcx>( || lang_items.fn_once_trait() == Some(p.def_id())) && p.self_ty() == ty => { - let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); + let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); if inputs.map_or(false, |inputs| i != inputs) { // Multiple different fn trait impls. Is this even allowed? return None; @@ -744,7 +744,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option for (pred, _) in cx .tcx .explicit_item_bounds(ty.def_id) - .subst_iter_copied(cx.tcx, ty.substs) + .arg_iter_copied(cx.tcx, ty.args) { match pred.kind().skip_binder() { ty::ClauseKind::Trait(p) @@ -752,7 +752,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => { - let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); + let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); if inputs.map_or(false, |inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? @@ -793,7 +793,7 @@ impl core::ops::Add for EnumValue { #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { - match tcx.type_of(id).subst_identity().kind() { + match tcx.type_of(id).instantiate_identity().kind() { ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() { 1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8), 2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16), @@ -927,7 +927,7 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< Some((adt, adt.variant_with_id(var_id))) }, Res::SelfCtor(id) => { - let adt = cx.tcx.type_of(id).subst_identity().ty_adt_def().unwrap(); + let adt = cx.tcx.type_of(id).instantiate_identity().ty_adt_def().unwrap(); Some((adt, adt.non_enum_variant())) }, _ => None, @@ -1025,13 +1025,13 @@ pub fn make_projection<'tcx>( tcx: TyCtxt<'tcx>, container_id: DefId, assoc_ty: Symbol, - substs: impl IntoIterator>>, + args: impl IntoIterator>>, ) -> Option> { fn helper<'tcx>( tcx: TyCtxt<'tcx>, container_id: DefId, assoc_ty: Symbol, - substs: SubstsRef<'tcx>, + args: GenericArgsRef<'tcx>, ) -> Option> { let Some(assoc_item) = tcx .associated_items(container_id) @@ -1052,18 +1052,18 @@ pub fn make_projection<'tcx>( .map(|x| &x.kind); debug_assert!( - generic_count == substs.len(), - "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\ + generic_count == args.len(), + "wrong number of args for `{:?}`: found `{}` expected `{generic_count}`.\n\ note: the expected parameters are: {:#?}\n\ - the given arguments are: `{substs:#?}`", + the given arguments are: `{args:#?}`", assoc_item.def_id, - substs.len(), + args.len(), params.map(ty::GenericParamDefKind::descr).collect::>(), ); if let Some((idx, (param, arg))) = params .clone() - .zip(substs.iter().map(GenericArg::unpack)) + .zip(args.iter().map(GenericArg::unpack)) .enumerate() .find(|(_, (param, arg))| { !matches!( @@ -1078,20 +1078,20 @@ pub fn make_projection<'tcx>( false, "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\ note: the expected parameters are {:#?}\n\ - the given arguments are {substs:#?}", + the given arguments are {args:#?}", param.descr(), params.map(ty::GenericParamDefKind::descr).collect::>() ); } } - Some(tcx.mk_alias_ty(assoc_item.def_id, substs)) + Some(tcx.mk_alias_ty(assoc_item.def_id, args)) } helper( tcx, container_id, assoc_ty, - tcx.mk_substs_from_iter(substs.into_iter().map(Into::into)), + tcx.mk_args_from_iter(args.into_iter().map(Into::into)), ) } @@ -1106,25 +1106,25 @@ pub fn make_normalized_projection<'tcx>( param_env: ParamEnv<'tcx>, container_id: DefId, assoc_ty: Symbol, - substs: impl IntoIterator>>, + args: impl IntoIterator>>, ) -> Option> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] if let Some((i, subst)) = ty - .substs + .args .iter() .enumerate() .find(|(_, subst)| subst.has_late_bound_regions()) { debug_assert!( false, - "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + "args contain late-bound region at index `{i}` which can't be normalized.\n\ use `TyCtxt::erase_late_bound_regions`\n\ note: subst is `{subst:#?}`", ); return None; } - match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx,ty.def_id, ty.substs)) { + match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx,ty.def_id, ty.args)) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1132,7 +1132,7 @@ pub fn make_normalized_projection<'tcx>( }, } } - helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) } /// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] @@ -1147,7 +1147,7 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { && is_interior_mut_ty(cx, inner_ty) }, ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)), - ty::Adt(def, substs) => { + ty::Adt(def, args) => { // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to // that of their type parameters. Note: we don't include `HashSet` and `HashMap` // because they have no impl for `Hash` or `Ord`. @@ -1168,7 +1168,7 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); if is_std_collection || is_box { // The type is mutable if any of its type parameters are - substs.types().any(|ty| is_interior_mut_ty(cx, ty)) + args.types().any(|ty| is_interior_mut_ty(cx, ty)) } else { !ty.has_escaping_bound_vars() && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() @@ -1184,19 +1184,19 @@ pub fn make_normalized_projection_with_regions<'tcx>( param_env: ParamEnv<'tcx>, container_id: DefId, assoc_ty: Symbol, - substs: impl IntoIterator>>, + args: impl IntoIterator>>, ) -> Option> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] if let Some((i, subst)) = ty - .substs + .args .iter() .enumerate() .find(|(_, subst)| subst.has_late_bound_regions()) { debug_assert!( false, - "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + "args contain late-bound region at index `{i}` which can't be normalized.\n\ use `TyCtxt::erase_late_bound_regions`\n\ note: subst is `{subst:#?}`", ); @@ -1207,7 +1207,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( .infer_ctxt() .build() .at(&cause, param_env) - .query_normalize(Ty::new_projection(tcx,ty.def_id, ty.substs)) + .query_normalize(Ty::new_projection(tcx,ty.def_id, ty.args)) { Ok(ty) => Some(ty.value), Err(e) => { @@ -1216,7 +1216,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( }, } } - helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) } pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 1d336b3cdc0..bb488c2dcb3 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -1,5 +1,5 @@ // originally from rustc ./tests/ui/regions/issue-78262.rs -// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() +// ICE: to get the signature of a closure, use args.as_closure().sig() not fn_sig() #![allow(clippy::upper_case_acronyms)] trait TT {} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index bf44bcb564e..db7bd99e0ae 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -331,7 +331,7 @@ impl dyn TestTrait + '_ { } // https://github.com/rust-lang/rust-clippy/issues/7746 -fn angle_brackets_and_substs() { +fn angle_brackets_and_args() { let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); array_opt.map(<[u8; 3]>::as_slice); diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index b2af4bf0953..52fc17686fd 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -331,7 +331,7 @@ impl dyn TestTrait + '_ { } // https://github.com/rust-lang/rust-clippy/issues/7746 -fn angle_brackets_and_substs() { +fn angle_brackets_and_args() { let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); array_opt.map(|a| a.as_slice()); From 1f82f6ddf30568af39b2f8bed6c723822b39aabe Mon Sep 17 00:00:00 2001 From: Caio Date: Fri, 14 Jul 2023 13:51:34 -0300 Subject: [PATCH 07/90] [significant_drop_tightening] Fix #11160 --- clippy_lints/src/significant_drop_tightening.rs | 11 +++++++---- tests/ui/significant_drop_tightening.fixed | 7 +++++++ tests/ui/significant_drop_tightening.rs | 7 +++++++ tests/ui/significant_drop_tightening.stderr | 6 +++--- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index bf1059a8b29..8f8614ab305 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local}; +use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -236,7 +236,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx fn manage_has_expensive_expr_after_last_attr(&mut self) { let has_expensive_stmt = match self.ap.curr_stmt.kind { - hir::StmtKind::Expr(expr) if !is_expensive_expr(expr) => false, + hir::StmtKind::Expr(expr) if is_inexpensive_expr(expr) => false, hir::StmtKind::Local(local) if let Some(expr) = local.init && let hir::ExprKind::Path(_) = expr.kind => false, _ => true @@ -445,6 +445,9 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_ } } -fn is_expensive_expr(expr: &hir::Expr<'_>) -> bool { - !matches!(expr.kind, hir::ExprKind::Path(_)) +fn is_inexpensive_expr(expr: &hir::Expr<'_>) -> bool { + let actual = peel_hir_expr_unary(expr).0; + let is_path = matches!(actual.kind, hir::ExprKind::Path(_)); + let is_lit = matches!(actual.kind, hir::ExprKind::Lit(_)); + is_path || is_lit } diff --git a/tests/ui/significant_drop_tightening.fixed b/tests/ui/significant_drop_tightening.fixed index eb8524167c4..aa6690b77e1 100644 --- a/tests/ui/significant_drop_tightening.fixed +++ b/tests/ui/significant_drop_tightening.fixed @@ -51,6 +51,13 @@ pub fn issue_11128() { } } +pub fn issue_11160() -> bool { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + true +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.rs b/tests/ui/significant_drop_tightening.rs index f7fa65ea922..a0bf657df55 100644 --- a/tests/ui/significant_drop_tightening.rs +++ b/tests/ui/significant_drop_tightening.rs @@ -50,6 +50,13 @@ pub fn issue_11128() { } } +pub fn issue_11160() -> bool { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + true +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.stderr b/tests/ui/significant_drop_tightening.stderr index ca4fede17c9..45ca73af3a8 100644 --- a/tests/ui/significant_drop_tightening.stderr +++ b/tests/ui/significant_drop_tightening.stderr @@ -23,7 +23,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:79:13 + --> $DIR/significant_drop_tightening.rs:86:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -43,7 +43,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:100:13 + --> $DIR/significant_drop_tightening.rs:107:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -67,7 +67,7 @@ LL + | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:106:17 + --> $DIR/significant_drop_tightening.rs:113:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); From f928d7821184dff5ab648594a2b6c13f59a32fba Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 16 Jul 2023 11:50:15 +0200 Subject: [PATCH 08/90] clippy-driver: Update bug URL to use the ice template --- src/driver.rs | 2 +- tests/ui-internal/custom_ice_message.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index ee17feed77a..1d89477dcc1 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -192,7 +192,7 @@ You can use tool lints to allow or deny lints from your code, eg.: ); } -const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; #[allow(clippy::too_many_lines)] pub fn main() { diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index b88aeae2a9b..31df0ebd9fd 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -3,7 +3,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: the compiler unexpectedly panicked. this is a bug. -note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml note: rustc running on From eaccc6d38f356fb27bb69aa706f3862b2dc71276 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 3 Jul 2023 14:42:50 +0200 Subject: [PATCH 09/90] [`semicolon_if_nothing_returned`]: enable the autofix --- clippy_lints/src/semicolon_if_nothing_returned.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 355f907e257..c9547cd95dc 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { if let Some(expr) = block.expr; let t_expr = cx.typeck_results().expr_ty(expr); if t_expr.is_unit(); - let mut app = Applicability::MaybeIncorrect; + let mut app = Applicability::MachineApplicable; if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0; if !snippet.ends_with('}') && !snippet.ends_with(';'); if cx.sess().source_map().is_multiline(block.span); From 8ebdd62620d575abdcf77aaf8ddd27c699ef66a7 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jul 2023 22:48:36 +0200 Subject: [PATCH 10/90] [`semicolon_if_nothing_returned`]: Update the tests with the autofix --- tests/ui/semicolon_if_nothing_returned.fixed | 123 ++++++++++++++++++ tests/ui/semicolon_if_nothing_returned.rs | 3 +- tests/ui/semicolon_if_nothing_returned.stderr | 10 +- 3 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 tests/ui/semicolon_if_nothing_returned.fixed diff --git a/tests/ui/semicolon_if_nothing_returned.fixed b/tests/ui/semicolon_if_nothing_returned.fixed new file mode 100644 index 00000000000..653f4533b33 --- /dev/null +++ b/tests/ui/semicolon_if_nothing_returned.fixed @@ -0,0 +1,123 @@ +//@run-rustfix +#![warn(clippy::semicolon_if_nothing_returned)] +#![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] + +fn get_unit() {} + +// the functions below trigger the lint +fn main() { + println!("Hello"); +} + +fn hello() { + get_unit(); +} + +fn basic101(x: i32) { + let y: i32; + y = x + 1; +} + +#[rustfmt::skip] +fn closure_error() { + let _d = || { + hello(); + }; +} + +#[rustfmt::skip] +fn unsafe_checks_error() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { + ptr::drop_in_place(s.as_mut_ptr()); + }; +} + +// this is fine +fn print_sum(a: i32, b: i32) { + println!("{}", a + b); + assert_eq!(true, false); +} + +fn foo(x: i32) { + let y: i32; + if x < 1 { + y = 4; + } else { + y = 5; + } +} + +fn bar(x: i32) { + let y: i32; + match x { + 1 => y = 4, + _ => y = 32, + } +} + +fn foobar(x: i32) { + let y: i32; + 'label: { + y = x + 1; + } +} + +fn loop_test(x: i32) { + let y: i32; + for &ext in &["stdout", "stderr", "fixed"] { + println!("{}", ext); + } +} + +fn closure() { + let _d = || hello(); +} + +#[rustfmt::skip] +fn closure_block() { + let _d = || { hello() }; +} + +unsafe fn some_unsafe_op() {} +unsafe fn some_other_unsafe_fn() {} + +fn do_something() { + unsafe { some_unsafe_op() }; + + unsafe { some_other_unsafe_fn() }; +} + +fn unsafe_checks() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) }; +} + +// Issue #7768 +#[rustfmt::skip] +fn macro_with_semicolon() { + macro_rules! repro { + () => { + while false { + } + }; + } + repro!(); +} + +fn function_returning_option() -> Option { + Some(1) +} + +// No warning +fn let_else_stmts() { + let Some(x) = function_returning_option() else { + return; + }; +} diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 8e7f1d862cf..9db038219b4 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,5 +1,6 @@ +//@run-rustfix #![warn(clippy::semicolon_if_nothing_returned)] -#![allow(clippy::redundant_closure, clippy::uninlined_format_args)] +#![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] fn get_unit() {} diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr index 8d9a67585cf..78813e7cc1c 100644 --- a/tests/ui/semicolon_if_nothing_returned.stderr +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -1,5 +1,5 @@ error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:8:5 + --> $DIR/semicolon_if_nothing_returned.rs:9:5 | LL | println!("Hello") | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");` @@ -7,25 +7,25 @@ LL | println!("Hello") = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:12:5 + --> $DIR/semicolon_if_nothing_returned.rs:13:5 | LL | get_unit() | ^^^^^^^^^^ help: add a `;` here: `get_unit();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:17:5 + --> $DIR/semicolon_if_nothing_returned.rs:18:5 | LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:23:9 + --> $DIR/semicolon_if_nothing_returned.rs:24:9 | LL | hello() | ^^^^^^^ help: add a `;` here: `hello();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:34:9 + --> $DIR/semicolon_if_nothing_returned.rs:35:9 | LL | ptr::drop_in_place(s.as_mut_ptr()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` From d6d530fd0b92ccec4a22e69cdebe6c4c942c8166 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 17 Jul 2023 10:19:29 +0200 Subject: [PATCH 11/90] Merge commit 'd9c24d1b1ee61f276e550b967409c9f155eac4e3' into clippyup --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 2 +- CHANGELOG.md | 180 +++++++----- Cargo.toml | 13 +- README.md | 2 +- book/src/README.md | 2 +- .../infrastructure/changelog_update.md | 22 -- book/src/development/speedtest.md | 24 ++ book/src/lint_configuration.md | 2 +- clippy_dev/src/new_lint.rs | 8 +- clippy_dev/src/setup/intellij.rs | 2 +- clippy_dev/src/update_lints.rs | 5 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/allow_attributes.rs | 3 +- clippy_lints/src/arc_with_non_send_sync.rs | 76 ++--- clippy_lints/src/assertions_on_constants.rs | 12 +- clippy_lints/src/attrs.rs | 6 +- clippy_lints/src/blocks_in_if_conditions.rs | 6 +- clippy_lints/src/bool_assert_comparison.rs | 14 +- clippy_lints/src/bool_to_int_with_if.rs | 4 +- clippy_lints/src/borrow_deref_ref.rs | 3 +- clippy_lints/src/box_default.rs | 14 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 6 +- .../src/casts/cast_slice_different_sizes.rs | 6 +- .../src/casts/cast_slice_from_raw_parts.rs | 3 +- clippy_lints/src/casts/ptr_cast_constness.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/copies.rs | 3 +- clippy_lints/src/crate_in_macro_def.rs | 3 +- clippy_lints/src/dbg_macro.rs | 4 +- clippy_lints/src/declared_lints.rs | 6 + .../src/default_constructed_unit_structs.rs | 6 +- .../src/default_instead_of_iter_empty.rs | 3 +- clippy_lints/src/default_numeric_fallback.rs | 12 +- clippy_lints/src/dereference.rs | 45 +-- clippy_lints/src/derivable_impls.rs | 14 +- clippy_lints/src/derive.rs | 7 +- clippy_lints/src/disallowed_methods.rs | 2 +- clippy_lints/src/disallowed_names.rs | 3 +- clippy_lints/src/doc.rs | 15 +- clippy_lints/src/drop_forget_ref.rs | 3 +- clippy_lints/src/empty_drop.rs | 3 +- .../src/empty_structs_with_brackets.rs | 3 +- clippy_lints/src/endian_bytes.rs | 6 +- clippy_lints/src/entry.rs | 33 ++- clippy_lints/src/escape.rs | 3 +- clippy_lints/src/excessive_nesting.rs | 11 +- clippy_lints/src/explicit_write.rs | 2 +- .../src/extra_unused_type_parameters.rs | 9 +- clippy_lints/src/floating_point_arithmetic.rs | 10 +- clippy_lints/src/format.rs | 4 +- clippy_lints/src/format_args.rs | 10 +- clippy_lints/src/format_impl.rs | 8 +- clippy_lints/src/from_over_into.rs | 14 +- clippy_lints/src/from_raw_with_void_ptr.rs | 3 +- .../src/functions/impl_trait_in_params.rs | 6 +- .../src/functions/misnamed_getters.rs | 19 +- clippy_lints/src/functions/must_use.rs | 9 +- clippy_lints/src/functions/too_many_lines.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 3 +- clippy_lints/src/if_then_some_else_none.rs | 8 +- clippy_lints/src/implicit_return.rs | 10 +- clippy_lints/src/incorrect_impls.rs | 160 ++++++++-- clippy_lints/src/index_refutable_slice.rs | 3 +- clippy_lints/src/inherent_impl.rs | 3 +- clippy_lints/src/init_numbered_fields.rs | 2 +- clippy_lints/src/instant_subtraction.rs | 3 +- clippy_lints/src/items_after_test_module.rs | 5 +- .../src/iter_not_returning_iterator.rs | 7 +- clippy_lints/src/large_enum_variant.rs | 6 +- clippy_lints/src/large_futures.rs | 3 +- clippy_lints/src/large_include_file.rs | 3 +- clippy_lints/src/large_stack_frames.rs | 6 +- clippy_lints/src/len_zero.rs | 24 +- clippy_lints/src/let_if_seq.rs | 3 +- clippy_lints/src/let_underscore.rs | 6 +- clippy_lints/src/lib.rs | 20 +- clippy_lints/src/lines_filter_map_ok.rs | 7 +- clippy_lints/src/literal_representation.rs | 4 +- clippy_lints/src/loops/manual_find.rs | 14 +- clippy_lints/src/loops/manual_flatten.rs | 3 +- .../src/loops/manual_while_let_some.rs | 9 +- clippy_lints/src/loops/missing_spin_loop.rs | 2 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 3 +- clippy_lints/src/loops/never_loop.rs | 4 +- .../src/loops/while_immutable_condition.rs | 3 +- .../src/loops/while_let_on_iterator.rs | 14 +- clippy_lints/src/macro_use.rs | 3 +- clippy_lints/src/manual_clamp.rs | 14 +- clippy_lints/src/manual_float_methods.rs | 173 +++++++++++ clippy_lints/src/manual_is_ascii_check.rs | 8 +- clippy_lints/src/manual_let_else.rs | 49 ++-- clippy_lints/src/manual_range_patterns.rs | 31 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_strip.rs | 3 +- clippy_lints/src/map_unit_fn.rs | 6 +- clippy_lints/src/match_result_ok.rs | 3 +- .../matches/infallible_destructuring_match.rs | 2 +- clippy_lints/src/matches/manual_filter.rs | 2 +- clippy_lints/src/matches/manual_map.rs | 2 +- clippy_lints/src/matches/manual_utils.rs | 9 +- clippy_lints/src/matches/match_as_ref.rs | 2 +- .../src/matches/match_like_matches.rs | 6 +- .../src/matches/match_on_vec_items.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 5 +- clippy_lints/src/matches/match_wild_enum.rs | 4 +- clippy_lints/src/matches/mod.rs | 4 +- .../src/matches/redundant_pattern_match.rs | 112 +++---- .../matches/significant_drop_in_scrutinee.rs | 3 +- clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/matches/try_err.rs | 2 +- .../src/methods/bind_instead_of_map.rs | 7 +- ...se_sensitive_file_extension_comparisons.rs | 6 +- clippy_lints/src/methods/chars_cmp.rs | 3 +- .../src/methods/chars_cmp_with_unwrap.rs | 3 +- clippy_lints/src/methods/clone_on_copy.rs | 4 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- .../src/methods/collapsible_str_replace.rs | 3 +- clippy_lints/src/methods/drain_collect.rs | 9 +- clippy_lints/src/methods/err_expect.rs | 3 +- clippy_lints/src/methods/expect_fun_call.rs | 4 +- clippy_lints/src/methods/extend_with_drain.rs | 2 +- clippy_lints/src/methods/filter_map.rs | 4 +- .../src/methods/filter_map_identity.rs | 3 +- clippy_lints/src/methods/filter_map_next.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 51 +++- clippy_lints/src/methods/flat_map_identity.rs | 3 +- clippy_lints/src/methods/flat_map_option.rs | 3 +- clippy_lints/src/methods/get_unwrap.rs | 2 +- .../src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/inspect_for_each.rs | 3 +- .../src/methods/is_digit_ascii_radix.rs | 6 +- clippy_lints/src/methods/iter_kv_map.rs | 3 +- .../src/methods/iter_overeager_cloned.rs | 2 +- clippy_lints/src/methods/iter_skip_next.rs | 3 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- .../methods/manual_saturating_arithmetic.rs | 10 +- clippy_lints/src/methods/manual_str_repeat.rs | 2 +- clippy_lints/src/methods/manual_try_fold.rs | 18 +- .../src/methods/map_collect_result_unit.rs | 2 +- clippy_lints/src/methods/map_flatten.rs | 3 +- clippy_lints/src/methods/map_identity.rs | 3 +- clippy_lints/src/methods/map_unwrap_or.rs | 5 +- clippy_lints/src/methods/mod.rs | 74 ++++- clippy_lints/src/methods/mut_mutex_lock.rs | 3 +- clippy_lints/src/methods/needless_collect.rs | 5 +- .../src/methods/needless_option_as_deref.rs | 6 +- .../src/methods/obfuscated_if_else.rs | 3 +- .../src/methods/option_map_or_none.rs | 3 +- .../src/methods/option_map_unwrap_or.rs | 9 +- clippy_lints/src/methods/or_fun_call.rs | 4 +- clippy_lints/src/methods/or_then_unwrap.rs | 8 +- .../src/methods/range_zip_with_len.rs | 3 +- .../src/methods/read_line_without_trim.rs | 74 +++++ clippy_lints/src/methods/seek_from_current.rs | 8 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/string_extend_chars.rs | 2 +- .../methods/suspicious_command_arg_space.rs | 3 +- .../src/methods/suspicious_to_owned.rs | 3 +- clippy_lints/src/methods/type_id_on_box.rs | 62 ++++ .../src/methods/uninit_assumed_init.rs | 3 +- .../src/methods/unnecessary_filter_map.rs | 3 +- clippy_lints/src/methods/unnecessary_fold.rs | 3 +- .../src/methods/unnecessary_iter_cloned.rs | 3 +- clippy_lints/src/methods/unnecessary_join.rs | 3 +- .../src/methods/unnecessary_literal_unwrap.rs | 24 +- .../src/methods/unnecessary_to_owned.rs | 22 +- .../src/methods/unwrap_or_else_default.rs | 8 +- clippy_lints/src/methods/useless_asref.rs | 2 +- clippy_lints/src/min_ident_chars.rs | 13 +- clippy_lints/src/missing_assert_message.rs | 12 +- .../src/missing_enforced_import_rename.rs | 7 +- clippy_lints/src/missing_fields_in_debug.rs | 31 +- clippy_lints/src/module_style.rs | 4 +- .../src/multiple_unsafe_ops_per_block.rs | 12 +- clippy_lints/src/mutable_debug_assertion.rs | 8 +- clippy_lints/src/needless_bool.rs | 6 +- clippy_lints/src/needless_borrowed_ref.rs | 4 +- clippy_lints/src/needless_else.rs | 6 +- clippy_lints/src/needless_for_each.rs | 11 +- clippy_lints/src/needless_if.rs | 5 +- clippy_lints/src/needless_late_init.rs | 4 +- .../src/needless_parens_on_range_literals.rs | 8 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 273 ++++++++++++++++++ clippy_lints/src/needless_pass_by_value.rs | 10 +- clippy_lints/src/no_effect.rs | 3 +- clippy_lints/src/non_copy_const.rs | 60 ++-- clippy_lints/src/non_expressive_names.rs | 8 +- clippy_lints/src/nonstandard_macro_braces.rs | 6 +- clippy_lints/src/only_used_in_recursion.rs | 3 +- .../src/operators/arithmetic_side_effects.rs | 30 +- .../src/operators/assign_op_pattern.rs | 3 +- clippy_lints/src/operators/eq_op.rs | 3 +- .../src/operators/misrefactored_assign_op.rs | 3 +- clippy_lints/src/operators/op_ref.rs | 4 +- clippy_lints/src/option_if_let_else.rs | 5 +- clippy_lints/src/panic_in_result_fn.rs | 10 +- clippy_lints/src/panic_unimplemented.rs | 4 +- clippy_lints/src/partialeq_to_none.rs | 7 +- clippy_lints/src/pass_by_ref_or_value.rs | 3 +- clippy_lints/src/ptr.rs | 14 +- clippy_lints/src/ptr_offset_with_cast.rs | 4 +- clippy_lints/src/question_mark.rs | 64 +++- clippy_lints/src/ranges.rs | 3 +- clippy_lints/src/raw_strings.rs | 14 +- clippy_lints/src/rc_clone_in_vec_init.rs | 15 +- clippy_lints/src/read_zero_byte_vec.rs | 10 +- clippy_lints/src/redundant_async_block.rs | 13 +- clippy_lints/src/redundant_closure_call.rs | 3 +- clippy_lints/src/redundant_slicing.rs | 3 +- clippy_lints/src/reference.rs | 2 +- clippy_lints/src/regex.rs | 66 +++-- clippy_lints/src/returns.rs | 6 +- clippy_lints/src/semicolon_block.rs | 10 +- clippy_lints/src/shadow.rs | 4 +- .../src/significant_drop_tightening.rs | 26 +- clippy_lints/src/single_call_fn.rs | 4 +- .../src/single_component_path_imports.rs | 7 +- clippy_lints/src/single_range_in_vec_init.rs | 10 +- clippy_lints/src/size_of_in_element_count.rs | 3 +- clippy_lints/src/size_of_ref.rs | 4 +- clippy_lints/src/std_instead_of_core.rs | 6 +- clippy_lints/src/strings.rs | 6 +- clippy_lints/src/strlen_on_c_strings.rs | 2 +- clippy_lints/src/suspicious_doc_comments.rs | 3 +- .../src/suspicious_xor_used_as_pow.rs | 3 +- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/tests_outside_test_module.rs | 9 +- clippy_lints/src/to_digit_is_some.rs | 2 +- .../transmute/transmute_int_to_non_zero.rs | 6 +- .../src/transmute/transmute_ptr_to_ptr.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 3 +- .../transmutes_expressible_as_ptr_casts.rs | 3 +- .../src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/transmute/utils.rs | 3 +- clippy_lints/src/tuple_array_conversions.rs | 18 +- clippy_lints/src/types/borrowed_box.rs | 5 +- clippy_lints/src/types/box_collection.rs | 3 +- clippy_lints/src/types/linked_list.rs | 3 +- clippy_lints/src/types/option_option.rs | 3 +- clippy_lints/src/types/rc_buffer.rs | 7 +- clippy_lints/src/types/rc_mutex.rs | 3 +- .../src/types/redundant_allocation.rs | 9 +- clippy_lints/src/types/vec_box.rs | 3 +- .../src/undocumented_unsafe_blocks.rs | 11 +- clippy_lints/src/unit_types/unit_cmp.rs | 4 +- clippy_lints/src/unnecessary_box_returns.rs | 26 +- .../src/unnecessary_owned_empty_strings.rs | 3 +- .../src/unnecessary_struct_initialization.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 3 +- clippy_lints/src/unnested_or_patterns.rs | 7 +- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_lints/src/unused_unit.rs | 3 +- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/use_self.rs | 11 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/conf.rs | 2 +- .../src/utils/format_args_collector.rs | 4 +- .../utils/internal_lints/if_chain_style.rs | 4 +- .../src/utils/internal_lints/invalid_paths.rs | 3 +- .../internal_lints/lint_without_lint_pass.rs | 3 +- .../internal_lints/metadata_collector.rs | 19 +- clippy_lints/src/vec.rs | 6 +- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_lints/src/visibility.rs | 6 +- clippy_lints/src/wildcard_imports.rs | 6 +- clippy_lints/src/write.rs | 22 +- clippy_test_deps/Cargo.toml | 23 -- clippy_test_deps/src/lib.rs | 14 - clippy_utils/Cargo.toml | 2 +- clippy_utils/src/attrs.rs | 7 +- clippy_utils/src/check_proc_macro.rs | 20 +- clippy_utils/src/comparisons.rs | 4 +- clippy_utils/src/consts.rs | 10 +- clippy_utils/src/eager_or_lazy.rs | 35 ++- clippy_utils/src/higher.rs | 8 +- clippy_utils/src/hir_utils.rs | 3 +- clippy_utils/src/lib.rs | 65 ++++- clippy_utils/src/macros.rs | 20 +- clippy_utils/src/mir/mod.rs | 23 +- clippy_utils/src/mir/possible_borrower.rs | 12 +- clippy_utils/src/paths.rs | 12 +- clippy_utils/src/qualify_min_const_fn.rs | 9 +- clippy_utils/src/source.rs | 3 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 35 +-- clippy_utils/src/usage.rs | 10 +- declare_clippy_lint/Cargo.toml | 2 +- lintcheck/src/config.rs | 3 +- lintcheck/src/main.rs | 4 +- lintcheck/src/recursive.rs | 3 +- rust-toolchain | 2 +- rustfmt.toml | 1 + src/driver.rs | 7 + tests/compile-test.rs | 162 ++++++++++- tests/lint_message_convention.rs | 6 +- .../uninlined_format_args.stderr | 2 +- .../auxiliary/proc_macros.rs | 11 +- tests/ui-toml/toml_trivially_copy/test.rs | 1 + tests/ui-toml/toml_trivially_copy/test.stderr | 4 +- tests/ui-toml/unwrap_used/unwrap_used.rs | 4 +- tests/ui-toml/unwrap_used/unwrap_used.stderr | 86 +++--- tests/ui/arc_with_non_send_sync.rs | 15 +- tests/ui/arc_with_non_send_sync.stderr | 33 ++- tests/ui/arithmetic_side_effects.rs | 5 + tests/ui/as_conversions.rs | 3 +- tests/ui/as_conversions.stderr | 6 +- tests/ui/auxiliary/macro_use_helper.rs | 3 +- tests/ui/auxiliary/proc_macro_attr.rs | 4 +- tests/ui/auxiliary/proc_macros.rs | 11 +- tests/ui/bind_instead_of_map.stderr | 2 +- tests/ui/bind_instead_of_map_multipart.stderr | 10 +- tests/ui/bool_comparison.fixed | 1 + tests/ui/bool_comparison.rs | 1 + tests/ui/bool_comparison.stderr | 44 +-- tests/ui/borrow_box.rs | 6 +- tests/ui/borrow_box.stderr | 20 +- tests/ui/crashes/ice-7169.stderr | 2 +- tests/ui/crashes/ice-8250.stderr | 2 +- tests/ui/default_trait_access.fixed | 3 +- tests/ui/default_trait_access.rs | 3 +- tests/ui/default_trait_access.stderr | 16 +- tests/ui/deref_addrof.stderr | 20 +- tests/ui/deref_addrof_double_trigger.stderr | 6 +- tests/ui/derive.rs | 6 +- tests/ui/derive.stderr | 20 +- tests/ui/derive_ord_xor_partial_ord.rs | 1 + tests/ui/derive_ord_xor_partial_ord.stderr | 16 +- tests/ui/entry.stderr | 20 +- tests/ui/entry_btree.stderr | 2 +- tests/ui/entry_with_else.stderr | 14 +- tests/ui/expect_fun_call.stderr | 30 +- tests/ui/explicit_auto_deref.stderr | 78 ++--- tests/ui/explicit_deref_methods.stderr | 24 +- tests/ui/explicit_write.stderr | 26 +- tests/ui/extend_with_drain.stderr | 8 +- tests/ui/filter_map_next_fixable.stderr | 4 +- tests/ui/get_first.fixed | 4 +- tests/ui/get_first.rs | 4 +- tests/ui/get_first.stderr | 6 +- tests/ui/get_unwrap.fixed | 4 +- tests/ui/get_unwrap.rs | 4 +- tests/ui/get_unwrap.stderr | 94 +++--- .../incorrect_clone_impl_on_copy_type.stderr | 4 +- ...correct_partial_ord_impl_on_ord_type.fixed | 114 ++++++++ .../incorrect_partial_ord_impl_on_ord_type.rs | 118 ++++++++ ...orrect_partial_ord_impl_on_ord_type.stderr | 31 ++ .../ui/infallible_destructuring_match.stderr | 8 +- tests/ui/infinite_loop.stderr | 10 +- tests/ui/issue-7447.rs | 5 +- tests/ui/issue-7447.stderr | 4 +- tests/ui/iter_cloned_collect.fixed | 3 +- tests/ui/iter_cloned_collect.rs | 3 +- tests/ui/iter_cloned_collect.stderr | 10 +- tests/ui/iter_overeager_cloned.stderr | 14 +- tests/ui/iter_with_drain.stderr | 12 +- tests/ui/let_and_return.rs | 61 ++-- tests/ui/let_and_return.stderr | 22 +- tests/ui/let_underscore_future.stderr | 10 +- tests/ui/let_underscore_lock.rs | 3 +- tests/ui/let_underscore_lock.stderr | 8 +- tests/ui/let_underscore_untyped.rs | 3 +- tests/ui/let_underscore_untyped.stderr | 20 +- tests/ui/manual_filter.stderr | 30 +- tests/ui/manual_float_methods.rs | 55 ++++ tests/ui/manual_float_methods.stderr | 80 +++++ tests/ui/manual_let_else.rs | 8 +- tests/ui/manual_let_else.stderr | 2 +- tests/ui/manual_let_else_question_mark.fixed | 63 ++++ tests/ui/manual_let_else_question_mark.rs | 68 +++++ tests/ui/manual_let_else_question_mark.stderr | 55 ++++ tests/ui/manual_map_option.stderr | 42 +-- tests/ui/manual_map_option_2.stderr | 10 +- tests/ui/manual_range_patterns.fixed | 4 + tests/ui/manual_range_patterns.rs | 4 + tests/ui/manual_range_patterns.stderr | 16 +- tests/ui/manual_retain.fixed | 7 +- tests/ui/manual_retain.rs | 7 +- tests/ui/manual_retain.stderr | 38 +-- tests/ui/manual_split_once.stderr | 28 +- tests/ui/manual_str_repeat.stderr | 20 +- tests/ui/manual_try_fold.rs | 4 +- tests/ui/manual_try_fold.stderr | 8 +- tests/ui/map_collect_result_unit.stderr | 4 +- tests/ui/map_unwrap_or.stderr | 2 +- tests/ui/map_unwrap_or_fixable.stderr | 4 +- tests/ui/match_as_ref.fixed | 4 +- tests/ui/match_as_ref.rs | 4 +- tests/ui/match_as_ref.stderr | 8 +- tests/ui/match_expr_like_matches_macro.stderr | 28 +- tests/ui/match_on_vec_items.stderr | 16 +- tests/ui/match_ref_pats.stderr | 4 +- tests/ui/match_same_arms2.stderr | 2 +- .../match_wildcard_for_single_variants.stderr | 20 +- tests/ui/methods.rs | 5 +- tests/ui/methods.stderr | 4 +- tests/ui/methods_fixable.stderr | 2 +- tests/ui/methods_unfixable.rs | 10 + tests/ui/methods_unfixable.stderr | 15 + tests/ui/min_ident_chars.rs | 3 +- tests/ui/min_ident_chars.stderr | 58 ++-- tests/ui/min_max.rs | 4 +- tests/ui/min_max.stderr | 26 +- tests/ui/missing_doc.rs | 4 +- tests/ui/missing_spin_loop.stderr | 12 +- tests/ui/missing_spin_loop_no_std.stderr | 2 +- tests/ui/must_use_candidates.fixed | 7 +- tests/ui/must_use_candidates.rs | 7 +- tests/ui/must_use_candidates.stderr | 10 +- tests/ui/mut_from_ref.rs | 2 +- tests/ui/mut_key.rs | 3 +- tests/ui/mut_key.stderr | 44 +-- tests/ui/mut_mut.rs | 7 +- tests/ui/mut_mut.stderr | 18 +- tests/ui/mut_reference.stderr | 16 +- tests/ui/needless_borrow.fixed | 12 + tests/ui/needless_borrow.rs | 12 + tests/ui/needless_borrow_pat.stderr | 24 +- tests/ui/needless_else.stderr | 2 +- tests/ui/needless_if.fixed | 3 +- tests/ui/needless_if.rs | 3 +- tests/ui/needless_if.stderr | 14 +- tests/ui/needless_option_as_deref.stderr | 6 +- tests/ui/needless_pass_by_ref_mut.rs | 105 +++++++ tests/ui/needless_pass_by_ref_mut.stderr | 28 ++ tests/ui/needless_splitn.stderr | 26 +- tests/ui/numbered_fields.stderr | 4 +- tests/ui/option_map_unit_fn_fixable.stderr | 38 +-- tests/ui/or_fun_call.fixed | 3 +- tests/ui/or_fun_call.rs | 3 +- tests/ui/or_fun_call.stderr | 118 ++++---- tests/ui/or_then_unwrap.stderr | 6 +- tests/ui/panic_in_result_fn.stderr | 74 +---- tests/ui/panic_in_result_fn_assertions.stderr | 12 +- tests/ui/print_literal.stderr | 24 +- tests/ui/ptr_arg.rs | 3 +- tests/ui/ptr_arg.stderr | 46 +-- tests/ui/read_line_without_trim.fixed | 36 +++ tests/ui/read_line_without_trim.rs | 36 +++ tests/ui/read_line_without_trim.stderr | 73 +++++ tests/ui/read_zero_byte_vec.rs | 2 +- tests/ui/redundant_allocation.rs | 9 +- tests/ui/redundant_allocation.stderr | 40 +-- tests/ui/redundant_allocation_fixable.fixed | 12 +- tests/ui/redundant_allocation_fixable.rs | 12 +- tests/ui/redundant_allocation_fixable.stderr | 24 +- ...dundant_pattern_matching_drop_order.stderr | 44 +-- .../redundant_pattern_matching_ipaddr.fixed | 6 +- tests/ui/redundant_pattern_matching_ipaddr.rs | 6 +- .../redundant_pattern_matching_ipaddr.stderr | 84 +++--- .../redundant_pattern_matching_option.stderr | 56 ++-- .../ui/redundant_pattern_matching_poll.stderr | 36 +-- .../redundant_pattern_matching_result.stderr | 56 ++-- tests/ui/ref_binding_to_reference.stderr | 14 +- tests/ui/result_map_unit_fn_fixable.stderr | 36 +-- tests/ui/result_map_unit_fn_unfixable.stderr | 12 +- tests/ui/self_assignment.rs | 2 +- tests/ui/semicolon_if_nothing_returned.rs | 4 +- .../ui/should_impl_trait/method_list_2.stderr | 11 +- tests/ui/significant_drop_in_scrutinee.rs | 3 +- tests/ui/significant_drop_in_scrutinee.stderr | 52 ++-- tests/ui/significant_drop_tightening.fixed | 23 ++ tests/ui/significant_drop_tightening.rs | 23 ++ tests/ui/significant_drop_tightening.stderr | 6 +- tests/ui/single_component_path_imports.fixed | 4 + tests/ui/single_component_path_imports.rs | 4 + tests/ui/single_component_path_imports.stderr | 4 +- ...gle_component_path_imports_nested_first.rs | 2 + ...component_path_imports_nested_first.stderr | 4 +- ...ingle_component_path_imports_self_after.rs | 1 + ...ngle_component_path_imports_self_before.rs | 1 + tests/ui/single_match.stderr | 36 +-- tests/ui/single_match_else.stderr | 18 +- tests/ui/slow_vector_initialization.stderr | 10 +- tests/ui/string_extend.stderr | 8 +- tests/ui/strlen_on_c_strings.stderr | 14 +- tests/ui/to_digit_is_some.stderr | 4 +- tests/ui/trivially_copy_pass_by_ref.rs | 3 +- tests/ui/trivially_copy_pass_by_ref.stderr | 36 +-- tests/ui/try_err.stderr | 22 +- tests/ui/type_id_on_box.fixed | 40 +++ tests/ui/type_id_on_box.rs | 40 +++ tests/ui/type_id_on_box.stderr | 36 +++ tests/ui/unnecessary_cast_unfixable.rs | 22 ++ tests/ui/unnecessary_cast_unfixable.stderr | 16 + tests/ui/unnecessary_clone.stderr | 12 +- tests/ui/unnecessary_literal_unwrap.fixed | 21 ++ tests/ui/unnecessary_literal_unwrap.rs | 21 ++ tests/ui/unnecessary_literal_unwrap.stderr | 86 +++++- tests/ui/unnecessary_to_owned.fixed | 3 +- tests/ui/unnecessary_to_owned.rs | 3 +- tests/ui/unsafe_removed_from_name.rs | 8 + tests/ui/unsafe_removed_from_name.stderr | 16 +- tests/ui/unused_io_amount.rs | 2 +- tests/ui/unused_peekable.rs | 3 +- tests/ui/unused_peekable.stderr | 16 +- tests/ui/unwrap_or.stderr | 4 +- tests/ui/useless_asref.fixed | 6 +- tests/ui/useless_asref.rs | 6 +- tests/ui/useless_asref.stderr | 44 +-- tests/ui/vec.fixed | 40 +++ tests/ui/vec.rs | 40 +++ tests/ui/vec.stderr | 24 +- tests/ui/wildcard_enum_match_arm.stderr | 12 +- tests/ui/wildcard_imports.fixed | 1 + tests/ui/wildcard_imports.rs | 1 + tests/ui/wildcard_imports.stderr | 24 +- .../wildcard_imports_2021.edition2018.fixed | 1 + .../wildcard_imports_2021.edition2018.stderr | 24 +- .../wildcard_imports_2021.edition2021.fixed | 1 + .../wildcard_imports_2021.edition2021.stderr | 24 +- tests/ui/wildcard_imports_2021.rs | 1 + tests/ui/write_literal.stderr | 24 +- tests/ui/write_literal_2.stderr | 28 +- util/fetch_prs_between.sh | 22 +- 516 files changed, 5266 insertions(+), 2562 deletions(-) create mode 100644 book/src/development/speedtest.md create mode 100644 clippy_lints/src/manual_float_methods.rs create mode 100644 clippy_lints/src/methods/read_line_without_trim.rs create mode 100644 clippy_lints/src/methods/type_id_on_box.rs create mode 100644 clippy_lints/src/needless_pass_by_ref_mut.rs delete mode 100644 clippy_test_deps/Cargo.toml delete mode 100644 clippy_test_deps/src/lib.rs create mode 100644 tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed create mode 100644 tests/ui/incorrect_partial_ord_impl_on_ord_type.rs create mode 100644 tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr create mode 100644 tests/ui/manual_float_methods.rs create mode 100644 tests/ui/manual_float_methods.stderr create mode 100644 tests/ui/manual_let_else_question_mark.fixed create mode 100644 tests/ui/manual_let_else_question_mark.rs create mode 100644 tests/ui/manual_let_else_question_mark.stderr create mode 100644 tests/ui/methods_unfixable.rs create mode 100644 tests/ui/methods_unfixable.stderr create mode 100644 tests/ui/needless_pass_by_ref_mut.rs create mode 100644 tests/ui/needless_pass_by_ref_mut.stderr create mode 100644 tests/ui/read_line_without_trim.fixed create mode 100644 tests/ui/read_line_without_trim.rs create mode 100644 tests/ui/read_line_without_trim.stderr create mode 100644 tests/ui/type_id_on_box.fixed create mode 100644 tests/ui/type_id_on_box.rs create mode 100644 tests/ui/type_id_on_box.stderr create mode 100644 tests/ui/unnecessary_cast_unfixable.rs create mode 100644 tests/ui/unnecessary_cast_unfixable.stderr diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index c582c28cd3c..410ff53a251 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -50,7 +50,7 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings,internal + run: cargo build --tests --features deny-warnings,internal - name: Test run: cargo test --features deny-warnings,internal diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index d5ab313ba0e..4eb11a3ac85 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -106,7 +106,7 @@ jobs: echo "$SYSROOT/bin" >> $GITHUB_PATH - name: Build - run: cargo build --features deny-warnings,internal + run: cargo build --tests --features deny-warnings,internal - name: Test if: runner.os == 'Linux' diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d822083d8..b3b6e3b865f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,74 @@ document. ## Unreleased / Beta / In Rust Nightly -[83e42a23...master](https://github.com/rust-lang/rust-clippy/compare/83e42a23...master) +[435a8ad8...master](https://github.com/rust-lang/rust-clippy/compare/435a8ad8...master) + +## Rust 1.71 + +Current stable, released 2023-07-13 + + + +We're trying out a new shorter changelog format, that only contains significant changes. +You can check out the list of merged pull requests for a list of all changes. +If you have any feedback related to the new format, please share it in +[#10847](https://github.com/rust-lang/rust-clippy/issues/10847) + +[View all 78 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-04-11T20%3A05%3A26Z..2023-05-20T13%3A48%3A17Z+base%3Amaster) + +### New Lints + +* [`non_minimal_cfg`] + [#10763](https://github.com/rust-lang/rust-clippy/pull/10763) +* [`manual_next_back`] + [#10769](https://github.com/rust-lang/rust-clippy/pull/10769) +* [`ref_patterns`] + [#10736](https://github.com/rust-lang/rust-clippy/pull/10736) +* [`default_constructed_unit_structs`] + [#10716](https://github.com/rust-lang/rust-clippy/pull/10716) +* [`manual_while_let_some`] + [#10647](https://github.com/rust-lang/rust-clippy/pull/10647) +* [`needless_bool_assign`] + [#10432](https://github.com/rust-lang/rust-clippy/pull/10432) +* [`items_after_test_module`] + [#10578](https://github.com/rust-lang/rust-clippy/pull/10578) + +### Moves and Deprecations + +* Rename `integer_arithmetic` to `arithmetic_side_effects` + [#10674](https://github.com/rust-lang/rust-clippy/pull/10674) +* Moved [`redundant_clone`] to `nursery` (Now allow-by-default) + [#10873](https://github.com/rust-lang/rust-clippy/pull/10873) + +### Enhancements + +* [`invalid_regex`]: Now supports the new syntax introduced after regex v1.8.0 + [#10682](https://github.com/rust-lang/rust-clippy/pull/10682) +* [`semicolon_outside_block`]: Added [`semicolon-outside-block-ignore-multiline`] as a new config value. + [#10656](https://github.com/rust-lang/rust-clippy/pull/10656) +* [`semicolon_inside_block`]: Added [`semicolon-inside-block-ignore-singleline`] as a new config value. + [#10656](https://github.com/rust-lang/rust-clippy/pull/10656) +* [`unnecessary_box_returns`]: Added [`unnecessary-box-size`] as a new config value to set the maximum + size of `T` in `Box` to be linted. + [#10651](https://github.com/rust-lang/rust-clippy/pull/10651) + +### Documentation Improvements + +* `cargo clippy --explain LINT` now shows possible configuration options for the explained lint + [#10751](https://github.com/rust-lang/rust-clippy/pull/10751) +* New config values mentioned in this changelog will now be linked. + [#10889](https://github.com/rust-lang/rust-clippy/pull/10889) +* Several sections of [Clippy's book] have been reworked + [#10652](https://github.com/rust-lang/rust-clippy/pull/10652) + [#10622](https://github.com/rust-lang/rust-clippy/pull/10622) + +[Clippy's book]: https://doc.rust-lang.org/clippy/ ## Rust 1.70 -Current stable, released 2023-06-01 +Released 2023-06-01 -[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+) +[View all 91 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-02-26T01%3A05%3A43Z..2023-04-11T13%3A27%3A30Z+base%3Amaster) ### New Lints @@ -137,7 +198,7 @@ Current stable, released 2023-06-01 Released 2023-04-20 -[**View 86 PRs merged since 1.68**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-03-09..2023-04-20+base%3Amaster+sort%3Amerged-desc+) +[View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-01-13T06%3A12%3A46Z..2023-02-25T23%3A48%3A10Z+base%3Amaster) ### New Lints @@ -252,7 +313,7 @@ Released 2023-04-20 Released 2023-03-09 -[**View 85 PRs merged since 1.67**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-01-26..2023-03-09+base%3Amaster+sort%3Amerged-desc+) +[View all 76 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-12-01T20%3A40%3A04Z..2023-01-12T18%3A58%3A59Z+base%3Amaster) ### New Lints @@ -399,7 +460,7 @@ Released 2023-03-09 Released 2023-01-26 -[**View 68 PRs merged since 1.66**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-12-15..2023-01-26+base%3Amaster+sort%3Amerged-desc+) +[View all 104 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-10-23T13%3A35%3A19Z..2022-12-01T13%3A34%3A39Z+base%3Amaster) ### New Lints @@ -590,8 +651,7 @@ Released 2023-01-26 Released 2022-12-15 -[**View 93 PRs merged since 1.65**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-11-03..2022-12-15+base%3Amaster+sort%3Amerged-desc+) - +[View all 116 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-09-09T17%3A32%3A39Z..2022-10-23T11%3A27%3A24Z+base%3Amaster) ### New Lints @@ -762,8 +822,7 @@ Released 2022-12-15 Released 2022-11-03 -[**View 129 PRs merged since 1.64**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-09-22..2022-11-03+base%3Amaster+sort%3Amerged-desc+) - +[View all 86 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-07-29T01%3A09%3A31Z..2022-09-09T00%3A01%3A54Z+base%3Amaster) ### Important Changes @@ -907,8 +966,7 @@ Released 2022-11-03 Released 2022-09-22 -[**View 92 PRs merged since 1.63**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-08-11..2022-09-22+base%3Amaster+sort%3Amerged-desc+) - +[View all 110 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-06-17T21%3A25%3A31Z..2022-07-28T17%3A11%3A18Z+base%3Amaster) ### New Lints @@ -1058,8 +1116,7 @@ Released 2022-09-22 Released 2022-08-11 -[**View 100 PRs merged since 1.62**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-06-30..2022-08-11+base%3Amaster+sort%3Amerged-desc+) - +[View all 91 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-05-05T17%3A24%3A22Z..2022-06-16T14%3A24%3A48Z+base%3Amaster) ### New Lints @@ -1205,8 +1262,7 @@ Released 2022-08-11 Released 2022-06-30 -[**View 104 PRs merged since 1.61**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-05-19..2022-06-30+base%3Amaster+sort%3Amerged-desc+) - +[View all 90 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-03-25T17%3A22%3A30Z..2022-05-05T13%3A29%3A44Z+base%3Amaster) ### New Lints @@ -1363,8 +1419,7 @@ Released 2022-06-30 Released 2022-05-19 -[**View 93 PRs merged since 1.60**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-04-07..2022-05-19+base%3Amaster+sort%3Amerged-desc+) - +[View all 60 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2022-02-11T16%3A54%3A41Z..2022-03-24T13%3A42%3A25Z+base%3Amaster) ### New Lints @@ -1465,8 +1520,7 @@ Released 2022-05-19 Released 2022-04-07 -[**View 75 PRs merged since 1.59**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-02-24..2022-04-07+base%3Amaster+sort%3Amerged-desc+) - +[View all 73 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-12-31T17%3A53%3A37Z..2022-02-10T17%3A31%3A37Z+base%3Amaster) ### New Lints @@ -1598,8 +1652,7 @@ Released 2022-04-07 Released 2022-02-24 -[**View 63 PRs merged since 1.58**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-01-13..2022-02-24+base%3Amaster+sort%3Amerged-desc+) - +[View all 94 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-11-04T12%3A40%3A18Z..2021-12-30T13%3A36%3A20Z+base%3Amaster) ### New Lints @@ -1763,8 +1816,7 @@ Released 2022-02-24 Released 2022-01-13 -[**View 73 PRs merged since 1.57**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-12-02..2022-01-13+base%3Amaster+sort%3Amerged-desc+) - +[View all 68 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-10-07T09%3A49%3A18Z..2021-11-04T12%3A20%3A12Z+base%3Amaster) ### Rust 1.58.1 @@ -1885,8 +1937,7 @@ Released 2022-01-13 Released 2021-12-02 -[**View 92 PRs merged since 1.56**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-10-21..2021-12-02+base%3Amaster+sort%3Amerged-desc+) - +[View all 148 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-08-12T20%3A36%3A04Z..2021-11-03T17%3A57%3A59Z+base%3Amaster) ### New Lints @@ -2037,7 +2088,7 @@ Released 2021-12-02 Released 2021-10-21 -[**View 92 PRs merged since 1.55**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-09-09..2021-10-21+base%3Amaster+sort%3Amerged-desc+) +[View all 38 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-07-19T14%3A33%3A33Z..2021-08-12T09%3A28%3A38Z+base%3Amaster) ### New Lints @@ -2103,7 +2154,7 @@ Released 2021-10-21 Released 2021-09-09 -[**View 61 PRs merged since 1.54**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-07-29..2021-09-09+base%3Amaster+sort%3Amerged-desc+) +[View all 83 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-06-03T07%3A23%3A59Z..2021-07-29T11%3A47%3A32Z+base%3Amaster) ### Important Changes @@ -2221,8 +2272,7 @@ Released 2021-09-09 Released 2021-07-29 -[**View 68 PRs merged since 1.53**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-06-17..2021-07-29+base%3Amaster+sort%3Amerged-desc+) - +[View all 74 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-04-27T23%3A51%3A18Z..2021-06-03T06%3A54%3A07Z+base%3Amaster) ### New Lints @@ -2350,7 +2400,7 @@ Released 2021-07-29 Released 2021-06-17 -[**View 80 PRs merged since 1.52**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-05-06..2021-06-17+base%3Amaster+sort%3Amerged-desc+) +[View all 126 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-03-12T22%3A49%3A20Z..2021-04-27T14%3A38%3A20Z+base%3Amaster) ### New Lints @@ -2534,8 +2584,7 @@ Released 2021-06-17 Released 2021-05-06 -[**View 113 PRs merged since 1.51**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-03-25..2021-05-06+base%3Amaster+sort%3Amerged-desc+) - +[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2021-02-03T15%3A59%3A06Z..2021-03-11T20%3A06%3A43Z+base%3Amaster) ### New Lints @@ -2670,8 +2719,7 @@ Released 2021-05-06 Released 2021-03-25 -[**View 117 PRs merged since 1.50**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-02-11..2021-03-25+base%3Amaster+sort%3Amerged-desc+) - +[View all 78 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-12-21T15%3A43%3A04Z..2021-02-03T04%3A21%3A10Z+base%3Amaster) ### New Lints @@ -2786,8 +2834,7 @@ Released 2021-03-25 Released 2021-02-11 -[**View 90 PRs merged since 1.49**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-12-31..2021-02-11+base%3Amaster+sort%3Amerged-desc+) - +[View all 119 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-11-06T18%3A32%3A40Z..2021-01-03T14%3A51%3A18Z+base%3Amaster) ### New Lints @@ -2916,8 +2963,7 @@ Released 2021-02-11 Released 2020-12-31 -[**View 85 PRs merged since 1.48**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-11-19..2020-12-31+base%3Amaster+sort%3Amerged-desc+) - +[View all 107 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-09-24T14%3A05%3A12Z..2020-11-05T13%3A35%3A44Z+base%3Amaster) ### New Lints @@ -3023,7 +3069,7 @@ Released 2020-12-31 Released 2020-11-19 -[**View 112 PRs merged since 1.47**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-10-08..2020-11-19+base%3Amaster+sort%3Amerged-desc+) +[View all 99 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-08-11T13%3A14%3A38Z..2020-09-23T18%3A55%3A22Z+base%3Amaster) ### New lints @@ -3141,8 +3187,7 @@ Released 2020-11-19 Released 2020-10-08 -[**View 80 PRs merged since 1.46**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-08-27..2020-10-08+base%3Amaster+sort%3Amerged-desc+) - +[View all 76 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-06-23T16%3A27%3A11Z..2020-08-11T12%3A52%3A41Z+base%3Amaster) ### New lints @@ -3244,8 +3289,7 @@ Released 2020-10-08 Released 2020-08-27 -[**View 93 PRs merged since 1.45**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-07-16..2020-08-27+base%3Amaster+sort%3Amerged-desc+) - +[View all 48 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-05-31T12%3A50%3A53Z..2020-06-23T15%3A00%3A32Z+base%3Amaster) ### New lints @@ -3307,8 +3351,7 @@ Released 2020-08-27 Released 2020-07-16 -[**View 65 PRs merged since 1.44**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-06-04..2020-07-16+base%3Amaster+sort%3Amerged-desc+) - +[View all 81 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-04-18T20%3A18%3A04Z..2020-05-27T19%3A25%3A04Z+base%3Amaster) ### New lints @@ -3385,8 +3428,7 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565 Released 2020-06-04 -[**View 88 PRs merged since 1.43**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-04-23..2020-06-04+base%3Amaster+sort%3Amerged-desc+) - +[View all 124 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-03-05T17%3A30%3A53Z..2020-04-18T09%3A20%3A51Z+base%3Amaster) ### New lints @@ -3469,8 +3511,7 @@ Released 2020-06-04 Released 2020-04-23 -[**View 121 PRs merged since 1.42**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-03-12..2020-04-23+base%3Amaster+sort%3Amerged-desc+) - +[View all 91 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2020-01-26T16%3A01%3A11Z..2020-03-04T16%3A45%3A37Z+base%3Amaster) ### New lints @@ -3528,7 +3569,7 @@ Released 2020-04-23 Released 2020-03-12 -[**View 106 PRs merged since 1.41**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-01-30..2020-03-12+base%3Amaster+sort%3Amerged-desc+) +[View all 101 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-12-15T01%3A40%3A34Z..2020-01-26T11%3A22%3A13Z+base%3Amaster) ### New lints @@ -3595,7 +3636,7 @@ Released 2020-03-12 Released 2020-01-30 -[**View 107 PRs merged since 1.40**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-12-19..2020-01-30+base%3Amaster+sort%3Amerged-desc+) +[View all 74 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-10-28T20%3A50%3A24Z..2019-12-12T00%3A53%3A03Z+base%3Amaster) * New Lints: * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697) @@ -3640,8 +3681,7 @@ Released 2020-01-30 Released 2019-12-19 -[**View 69 😺 PRs merged since 1.39**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-11-07..2019-12-19+base%3Amaster+sort%3Amerged-desc+) - +[View all 76 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-09-23T06%3A18%3A04Z..2019-10-28T17%3A34%3A55Z+base%3Amaster) * New Lints: * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537) @@ -3683,7 +3723,7 @@ Released 2019-12-19 Released 2019-11-07 -[**View 84 PRs merged since 1.38**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-09-26..2019-11-07+base%3Amaster+sort%3Amerged-desc+) +[View all 100 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-08-11T19%3A21%3A38Z..2019-09-22T12%3A07%3A39Z+base%3Amaster) * New Lints: * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479) @@ -3727,7 +3767,7 @@ Released 2019-11-07 Released 2019-09-26 -[**View 102 PRs merged since 1.37**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-08-15..2019-09-26+base%3Amaster+sort%3Amerged-desc+) +[View all 76 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-06-30T13%3A40%3A26Z..2019-08-11T09%3A47%3A27Z+base%3Amaster) * New Lints: * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203) @@ -3757,7 +3797,7 @@ Released 2019-09-26 Released 2019-08-15 -[**View 83 PRs merged since 1.36**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-07-04..2019-08-15+base%3Amaster+sort%3Amerged-desc+) +[View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-05-19T08%3A11%3A23Z..2019-06-25T23%3A22%3A22Z+base%3Amaster) * New Lints: * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088) @@ -3781,8 +3821,7 @@ Released 2019-08-15 Released 2019-07-04 -[**View 75 PRs merged since 1.35**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-05-20..2019-07-04+base%3Amaster+sort%3Amerged-desc+) - +[View all 81 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-04-10T09%3A41%3A56Z..2019-05-18T00%3A29%3A40Z+base%3Amaster) * New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039) * New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954) @@ -3813,8 +3852,7 @@ Released 2019-07-04 Released 2019-05-20 -[**View 90 PRs merged since 1.34**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-04-10..2019-05-20+base%3Amaster+sort%3Amerged-desc+) - +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e) * New lint: `drop_bounds` to detect `T: Drop` bounds * Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101) @@ -3842,8 +3880,7 @@ Released 2019-05-20 Released 2019-04-10 -[**View 66 PRs merged since 1.33**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-02-26..2019-04-10+base%3Amaster+sort%3Amerged-desc+) - +[View all 61 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2019-01-17T17%3A45%3A39Z..2019-02-19T08%3A24%3A05Z+base%3Amaster) * New lint: [`assertions_on_constants`] to detect for example `assert!(true)` * New lint: [`dbg_macro`] to detect uses of the `dbg!` macro @@ -3873,7 +3910,7 @@ Released 2019-04-10 Released 2019-02-26 -[**View 83 PRs merged since 1.32**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-01-17..2019-02-26+base%3Amaster+sort%3Amerged-desc+) +[View all 120 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-11-28T06%3A19%3A50Z..2019-01-15T09%3A27%3A02Z+base%3Amaster) * New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`] * The `rust-clippy` repository is now part of the `rust-lang` org. @@ -3906,7 +3943,7 @@ Released 2019-02-26 Released 2019-01-17 -[**View 106 PRs merged since 1.31**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-12-06..2019-01-17+base%3Amaster+sort%3Amerged-desc+) +[View all 71 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-10-24T05%3A02%3A21Z..2018-11-27T17%3A29%3A34Z+base%3Amaster) * New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`, [`redundant_clone`], [`wildcard_dependencies`], @@ -3936,8 +3973,7 @@ Released 2019-01-17 Released 2018-12-06 -[**View 85 PRs merged since 1.30**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-10-25..2018-12-06+base%3Amaster+sort%3Amerged-desc+) - +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2) * Clippy has been relicensed under a dual MIT / Apache license. See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more @@ -3977,9 +4013,7 @@ Released 2018-12-06 Released 2018-10-25 -[**View 106 PRs merged since 1.29**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-09-13..2018-10-25+base%3Amaster+sort%3Amerged-desc+) - - +[View all 88 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-08-02T16%3A54%3A12Z..2018-09-17T09%3A44%3A06Z+base%3Amaster) * Deprecate `assign_ops` lint * New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], [`needless_collect`], [`copy_iterator`] @@ -4861,6 +4895,7 @@ Released 2018-09-13 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor [`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type +[`incorrect_partial_ord_impl_on_ord_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_partial_ord_impl_on_ord_type [`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice [`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 @@ -4941,6 +4976,8 @@ Released 2018-09-13 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check +[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite +[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map @@ -5047,6 +5084,7 @@ Released 2018-09-13 [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals +[`needless_pass_by_ref_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_pub_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pub_self [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark @@ -5134,6 +5172,7 @@ Released 2018-09-13 [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex +[`read_line_without_trim`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_line_without_trim [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation @@ -5266,6 +5305,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`tuple_array_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity +[`type_id_on_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_id_on_box [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks diff --git a/Cargo.toml b/Cargo.toml index 76c804f935e..0fb3a3a984b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.72" +version = "0.1.73" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -36,6 +36,17 @@ walkdir = "2.3" filetime = "0.2" itertools = "0.10.1" +# UI test dependencies +clippy_utils = { path = "clippy_utils" } +derive-new = "0.5" +if_chain = "1.0" +quote = "1.0" +serde = { version = "1.0.125", features = ["derive"] } +syn = { version = "2.0", features = ["full"] } +futures = "0.3" +parking_lot = "0.12" +tokio = { version = "1", features = ["io-util"] } + [build-dependencies] rustc_tools_util = "0.3.0" diff --git a/README.md b/README.md index d712d3e6750..5d490645d89 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 over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 3b627096268..486ea3df704 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 52445494436..df9b1bbe18f 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -56,28 +56,6 @@ and open that file in your editor of choice. When updating the changelog it's also a good idea to make sure that `commit1` is already correct in the current changelog. -#### PR ranges - -We developed the concept of PR ranges to help the user understand the size of a new update. To create a PR range, -get the current release date and the date that the last version was released (YYYY-MM-DD) and use the following link: - -``` -[**View PRs merged since 1.**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A..+base%3Amaster+sort%3Amerged-desc+) -``` - -> Note: Be sure to check click the link and check how many PRs got merged between - -Example: - -``` -[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+) -``` - -Which renders to: -[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+) - -Note that **commit ranges should not be included**, only PR ranges. - ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you diff --git a/book/src/development/speedtest.md b/book/src/development/speedtest.md new file mode 100644 index 00000000000..0db718e6ad6 --- /dev/null +++ b/book/src/development/speedtest.md @@ -0,0 +1,24 @@ +# Speedtest +`SPEEDTEST` is the tool we use to measure lint's performance, it works by executing the same test several times. + +It's useful for measuring changes to current lints and deciding if the performance changes too much. `SPEEDTEST` is +accessed by the `SPEEDTEST` (and `SPEEDTEST_*`) environment variables. + +## Checking Speedtest + +To do a simple speed test of a lint (e.g. `allow_attributes`), use this command. + +```sh +$ SPEEDTEST=ui TESTNAME="allow_attributes" cargo uitest -- --nocapture +``` + +This will test all `ui` tests (`SPEEDTEST=ui`) whose names start with `allow_attributes`. By default, `SPEEDTEST` will +iterate your test 1000 times. But you can change this with `SPEEDTEST_ITERATIONS`. + +```sh +$ SPEEDTEST=toml SPEEDTEST_ITERATIONS=100 TESTNAME="semicolon_block" cargo uitest -- --nocapture +``` + +> **WARNING**: Be sure to use `-- --nocapture` at the end of the command to see the average test time. If you don't +> use `-- --nocapture` (e.g. `SPEEDTEST=ui` `TESTNAME="let_underscore_untyped" cargo uitest -- --nocapture`), this +> will not show up. diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 60d7ce6e615..f8073dac330 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -175,7 +175,7 @@ The maximum amount of nesting a block can reside in ## `disallowed-names` The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value -`".."` can be used as part of the list to indicate, that the configured values should be appended to the +`".."` can be used as part of the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. **Default Value:** `["foo", "baz", "quux"]` (`Vec`) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index f11aa547bd7..f39bc06e6d7 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -358,6 +358,10 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let mod_file_path = ty_dir.join("mod.rs"); let context_import = setup_mod_file(&mod_file_path, lint)?; + let pass_lifetimes = match context_import { + "LateContext" => "<'_>", + _ => "", + }; let name_upper = lint.name.to_uppercase(); let mut lint_file_contents = String::new(); @@ -372,7 +376,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R use super::{name_upper}; // TODO: Adjust the parameters as necessary - pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{ + pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: &Msrv) {{ if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ return; }} @@ -389,7 +393,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R use super::{name_upper}; // TODO: Adjust the parameters as necessary - pub(super) fn check(cx: &{context_import}) {{ + pub(super) fn check(cx: &{context_import}{pass_lifetimes}) {{ todo!(); }} "# diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index efdb158c21e..a7138f36a4e 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -37,7 +37,7 @@ impl ClippyProjectInfo { pub fn setup_rustc_src(rustc_path: &str) { let Ok(rustc_source_dir) = check_and_get_rustc_dir(rustc_path) else { - return + return; }; for project in CLIPPY_PROJECTS { diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 7213c9dfede..7c2e06ea69a 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -340,7 +340,10 @@ pub fn deprecate(name: &str, reason: Option<&String>) { let name_upper = name.to_uppercase(); let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c23054443bb..11136867ff0 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.72" +version = "0.1.73" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/allow_attributes.rs b/clippy_lints/src/allow_attributes.rs index eb21184713e..e1ef514edfd 100644 --- a/clippy_lints/src/allow_attributes.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -1,5 +1,6 @@ use ast::{AttrStyle, Attribute}; -use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index a1e44668e1a..7adcd9ad055 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,12 +1,11 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::last_path_segment; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use if_chain::if_chain; - use rustc_hir::{Expr, ExprKind}; -use rustc_lint::LateContext; -use rustc_lint::LateLintPass; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_middle::ty::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -15,8 +14,8 @@ declare_clippy_lint! { /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`. /// /// ### Why is this bad? - /// Wrapping a type in Arc doesn't add thread safety to the underlying data, so data races - /// could occur when touching the underlying data. + /// `Arc` is only `Send`/`Sync` when `T` is [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), + /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` /// /// ### Example /// ```rust @@ -24,16 +23,17 @@ declare_clippy_lint! { /// # use std::sync::Arc; /// /// fn main() { - /// // This is safe, as `i32` implements `Send` and `Sync`. + /// // This is fine, as `i32` implements `Send` and `Sync`. /// let a = Arc::new(42); /// - /// // This is not safe, as `RefCell` does not implement `Sync`. + /// // `RefCell` is `!Sync`, so either the `Arc` should be replaced with an `Rc` + /// // or the `RefCell` replaced with something like a `RwLock` /// let b = Arc::new(RefCell::new(42)); /// } /// ``` #[clippy::version = "1.72.0"] pub ARC_WITH_NON_SEND_SYNC, - correctness, + suspicious, "using `Arc` with a type that does not implement `Send` or `Sync`" } declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); @@ -41,32 +41,38 @@ declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl LateLintPass<'_> for ArcWithNonSendSync { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Arc); - if let ExprKind::Call(func, [arg]) = expr.kind; - if let ExprKind::Path(func_path) = func.kind; - if last_path_segment(&func_path).ident.name == sym::new; - if let arg_ty = cx.typeck_results().expr_ty(arg); - if !matches!(arg_ty.kind(), ty::Param(_)); - if !cx.tcx - .lang_items() - .sync_trait() - .map_or(false, |id| implements_trait(cx, arg_ty, id, &[])) || - !cx.tcx - .get_diagnostic_item(sym::Send) - .map_or(false, |id| implements_trait(cx, arg_ty, id, &[])); + if is_type_diagnostic_item(cx, ty, sym::Arc) + && let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(func_path) = func.kind + && last_path_segment(&func_path).ident.name == sym::new + && let arg_ty = cx.typeck_results().expr_ty(arg) + // make sure that the type is not and does not contain any type parameters + && arg_ty.walk().all(|arg| { + !matches!(arg.unpack(), GenericArgKind::Type(ty) if matches!(ty.kind(), ty::Param(_))) + }) + && let Some(send) = cx.tcx.get_diagnostic_item(sym::Send) + && let Some(sync) = cx.tcx.lang_items().sync_trait() + && let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[])) + && !(is_send && is_sync) + { + span_lint_and_then( + cx, + ARC_WITH_NON_SEND_SYNC, + expr.span, + "usage of an `Arc` that is not `Send` or `Sync`", + |diag| with_forced_trimmed_paths!({ + if !is_send { + diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`")); + } + if !is_sync { + diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`")); + } - then { - span_lint_and_help( - cx, - ARC_WITH_NON_SEND_SYNC, - expr.span, - "usage of `Arc` where `T` is not `Send` or `Sync`", - None, - "consider using `Rc` instead or wrapping `T` in a std::sync type like \ - `Mutex`", - ); - } + diag.note(format!("required for `{ty}` to implement `Send` and `Sync`")); + + diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`"); + } + )); } } } diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a8dc0cb3b58..b90914e936a 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -31,14 +31,20 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, e) else { + return; + }; let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { Some(sym::debug_assert_macro) => true, Some(sym::assert_macro) => false, _ => return, }; - let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; - let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return }; + let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { + return; + }; + let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { + return; + }; if val { span_lint_and_help( cx, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 2ba78f99569..2a5be275615 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,12 +1,10 @@ //! checks for attributes +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_from_proc_macro; use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{ - diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, - is_from_proc_macro, -}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 9c053247402..1593d7b0fb3 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::get_parent_expr; -use clippy_utils::higher; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{get_parent_expr, higher}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -85,8 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { ); } } else { - let span = - block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); + let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); if span.from_expansion() || expr.span.from_expansion() { return; } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index d984fddc57a..4503597713a 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -61,7 +61,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - ) }) .map_or(false, |assoc_item| { - let proj = Ty::new_projection(cx.tcx,assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); + let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() @@ -70,14 +70,18 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; let macro_name = cx.tcx.item_name(macro_call.def_id); let eq_macro = match macro_name.as_str() { "assert_eq" | "debug_assert_eq" => true, "assert_ne" | "debug_assert_ne" => false, _ => return, }; - let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + return; + }; let a_span = a.span.source_callsite(); let b_span = b.span.source_callsite(); @@ -126,7 +130,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())]; if bool_value ^ eq_macro { - let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return }; + let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { + return; + }; suggestions.push((non_lit_expr.span, (!sugg).to_string())); } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index bdb3a011602..1828dd65152 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -4,7 +4,9 @@ use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use clippy_utils::{in_constant, is_else_clause, is_integer_literal}; use rustc_errors::Applicability; declare_clippy_lint! { diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 814108ed8a7..b3dbbb08f8e 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -1,9 +1,8 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_from_proc_macro; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_lint_allowed}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index e42c3fe2432..fa9c525fc08 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,12 +1,10 @@ -use clippy_utils::{ - diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, - path_def_id, paths, ty::expr_sig, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::macro_backtrace; +use clippy_utils::ty::expr_sig; +use clippy_utils::{get_parent_node, is_default_equivalent, match_path, path_def_id, paths}; use rustc_errors::Applicability; -use rustc_hir::{ - intravisit::{walk_ty, Visitor}, - Block, Expr, ExprKind, Local, Node, QPath, TyKind, -}; +use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_forced_trimmed_paths; diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs index fa1550a0ef9..1e56ed5f450 100644 --- a/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -3,10 +3,8 @@ use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::{ - mir::Mutability, - ty::{self, Ty, TypeAndMut}, -}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty::{self, Ty, TypeAndMut}; use super::AS_PTR_CAST_MUT; diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 27cc5a1c3f0..4d9cc4cacc3 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,10 +1,12 @@ +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_then, source}; +use clippy_utils::source; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TypeAndMut}; use super::CAST_SLICE_DIFFERENT_SIZES; diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 1233c632a79..5e0123842b0 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, Expr, ExprKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index f0c1df01430..ce1ab10910c 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,6 +1,6 @@ -use clippy_utils::msrvs::POINTER_CAST_CONSTNESS; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{Msrv, POINTER_CAST_CONSTNESS}; use clippy_utils::sugg::Sugg; -use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 71cf2aea0f8..ae56f38d9ad 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>( &format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", cast_str.clone(), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1c321f46e2d..e3a09636e24 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -10,8 +10,7 @@ use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; -use rustc_hir::intravisit; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; +use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::query::Key; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 7436e9ce811..726674d88f1 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -5,7 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{symbol::sym, Span}; +use rustc_span::symbol::sym; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index ea17e7a6071..49452136d6f 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -71,7 +71,9 @@ impl DbgMacro { impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml if self.allow_dbg_in_tests diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b60f0738f64..498d657b31f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -206,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO, + crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO, crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, @@ -272,6 +273,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_float_methods::MANUAL_IS_FINITE_INFO, + crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, @@ -388,6 +391,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, + crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, crate::methods::SEARCH_IS_SOME_INFO, @@ -403,6 +407,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, crate::methods::UNIT_HASH_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, @@ -468,6 +473,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_if::NEEDLESS_IF_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, + crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO, crate::needless_update::NEEDLESS_UPDATE_INFO, diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index ca9514ccc7d..a294c693787 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,5 +1,7 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, is_ty_alias, match_def_path, paths}; -use hir::{def::Res, ExprKind}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_ty_alias, match_def_path, paths}; +use hir::def::Res; +use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index f296b80d283..572990aaba1 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::last_path_segment; use clippy_utils::source::snippet_with_context; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::{last_path_segment, match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 9217edcef07..d09428dbc1f 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -4,15 +4,11 @@ use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{ - intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, -}; +use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{ - lint::in_external_macro, - ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, -}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::iter; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 0e7efd53390..8a9d978a106 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -12,12 +12,11 @@ use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, - def_id::{DefId, LocalDefId}, - BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, + ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; @@ -30,9 +29,11 @@ use rustc_middle::ty::{ ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults, }; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::sym, Span, Symbol}; +use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::collections::VecDeque; declare_clippy_lint! { @@ -77,6 +78,11 @@ declare_clippy_lint! { /// Suggests that the receiver of the expression borrows /// the expression. /// + /// ### Known problems + /// The lint cannot tell when the implementation of a trait + /// for `&T` and `T` do different things. Removing a borrow + /// in such a case can change the semantics of the code. + /// /// ### Example /// ```rust /// fn fun(_a: &i32) {} @@ -589,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { pat.spans, "this pattern creates a reference to a reference", |diag| { - diag.multipart_suggestion("try this", replacements, app); + diag.multipart_suggestion("try", replacements, app); }, ); } @@ -1123,7 +1129,9 @@ fn needless_borrow_impl_arg_position<'tcx>( let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) }; + let Some(callee_def_id) = fn_def_id(cx, parent) else { + return Position::Other(precedence); + }; let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); let args_with_expr_ty = cx .typeck_results() @@ -1252,7 +1260,12 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { .in_definition_order() .any(|assoc_item| { if assoc_item.fn_has_self_parameter { - let self_ty = cx.tcx.fn_sig(assoc_item.def_id).instantiate_identity().skip_binder().inputs()[0]; + let self_ty = cx + .tcx + .fn_sig(assoc_item.def_id) + .instantiate_identity() + .skip_binder() + .inputs()[0]; matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) } else { false @@ -1296,8 +1309,8 @@ fn referent_used_exactly_once<'tcx>( possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, reference: &Expr<'tcx>, ) -> bool { - let mir = enclosing_mir(cx.tcx, reference.hir_id); - if let Some(local) = expr_local(cx.tcx, reference) + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) && let [location] = *local_assignments(mir, local).as_slice() && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind @@ -1442,9 +1455,7 @@ fn ty_auto_deref_stability<'tcx>( ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { Position::ReborrowStable(precedence).into() }, - ty::Adt(_, args) if args.has_non_region_param() => { - TyPosition::new_deref_stable_for_result(precedence, ty) - }, + ty::Adt(_, args) if args.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Bool | ty::Char | ty::Int(_) @@ -1531,7 +1542,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data Mutability::Not => "explicit `deref` method call", Mutability::Mut => "explicit `deref_mut` method call", }, - "try this", + "try", format!("{addr_of_str}{deref_str}{expr_str}"), app, ); @@ -1593,7 +1604,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } else { format!("{prefix}{snip}") }; - diag.span_suggestion(data.span, "try this", sugg, app); + diag.span_suggestion(data.span, "try", sugg, app); }, ); }, @@ -1620,7 +1631,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data |diag| { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(data.span, "try this", snip.into_owned(), app); + diag.span_suggestion(data.span, "try", snip.into_owned(), app); }, ); }, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 71b5104bed8..9a85cc4ce2d 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -3,10 +3,9 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{ - self as hir, - def::{CtorKind, CtorOf, DefKind, Res}, - Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, + self as hir, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; @@ -99,10 +98,11 @@ fn check_struct<'tcx>( if let Some(PathSegment { args, .. }) = p.segments.last() { let args = args.map(|a| a.args).unwrap_or(&[]); - // ty_args contains the generic parameters of the type declaration, while args contains the arguments - // used at instantiation time. If both len are not equal, it means that some parameters were not - // provided (which means that the default values were used); in this case we will not risk - // suggesting too broad a rewrite. We won't either if any argument is a type or a const. + // ty_args contains the generic parameters of the type declaration, while args contains the + // arguments used at instantiation time. If both len are not equal, it means that some + // parameters were not provided (which means that the default values were used); in this + // case we will not risk suggesting too broad a rewrite. We won't either if any argument + // is a type or a const. if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { return; } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 78e7f93e2bf..c343f248d06 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::paths; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{is_lint_allowed, match_def_path}; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -334,7 +333,9 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, }; - let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { + return; + }; let (ty_adt, ty_subs) = match *ty.kind() { // Unions can't derive clone. ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index ca8671c8f1a..95d3f7547b4 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { path_def_id(cx, expr) }; let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else { - return + return; }; let conf = match self.disallowed.get(&def_id) { Some(&index) => &self.conf_disallowed[index], diff --git a/clippy_lints/src/disallowed_names.rs b/clippy_lints/src/disallowed_names.rs index 6e6615f08ee..04c2d44137a 100644 --- a/clippy_lints/src/disallowed_names.rs +++ b/clippy_lints/src/disallowed_names.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint, is_test_module_or_function}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_test_module_or_function; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{Item, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 87d88f70752..e5f39d102cd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -31,9 +31,8 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; -use std::io; use std::ops::Range; -use std::thread; +use std::{io, thread}; use url::Url; declare_clippy_lint! { @@ -295,7 +294,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + return; + }; match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { @@ -339,7 +340,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + return; + }; if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { lint_for_missing_headers(cx, item.owner_id, sig, headers, None, None); @@ -349,7 +352,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + return; + }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 976ce47e869..14122abbf2c 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::get_parent_node; -use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use clippy_utils::{get_parent_node, is_must_use_func_call}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs index ec063c0f777..209fb66fa40 100644 --- a/clippy_lints/src/empty_drop.rs +++ b/clippy_lints/src/empty_drop.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index c3a020433de..282157181ab 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Item, ItemKind, VariantData}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index f470987833e..dda14b4df53 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -1,8 +1,10 @@ use crate::Lint; -use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_lint_allowed; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::Ty}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Symbol; use std::borrow::Cow; diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index ee5a875ade7..6197b5b19eb 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,18 +1,14 @@ -use clippy_utils::higher; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::{ - can_move_expr_to_closure_no_visit, - diagnostics::span_lint_and_sugg, - is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, - source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, - SpanlessEq, + can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, + paths, peel_hir_expr_while, SpanlessEq, }; use core::fmt::{self, Write}; use rustc_errors::Applicability; -use rustc_hir::{ - hir_id::HirIdSet, - intravisit::{walk_expr, Visitor}, - Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp, -}; +use rustc_hir::hir_id::HirIdSet; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{Span, SyntaxContext, DUMMY_SP}; @@ -69,16 +65,21 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { return; } - let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else { - return + let Some(higher::If { + cond: cond_expr, + then: then_expr, + r#else: else_expr, + }) = higher::If::hir(expr) + else { + return; }; let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else { - return + return; }; let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else { - return + return; }; let mut app = Applicability::MachineApplicable; @@ -186,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { MAP_ENTRY, expr.span, &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), - "try this", + "try", sugg, app, ); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index a51a8ee09f6..dbe3453e7bf 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir; -use rustc_hir::intravisit; -use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir::{self, intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs index d04d833e630..8911f1872c2 100644 --- a/clippy_lints/src/excessive_nesting.rs +++ b/clippy_lints/src/excessive_nesting.rs @@ -1,9 +1,8 @@ -use clippy_utils::{diagnostics::span_lint_and_help, source::snippet}; -use rustc_ast::{ - node_id::NodeSet, - visit::{walk_block, walk_item, Visitor}, - Block, Crate, Inline, Item, ItemKind, ModKind, NodeId, -}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use rustc_ast::node_id::NodeSet; +use rustc_ast::visit::{walk_block, walk_item, Visitor}; +use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 315df6c714f..4b9ca8c917e 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { EXPLICIT_WRITE, expr.span, &format!("use of `{used}.unwrap()`"), - "try this", + "try", format!("{prefix}{sugg_mac}!({inputs_snippet})"), applicability, ); diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 126bed6789c..c18006a71c2 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::is_from_proc_macro; -use clippy_utils::trait_ref_of_method; +use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; @@ -12,10 +11,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{ - def_id::{DefId, LocalDefId}, - Span, -}; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 5e0fcd74339..29e5315f88b 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,10 +1,8 @@ -use clippy_utils::consts::{ - constant, constant_simple, Constant, - Constant::{Int, F32, F64}, -}; +use clippy_utils::consts::Constant::{Int, F32, F64}; +use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - diagnostics::span_lint_and_sugg, eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, - numeric_literal, peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d34d6e9279e..f4f8bdc2c44 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -43,7 +43,9 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { return; } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 08e45ed7d0e..01c714c414b 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -14,10 +14,8 @@ use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatPlaceholder, FormatTrait, }; -use rustc_errors::{ - Applicability, - SuggestionStyle::{CompletelyHidden, ShowCode}, -}; +use rustc_errors::Applicability; +use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; @@ -188,7 +186,9 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; if !is_format_macro(cx, macro_call.def_id) { return; } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 3ddee1842a3..76369bccf9e 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -7,8 +7,8 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; -use rustc_span::{sym, symbol::kw, Symbol}; +use rustc_span::symbol::kw; +use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -127,7 +127,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(format_trait_impl) = self.format_trait_impl else { return }; + let Some(format_trait_impl) = self.format_trait_impl else { + return; + }; if format_trait_impl.name == sym::Display { check_to_string_in_display(cx, expr); diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 35061fc64c9..2b899e21ef5 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -10,7 +10,8 @@ use rustc_hir::{ TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::{hir::nested_filter::OnlyBodies, ty}; +use rustc_middle::hir::nested_filter::OnlyBodies; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -76,7 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args - && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::instantiate_identity) + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + .map(ty::EarlyBinder::instantiate_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _)) { @@ -163,10 +165,14 @@ fn convert_to_from( return None; } let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); - let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { + return None; + }; let body = cx.tcx.hir().body(body_id); let [input] = body.params else { return None }; - let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { + return None; + }; let from = snippet_opt(cx, self_ty.span)?; let into = snippet_opt(cx, target_ty.span)?; diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index 096508dc4f1..5e859d97c62 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -4,8 +4,7 @@ use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::RawPtr; -use rustc_middle::ty::TypeAndMut; +use rustc_middle::ty::{RawPtr, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index d3d0d91c1be..597fca88885 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -1,6 +1,8 @@ -use clippy_utils::{diagnostics::span_lint_and_then, is_in_test_function}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_in_test_function; -use rustc_hir::{intravisit::FnKind, Body, HirId}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, HirId}; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index b244b913314..18f7368dafb 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, ImplicitSelfKind, Unsafety}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Unsafety}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -12,8 +13,8 @@ use super::MISNAMED_GETTERS; pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) { let FnKind::Method(ref ident, sig) = kind else { - return; - }; + return; + }; // Takes only &(mut) self if decl.inputs.len() != 1 { @@ -25,8 +26,8 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: let name = match decl.implicit_self { ImplicitSelfKind::MutRef => { let Some(name) = name.strip_suffix("_mut") else { - return; - }; + return; + }; name }, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, @@ -76,7 +77,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: for adjusted_type in iter::once(typeck_results.expr_ty(self_data)) .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target)) { - let ty::Adt(def,_) = adjusted_type.kind() else { + let ty::Adt(def, _) = adjusted_type.kind() else { continue; }; @@ -91,13 +92,15 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: } let Some(used_field) = used_field else { - // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number. + // Can happen if the field access is a tuple. We don't lint those because the getter name could not + // start with a number. return; }; let Some(correct_field) = correct_field else { // There is no field corresponding to the getter name. - // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is + // FIXME: This can be a false positive if the correct field is reachable through deeper + // autodereferences than used_field is return; }; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 1b173de856b..83445c7a0ca 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,14 +1,13 @@ use hir::FnSig; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::def_id::DefIdSet; -use rustc_hir::{self as hir, def::Res, QPath}; +use rustc_hir::{self as hir, QPath}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::{ - lint::in_external_macro, - ty::{self, Ty}, -}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index bd473ac7e51..34f1bf3b2b1 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -23,7 +23,7 @@ pub(super) fn check_fn( } let Some(code_snippet) = snippet_opt(cx, body.value.span) else { - return + return; }; let mut line_count: u64 = 0; let mut in_comment = false; diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 9ea8c494cfc..e614a8f694f 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::SpanlessEq; +use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Diagnostic; use rustc_hir::intravisit::{self as visit, Visitor}; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 725bd3d54bc..ab6ad3f3b3a 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -119,7 +119,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool { stmts.iter().any(|stmt| { - let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false }; + let Stmt { + kind: StmtKind::Semi(e), + .. + } = stmt + else { + return false; + }; contains_return(e) }) diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 372b6ead3fe..a6b035d5106 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,9 +1,7 @@ -use clippy_utils::{ - diagnostics::span_lint_hir_and_then, - get_async_fn_body, is_async_fn, - source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, - visitors::for_each_expr, -}; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs index 7b95116ee4e..c5da6039ac3 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/incorrect_impls.rs @@ -1,11 +1,15 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, path_res}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::EarlyBinder; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, symbol}; +use rustc_span::sym; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does @@ -46,25 +50,80 @@ declare_clippy_lint! { correctness, "manual implementation of `Clone` on a `Copy` type" } -declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]); +declare_clippy_lint! { + /// ### What it does + /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is + /// necessary. + /// + /// ### Why is this bad? + /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by + /// wrapping the result of `cmp` in `Some` for `partial_cmp`. Not doing this may silently + /// introduce an error upon refactoring. + /// + /// ### Limitations + /// Will not lint if `Self` and `Rhs` do not have the same type. + /// + /// ### Example + /// ```rust + /// # use std::cmp::Ordering; + /// #[derive(Eq, PartialEq)] + /// struct A(u32); + /// + /// impl Ord for A { + /// fn cmp(&self, other: &Self) -> Ordering { + /// // ... + /// # todo!(); + /// } + /// } + /// + /// impl PartialOrd for A { + /// fn partial_cmp(&self, other: &Self) -> Option { + /// // ... + /// # todo!(); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::cmp::Ordering; + /// #[derive(Eq, PartialEq)] + /// struct A(u32); + /// + /// impl Ord for A { + /// fn cmp(&self, other: &Self) -> Ordering { + /// // ... + /// # todo!(); + /// } + /// } + /// + /// impl PartialOrd for A { + /// fn partial_cmp(&self, other: &Self) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, + correctness, + "manual implementation of `PartialOrd` when `Ord` is already implemented" +} +declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]); impl LateLintPass<'_> for IncorrectImpls { - #[expect(clippy::needless_return)] + #[expect(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { - let node = get_parent_node(cx.tcx, impl_item.hir_id()); - let Some(Node::Item(item)) = node else { - return; - }; - let ItemKind::Impl(imp) = item.kind else { + let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else { return; }; let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { return; }; - let trait_impl_def_id = trait_impl.def_id; if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { return; } + let ItemKind::Impl(imp) = item.kind else { + return; + }; let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { return; }; @@ -72,15 +131,12 @@ impl LateLintPass<'_> for IncorrectImpls { let ExprKind::Block(block, ..) = body.value.kind else { return; }; - // Above is duplicated from the `duplicate_manual_partial_ord_impl` branch. - // Remove it while solving conflicts once that PR is merged. - // Actual implementation; remove this comment once aforementioned PR is merged - if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl_def_id) + if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait( cx, - hir_ty_to_ty(cx.tcx, imp.self_ty), + trait_impl.self_ty(), copy_def_id, &[], ) @@ -88,9 +144,9 @@ impl LateLintPass<'_> for IncorrectImpls { if impl_item.ident.name == sym::clone { if block.stmts.is_empty() && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, inner) = expr.kind - && let ExprKind::Path(qpath) = inner.kind - && last_path_segment(&qpath).ident.name == symbol::kw::SelfLower + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower {} else { span_lint_and_sugg( cx, @@ -112,7 +168,7 @@ impl LateLintPass<'_> for IncorrectImpls { INCORRECT_CLONE_IMPL_ON_COPY_TYPE, impl_item.span, "incorrect implementation of `clone_from` on a `Copy` type", - "remove this", + "remove it", String::new(), Applicability::MaybeIncorrect, ); @@ -120,5 +176,69 @@ impl LateLintPass<'_> for IncorrectImpls { return; } } + + if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) + && impl_item.ident.name == sym::partial_cmp + && let Some(ord_def_id) = cx + .tcx + .diagnostic_items(trait_impl.def_id.krate) + .name_to_id + .get(&sym::Ord) + && implements_trait( + cx, + hir_ty_to_ty(cx.tcx, imp.self_ty), + *ord_def_id, + trait_impl.args, + ) + { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) = expr.kind + && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + && let ExprKind::MethodCall(cmp_path, _, [other_expr], ..) = cmp_expr.kind + && cmp_path.ident.name == sym::cmp + && let Res::Local(..) = path_res(cx, other_expr) + {} else { + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + if let [lhs, rhs, ..] = trait_impl.args.as_slice() && lhs != rhs { + return; + } + + span_lint_and_then( + cx, + INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, + item.span, + "incorrect implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + + let suggs = if let Some(other_ident) = other.pat.simple_ident() { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + } else { + vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ] + }; + + diag.multipart_suggestion( + "change this to", + suggs, + Applicability::Unspecified, + ); + } + ); + } + } } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 7a269e98ff1..01a7c497cbe 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -13,7 +13,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::Ident, Span}; +use rustc_span::symbol::Ident; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 3ac40401e89..3d1113ff9cc 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -3,7 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 7e1548531f1..f95d2c2edb1 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { INIT_NUMBERED_FIELDS, e.span, "used a field initializer for a tuple struct", - "try this instead", + "try", snippet, appl, ); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 34e9991582c..8df7dfb8b9e 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -7,7 +7,8 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{source_map::Spanned, sym}; +use rustc_span::source_map::Spanned; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index 40378ee8205..55a43e91562 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_from_proc_macro, is_in_cfg_test}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_from_proc_macro, is_in_cfg_test}; use rustc_hir::{HirId, ItemId, ItemKind, Mod}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -32,7 +33,7 @@ declare_clippy_lint! { /// // [...] /// } /// ``` - #[clippy::version = "1.70.0"] + #[clippy::version = "1.71.0"] pub ITEMS_AFTER_TEST_MODULE, style, "An item was found after the testing module `tests`" diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index bb100ec632e..066d2c4b787 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -1,5 +1,8 @@ -use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait}; -use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::get_parent_node; +use clippy_utils::ty::implements_trait; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 693218f8a9c..b22b57a3006 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,10 +1,8 @@ //! lint when there is a large size difference between variants on an enum +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{ - diagnostics::span_lint_and_then, - ty::{approx_ty_size, is_copy, AdtVariantInfo}, -}; +use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 087c4a65250..d67d5899350 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 424c0d9e798..566901de347 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -2,8 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; -use rustc_hir::Expr; -use rustc_hir::ExprKind; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 9c0cc978a39..7aa1446d53d 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -4,11 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::fn_has_unsatisfiable_preds; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; -use rustc_hir::Body; -use rustc_hir::FnDecl; +use rustc_hir::{Body, FnDecl}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_tool_lint; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; declare_clippy_lint! { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 83fa25c795a..50c433d3161 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,22 +1,23 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def_id::DefIdSet; +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_hir::lang_items::LangItem; use rustc_hir::{ - def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, - GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy, - QPath, TraitItemRef, TyKind, TypeBindingKind, + AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, + ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy, QPath, TraitItemRef, TyKind, + TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{ - source_map::{Span, Spanned, Symbol}, - symbol::sym, -}; +use rustc_span::source_map::{Span, Spanned, Symbol}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -145,7 +146,10 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let Some(local_id) = ty_id.as_local(); let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); - if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()); + if let Some(output) = parse_len_output( + cx, + cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder() + ); then { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index db41bc67da1..2f6f36c3960 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::path_to_local_id; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local_id, visitors::is_local_used}; +use clippy_utils::visitors::is_local_used; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index b7a47002080..e7c875ab3a9 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; -use clippy_utils::{is_must_use_func_call, paths}; +use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::GenericArgKind; -use rustc_middle::ty::IsSuggestable; +use rustc_middle::ty::{GenericArgKind, IsSuggestable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{BytePos, Span}; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 365cc4c59ad..5e62cfd70ec 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -183,6 +183,7 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; +mod manual_float_methods; mod manual_is_ascii_check; mod manual_let_else; mod manual_main_separator_str; @@ -228,6 +229,7 @@ mod needless_for_each; mod needless_if; mod needless_late_init; mod needless_parens_on_range_literals; +mod needless_pass_by_ref_mut; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -345,11 +347,10 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` +use crate::utils::conf::metadata::get_configuration_metadata; +use crate::utils::conf::TryConf; pub use crate::utils::conf::{lookup_conf_file, Conf}; -use crate::utils::{ - conf::{metadata::get_configuration_metadata, TryConf}, - FindAll, -}; +use crate::utils::FindAll; /// Register all pre expansion lints /// @@ -662,7 +663,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); let matches_for_let_else = conf.matches_for_let_else; - store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); @@ -722,7 +722,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); - store.register_late_pass(|_| Box::new(regex::Regex)); + store.register_late_pass(|_| Box::::default()); let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); @@ -771,7 +771,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(msrv(), matches_for_let_else))); store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed)); store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); @@ -1056,6 +1056,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let stack_size_threshold = conf.stack_size_threshold; store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); + store.register_late_pass(move |_| { + Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new( + avoid_breaking_exported_api, + )) + }); store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls)); store.register_late_pass(move |_| { Box::new(single_call_fn::SingleCallFn { @@ -1072,6 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); + store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index 09b2032e20f..49425ff0a8e 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,7 +1,6 @@ -use clippy_utils::{ - diagnostics::span_lint_and_then, is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths, - ty::match_type, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::match_type; +use clippy_utils::{is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index dadcd9c5135..09ca0317337 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,7 +264,7 @@ impl LiteralDigitGrouping { return; } - if Self::is_literal_uuid_formatted(&mut num_lit) { + if Self::is_literal_uuid_formatted(&num_lit) { return; } @@ -376,7 +376,7 @@ impl LiteralDigitGrouping { /// /// Returns `true` if the radix is hexadecimal, and the groups match the /// UUID format of 8-4-4-4-12. - fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool { + fn is_literal_uuid_formatted(num_lit: &NumericLiteral<'_>) -> bool { if num_lit.radix != Radix::Hexadecimal { return false; } diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 4bb9936e9cd..0aaa66e6bce 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,14 +1,14 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FIND; -use clippy_utils::{ - diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt, - source::snippet_with_applicability, ty::implements_trait, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::implements_trait; +use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{ - def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind, -}; +use rustc_hir::def::Res; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 1e02a30e35f..559a2c03f14 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,9 +1,8 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::higher; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, peel_blocks_with_stmt}; +use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index cb9c84be4c7..ca584a454d0 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,9 +1,6 @@ -use clippy_utils::{ - diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}, - match_def_path, paths, - source::snippet, - SpanlessEq, -}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 0e49f08d898..7b7d19c753f 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' MISSING_SPIN_LOOP, body.span, "busy-waiting loop should at least have a spin loop hint", - "try this", + "try", (if is_no_std_crate(cx) { "{ core::hint::spin_loop() }" } else { diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 529189b52ac..ffd29ab7630 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -601,7 +601,7 @@ declare_clippy_lint! { /// // use `number` /// } /// ``` - #[clippy::version = "1.70.0"] + #[clippy::version = "1.71.0"] pub MANUAL_WHILE_LET_SOME, style, "checking for emptiness of a `Vec` in the loop condition and popping an element in the body" diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 4dae93f6028..b83d148b5f2 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -7,7 +7,8 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::{mir::FakeReadCause, ty}; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty; use rustc_span::source_map::Span; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index ee338c6beb0..7da9121fbb3 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,9 +1,9 @@ use super::utils::make_iterator_snippet; use super::NEVER_LOOP; -use clippy_utils::consts::constant; +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; -use clippy_utils::{consts::Constant, diagnostics::span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index d1a1f773f87..7f24f3c5dc2 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -6,8 +6,7 @@ use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, HirIdSet, QPath}; use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 55989f8a446..8019f906aca 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,18 +1,18 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{ - get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used, -}; +use clippy_utils::visitors::is_res_used; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{symbol::sym, Symbol}; +use rustc_span::symbol::sym; +use rustc_span::Symbol; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { @@ -332,7 +332,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) { let Res::Local(local_id) = iter_expr.path else { - return true + return true; }; let mut v = NestedLoopVisitor { cx, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 8e322a97907..9b158f18f62 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -8,7 +8,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{edition::Edition, sym, Span}; +use rustc_span::edition::Edition; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 440362b96b4..e75666e61f5 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -4,21 +4,19 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::MaybePath; use clippy_utils::{ eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, + peel_blocks_with_stmt, MaybePath, }; use itertools::Itertools; -use rustc_errors::Applicability; -use rustc_errors::Diagnostic; -use rustc_hir::{ - def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, -}; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::def::Res; +use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::sym, Span}; +use rustc_span::symbol::sym; +use rustc_span::Span; use std::ops::Deref; declare_clippy_lint! { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs new file mode 100644 index 00000000000..085c73a5f9f --- /dev/null +++ b/clippy_lints/src/manual_float_methods.rs @@ -0,0 +1,173 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::{is_from_proc_macro, path_to_local}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual `is_infinite` reimplementations + /// (i.e., `x == ::INFINITY || x == ::NEG_INFINITY`). + /// + /// ### Why is this bad? + /// The method `is_infinite` is shorter and more readable. + /// + /// ### Example + /// ```rust + /// # let x = 1.0f32; + /// if x == f32::INFINITY || x == f32::NEG_INFINITY {} + /// ``` + /// Use instead: + /// ```rust + /// # let x = 1.0f32; + /// if x.is_infinite() {} + /// ``` + #[clippy::version = "1.72.0"] + pub MANUAL_IS_INFINITE, + style, + "use dedicated method to check if a float is infinite" +} +declare_clippy_lint! { + /// ### What it does + /// Checks for manual `is_finite` reimplementations + /// (i.e., `x != ::INFINITY && x != ::NEG_INFINITY`). + /// + /// ### Why is this bad? + /// The method `is_finite` is shorter and more readable. + /// + /// ### Example + /// ```rust + /// # let x = 1.0f32; + /// if x != f32::INFINITY && x != f32::NEG_INFINITY {} + /// if x.abs() < f32::INFINITY {} + /// ``` + /// Use instead: + /// ```rust + /// # let x = 1.0f32; + /// if x.is_finite() {} + /// if x.is_finite() {} + /// ``` + #[clippy::version = "1.72.0"] + pub MANUAL_IS_FINITE, + style, + "use dedicated method to check if a float is finite" +} +declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]); + +#[derive(Clone, Copy)] +enum Variant { + ManualIsInfinite, + ManualIsFinite, +} + +impl Variant { + pub fn lint(self) -> &'static Lint { + match self { + Self::ManualIsInfinite => MANUAL_IS_INFINITE, + Self::ManualIsFinite => MANUAL_IS_FINITE, + } + } + + pub fn msg(self) -> &'static str { + match self { + Self::ManualIsInfinite => "manually checking if a float is infinite", + Self::ManualIsFinite => "manually checking if a float is finite", + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !in_external_macro(cx.sess(), expr.span) + && (!cx.param_env.is_const() || cx.tcx.features().active(sym!(const_float_classify))) + && let ExprKind::Binary(kind, lhs, rhs) = expr.kind + && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind + && let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind + // Checking all possible scenarios using a function would be a hopeless task, as we have + // 16 possible alignments of constants/operands. For now, let's use `partition`. + && let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] + .into_iter() + .partition::>, _>(|i| path_to_local(i).is_some()) + && let [first, second] = &*operands + && let Some([const_1, const_2]) = constants + .into_iter() + .map(|i| constant(cx, cx.typeck_results(), i)) + .collect::>>() + .as_deref() + && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) + // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in + // case somebody does that for some reason + && (is_infinity(const_1) && is_neg_infinity(const_2) + || is_neg_infinity(const_1) && is_infinity(const_2)) + && !is_from_proc_macro(cx, expr) + && let Some(local_snippet) = snippet_opt(cx, first.span) + { + let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, + (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite, + _ => return, + }; + + span_lint_and_then( + cx, + variant.lint(), + expr.span, + variant.msg(), + |diag| { + match variant { + Variant::ManualIsInfinite => { + diag.span_suggestion( + expr.span, + "use the dedicated method instead", + format!("{local_snippet}.is_infinite()"), + Applicability::MachineApplicable, + ); + }, + Variant::ManualIsFinite => { + // TODO: There's probably some better way to do this, i.e., create + // multiple suggestions with notes between each of them + diag.span_suggestion_verbose( + expr.span, + "use the dedicated method instead", + format!("{local_snippet}.is_finite()"), + Applicability::MaybeIncorrect, + ) + .span_suggestion_verbose( + expr.span, + "this will alter how it handles NaN; if that is a problem, use instead", + format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"), + Applicability::MaybeIncorrect, + ) + .span_suggestion_verbose( + expr.span, + "or, for conciseness", + format!("!{local_snippet}.is_infinite()"), + Applicability::MaybeIncorrect, + ); + }, + } + }, + ); + } + } +} + +fn is_infinity(constant: &Constant<'_>) -> bool { + match constant { + Constant::F32(float) => *float == f32::INFINITY, + Constant::F64(float) => *float == f64::INFINITY, + _ => false, + } +} + +fn is_neg_infinity(constant: &Constant<'_>) -> bool { + match constant { + Constant::F32(float) => *float == f32::NEG_INFINITY, + Constant::F64(float) => *float == f64::NEG_INFINITY, + _ => false, + } +} diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 31264261f5d..f264424470d 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,12 +1,16 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::root_macro_call; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{higher, in_constant}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{def_id::DefId, sym, Span}; +use rustc_span::def_id::DefId; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 59e421c1622..c531137b785 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -1,18 +1,17 @@ +use crate::question_mark::{QuestionMark, QUESTION_MARK}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::peel_blocks; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Descend, Visitable}; -use if_chain::if_chain; +use clippy_utils::{is_lint_allowed, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::declare_tool_lint; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use serde::Deserialize; @@ -50,25 +49,8 @@ declare_clippy_lint! { "manual implementation of a let...else statement" } -pub struct ManualLetElse { - msrv: Msrv, - matches_behaviour: MatchLintBehaviour, -} - -impl ManualLetElse { - #[must_use] - pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { - Self { - msrv, - matches_behaviour, - } - } -} - -impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); - -impl<'tcx> LateLintPass<'tcx> for ManualLetElse { - fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { +impl<'tcx> QuestionMark { + pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { return; } @@ -81,11 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { match if_let_or_match { - IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { - if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then); - if let Some(if_else) = if_else; - if expr_diverges(cx, if_else); - then { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => { + if + let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) && + let Some(if_else) = if_else && + expr_diverges(cx, if_else) && + let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) && + (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none()) + { emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else); } }, @@ -128,8 +113,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { } }; } - - extract_msrv_attr!(LateContext); } fn emit_manual_let_else( @@ -208,7 +191,9 @@ fn replace_in_pattern( match pat.kind { PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some(pat_to_put) = ident_map.get(&binding_name.name) else { break 'a }; + let Some(pat_to_put) = ident_map.get(&binding_name.name) else { + break 'a; + }; let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); if let Some(subpt) = opt_subpt { let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 65ff555209a..39d8b20d38d 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -2,12 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_hir::ExprKind; -use rustc_hir::PatKind; -use rustc_hir::RangeEnd; -use rustc_lint::LintContext; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -19,6 +15,10 @@ declare_clippy_lint! { /// ### Why is this bad? /// Using an explicit range is more concise and easier to read. /// + /// ### Known issues + /// This lint intentionally does not handle numbers greater than `i128::MAX` for `u128` literals + /// in order to support negative numbers. + /// /// ### Example /// ```rust /// let x = 6; @@ -36,11 +36,14 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_u128(expr: &Expr<'_>) -> Option { - if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &Expr<'_>) -> Option { + if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { + expr_as_i128(expr).map(|num| -num) + } else if let ExprKind::Lit(lit) = expr.kind && let LitKind::Int(num, _) = lit.node { - Some(num) + // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. + num.try_into().ok() } else { None } @@ -56,22 +59,22 @@ impl LateLintPass<'_> for ManualRangePatterns { if let PatKind::Or(pats) = pat.kind && pats.len() >= 3 { - let mut min = u128::MAX; - let mut max = 0; + let mut min = i128::MAX; + let mut max = i128::MIN; let mut numbers_found = FxHashSet::default(); let mut ranges_found = Vec::new(); for pat in pats { if let PatKind::Lit(lit) = pat.kind - && let Some(num) = expr_as_u128(lit) + && let Some(num) = expr_as_i128(lit) { numbers_found.insert(num); min = min.min(num); max = max.max(num); } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind - && let Some(left) = expr_as_u128(left) - && let Some(right) = expr_as_u128(right) + && let Some(left) = expr_as_i128(left) + && let Some(right) = expr_as_i128(right) && right >= left { min = min.min(left); diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index aafee92713f..0e89ca132ef 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -119,7 +119,7 @@ fn check_for_either_unsigned_int_constant<'a>( } fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None }; + let int_const = constant_full_int(cx, cx.typeck_results(), expr)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 93d977a5c96..2b9def1a688 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -8,8 +8,7 @@ use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::BinOpKind; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 75605fb3091..f0a0f482af2 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -226,7 +226,7 @@ fn lint_map_unit_fn( ); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { - diag.span_suggestion(stmt.span, "try this", suggestion, applicability); + diag.span_suggestion(stmt.span, "try", suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); @@ -241,7 +241,7 @@ fn lint_map_unit_fn( snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); - diag.span_suggestion(stmt.span, "try this", suggestion, applicability); + diag.span_suggestion(stmt.span, "try", suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", @@ -249,7 +249,7 @@ fn lint_map_unit_fn( snippet(cx, binding.pat.span, "_"), snippet(cx, var_arg.span, "_"), ); - diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders); + diag.span_suggestion(stmt.span, "try", suggestion, Applicability::HasPlaceholders); } }); } diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 6ec9784038c..841c020f2d3 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; -use clippy_utils::is_res_lang_ctor; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{higher, is_res_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index d18c92caba2..3329f93b73c 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -28,7 +28,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { local.span, "you seem to be trying to use `match` to destructure a single infallible pattern. \ Consider using `let`", - "try this", + "try", format!( "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index f6bf0e7aa1a..e0181a4757c 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -143,7 +143,7 @@ fn check<'tcx>( MANUAL_FILTER, expr.span, "manual implementation of `Option::filter`", - "try this", + "try", if sugg_info.needs_brackets { format!( "{{ {}{}.filter({body_str}) }}", diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index aaba239677f..ed3d8b09fdc 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -58,7 +58,7 @@ fn check<'tcx>( MANUAL_MAP, expr.span, "manual implementation of `Option::map`", - "try this", + "try", if sugg_info.needs_brackets { format!( "{{ {}{}.map({}) }}", diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 5b7644a5383..6b611f567ae 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,14 +1,17 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use crate::map_unit_fn::OPTION_MAP_UNIT_FN; +use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 29c6e11134f..d51cca040a2 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -46,7 +46,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: MATCH_AS_REF, expr.span, &format!("use `{suggestion}()` instead"), - "try this", + "try", format!( "{}.{suggestion}(){cast}", snippet_with_applicability(cx, ex.span, "_", &mut applicability), diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 0064619ef89..e2ddf11abe2 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,9 +1,7 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lint_allowed; -use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::span_contains_comment; +use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat, PatKind, QPath}; @@ -139,7 +137,7 @@ where MATCH_LIKE_MATCHES_MACRO, expr.span, &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), - "try this", + "try", format!( "{}matches!({}, {pat_and_guard})", if b0 { "" } else { "!" }, diff --git a/clippy_lints/src/matches/match_on_vec_items.rs b/clippy_lints/src/matches/match_on_vec_items.rs index 2917f85c45f..89dcac4d849 100644 --- a/clippy_lints/src/matches/match_on_vec_items.rs +++ b/clippy_lints/src/matches/match_on_vec_items.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { MATCH_ON_VEC_ITEMS, scrutinee.span, "indexing into a vector may panic", - "try this", + "try", format!( "{}.get({})", snippet(cx, vec.span, ".."), diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 3d2fbea63f5..6fc79faddbe 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -2,8 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; use core::cmp::Ordering; -use core::iter; -use core::slice; +use core::{iter, slice}; use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -240,7 +239,7 @@ impl<'a> NormalizedPat<'a> { }, PatKind::TupleStruct(ref path, pats, wild_idx) => { let Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else { - return Self::Wild + return Self::Wild; }; let (var_id, variant) = if adt.is_enum() { match cx.qpath_res(path, pat.hir_id).opt_def_id() { diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 3126b590180..8d22ceb47f8 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -143,7 +143,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, "wildcard matches only a single variant and will also match any future added variants", - "try this", + "try", format_suggestion(x), Applicability::MaybeIncorrect, ), @@ -161,7 +161,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, - "try this", + "try", suggestions.join(" | "), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 00fa3eb9bfe..d1061171e4d 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1125,8 +1125,8 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar //|^ let found = arm_spans.try_fold(start, |start, range| { let Some((end, next_start)) = range else { - // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were - // found. + // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute + // were found. return Err(()); }; let span = SpanData { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 039c2134cf8..ad38f1394b4 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -45,6 +45,62 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { } } +fn find_method_and_type<'tcx>( + cx: &LateContext<'tcx>, + check_pat: &Pat<'_>, + op_ty: Ty<'tcx>, +) -> Option<(&'static str, Ty<'tcx>)> { + match check_pat.kind { + PatKind::TupleStruct(ref qpath, args, rest) => { + let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild)); + let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_))); + + if is_wildcard || is_rest { + let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id); + let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { + return None; + }; + let lang_items = cx.tcx.lang_items(); + if Some(id) == lang_items.result_ok_variant() { + Some(("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))) + } else if Some(id) == lang_items.result_err_variant() { + Some(("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))) + } else if Some(id) == lang_items.option_some_variant() { + Some(("is_some()", op_ty)) + } else if Some(id) == lang_items.poll_ready_variant() { + Some(("is_ready()", op_ty)) + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) { + Some(("is_ipv4()", op_ty)) + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) { + Some(("is_ipv6()", op_ty)) + } else { + None + } + } else { + None + } + }, + PatKind::Path(ref path) => { + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + { + let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { + "is_none()" + } else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) { + "is_pending()" + } else { + return None; + }; + // `None` and `Pending` don't have an inner type. + Some((method, cx.tcx.types.unit)) + } else { + None + } + }, + _ => None, + } +} + fn find_sugg_for_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -62,52 +118,8 @@ fn find_sugg_for_if_let<'tcx>( let op_ty = cx.typeck_results().expr_ty(let_expr); // Determine which function should be used, and the type contained by the corresponding // variant. - let (good_method, inner_ty) = match check_pat.kind { - PatKind::TupleStruct(ref qpath, args, rest) => { - let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild)); - let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_))); - - if is_wildcard || is_rest { - let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id); - let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return }; - let lang_items = cx.tcx.lang_items(); - if Some(id) == lang_items.result_ok_variant() { - ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) - } else if Some(id) == lang_items.result_err_variant() { - ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty)) - } else if Some(id) == lang_items.option_some_variant() { - ("is_some()", op_ty) - } else if Some(id) == lang_items.poll_ready_variant() { - ("is_ready()", op_ty) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) { - ("is_ipv4()", op_ty) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) { - ("is_ipv6()", op_ty) - } else { - return; - } - } else { - return; - } - }, - PatKind::Path(ref path) => { - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) - && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - { - let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { - "is_none()" - } else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) { - "is_pending()" - } else { - return; - }; - // `None` and `Pending` don't have an inner type. - (method, cx.tcx.types.unit) - } else { - return; - } - }, - _ => return, + let Some((good_method, inner_ty)) = find_method_and_type(cx, check_pat, op_ty) else { + return; }; // If this is the last expression in a block or there is an else clause then the whole @@ -175,7 +187,7 @@ fn find_sugg_for_if_let<'tcx>( .maybe_par() .to_string(); - diag.span_suggestion(span, "try this", format!("{keyword} {sugg}.{good_method}"), app); + diag.span_suggestion(span, "try", format!("{keyword} {sugg}.{good_method}"), app); if needs_drop { diag.note("this will change drop order of the result, as well as all temporaries"); @@ -200,7 +212,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op REDUNDANT_PATTERN_MATCHING, span, &format!("redundant pattern matching, consider using `{good_method}`"), - "try this", + "try", format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), Applicability::MachineApplicable, ); @@ -336,7 +348,9 @@ enum Item { } fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool { - let Some(id) = cx.typeck_results().qpath_res(path, pat.hir_id).opt_def_id() else { return false }; + let Some(id) = cx.typeck_results().qpath_res(path, pat.hir_id).opt_def_id() else { + return false; + }; match expected_item { Item::Lang(expected_lang_item) => cx diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 02500fac293..ee0fdb35313 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -6,8 +6,7 @@ use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty::GenericArgKind; -use rustc_middle::ty::{Ty, TypeAndMut}; +use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut}; use rustc_span::Span; use super::SIGNIFICANT_DROP_IN_SCRUTINEE; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 35627d6c649..6b05c6bfffd 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -136,7 +136,7 @@ fn report_single_pattern( } }; - span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app); + span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } fn check_opt_like<'a>( diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 3a7f1e034f3..99a748489b4 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -70,7 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine TRY_ERR, expr.span, "returning an `Err(_)` with the `?` operator", - "try this", + "try", suggestion, applicability, ); diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 00853348840..3a8cc41748e 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,7 +1,8 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions}; +use clippy_utils::visitors::find_all_ret_expressions; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -87,7 +88,7 @@ pub(crate) trait BindInsteadOfMap { BIND_INSTEAD_OF_MAP, expr.span, &msg, - "try this", + "try", note, app, ); @@ -124,7 +125,7 @@ pub(crate) trait BindInsteadOfMap { span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { multispan_sugg_with_applicability( diag, - "try this", + "try", Applicability::MachineApplicable, std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain( suggs diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index fb10f782f7a..d5897822eda 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; -use clippy_utils::source::{indent_of, reindent_multiline}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::{source_map::Spanned, Span}; +use rustc_span::source_map::Spanned; +use rustc_span::Span; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index 079df2226d1..0e41f3c2107 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -4,8 +4,7 @@ use clippy_utils::{method_chain_args, path_def_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_lint::Lint; +use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 8984b2cf8fd..c9d50a5b03d 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -5,8 +5,7 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_lint::Lint; +use rustc_lint::{LateContext, Lint}; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. pub(super) fn check( diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 65fd50dff58..7eb325ee7b5 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -5,7 +5,9 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_middle::ty::{self}; use rustc_span::symbol::{sym, Symbol}; use super::CLONE_ON_COPY; diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 5e8ad0861f3..ddf3c9f27df 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -42,7 +42,7 @@ pub(super) fn check( CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", - "try this", + "try", format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), app, ); diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 5e01ed90ff0..5409ede6008 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -8,8 +8,7 @@ use rustc_hir as hir; use rustc_lint::LateContext; use std::collections::VecDeque; -use super::method_call; -use super::COLLAPSIBLE_STR_REPLACE; +use super::{method_call, COLLAPSIBLE_STR_REPLACE}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index d0c79dc11b0..6a82d8f756a 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -4,17 +4,12 @@ use clippy_utils::is_range_full; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_hir::ExprKind; -use rustc_hir::LangItem; -use rustc_hir::Path; -use rustc_hir::QPath; +use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::query::Key; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_span::sym; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; /// Checks if both types match the given diagnostic item, e.g.: /// diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 4ab06bf2ae8..3d82441c0e6 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,8 +1,7 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::has_debug_impl; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 9af11f08c26..d3e90e4bba3 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -144,7 +144,7 @@ pub(super) fn check<'tcx>( EXPECT_FUN_CALL, span_replace_word, &format!("use of `{name}` followed by a function call"), - "try this", + "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); @@ -162,7 +162,7 @@ pub(super) fn check<'tcx>( EXPECT_FUN_CALL, span_replace_word, &format!("use of `{name}` followed by a function call"), - "try this", + "try", format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, ); diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index 37b28463527..495b266529b 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: EXTEND_WITH_DRAIN, expr.span, "use of `extend` instead of `append` for adding the full range of a second vector", - "try this", + "try", format!( "{}.append({}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index fc80f2eeae0..597a423b537 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -13,9 +13,7 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol}; use std::borrow::Cow; -use super::MANUAL_FILTER_MAP; -use super::MANUAL_FIND_MAP; -use super::OPTION_FILTER_MAP; +use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP}; fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index d1b5e945dfd..3337b250c0e 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -3,7 +3,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::FILTER_MAP_IDENTITY; diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 175e04f8ac0..3f89e593148 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( FILTER_MAP_NEXT, expr.span, msg, - "try this", + "try", format!("{iter_snippet}.find_map({filter_snippet})"), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index edcec0fc101..ce7f9997b5a 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; +use rustc_ast::{BindingAnnotation, Mutability}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -8,6 +9,21 @@ use rustc_span::sym; use super::FILTER_NEXT; +fn path_to_local(expr: &hir::Expr<'_>) -> Option { + match expr.kind { + hir::ExprKind::Field(f, _) => path_to_local(f), + hir::ExprKind::Index(recv, _) => path_to_local(recv), + hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { + res: rustc_hir::def::Res::Local(local), + .. + }, + )) => Some(*local), + _ => None, + } +} + /// lint use of `filter().next()` for `Iterators` pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -26,15 +42,30 @@ pub(super) fn check<'tcx>( if filter_snippet.lines().count() <= 1 { let iter_snippet = snippet(cx, recv.span, ".."); // add note if not multi-line - span_lint_and_sugg( - cx, - FILTER_NEXT, - expr.span, - msg, - "try this", - format!("{iter_snippet}.find({filter_snippet})"), - Applicability::MachineApplicable, - ); + span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { + let (applicability, pat) = if let Some(id) = path_to_local(recv) + && let Some(hir::Node::Pat(pat)) = cx.tcx.hir().find(id) + && let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; + + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.find({filter_snippet})"), + applicability, + ); + + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"), + ); + } + }); } else { span_lint(cx, FILTER_NEXT, expr.span, msg); } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 6f911d79d0b..84a21de0ac8 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -3,7 +3,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::FLAT_MAP_IDENTITY; diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 4737a101345..172c397fbc8 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -4,7 +4,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::FLAT_MAP_OPTION; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index e35fb12ed79..a8f090d1de1 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -71,7 +71,7 @@ pub(super) fn check<'tcx>( GET_UNWRAP, span, &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), - "try this", + "try", format!( "{borrow_str}{}[{get_args_str}]", snippet_with_applicability(cx, recv.span, "..", &mut applicability) diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 37da52dc733..631741d9290 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::{sym, Symbol}; use super::INEFFICIENT_TO_STRING; diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 7fd3ef1a622..23cc192c38e 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::INSPECT_FOR_EACH; diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 301aff5ae6a..120f3d5f42c 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,10 +1,10 @@ //! Lint for `c.is_digit(10)` use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{ - consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability, -}; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index c87f5daab6f..674d3451748 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -9,8 +9,7 @@ use clippy_utils::visitors::is_local_used; use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::sym; -use rustc_span::Span; +use rustc_span::{sym, Span}; /// lint use of: /// - `hashmap.iter().map(|(_, v)| v)` diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index b4210d87510..9f7ec19aa59 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( if let Some(mut snip) = snippet_opt(cx, method_span) { snip.push_str(trailing_clone); let replace_span = expr.span.with_lo(cloned_recv.span.hi()); - diag.span_suggestion(replace_span, "try this", snip, Applicability::MachineApplicable); + diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable); } } ); diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 279175e20c3..39af52141bb 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::path_to_local; use clippy_utils::source::snippet; +use clippy_utils::{is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingAnnotation, Node, PatKind}; diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index f6772c5c6b3..2ab721ace84 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -21,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span ITER_WITH_DRAIN, span.with_hi(expr.span.hi()), &format!("`drain(..)` used on a `{ty_name}`"), - "try this", + "try", "into_iter()".to_string(), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index a7284c64497..540425eef8c 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -21,13 +21,13 @@ pub fn check( return; } - let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return }; + let Some(mm) = is_min_or_max(cx, unwrap_arg) else { + return; + }; if ty.is_signed() { - use self::{ - MinMax::{Max, Min}, - Sign::{Neg, Pos}, - }; + use self::MinMax::{Max, Min}; + use self::Sign::{Neg, Pos}; let Some(sign) = lit_sign(arith_rhs) else { return; diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a08f7254053..ab13d30d845 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -88,7 +88,7 @@ pub(super) fn check( MANUAL_STR_REPEAT, collect_expr.span, "manual implementation of `str::repeat` using iterators", - "try this", + "try", format!("{val_str}.repeat({count_snip})"), app ) diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 576a58499b1..dabed0affcf 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -1,15 +1,11 @@ -use clippy_utils::{ - diagnostics::span_lint_and_sugg, - is_from_proc_macro, - msrvs::{Msrv, ITERATOR_TRY_FOLD}, - source::snippet_opt, - ty::implements_trait, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::msrvs::{Msrv, ITERATOR_TRY_FOLD}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; -use rustc_hir::{ - def::{DefKind, Res}, - Expr, ExprKind, -}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::Span; diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 042bab805b8..01cdd02e602 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr MAP_COLLECT_RESULT_UNIT, expr.span, "`.map().collect()` can be replaced with `.try_for_each()`", - "try this", + "try", format!( "{}.try_for_each({})", snippet(cx, iter.span, ".."), diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 950902d455b..e74a764551c 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -6,7 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{symbol::sym, Span}; +use rustc_span::symbol::sym; +use rustc_span::Span; use super::MAP_FLATTEN; diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 0f25ef82ed4..7be1ce483f6 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -4,7 +4,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::MAP_IDENTITY; diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 3122f72ee91..5464e455dea 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -11,7 +11,8 @@ use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s -/// Return true if lint triggered +/// +/// Returns true if the lint was emitted pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -63,7 +64,7 @@ pub(super) fn check<'tcx>( MAP_UNWRAP_OR, expr.span, msg, - "try this", + "try", format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9ab78c6f8e0..123ab520e48 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -72,6 +72,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod range_zip_with_len; +mod read_line_without_trim; mod repeat_once; mod search_is_some; mod seek_from_current; @@ -88,6 +89,7 @@ mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; +mod type_id_on_box; mod uninit_assumed_init; mod unit_hash; mod unnecessary_filter_map; @@ -2925,6 +2927,37 @@ declare_clippy_lint! { "use of sort() when sort_unstable() is equivalent" } +declare_clippy_lint! { + /// ### What it does + /// Looks for calls to ` as Any>::type_id`. + /// + /// ### Why is this bad? + /// This most certainly does not do what the user expects and is very easy to miss. + /// Calling `type_id` on a `Box` calls `type_id` on the `Box<..>` itself, + /// so this will return the `TypeId` of the `Box` type (not the type id + /// of the value referenced by the box!). + /// + /// ### Example + /// ```rust,ignore + /// use std::any::{Any, TypeId}; + /// + /// let any_box: Box = Box::new(42_i32); + /// assert_eq!(any_box.type_id(), TypeId::of::()); // ⚠️ this fails! + /// ``` + /// Use instead: + /// ```rust + /// use std::any::{Any, TypeId}; + /// + /// let any_box: Box = Box::new(42_i32); + /// assert_eq!((*any_box).type_id(), TypeId::of::()); + /// // ^ dereference first, to call `type_id` on `dyn Any` + /// ``` + #[clippy::version = "1.72.0"] + pub TYPE_ID_ON_BOX, + suspicious, + "calling `.type_id()` on `Box`" +} + declare_clippy_lint! { /// ### What it does /// Detects `().hash(_)`. @@ -3316,6 +3349,35 @@ declare_clippy_lint! { "checks for usage of `Iterator::fold` with a type that implements `Try`" } +declare_clippy_lint! { + /// Looks for calls to [`Stdin::read_line`] to read a line from the standard input + /// into a string, then later attempting to parse this string into a type without first trimming it, which will + /// always fail because the string has a trailing newline in it. + /// + /// ### Why is this bad? + /// The `.parse()` call will always fail. + /// + /// ### Example + /// ```rust,ignore + /// let mut input = String::new(); + /// std::io::stdin().read_line(&mut input).expect("Failed to read a line"); + /// let num: i32 = input.parse().expect("Not a number!"); + /// assert_eq!(num, 42); // we never even get here! + /// ``` + /// Use instead: + /// ```rust,ignore + /// let mut input = String::new(); + /// std::io::stdin().read_line(&mut input).expect("Failed to read a line"); + /// let num: i32 = input.trim_end().parse().expect("Not a number!"); + /// // ^^^^^^^^^^^ remove the trailing newline + /// assert_eq!(num, 42); + /// ``` + #[clippy::version = "1.72.0"] + pub READ_LINE_WITHOUT_TRIM, + correctness, + "calling `Stdin::read_line`, then trying to parse it without first trimming" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3389,6 +3451,7 @@ impl_lint_pass!(Methods => [ STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, ITER_WITH_DRAIN, + TYPE_ID_ON_BOX, USELESS_ASREF, UNNECESSARY_FOLD, UNNECESSARY_FILTER_MAP, @@ -3435,6 +3498,7 @@ impl_lint_pass!(Methods => [ REPEAT_ONCE, STABLE_SORT_PRIMITIVE, UNIT_HASH, + READ_LINE_WITHOUT_TRIM, UNNECESSARY_SORT_BY, VEC_RESIZE_TO_ZERO, VERBOSE_FILE_READS, @@ -3846,6 +3910,9 @@ impl Methods { ("read_to_string", [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG); }, + ("read_line", [arg]) => { + read_line_without_trim::check(cx, expr, recv, arg); + } ("repeat", [arg]) => { repeat_once::check(cx, expr, recv, arg); }, @@ -3914,6 +3981,9 @@ impl Methods { ("to_os_string" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, + ("type_id", []) => { + type_id_on_box::check(cx, recv, expr.span); + } ("unwrap", []) => { match method_call(recv) { Some(("get", recv, [get_arg], _, _)) => { @@ -3949,7 +4019,7 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_default", []) => { + ("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); } ("unwrap_or_else", [u_arg]) => { @@ -4134,7 +4204,7 @@ impl SelfKind { }; let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { - return false + return false; }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index fe024ee8dff..2855e23bf5b 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item}; +use clippy_utils::expr_custom_deref_adjustment; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index f34f1cf2704..dbd965d6506 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -4,10 +4,9 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; use clippy_utils::{ - can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, - CaptureKind, + can_move_expr_to_closure, fn_def_id, get_enclosing_block, get_parent_node, higher, is_trait_method, path_to_local, + path_to_local_id, CaptureKind, }; -use clippy_utils::{fn_def_id, higher}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index 7030baf19ff..eaae8613d44 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -17,7 +17,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == "as_deref_mut" && recv.is_syntactic_place_expr() { - let Res::Local(binding_id) = path_res(cx, recv) else { return }; + let Res::Local(binding_id) = path_res(cx, recv) else { + return; + }; if local_used_after_expr(cx, binding_id, recv) { return; @@ -29,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name NEEDLESS_OPTION_AS_DEREF, expr.span, "derefed type is same as origin", - "try this", + "try", snippet_opt(cx, recv.span).unwrap(), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index eada530d670..697eab32a33 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,5 +1,6 @@ use super::OBFUSCATED_IF_ELSE; -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_with_applicability}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 41ceef19e3a..cb6a2306857 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -8,8 +8,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use super::OPTION_MAP_OR_NONE; -use super::RESULT_MAP_OR_INTO_OPTION; +use super::{OPTION_MAP_OR_NONE, RESULT_MAP_OR_INTO_OPTION}; // The expression inside a closure may or may not have surrounding braces // which causes problems when generating a suggestion. diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index f4f158c0439..fcbe005fb28 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,17 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_copy; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_path, Visitor}; -use rustc_hir::ExprKind; -use rustc_hir::Node; -use rustc_hir::PatKind; -use rustc_hir::QPath; -use rustc_hir::{self, HirId, Path}; +use rustc_hir::{self, ExprKind, HirId, Node, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 7ce28ea93e0..9165c1248f0 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( OR_FUN_CALL, method_span.with_hi(span.hi()), &format!("use of `{name}` followed by a call to `{path}`"), - "try this", + "try", format!("{sugg}()"), Applicability::MachineApplicable, ); @@ -139,7 +139,7 @@ pub(super) fn check<'tcx>( OR_FUN_CALL, span_replace_word, &format!("use of `{name}` followed by a function call"), - "try this", + "try", format!("{name}_{suffix}({sugg})"), app, ); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 55ba6e262df..7b0bdcf99e7 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,8 +1,10 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res}; +use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; -use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::{sym, Span}; @@ -50,7 +52,7 @@ pub(super) fn check<'tcx>( OR_THEN_UNWRAP, unwrap_expr.span.with_lo(or_span.lo()), title, - "try this", + "try", suggestion, applicability, ); diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 867a3b40237..f253d8de91f 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; -use clippy_utils::{higher, SpanlessEq}; -use clippy_utils::{is_integer_const, is_trait_method}; +use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs new file mode 100644 index 00000000000..8add0656101 --- /dev/null +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -0,0 +1,74 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, match_def_path}; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::READ_LINE_WITHOUT_TRIM; + +/// Will a `.parse::()` call fail if the input has a trailing newline? +fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { + // only allow a very limited set of types for now, for which we 100% know parsing will fail + matches!(ty.kind(), ty::Float(_) | ty::Bool | ty::Int(_) | ty::Uint(_)) +} + +pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"]) + && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind + && let Res::Local(local_id) = path.res + { + // We've checked that `call` is a call to `Stdin::read_line()` with the right receiver, + // now let's check if the first use of the string passed to `::read_line()` is + // parsed into a type that will always fail if it has a trailing newline. + for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| { + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(segment, .., span) = parent.kind + && segment.ident.name == sym!(parse) + && let parse_result_ty = cx.typeck_results().expr_ty(parent) + && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) + && let ty::Adt(_, substs) = parse_result_ty.kind() + && let Some(ok_ty) = substs[0].as_type() + && parse_fails_on_trailing_newline(ok_ty) + { + let local_snippet = snippet(cx, expr.span, ""); + span_lint_and_then( + cx, + READ_LINE_WITHOUT_TRIM, + span, + "calling `.parse()` without trimming the trailing newline character", + |diag| { + diag.span_note(call.span, "call to `.read_line()` here, \ + which leaves a trailing newline character in the buffer, \ + which in turn will cause `.parse()` to fail"); + + diag.span_suggestion( + expr.span, + "try", + format!("{local_snippet}.trim_end()"), + Applicability::MachineApplicable, + ); + } + ); + } + + // only consider the first use to prevent this scenario: + // ``` + // let mut s = String::new(); + // std::io::stdin().read_line(&mut s); + // s.pop(); + // let _x: i32 = s.parse().unwrap(); + // ``` + // this is actually fine, because the pop call removes the trailing newline. + ControlFlow::<(), ()>::Break(()) + }); + } +} diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs index c028e954381..f3d6a15ede0 100644 --- a/clippy_lints/src/methods/seek_from_current.rs +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -3,10 +3,10 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability, - ty::implements_trait, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, match_def_path, paths}; use super::SEEK_FROM_CURRENT; diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 88a3c2620a4..41986551da4 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -55,7 +55,7 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_ NEEDLESS_SPLITN, expr.span, &format!("unnecessary use of `{r}splitn`"), - "try this", + "try", format!( "{}.{r}split({})", snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0, @@ -110,7 +110,7 @@ fn check_manual_split_once( IterUsageKind::Nth(_) => return, }; - span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); + span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try", sugg, app); } /// checks for diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 2c20c6d752d..c7885f689d7 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -34,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr STRING_EXTEND_CHARS, expr.span, "calling `.extend(_.chars())`", - "try this", + "try", format!( "{}.push_str({ref_str}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index 73632c5a357..bc8f0176764 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths; use clippy_utils::ty::match_type; -use rustc_ast as ast; use rustc_errors::{Applicability, Diagnostic}; -use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::Span; +use {rustc_ast as ast, rustc_hir as hir}; use super::SUSPICIOUS_COMMAND_ARG_SPACE; diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index e818f1892e5..9eb8d6e6e78 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -5,7 +5,8 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty::{self, print::with_forced_trimmed_paths}; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_middle::ty::{self}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; diff --git a/clippy_lints/src/methods/type_id_on_box.rs b/clippy_lints/src/methods/type_id_on_box.rs new file mode 100644 index 00000000000..35137c97101 --- /dev/null +++ b/clippy_lints/src/methods/type_id_on_box.rs @@ -0,0 +1,62 @@ +use crate::methods::TYPE_ID_ON_BOX; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::{self, ExistentialPredicate, Ty}; +use rustc_span::{sym, Span}; + +fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + if let ty::Dynamic(preds, ..) = ty.kind() { + preds.iter().any(|p| match p.skip_binder() { + ExistentialPredicate::Trait(tr) => cx.tcx.is_diagnostic_item(sym::Any, tr.def_id), + _ => false, + }) + } else { + false + } +} + +pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) { + let recv_adjusts = cx.typeck_results().expr_adjustments(receiver); + + if let Some(Adjustment { target: recv_ty, .. }) = recv_adjusts.last() + && let ty::Ref(_, ty, _) = recv_ty.kind() + && let ty::Adt(adt, substs) = ty.kind() + && adt.is_box() + && is_dyn_any(cx, substs.type_at(0)) + { + span_lint_and_then( + cx, + TYPE_ID_ON_BOX, + call_span, + "calling `.type_id()` on a `Box`", + |diag| { + let derefs = recv_adjusts + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .count(); + + let mut sugg = "*".repeat(derefs + 1); + sugg += &snippet(cx, receiver.span, ""); + + diag.note( + "this returns the type id of the literal type `Box` instead of the \ + type id of the boxed value, which is most likely not what you want" + ) + .note( + "if this is intentional, use `TypeId::of::>()` instead, \ + which makes it more clear" + ) + .span_suggestion( + receiver.span, + "consider dereferencing first", + format!("({sugg})"), + Applicability::MaybeIncorrect, + ); + }, + ); + } +} diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index a1c6294737c..bc9c518dbcf 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty}; +use clippy_utils::is_path_diagnostic_item; +use clippy_utils::ty::is_uninit_value_valid_for_ty; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 1cef6226ad4..cc64a2e7948 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -11,8 +11,7 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::sym; -use super::UNNECESSARY_FILTER_MAP; -use super::UNNECESSARY_FIND_MAP; +use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { if !is_trait_method(cx, expr, sym::Iterator) { diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 8ec15a1c1b7..6e23754bf46 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -8,7 +8,8 @@ use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{source_map::Span, sym}; +use rustc_span::source_map::Span; +use rustc_span::sym; use super::UNNECESSARY_FOLD; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 52a4ff7d1ae..0c72c13a3ca 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -5,7 +5,8 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::{fn_def_id, get_parent_expr}; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, Expr, ExprKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index 087e1e4343b..d0c62fb56dc 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 7877f6a386c..111bcaaecec 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_then, is_res_lang_ctor, last_path_segment, path_res, MaybePath}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -29,6 +30,11 @@ pub(super) fn check( args: &[hir::Expr<'_>], ) { let init = clippy_utils::expr_or_init(cx, recv); + if init.span.from_expansion() { + // don't lint if the receiver or binding initializer comes from a macro + // (e.g. `let x = option_env!(..); x.unwrap()`) + return; + } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { let Some(qpath) = call.qpath_opt() else { return }; @@ -62,6 +68,22 @@ pub(super) fn check( (expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()), (expr.span.with_lo(args[0].span.hi()), ")".to_string()), ]), + ("Some" | "Ok", "unwrap_unchecked", _) | ("Err", "unwrap_err_unchecked", _) => { + let mut suggs = vec![ + (recv.span.with_hi(call_args[0].span.lo()), String::new()), + (expr.span.with_lo(call_args[0].span.hi()), String::new()), + ]; + // try to also remove the unsafe block if present + if let hir::Node::Block(block) = cx.tcx.hir().get_parent(expr.hir_id) + && let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules + { + suggs.extend([ + (block.span.shrink_to_lo().to(expr.span.shrink_to_lo()), String::new()), + (expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new()) + ]); + } + Some(suggs) + }, (_, _, Some(_)) => None, ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ ( diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 21e2638e5b9..5c5ee262052 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -7,16 +7,20 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node}; use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{self, ClauseKind, EarlyBinder, ParamTy, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_middle::ty::{ + self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, + TraitPredicate, Ty, +}; use rustc_span::{sym, Symbol}; -use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; use super::UNNECESSARY_TO_OWNED; @@ -319,7 +323,12 @@ fn skip_addr_of_ancestors<'tcx>( fn get_callee_generic_args_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(DefId, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> { +) -> Option<( + DefId, + GenericArgsRef<'tcx>, + Option<&'tcx Expr<'tcx>>, + &'tcx [Expr<'tcx>], +)> { if_chain! { if let ExprKind::Call(callee, args) = expr.kind; let callee_ty = cx.typeck_results().expr_ty(callee); @@ -388,7 +397,8 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< } } Node::Expr(parent_expr) => { - if let Some((callee_def_id, call_generic_args, recv, call_args)) = get_callee_generic_args_and_args(cx, parent_expr) + if let Some((callee_def_id, call_generic_args, recv, call_args)) + = get_callee_generic_args_and_args(cx, parent_expr) { // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually // call `tcx.try_subst_and_normalize_erasing_regions` further down diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs index 045f739e64d..474a33b67e1 100644 --- a/clippy_lints/src/methods/unwrap_or_else_default.rs +++ b/clippy_lints/src/methods/unwrap_or_else_default.rs @@ -1,10 +1,10 @@ //! Lint for `some_result_or_option.unwrap_or_else(Default::default)` use super::UNWRAP_OR_ELSE_DEFAULT; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability, - ty::is_type_diagnostic_item, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent_call; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index c1139d84e2f..b5f810eddf4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, USELESS_ASREF, expr.span, &format!("this call to `{call_name}` does nothing"), - "try this", + "try", snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, ); diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index d49bb0ca6e2..2a60f2faca0 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -1,10 +1,9 @@ -use clippy_utils::{diagnostics::span_lint, is_from_proc_macro}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{ - def::{DefKind, Res}, - intravisit::{walk_item, Visitor}, - GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, -}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_item, Visitor}; +use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -25,7 +24,7 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// for m in movies { - /// let title = m.t; + /// let title = m.t; /// } /// ``` /// Use instead: diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index 4dbb79334ca..c17f00c4275 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -46,7 +46,9 @@ declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]); impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) { Some(sym::assert_macro | sym::debug_assert_macro) => true, Some( @@ -61,10 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { } let panic_expn = if single_argument { - let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return }; + let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { + return; + }; panic_expn } else { - let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + return; + }; panic_expn }; diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 773174679db..96d83e114a4 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,8 +1,11 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, def_id::DefId, Item, ItemKind, UseKind}; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::{Item, ItemKind, UseKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 1138d1163a4..2f63b9b9f0b 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -1,23 +1,17 @@ use std::ops::ControlFlow; -use clippy_utils::{ - diagnostics::span_lint_and_then, - is_path_lang_item, paths, - ty::match_type, - visitors::{for_each_expr, Visitable}, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::match_type; +use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::{is_path_lang_item, paths}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::Block; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - def::{DefKind, Res}, - Expr, ImplItemKind, LangItem, Node, + Block, Expr, ExprKind, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Node, QPath, TyKind, VariantData, }; -use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind}; -use rustc_hir::{ImplItem, Item, VariantData}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; -use rustc_middle::ty::TypeckResults; +use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span, Symbol}; @@ -207,11 +201,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind - // don't trigger if self is a generic parameter, e.g. `impl Debug for T` - // this can only happen in core itself, where the trait is defined, - // but it caused ICEs in the past: - // https://github.com/rust-lang/rust-clippy/issues/10887 - && !matches!(self_path.res, Res::Def(DefKind::TyParam, _)) + // make sure that the self type is either a struct, an enum or a union + // this prevents ICEs such as when self is a type parameter or a primitive type + // (see #10887, #11063) + && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) // don't trigger if this impl was derived && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) @@ -222,7 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let body = cx.tcx.hir().body(*body_id) && let ExprKind::Block(block, _) = body.value.kind // inspect `self` - && let self_ty = cx.tcx.type_of(self_path.res.def_id()).skip_binder().peel_refs() + && let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs() && let Some(self_adt) = self_ty.ty_adt_def() && let Some(self_def_id) = self_adt.did().as_local() && let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id) diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 439cae812b7..efdc7560ee4 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -80,7 +80,9 @@ impl EarlyLintPass for ModStyle { let files = cx.sess().source_map().files(); - let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { return }; + let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { + return; + }; // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index e6fd65f001a..fe35126aab2 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,12 +1,8 @@ -use clippy_utils::{ - diagnostics::span_lint_and_then, - visitors::{for_each_expr_with_closures, Descend, Visitable}, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable}; use core::ops::ControlFlow::Continue; -use hir::{ - def::{DefKind, Res}, - BlockCheckMode, ExprKind, QPath, UnOp, Unsafety, -}; +use hir::def::{DefKind, Res}; +use hir::{BlockCheckMode, ExprKind, QPath, UnOp, Unsafety}; use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index d8647a99105..dea432fdbd5 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -39,7 +39,9 @@ declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, e) else { + return; + }; let macro_name = cx.tcx.item_name(macro_call.def_id); if !matches!( macro_name.as_str(), @@ -47,7 +49,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { ) { return; } - let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return }; + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { + return; + }; for arg in [lhs, rhs] { let mut visitor = MutArgVisitor::new(cx); visitor.visit_expr(arg); diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 62af42a3961..46457400abc 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -6,9 +6,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + get_parent_node, higher, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + SpanlessEq, }; -use clippy_utils::{higher, SpanlessEq}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp}; @@ -106,7 +106,7 @@ declare_clippy_lint! { /// # let mut skip: bool; /// skip = !must_keep(x, y); /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.71.0"] pub NEEDLESS_BOOL_ASSIGN, complexity, "setting the same boolean variable in both branches of an if-statement" diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 498e1408e52..11bf9e9ca17 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { + return; + }; match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs index 4ff1bf7ffc0..03bab86c6d7 100644 --- a/clippy_lints/src/needless_else.rs +++ b/clippy_lints/src/needless_else.rs @@ -1,5 +1,5 @@ -use clippy_utils::source::snippet_opt; -use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_opt, trim_span}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -51,7 +51,7 @@ impl EarlyLintPass for NeedlessElse { cx, NEEDLESS_ELSE, span, - "this else branch is empty", + "this `else` branch is empty", "you can remove it", String::new(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index c3b633fd6a0..98bf122fab7 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,11 +1,10 @@ use rustc_errors::Applicability; -use rustc_hir::{ - intravisit::{walk_expr, Visitor}, - Closure, Expr, ExprKind, Stmt, StmtKind, -}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span, sym, Symbol}; +use rustc_span::source_map::Span; +use rustc_span::{sym, Symbol}; use if_chain::if_chain; @@ -50,7 +49,7 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { - return + return; }; if_chain! { diff --git a/clippy_lints/src/needless_if.rs b/clippy_lints/src/needless_if.rs index ad5c3e1dc82..1ed7ea6b325 100644 --- a/clippy_lints/src/needless_if.rs +++ b/clippy_lints/src/needless_if.rs @@ -1,4 +1,7 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, higher::If, is_from_proc_macro, source::snippet_opt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::If; +use clippy_utils::is_from_proc_macro; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 5a9387b34cc..948454d13ae 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -86,7 +86,9 @@ fn contains_let(cond: &Expr<'_>) -> bool { } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { - let StmtKind::Local(local) = stmt.kind else { return false }; + let StmtKind::Local(local) = stmt.kind else { + return false; + }; !local.pat.walk_short(|pat| { if let PatKind::Binding(.., None) = pat.kind { !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs index da1b9d99931..d17a383e882 100644 --- a/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -1,8 +1,6 @@ -use clippy_utils::{ - diagnostics::span_lint_and_then, - higher, - source::{snippet, snippet_with_applicability}, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_ast::ast; use rustc_errors::Applicability; diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs new file mode 100644 index 00000000000..d323a16c2a7 --- /dev/null +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -0,0 +1,273 @@ +use super::needless_pass_by_value::requires_exact_signature; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::{is_from_proc_macro, is_self}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind}; +use rustc_hir_typeck::expr_use_visitor as euv; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::kw; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +declare_clippy_lint! { + /// ### What it does + /// Check if a `&mut` function argument is actually used mutably. + /// + /// Be careful if the function is publicly reexported as it would break compatibility with + /// users of this function. + /// + /// ### Why is this bad? + /// Less `mut` means less fights with the borrow checker. It can also lead to more + /// opportunities for parallelization. + /// + /// ### Example + /// ```rust + /// fn foo(y: &mut i32) -> i32 { + /// 12 + *y + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn foo(y: &i32) -> i32 { + /// 12 + *y + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub NEEDLESS_PASS_BY_REF_MUT, + suspicious, + "using a `&mut` argument when it's not mutated" +} + +#[derive(Copy, Clone)] +pub struct NeedlessPassByRefMut { + avoid_breaking_exported_api: bool, +} + +impl NeedlessPassByRefMut { + pub fn new(avoid_breaking_exported_api: bool) -> Self { + Self { + avoid_breaking_exported_api, + } + } +} + +impl_lint_pass!(NeedlessPassByRefMut => [NEEDLESS_PASS_BY_REF_MUT]); + +fn should_skip<'tcx>( + cx: &LateContext<'tcx>, + input: rustc_hir::Ty<'tcx>, + ty: Ty<'_>, + arg: &rustc_hir::Param<'_>, +) -> bool { + // We check if this a `&mut`. `ref_mutability` returns `None` if it's not a reference. + if !matches!(ty.ref_mutability(), Some(Mutability::Mut)) { + return true; + } + + if is_self(arg) { + return true; + } + + if let PatKind::Binding(.., name, _) = arg.pat.kind { + // If it's a potentially unused variable, we don't check it. + if name.name == kw::Underscore || name.as_str().starts_with('_') { + return true; + } + } + + // All spans generated from a proc-macro invocation are the same... + is_from_proc_macro(cx, &input) +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + fn_def_id: LocalDefId, + ) { + if span.from_expansion() { + return; + } + + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id); + + match kind { + FnKind::ItemFn(.., header) => { + let attrs = cx.tcx.hir().attrs(hir_id); + if header.abi != Abi::Rust || requires_exact_signature(attrs) { + return; + } + }, + FnKind::Method(..) => (), + FnKind::Closure => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if matches!( + item.kind, + ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) + ) { + return; + } + } + + let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); + let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig); + + // If there are no `&mut` argument, no need to go any further. + if !decl + .inputs + .iter() + .zip(fn_sig.inputs()) + .zip(body.params) + .any(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) + { + return; + } + + // Collect variables mutably used and spans which will need dereferencings from the + // function body. + let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { + let mut ctx = MutablyUsedVariablesCtxt::default(); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); + ctx + }; + + let mut it = decl + .inputs + .iter() + .zip(fn_sig.inputs()) + .zip(body.params) + .filter(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) + .peekable(); + if it.peek().is_none() { + return; + } + let show_semver_warning = self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(fn_def_id); + for ((&input, &_), arg) in it { + // Only take `&mut` arguments. + if_chain! { + if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind; + if !mutably_used_vars.contains(&canonical_id); + if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind; + then { + // If the argument is never used mutably, we emit the warning. + let sp = input.span; + span_lint_and_then( + cx, + NEEDLESS_PASS_BY_REF_MUT, + sp, + "this argument is a mutable reference, but not used mutably", + |diag| { + diag.span_suggestion( + sp, + "consider changing to".to_string(), + format!( + "&{}", + snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"), + ), + Applicability::Unspecified, + ); + if show_semver_warning { + diag.warn("changing this function will impact semver compatibility"); + } + }, + ); + } + } + } + } +} + +#[derive(Default)] +struct MutablyUsedVariablesCtxt { + mutably_used_vars: HirIdSet, + prev_bind: Option, + aliases: HirIdMap, +} + +impl MutablyUsedVariablesCtxt { + fn add_mutably_used_var(&mut self, mut used_id: HirId) { + while let Some(id) = self.aliases.get(&used_id) { + self.mutably_used_vars.insert(used_id); + used_id = *id; + } + self.mutably_used_vars.insert(used_id); + } +} + +impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { + fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { + if let euv::Place { + base: euv::PlaceBase::Local(vid), + base_ty, + .. + } = &cmt.place + { + if let Some(bind_id) = self.prev_bind.take() { + self.aliases.insert(bind_id, *vid); + } else if matches!(base_ty.ref_mutability(), Some(Mutability::Mut)) { + self.add_mutably_used_var(*vid); + } + } + } + + fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) { + self.prev_bind = None; + if let euv::Place { + base: euv::PlaceBase::Local(vid), + base_ty, + .. + } = &cmt.place + { + // If this is a mutable borrow, it was obviously used mutably so we add it. However + // for `UniqueImmBorrow`, it's interesting because if you do: `array[0] = value` inside + // a closure, it'll return this variant whereas if you have just an index access, it'll + // return `ImmBorrow`. So if there is "Unique" and it's a mutable reference, we add it + // to the mutably used variables set. + if borrow == ty::BorrowKind::MutBorrow + || (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut)) + { + self.add_mutably_used_var(*vid); + } + } + } + + fn mutate(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { + self.prev_bind = None; + if let euv::Place { + projections, + base: euv::PlaceBase::Local(vid), + .. + } = &cmt.place + { + if !projections.is_empty() { + self.add_mutably_used_var(*vid); + } + } + } + + fn copy(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { + self.prev_bind = None; + } + + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + + fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { + self.prev_bind = Some(id); + } +} diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 55b6e1606ee..5e26601537f 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -10,14 +10,14 @@ use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, + BindingAnnotation, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, + QPath, TyKind, }; -use rustc_hir::{HirIdSet, LangItem}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, TypeVisitableExt, Ty}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ( preds.iter().any(|t| cx.tcx.is_diagnostic_item(sym::Borrow, t.def_id())), !preds.is_empty() && { - let ty_empty_region = Ty::new_imm_ref(cx.tcx,cx.tcx.lifetimes.re_erased, ty); + let ty_empty_region = Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, ty); preds.iter().all(|t| { let ty_params = t.trait_ref.args.iter().skip(1).collect::>(); implements_trait(cx, ty_empty_region, t.def_id(), &ty_params) @@ -289,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } /// Functions marked with these attributes must have the exact signature. -fn requires_exact_signature(attrs: &[Attribute]) -> bool { +pub(crate) fn requires_exact_signature(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| { [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] .iter() diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index a4c7da7e48d..e6d3e72d1e6 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::peel_blocks; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; -use clippy_utils::{get_parent_node, is_lint_allowed}; +use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 12d4f33d428..87699fd0ca6 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -15,14 +15,12 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, InnerSpan, Span}; use rustc_target::abi::VariantIdx; -use rustc_middle::mir::interpret::EvalToValTreeResult; -use rustc_middle::mir::interpret::GlobalId; // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. @@ -154,24 +152,37 @@ fn is_value_unfrozen_raw<'tcx>( // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the // contained value. ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => { - val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)) - }, + ty::Array(ty, _) => val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)), ty::Adt(def, _) if def.is_union() => false, - ty::Adt(def, args) if def.is_enum() => { + ty::Adt(def, substs) if def.is_enum() => { let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = - VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); - fields.iter().copied().zip( - def.variants()[variant_index] + let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); + fields + .iter() + .copied() + .zip( + def.variants()[variant_index] + .fields + .iter() + .map(|field| field.ty(cx.tcx, substs)), + ) + .any(|(field, ty)| inner(cx, field, ty)) + }, + ty::Adt(def, substs) => val + .unwrap_branch() + .iter() + .zip( + def.non_enum_variant() .fields .iter() - .map(|field| field.ty(cx.tcx, args))).any(|(field, ty)| inner(cx, field, ty)) - } - ty::Adt(def, args) => { - val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))).any(|(field, ty)| inner(cx, *field, ty)) - } - ty::Tuple(tys) => val.unwrap_branch().iter().zip(tys).any(|(field, ty)| inner(cx, *field, ty)), + .map(|field| field.ty(cx.tcx, substs)), + ) + .any(|(field, ty)| inner(cx, *field, ty)), + ty::Tuple(tys) => val + .unwrap_branch() + .iter() + .zip(tys) + .any(|(field, ty)| inner(cx, *field, ty)), _ => false, } } @@ -208,7 +219,10 @@ fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty< let def_id = body_id.hir_id.owner.to_def_id(); let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); let instance = ty::Instance::new(def_id, args); - let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; + let cid = rustc_middle::mir::interpret::GlobalId { + instance, + promoted: None, + }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); is_value_unfrozen_raw(cx, result, ty) @@ -221,7 +235,6 @@ fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: D is_value_unfrozen_raw(cx, result, ty) } - pub fn const_eval_resolve<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -230,9 +243,12 @@ pub fn const_eval_resolve<'tcx>( ) -> EvalToValTreeResult<'tcx> { match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { Ok(Some(instance)) => { - let cid = GlobalId { instance, promoted: None }; + let cid = GlobalId { + instance, + promoted: None, + }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) - } + }, Ok(None) => Err(ErrorHandled::TooGeneric), Err(err) => Err(ErrorHandled::Reported(err.into())), } @@ -392,7 +408,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Make sure it is a const item. let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { - return + return; }; // Climb up to resolve any field access and explicit referencing. diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 9f6917c146f..d562047cbf1 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -91,7 +91,7 @@ struct ExistingName { struct SimilarNamesLocalVisitor<'a, 'tcx> { names: Vec, cx: &'a EarlyContext<'tcx>, - lint: &'a NonExpressiveNames, + lint: NonExpressiveNames, /// A stack of scopes containing the single-character bindings in each scope. single_char_names: Vec>, @@ -365,7 +365,7 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(self, cx, &item.attrs, &sig.decl, blk); + do_check(*self, cx, &item.attrs, &sig.decl, blk); } } @@ -380,12 +380,12 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(self, cx, &item.attrs, &sig.decl, blk); + do_check(*self, cx, &item.attrs, &sig.decl, blk); } } } -fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { +fn do_check(lint: NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { if !attrs.iter().any(|attr| attr.has_name(sym::test)) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 2d79a5c9008..bd194b93584 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,5 @@ -use std::{ - fmt, - hash::{Hash, Hasher}, -}; +use std::fmt; +use std::hash::{Hash, Hasher}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 3615c7ec9bf..3dc652f9dc0 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -7,8 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{EarlyBinder, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{self, ConstKind}; +use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 5c240276b76..35dd8fabe6e 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,26 +1,20 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::is_from_proc_macro; -use clippy_utils::{ - consts::{constant, constant_simple, Constant}, - diagnostics::span_lint, - is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, -}; -use rustc_ast as ast; +use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::{ - source_map::{Span, Spanned}, - Symbol, -}; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::Symbol; +use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ ["f32", "f32"], ["f64", "f64"], - ["std::num::Saturating", "std::num::Saturating"], - ["std::num::Wrapping", "std::num::Wrapping"], + ["std::num::Saturating", "*"], + ["std::num::Wrapping", "*"], ["std::string::String", "str"], ]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; @@ -200,7 +194,9 @@ impl ArithmeticSideEffects { ps: &hir::PathSegment<'tcx>, receiver: &hir::Expr<'tcx>, ) { - let Some(arg) = args.first() else { return; }; + let Some(arg) = args.first() else { + return; + }; if constant_simple(cx, cx.typeck_results(), receiver).is_some() { return; } @@ -225,7 +221,9 @@ impl ArithmeticSideEffects { un_expr: &hir::Expr<'tcx>, un_op: hir::UnOp, ) { - let hir::UnOp::Neg = un_op else { return; }; + let hir::UnOp::Neg = un_op else { + return; + }; if constant(cx, cx.typeck_results(), un_expr).is_some() { return; } diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 9bbf385fb59..c4572a09db6 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,9 +1,8 @@ -use clippy_utils::binop_traits; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, trait_ref_of_method}; +use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 78965b7d6f7..88d56631841 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -1,6 +1,7 @@ +use clippy_utils::ast_utils::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; +use clippy_utils::{eq_expr_value, is_in_test_function}; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 015f6c14e76..5eabb349ec1 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; -use clippy_utils::sugg; +use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index d7917e86a86..932dd470f9e 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -4,7 +4,9 @@ use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind}; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index abdccc47f54..40da002f4ff 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -6,10 +6,9 @@ use clippy_utils::{ }; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp, -}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::SyntaxContext; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 849cd03dd7b..a049427d85d 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -13,7 +13,7 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. + /// Checks for usage of `panic!` or assertions in a function of type result. /// /// ### Why is this bad? /// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. @@ -37,7 +37,7 @@ declare_clippy_lint! { #[clippy::version = "1.48.0"] pub PANIC_IN_RESULT_FN, restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion" + "functions of type `Result<..>` that contain `panic!()` or assertion" } declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); @@ -70,7 +70,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir }; if matches!( cx.tcx.item_name(macro_call.def_id).as_str(), - "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" + "panic" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) @@ -83,10 +83,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir cx, PANIC_IN_RESULT_FN, impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`", + "used `panic!()` or assertion in a function that returns `Result`", move |diag| { diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + "`panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); diag.span_note(panics, "return Err() instead of panicking"); }, diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 2f3007658ea..a72aefe91c1 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -76,7 +76,9 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; if is_panic(cx, macro_call.def_id) { if cx.tcx.hir().is_inside_const_context(expr.hir_id) { return; diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 456ded3fc02..d9f5d1642d7 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,7 +1,6 @@ -use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg, - ty::is_type_diagnostic_item, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 7b4812e98d8..41513647f05 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,5 +1,4 @@ -use std::cmp; -use std::iter; +use std::{cmp, iter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index eb7c008c7a4..264c38df11f 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -28,8 +28,7 @@ use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use std::fmt; -use std::iter; +use std::{fmt, iter}; declare_clippy_lint! { /// ### What it does @@ -166,7 +165,11 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, - cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder().inputs(), + cx.tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .skip_binder() + .inputs(), sig.decl.inputs, &sig.decl.output, &[], @@ -389,11 +392,12 @@ impl<'tcx> DerefTy<'tcx> { fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { match *self { Self::Str => cx.tcx.types.str_, - Self::Path => Ty::new_adt(cx.tcx, + Self::Path => Ty::new_adt( + cx.tcx, cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), List::empty(), ), - Self::Slice(_, ty) => Ty::new_slice(cx.tcx,ty), + Self::Slice(_, ty) => Ty::new_slice(cx.tcx, ty), } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 47b8891e123..20e032d4b01 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -50,12 +50,12 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { - return + return; }; // Check if the argument to the method call is a cast from usize let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { - return + return; }; let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index e3d940ad2a4..f5502cffb66 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,21 +1,24 @@ +use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, + eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_path_lang_item, is_res_lang_ctor, + pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, }; -use clippy_utils::{higher, is_path_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath}; +use rustc_hir::{ + BindingAnnotation, Block, ByRef, Expr, ExprKind, Local, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_session::declare_tool_lint; -use rustc_session::impl_lint_pass; -use rustc_span::{sym, symbol::Symbol}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does @@ -42,8 +45,9 @@ declare_clippy_lint! { "checks for expressions that could be replaced by the question mark operator" } -#[derive(Default)] pub struct QuestionMark { + pub(crate) msrv: Msrv, + pub(crate) matches_behaviour: MatchLintBehaviour, /// Keeps track of how many try blocks we are in at any point during linting. /// This allows us to answer the question "are we inside of a try block" /// very quickly, without having to walk up the parent chain, by simply checking @@ -51,7 +55,19 @@ pub struct QuestionMark { /// As for why we need this in the first place: try_block_depth_stack: Vec, } -impl_lint_pass!(QuestionMark => [QUESTION_MARK]); + +impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); + +impl QuestionMark { + #[must_use] + pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { + Self { + msrv, + matches_behaviour, + try_block_depth_stack: Vec::new(), + } + } +} enum IfBlockType<'hir> { /// An `if x.is_xxx() { a } else { b } ` expression. @@ -78,6 +94,29 @@ enum IfBlockType<'hir> { ), } +fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { + if let StmtKind::Local(Local { pat, init: Some(init_expr), els: Some(els), .. }) = stmt.kind && + let Block { stmts: &[], expr: Some(els), .. } = els && + let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els) + { + let mut applicability = Applicability::MaybeIncorrect; + let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); + let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); + let sugg = format!( + "let {receiver_str} = {init_expr_str}?;", + ); + span_lint_and_sugg( + cx, + QUESTION_MARK, + stmt.span, + "this `let...else` may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, + ); + } +} + fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool { match *if_block { IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => { @@ -259,6 +298,12 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool { } impl<'tcx> LateLintPass<'tcx> for QuestionMark { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !in_constant(cx, stmt.hir_id) { + check_let_some_else_return_none(cx, stmt); + } + self.check_manual_let_else(cx, stmt); + } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_constant(cx, expr.hir_id) { self.check_is_none_or_err_and_early_return(cx, expr); @@ -291,4 +336,5 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { .expect("blocks are always part of bodies and must have a depth") -= 1; } } + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d2018aba9e3..3287675a82d 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,10 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local}; +use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index f45bb1ef3e1..ccabb577cb7 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,10 +1,10 @@ -use std::{iter::once, ops::ControlFlow}; +use std::iter::once; +use std::ops::ControlFlow; -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; -use rustc_ast::{ - ast::{Expr, ExprKind}, - token::LitKind, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::token::LitKind; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -95,7 +95,7 @@ impl EarlyLintPass for RawStrings { // `once` so a raw string ending in hashes is still checked let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| { match b { - b'"' => (following_quote, req) = (true, 1), + b'"' if !following_quote => (following_quote, req) = (true, 1), // I'm a bit surprised the compiler didn't optimize this out, there's no // branch but it still ends up doing an unnecessary comparison, it's: // - cmp r9b,1h diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index e82aa3a7b98..8e85c55e756 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::paths; use clippy_utils::source::{indent_of, snippet}; use clippy_utils::ty::match_type; +use clippy_utils::{last_path_segment, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,9 +49,15 @@ declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); impl LateLintPass<'_> for RcCloneInVecInit { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; - let Some((symbol, func_span)) = ref_init(cx, elem) else { return; }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; + let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { + return; + }; + let Some((symbol, func_span)) = ref_init(cx, elem) else { + return; + }; emit_lint(cx, symbol, macro_call.span, elem, len, func_span); } diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index fa107858863..2bf90815caa 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,9 +1,7 @@ -use clippy_utils::{ - diagnostics::{span_lint, span_lint_and_sugg}, - higher::{get_vec_init_kind, VecInitKind}, - source::snippet, - visitors::for_each_expr, -}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; use core::ops::ControlFlow; use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index 05e52e6b38b..534b2762ba7 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -1,15 +1,14 @@ use std::ops::ControlFlow; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, - peel_blocks, - source::{snippet, walk_span_to_context}, - visitors::for_each_expr, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::source::{snippet, walk_span_to_context}; +use clippy_utils::visitors::for_each_expr; use rustc_errors::Applicability; use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::{lint::in_external_macro, ty::UpvarCapture}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::UpvarCapture; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index b6ce4ebc28f..4e3efe97b8c 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -6,8 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; -use rustc_hir::intravisit::Visitor as HirVisitor; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 2b65c08d25d..8277ce724d4 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -7,9 +7,8 @@ use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::ty::Ty; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::GenericArg; +use rustc_middle::ty::{GenericArg, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index a642e2da3ba..db870ec4c5b 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -94,7 +94,7 @@ impl EarlyLintPass for DerefAddrOf { DEREF_ADDROF, e.span, "immediately dereferencing a reference", - "try this", + "try", sugg.to_string(), applicability, ); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 674f8bf4c0f..b795e4b15ba 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -3,12 +3,12 @@ use std::fmt::Display; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_opt; -use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; +use clippy_utils::{def_path_def_ids, path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { @@ -55,26 +55,52 @@ declare_clippy_lint! { "trivial regular expressions" } -declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); +#[derive(Copy, Clone)] +enum RegexKind { + Unicode, + UnicodeSet, + Bytes, + BytesSet, +} + +#[derive(Default)] +pub struct Regex { + definitions: DefIdMap, +} + +impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); impl<'tcx> LateLintPass<'tcx> for Regex { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + // We don't use `match_def_path` here because that relies on matching the exact path, which changed + // between regex 1.8 and 1.9 + // + // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform + // the operation once and store the results + let mut resolve = |path, kind| { + for id in def_path_def_ids(cx, path) { + self.definitions.insert(id, kind); + } + }; + + resolve(&paths::REGEX_NEW, RegexKind::Unicode); + resolve(&paths::REGEX_BUILDER_NEW, RegexKind::Unicode); + resolve(&paths::REGEX_SET_NEW, RegexKind::UnicodeSet); + resolve(&paths::REGEX_BYTES_NEW, RegexKind::Bytes); + resolve(&paths::REGEX_BYTES_BUILDER_NEW, RegexKind::Bytes); + resolve(&paths::REGEX_BYTES_SET_NEW, RegexKind::BytesSet); + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, [arg]) = expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - then { - if match_def_path(cx, def_id, &paths::REGEX_NEW) || - match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) { - check_regex(cx, arg, true); - } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) || - match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) { - check_regex(cx, arg, false); - } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) { - check_set(cx, arg, true); - } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) { - check_set(cx, arg, false); - } + if let ExprKind::Call(fun, [arg]) = expr.kind + && let Some(def_id) = path_def_id(cx, fun) + && let Some(regex_kind) = self.definitions.get(&def_id) + { + match regex_kind { + RegexKind::Unicode => check_regex(cx, arg, true), + RegexKind::UnicodeSet => check_set(cx, arg, true), + RegexKind::Bytes => check_regex(cx, arg, false), + RegexKind::BytesSet => check_set(cx, arg, false), } } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4977df6c83f..2a494ff1fa6 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; use if_chain::if_chain; @@ -328,7 +328,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_with_closures(cx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx @@ -337,7 +337,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) .skip_binder() .output() .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) { ControlFlow::Break(()) } else { diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 419d7991f0e..88f295c72eb 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -148,12 +148,18 @@ impl LateLintPass<'_> for SemicolonBlock { expr: None, stmts: [.., stmt], .. - } = block else { return }; + } = block + else { + return; + }; let &Stmt { kind: StmtKind::Semi(expr), span, .. - } = stmt else { return }; + } = stmt + else { + return; + }; self.semicolon_outside_block(cx, block, expr, span); }, StmtKind::Semi(Expr { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 993f9373d85..78418b22392 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -106,7 +106,9 @@ impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - let PatKind::Binding(_, id, ident, _) = pat.kind else { return }; + let PatKind::Binding(_, id, ident, _) = pat.kind else { + return; + }; if pat.span.desugaring_kind().is_some() || pat.span.from_expansion() { return; diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 1493ad44ee5..3b282dbfa04 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,18 +1,16 @@ -use clippy_utils::{ - diagnostics::span_lint_and_then, - expr_or_init, get_attr, path_to_local, - source::{indent_of, snippet}, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::{expr_or_init, get_attr, path_to_local}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::{ - self as hir, - intravisit::{walk_expr, Visitor}, -}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{self as hir}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::Ident, Span, DUMMY_SP}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, DUMMY_SP}; use std::borrow::Cow; declare_clippy_lint! { @@ -333,7 +331,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o } }, hir::StmtKind::Semi(expr) => { - if has_drop(expr, &apa.first_bind_ident) { + if has_drop(expr, &apa.first_bind_ident, self.cx) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -430,11 +428,11 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident) -> bool { +fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool { if let hir::ExprKind::Call(fun, args) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind - && let [fun_ident, ..] = fun_path.segments - && fun_ident.ident.name == rustc_span::sym::drop + && let Res::Def(DefKind::Fn, did) = fun_path.res + && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) && let [first_arg, ..] = args && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind && let [first_arg_ps, .. ] = arg_path.segments diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 42753d2e951..7bbe98e0a7f 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{is_from_proc_macro, is_in_test_function}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl}; +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::lint::in_external_macro; diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 5743dd21c28..9c21d70c82c 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,11 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use rustc_ast::node_id::{NodeId, NodeMap}; +use rustc_ast::ptr::P; use rustc_ast::visit::{walk_expr, Visitor}; -use rustc_ast::{ptr::P, Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind}; +use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; +use rustc_span::edition::Edition; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index dfe8be7a6a6..321c8988988 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -1,7 +1,9 @@ -use clippy_utils::{ - diagnostics::span_lint_and_then, get_trait_def_id, higher::VecArgs, macros::root_macro_call_first_node, - source::snippet_opt, ty::implements_trait, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_trait_def_id; +use clippy_utils::higher::VecArgs; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 80c834066bb..bd783b4e005 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -4,8 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; -use rustc_hir::BinOpKind; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 8abec06c641..89ac8cd8ca9 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,4 +1,6 @@ -use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::path_def_id; +use clippy_utils::ty::peel_mid_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index a13bc7a5188..f239165276f 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_hir::{def::Res, HirId, Path, PathSegment}; +use rustc_hir::{HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, symbol::kw, Span}; +use rustc_span::symbol::kw; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 8658009eba4..58724852b3c 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; -use clippy_utils::{get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls}; +use clippy_utils::{ + get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, + peel_blocks, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 2f2e84fa35a..b3db5e9a422 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { STRLEN_ON_C_STRINGS, span, "using `libc::strlen` on a `CString` or `CStr` value", - "try this", + "try", format!("{val_name}.{method_name}().len()"), app, ); diff --git a/clippy_lints/src/suspicious_doc_comments.rs b/clippy_lints/src/suspicious_doc_comments.rs index e5746ca99ca..8be4ec3dc64 100644 --- a/clippy_lints/src/suspicious_doc_comments.rs +++ b/clippy_lints/src/suspicious_doc_comments.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; use if_chain::if_chain; -use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item}; +use rustc_ast::token::CommentKind; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Item}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs index 9c0dc8096d0..6fa49afe0ec 100644 --- a/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -1,4 +1,5 @@ -use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context}; +use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f7eef03d1d4..98158ed0aee 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -11,8 +11,8 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; -use rustc_span::SyntaxContext; -use rustc_span::{sym, symbol::Ident, Span}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/tests_outside_test_module.rs b/clippy_lints/src/tests_outside_test_module.rs index 0a0a77082e0..b356666d852 100644 --- a/clippy_lints/src/tests_outside_test_module.rs +++ b/clippy_lints/src/tests_outside_test_module.rs @@ -1,8 +1,11 @@ -use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function}; -use rustc_hir::{intravisit::FnKind, Body, FnDecl}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{def_id::LocalDefId, Span}; +use rustc_span::def_id::LocalDefId; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 2512500a6be..f1b703fde0e 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { TO_DIGIT_IS_SOME, expr.span, "use of `.to_digit(..).is_some()`", - "try this", + "try", if is_method_call { format!("{char_arg_snip}.is_digit({radix_snip})") } else { diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 5503653253c..c0d0d2b93dc 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -4,10 +4,8 @@ use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::{ - query::Key, - ty::{self, Ty}, -}; +use rustc_middle::query::Key; +use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; /// Checks for `transmute_int_to_non_zero` lint. diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 857d2ad8258..4ae4359eea0 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( "transmute from a pointer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(Ty::new_ptr(cx.tcx,*to_ty)); + let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty)); diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); } }, diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 9e5d7662426..c61eb0a9311 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -3,8 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, TypeAndMut, UintTy}; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 85cd74f23ef..513a913f56a 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -6,7 +6,8 @@ use rustc_ast::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::{cast::CastKind, Ty}; +use rustc_middle::ty::cast::CastKind; +use rustc_middle::ty::Ty; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index b6615410e25..088c8fda87a 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( let sugg = if *ptr_ty == rty_and_mut { arg.as_ty(to_ty) } else { - arg.as_ty(Ty::new_ptr(cx.tcx,rty_and_mut)).as_ty(to_ty) + arg.as_ty(Ty::new_ptr(cx.tcx, rty_and_mut)).as_ty(to_ty) }; diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 62efd13b8d9..1cf6cf8548a 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -2,7 +2,8 @@ use rustc_hir as hir; use rustc_hir::Expr; use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; -use rustc_middle::ty::{cast::CastKind, Ty}; +use rustc_middle::ty::cast::CastKind; +use rustc_middle::ty::Ty; use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index bd983306508..2e00ed042a8 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,13 +1,11 @@ -use clippy_utils::{ - diagnostics::span_lint_and_help, - is_from_proc_macro, - msrvs::{self, Msrv}, - path_to_local, -}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, HirId, Node, Pat}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use std::iter::once; @@ -16,8 +14,8 @@ declare_clippy_lint! { /// Checks for tuple<=>array conversions that are not done with `.into()`. /// /// ### Why is this bad? - /// It's unnecessary complexity. `.into()` works for tuples<=>arrays at or below 12 elements and - /// conveys the intent a lot better, while also leaving less room for hard to spot bugs! + /// It may be unnecessary complexity. `.into()` works for converting tuples + /// <=> arrays of up to 12 elements and may convey intent more clearly. /// /// ### Example /// ```rust,ignore @@ -31,7 +29,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.72.0"] pub TUPLE_ARRAY_CONVERSIONS, - complexity, + pedantic, "checks for tuple<=>array conversions that are not done with `.into()`" } impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]); diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index acdf5471069..306ca5724da 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -2,8 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{self as hir, GenericArg, GenericBounds, GenericParamKind}; -use rustc_hir::{HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind}; +use rustc_hir::{ + self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind, +}; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 43665a922d4..4a5a94f2630 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{path_def_id, qpath_generic_tys}; -use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; diff --git a/clippy_lints/src/types/linked_list.rs b/clippy_lints/src/types/linked_list.rs index 5fb708741e5..fba804bbe08 100644 --- a/clippy_lints/src/types/linked_list.rs +++ b/clippy_lints/src/types/linked_list.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{self as hir, def_id::DefId}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index 8767e3c30a6..60622903af1 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::{path_def_id, qpath_generic_tys}; use if_chain::if_chain; -use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 855137b14d8..f6c2d8d5a5e 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -22,7 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ app, ); } else { - let Some(ty) = qpath_generic_tys(qpath).next() else { return false }; + let Some(ty) = qpath_generic_tys(qpath).next() else { + return false; + }; let Some(id) = path_def_id(cx, ty) else { return false }; if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index a75972cf3dd..a616c3e4ea8 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{path_def_id, qpath_generic_tys}; use if_chain::if_chain; -use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index f7adc9d3555..5a986254fad 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::TypeVisitableExt; @@ -39,7 +40,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ return true; } - let Some(ty) = qpath_generic_tys(qpath).next() else { return false }; + let Some(ty) = qpath_generic_tys(qpath).next() else { + return false; + }; let Some(id) = path_def_id(cx, ty) else { return false }; let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) { Some(sym::Arc) => ("Arc", ty), @@ -49,7 +52,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let TyKind::Path(inner_qpath) = &ty.kind else { - return false + return false; }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(hir_ty) => { diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index d3062f3d2e3..decc183ad96 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -3,7 +3,8 @@ use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, GenericArg, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index a9deee96784..f2ef602012f 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -158,11 +158,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { - let ( - hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) - | hir::StmtKind::Expr(expr) - | hir::StmtKind::Semi(expr) - ) = stmt.kind else { return }; + let (hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + | hir::StmtKind::Expr(expr) + | hir::StmtKind::Semi(expr)) = stmt.kind + else { + return; + }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !in_external_macro(cx.tcx.sess, stmt.span) && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 226495dcbda..d4342ec5169 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -14,7 +14,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { "assert_ne" | "debug_assert_ne" => "fail", _ => return, }; - let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + return; + }; if !cx.typeck_results().expr_ty(left).is_unit() { return; } diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index e7449639f3a..ed2ef506381 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -1,6 +1,8 @@ -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; -use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; @@ -63,7 +65,9 @@ impl UnnecessaryBoxReturns { return; } - let FnRetTy::Return(return_ty_hir) = &decl.output else { return }; + let FnRetTy::Return(return_ty_hir) = &decl.output else { + return; + }; let return_ty = cx .tcx @@ -103,25 +107,33 @@ impl UnnecessaryBoxReturns { impl LateLintPass<'_> for UnnecessaryBoxReturns { fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { - let TraitItemKind::Fn(signature, _) = &item.kind else { return }; + let TraitItemKind::Fn(signature, _) = &item.kind else { + return; + }; self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name); } fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { // Ignore implementations of traits, because the lint should be on the // trait, not on the implementation of it. - let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return }; + let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { + return; + }; let ItemKind::Impl(parent) = parent.kind else { return }; if parent.of_trait.is_some() { return; } - let ImplItemKind::Fn(signature, ..) = &item.kind else { return }; + let ImplItemKind::Fn(signature, ..) = &item.kind else { + return; + }; self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name); } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let ItemKind::Fn(signature, ..) = &item.kind else { return }; + let ItemKind::Fn(signature, ..) = &item.kind else { + return; + }; self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name); } } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 6e802794f5a..57a4a429e12 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 084b031982d..f4111186c64 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,4 +1,7 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_copy; +use clippy_utils::{get_parent_expr, path_to_local}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5073eb02bd8..f34f8d0e353 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::visitors::find_all_ret_expressions; +use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index a57bf7ee822..9cf59577229 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -6,7 +6,8 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::PatKind::*; +use rustc_ast::{self as ast, Mutability, Pat, PatKind, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -162,9 +163,7 @@ fn unnest_or_patterns(pat: &mut P) -> bool { noop_visit_pat(p, self); // Don't have an or-pattern? Just quit early on. - let Or(alternatives) = &mut p.kind else { - return - }; + let Or(alternatives) = &mut p.kind else { return }; // Collapse or-patterns directly nested in or-patterns. let mut idx = 0; diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 0e526c216be..0fcb62017c6 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -48,7 +48,7 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { - return + return; }; match expr.kind { diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cad8da18c2f..95e74718d80 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::{ast, visit::FnKind, ClosureBinder}; +use rustc_ast::visit::FnKind; +use rustc_ast::{ast, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 377d3fb6f4e..c99b0290c0c 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local, usage::is_potentially_mutated}; +use clippy_utils::usage::is_potentially_mutated; +use clippy_utils::{higher, path_to_local}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 81dc426b389..50231d930d0 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -5,13 +5,12 @@ use clippy_utils::ty::same_type_and_consts; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; use rustc_hir::{ - self as hir, - def::{CtorOf, DefKind, Res}, - def_id::LocalDefId, - intravisit::{walk_inf, walk_ty, Visitor}, - Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, - ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, + HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 98f7bfb4ba1..92b694d3076 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_ty_alias; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; +use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -116,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { match e.kind { ExprKind::Match(_, arms, MatchSource::TryDesugar) => { let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else { - return + return; }; if let ExprKind::Call(_, [arg, ..]) = e.kind { self.try_desugar_arm.push(arg.hir_id); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f1d05c7529b..76654bfe536 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -319,7 +319,7 @@ define_Conf! { /// Lint: DISALLOWED_NAMES. /// /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value - /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. (disallowed_names: Vec = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()), /// Lint: SEMICOLON_INSIDE_BLOCK. diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 09fcb82c37c..6d3493523e6 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -71,7 +71,9 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { for between_span in between_spans { let mut seen_comma = false; - let Some(snippet) = snippet_opt(cx, between_span) else { return true }; + let Some(snippet) = snippet_opt(cx, between_span) else { + return true; + }; for token in tokenize(&snippet) { match token.kind { TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs index 883a5c08e5c..fe2f12fe833 100644 --- a/clippy_lints/src/utils/internal_lints/if_chain_style.rs +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -46,7 +46,9 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { } else { return; }; - let ExprKind::Block(then_block, _) = then.kind else { return }; + let ExprKind::Block(then_block, _) = then.kind else { + return; + }; let if_chain_span = is_expn_of(expr.span, "if_chain"); if !els { check_nested_if_chains(cx, expr, then_block, if_chain_span); diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 9afe02c1e47..94b56304bca 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -7,7 +7,8 @@ use rustc_hir::def::DefKind; use rustc_hir::Item; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, fast_reject::SimplifiedType, FloatTy}; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{self, FloatTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index f718207654f..87380f14f9a 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -3,10 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{is_lint_allowed, match_def_path, paths}; use if_chain::if_chain; -use rustc_ast as ast; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; @@ -18,6 +16,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; +use {rustc_ast as ast, rustc_hir as hir}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 107a62806a8..f49c3fadb07 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,11 +8,8 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::{ - collect_configs, - internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}, - ClippyConfiguration, -}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::{collect_configs, ClippyConfiguration}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -21,22 +18,22 @@ use if_chain::if_chain; use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{ - self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath, -}; +use rustc_hir::def::DefKind; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, intravisit, Closure, ExprKind, Item, ItemKind, Mutability, QPath}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::nested_filter; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Ident; use rustc_span::{sym, Loc, Span, Symbol}; -use serde::{ser::SerializeStruct, Serialize, Serializer}; +use serde::ser::SerializeStruct; +use serde::{Serialize, Serializer}; use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; /// This is the json output file of the lint collector. diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index d1bf292d711..fc17e7c6d5a 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -154,6 +154,10 @@ impl UselessVec { span: Span, suggest_slice: SuggestedType, ) { + if span.from_expansion() { + return; + } + let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { @@ -181,7 +185,7 @@ impl UselessVec { if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { return; } - let span = args[0].span.to(last.span); + let span = args[0].span.source_callsite().to(last.span.source_callsite()); let args = snippet_with_applicability(cx, span, "..", &mut applicability); match suggest_slice { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index bd5be0c9d7e..1d4fc24eb6c 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -74,7 +74,7 @@ impl VecPushSearcher { let mut needs_mut = false; let res = for_each_local_use_after_expr(cx, self.local_id, self.last_push_expr, |e| { let Some(parent) = get_parent_expr(cx, e) else { - return ControlFlow::Continue(()) + return ControlFlow::Continue(()); }; let adjusted_ty = cx.typeck_results().expr_ty_adjusted(e); let adjusted_mut = adjusted_ty.ref_mutability().unwrap_or(Mutability::Not); diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 43248bccc13..49637652001 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -1,10 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{symbol::kw, Span}; +use rustc_span::symbol::kw; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2a3d86988bb..d09d02a7dfd 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -3,10 +3,8 @@ use clippy_utils::is_test_module_or_function; use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{ - def::{DefKind, Res}, - Item, ItemKind, PathSegment, UseKind, -}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Item, ItemKind, PathSegment, UseKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f194dc5d4b2..a9957b18a53 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -272,9 +272,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; - let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return }; - let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return }; + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; + let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { + return; + }; + let Some(name) = diag_name.as_str().strip_suffix("_macro") else { + return; + }; let is_build_script = cx .sess() @@ -343,7 +349,9 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return }; + let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { + return; + }; let count_vertical_whitespace = || { format_args @@ -379,7 +387,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma &format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; + let Some(format_snippet) = snippet_opt(cx, format_string_span) else { + return; + }; if format_args.template.len() == 1 && last.as_str() == "\n" { // print!("\n"), write!(f, "\n") @@ -522,7 +532,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( - "try this", + "try", vec![(*placeholder_span, replacement), (removal_span, String::new())], Applicability::MachineApplicable, ); diff --git a/clippy_test_deps/Cargo.toml b/clippy_test_deps/Cargo.toml deleted file mode 100644 index 362c08e0d77..00000000000 --- a/clippy_test_deps/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "clippy_test_deps" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clippy_utils = { path = "../clippy_utils" } -derive-new = "0.5" -if_chain = "1.0" -itertools = "0.10.1" -quote = "1.0" -serde = { version = "1.0.125", features = ["derive"] } -syn = { version = "2.0", features = ["full"] } -futures = "0.3" -parking_lot = "0.12" -tokio = { version = "1", features = ["io-util"] } -regex = "1.5" -clippy_lints = { path = "../clippy_lints" } - -[features] -internal = ["clippy_lints/internal"] diff --git a/clippy_test_deps/src/lib.rs b/clippy_test_deps/src/lib.rs deleted file mode 100644 index 7d12d9af819..00000000000 --- a/clippy_test_deps/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index cfe686eb9b0..3926b954ec8 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.72" +version = "0.1.73" edition = "2021" publish = false diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 49cb9718ef6..51771f78d4f 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,5 +1,4 @@ -use rustc_ast::ast; -use rustc_ast::attr; +use rustc_ast::{ast, attr}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::sym; @@ -143,13 +142,13 @@ pub fn get_unique_attr<'a>( unique_attr } -/// Return true if the attributes contain any of `proc_macro`, +/// Returns true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool { attrs.iter().any(rustc_ast::Attribute::is_proc_macro_attr) } -/// Return true if the attributes contain `#[doc(hidden)]` +/// Returns true if the attributes contain `#[doc(hidden)]` pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { attrs .iter() diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c6d0b654f57..89f8a65c5ae 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -12,20 +12,20 @@ //! code was written, and check if the span contains that text. Note this will only work correctly //! if the span is not from a `macro_rules` based macro. -use rustc_ast::{ - ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy}, - token::CommentKind, - AttrStyle, -}; +use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy}; +use rustc_ast::token::CommentKind; +use rustc_ast::AttrStyle; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ - intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, - Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, - TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource, + Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, Impl, ImplItem, + ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, TraitItemKind, Ty, + TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource, }; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::{symbol::Ident, Span, Symbol}; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol}; use rustc_target::spec::abi::Abi; /// The search pattern to look for. Used by `span_matches_pat` @@ -339,7 +339,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), - // NOTE: This is missing `TraitObject`. It always return true then. + // NOTE: This is missing `TraitObject`. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } } diff --git a/clippy_utils/src/comparisons.rs b/clippy_utils/src/comparisons.rs index 7a18d5e818f..5e6bf227844 100644 --- a/clippy_utils/src/comparisons.rs +++ b/clippy_utils/src/comparisons.rs @@ -1,11 +1,11 @@ -//! Utility functions about comparison operators. +//! Utility functions for comparison operators. #![deny(clippy::missing_docs_in_private_items)] use rustc_hir::{BinOpKind, Expr}; #[derive(PartialEq, Eq, Debug, Copy, Clone)] -/// Represent a normalized comparison operator. +/// Represents a normalized comparison operator. pub enum Rel { /// `<` Lt, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 4832a38eceb..061086c4fc2 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -9,11 +9,9 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; -use rustc_middle::mir; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; -use rustc_middle::ty::{List, GenericArgsRef}; -use rustc_middle::{bug, span_bug}; +use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, List, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; use std::cmp::Ordering::{self, Equal}; @@ -155,7 +153,7 @@ impl<'tcx> Constant<'tcx> { }, (Self::Vec(l), Self::Vec(r)) => { let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { - return None + return None; }; iter::zip(l, r) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) @@ -267,7 +265,7 @@ pub fn constant_with_source<'tcx>( res.map(|x| (x, ctxt.source)) } -/// Attempts to evaluate an expression only if it's value is not dependent on other items. +/// Attempts to evaluate an expression only if its value is not dependent on other items. pub fn constant_simple<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 94b9006ed50..3c969572b8e 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -1,7 +1,7 @@ //! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa. //! //! Things to consider: -//! - has the expression side-effects? +//! - does the expression have side-effects? //! - is the expression computationally expensive? //! //! See lints: @@ -12,14 +12,14 @@ use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp}; +use rustc_hir::{Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::{sym, Symbol}; -use std::cmp; -use std::ops; +use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum EagernessSuggestion { @@ -68,19 +68,24 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Types where the only fields are generic types (or references to) with no trait bounds other // than marker traits. // Due to the limited operations on these types functions should be fairly cheap. - if def - .variants() - .iter() - .flat_map(|v| v.fields.iter()) - .any(|x| matches!(cx.tcx.type_of(x.did).instantiate_identity().peel_refs().kind(), ty::Param(_))) - && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, - _ => true, - }) - && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) + if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { + matches!( + cx.tcx.type_of(x.did).instantiate_identity().peel_refs().kind(), + ty::Param(_) + ) + }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + _ => true, + }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs_and_output { + match &**cx + .tcx + .fn_sig(fn_id) + .instantiate_identity() + .skip_binder() + .inputs_and_output + { [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange, _ => Lazy, } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index a61e4c38088..357ac40b455 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_span::{sym, symbol, Span}; /// The essential nodes of a desugared for loop as well as the entire span: -/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. pub struct ForLoop<'tcx> { /// `for` loop item pub pat: &'tcx hir::Pat<'tcx>, @@ -264,7 +264,7 @@ impl<'a> Range<'a> { } } -/// Represent the pre-expansion arguments of a `vec!` invocation. +/// Represents the pre-expansion arguments of a `vec!` invocation. pub enum VecArgs<'a> { /// `vec![elem; len]` Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>), @@ -398,7 +398,7 @@ impl<'hir> WhileLet<'hir> { } } -/// Converts a hir binary operator to the corresponding `ast` type. +/// Converts a `hir` binary operator to the corresponding `ast` type. #[must_use] pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind { match op { @@ -436,7 +436,7 @@ pub enum VecInitKind { WithExprCapacity(HirId), } -/// Checks if given expression is an initialization of `Vec` and returns its kind. +/// Checks if the given expression is an initialization of `Vec` and returns its kind. pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { if let ExprKind::Call(func, args) = expr.kind { match func.kind { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 3e1d7356414..fb359ee3bbe 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -5,10 +5,9 @@ use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; -use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, + GenericArgs, Guard, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5eed5ddf9e3..00e893fbdda 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -74,8 +74,7 @@ pub use self::hir_utils::{ use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::sync::OnceLock; -use std::sync::{Mutex, MutexGuard}; +use std::sync::{Mutex, MutexGuard, OnceLock}; use if_chain::if_chain; use itertools::Itertools; @@ -87,7 +86,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local, @@ -105,15 +104,14 @@ use rustc_middle::ty::fast_reject::SimplifiedType::{ ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, }; +use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - layout::IntegerExt, BorrowKind, ClosureKind, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UpvarCapture, + BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, }; -use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use crate::consts::{constant, miri_to_const, Constant}; @@ -823,7 +821,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< false } -/// Return true if the expr is equal to `Default::default` when evaluated. +/// Returns true if the expr is equal to `Default::default` when evaluated. pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool { if_chain! { if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind; @@ -2518,7 +2516,9 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator { /// Checks whether a given span has any comment token /// This checks for all types of comment: line "//", block "/**", doc "///" "//!" pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { - let Ok(snippet) = sm.span_to_snippet(span) else { return false }; + let Ok(snippet) = sm.span_to_snippet(span) else { + return false; + }; return tokenize(&snippet).any(|token| { matches!( token.kind, @@ -2527,7 +2527,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { }); } -/// Return all the comments a given span contains +/// Returns all the comments a given span contains +/// /// Comments are returned wrapped with their relevant delimiters pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { let snippet = sm.span_to_snippet(span).unwrap_or_default(); @@ -2542,6 +2543,50 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') } +/// Returns whether the given let pattern and else body can be turned into a question mark +/// +/// For this example: +/// ```ignore +/// let FooBar { a, b } = if let Some(a) = ex { a } else { return None }; +/// ``` +/// We get as parameters: +/// ```ignore +/// pat: Some(a) +/// else_body: return None +/// ``` + +/// And for this example: +/// ```ignore +/// let Some(FooBar { a, b }) = ex else { return None }; +/// ``` +/// We get as parameters: +/// ```ignore +/// pat: Some(FooBar { a, b }) +/// else_body: return None +/// ``` + +/// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because +/// the question mark operator is applicable here. Callers have to check whether we are in a +/// constant or not. +pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( + cx: &LateContext<'_>, + pat: &'a Pat<'hir>, + else_body: &Expr<'_>, +) -> Option<&'a Pat<'hir>> { + if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind && + is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) && + !is_refutable(cx, inner_pat) && + let else_body = peel_blocks(else_body) && + let ExprKind::Ret(Some(ret_val)) = else_body.kind && + let ExprKind::Path(ret_path) = ret_val.kind && + is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) + { + Some(inner_pat) + } else { + None + } +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 00f3abaec8a..173f9841d44 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -192,7 +192,9 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< /// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { - let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { + return false; + }; matches!( name, sym::core_panic_macro @@ -205,7 +207,9 @@ pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { /// Is `def_id` of `assert!` or `debug_assert!` pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { - let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { + return false; + }; matches!(name, sym::assert_macro | sym::debug_assert_macro) } @@ -223,13 +227,19 @@ pub enum PanicExpn<'a> { impl<'a> PanicExpn<'a> { pub fn parse(expr: &'a Expr<'a>) -> Option { - let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None }; - let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; + let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { + return None; + }; + let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { + return None; + }; let result = match path.segments.last().unwrap().ident.as_str() { "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, "panic" | "panic_str" => Self::Str(arg), "panic_display" => { - let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None }; + let ExprKind::AddrOf(_, _, e) = &arg.kind else { + return None; + }; Self::Display(e) }, "panic_fmt" => Self::Format(arg), diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 26c0015e87e..131f3c0aa39 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -101,21 +101,26 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle: /// Returns the `mir::Body` containing the node associated with `hir_id`. #[allow(clippy::module_name_repetitions)] -pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> { let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); - tcx.optimized_mir(body_owner_local_def_id.to_def_id()) + if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() { + Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id())) + } else { + None + } } /// Tries to determine the `Local` corresponding to `expr`, if any. /// This function is expensive and should be used sparingly. pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { - let mir = enclosing_mir(tcx, expr.hir_id); - mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { - if local_decl.source_info.span == expr.span { - Some(local) - } else { - None - } + enclosing_mir(tcx, expr.hir_id).and_then(|mir| { + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) }) } diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 920ce8e655b..703985b9d4b 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,11 +1,15 @@ -use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; +use super::possible_origin::PossibleOriginVisitor; +use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; -use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; -use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; +use rustc_middle::mir::visit::Visitor as _; +use rustc_middle::mir::{self, Mutability}; +use rustc_middle::ty::visit::TypeVisitor; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_mir_dataflow::impls::MaybeStorageLive; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; use std::borrow::Cow; use std::ops::ControlFlow; diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 0e6f01287b5..f3677e6f614 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -94,12 +94,12 @@ pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; +pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; +pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; +pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"]; +pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; +pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 515f80e0edf..1c29d614fa3 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -15,9 +15,8 @@ use rustc_middle::mir::{ Terminator, TerminatorKind, }; use rustc_middle::traits::{ImplSource, ObligationCause}; -use rustc_middle::ty::GenericArgKind; -use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; -use rustc_middle::ty::{BoundConstness, TraitRef}; +use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; @@ -125,7 +124,9 @@ fn check_rvalue<'tcx>( ) => check_operand(tcx, operand, span, body), Rvalue::Cast( CastKind::PointerCoercion( - PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::ReifyFnPointer, + PointerCoercion::UnsafeFnPointer + | PointerCoercion::ClosureFnPointer(_) + | PointerCoercion::ReifyFnPointer, ), _, _, diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 582337b47e8..dc4ee725681 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -8,8 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{LateContext, LintContext}; use rustc_session::Session; use rustc_span::source_map::{original_sp, SourceMap}; -use rustc_span::{hygiene, SourceFile}; -use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; +use rustc_span::{hygiene, BytePos, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; use std::ops::Range; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3953a7d8fa7..5d7e1494fcf 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -395,7 +395,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { } } -/// Return `true` if `sugg` is enclosed in parenthesis. +/// Returns `true` if `sugg` is enclosed in parenthesis. pub fn has_enclosing_paren(sugg: impl AsRef) -> bool { let mut chars = sugg.as_ref().chars(); if chars.next() == Some('(') { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 7687d361923..ad79143f4da 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -9,18 +9,16 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; -use rustc_infer::infer::{ - type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, - TyCtxtInferExt, -}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, - Region, RegionKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, + List, ParamEnv, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; -use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; @@ -741,11 +739,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option let mut output = None; let lang_items = cx.tcx.lang_items(); - for (pred, _) in cx - .tcx - .explicit_item_bounds(ty.def_id) - .arg_iter_copied(cx.tcx, ty.args) - { + for (pred, _) in cx.tcx.explicit_item_bounds(ty.def_id).arg_iter_copied(cx.tcx, ty.args) { match pred.kind().skip_binder() { ty::ClauseKind::Trait(p) if (lang_items.fn_trait() == Some(p.def_id()) @@ -940,8 +934,7 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc return false; }; let lang = tcx.lang_items(); - let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) - = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait()) + let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait()) else { return false; }; @@ -1033,10 +1026,12 @@ pub fn make_projection<'tcx>( assoc_ty: Symbol, args: GenericArgsRef<'tcx>, ) -> Option> { - let Some(assoc_item) = tcx - .associated_items(container_id) - .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) - else { + let Some(assoc_item) = tcx.associated_items(container_id).find_by_name_and_kind( + tcx, + Ident::with_dummy_span(assoc_ty), + AssocKind::Type, + container_id, + ) else { debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); return None; }; @@ -1124,7 +1119,7 @@ pub fn make_normalized_projection<'tcx>( ); return None; } - match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx,ty.def_id, ty.args)) { + match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx, ty.def_id, ty.args)) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1207,7 +1202,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( .infer_ctxt() .build() .at(&cause, param_env) - .query_normalize(Ty::new_projection(tcx,ty.def_id, ty.args)) + .query_normalize(Ty::new_projection(tcx, ty.def_id, ty.args)) { Ok(ty) => Some(ty.value), Err(e) => { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 9855085216f..20993398d1b 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,16 +1,14 @@ -use crate as utils; use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; use core::ops::ControlFlow; -use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId, Node}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Node}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; +use {crate as utils, rustc_hir as hir}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -156,7 +154,9 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { - let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false }; + let Some(block) = utils::get_enclosing_block(cx, local_id) else { + return false; + }; // for _ in 1..3 { // local diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 4dc906d00db..3633ed31d73 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.72" +version = "0.1.73" edition = "2021" publish = false diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 3f01e9bb0a7..e678d40795e 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,5 +1,6 @@ use clap::Parser; -use std::{num::NonZeroUsize, path::PathBuf}; +use std::num::NonZeroUsize; +use std::path::PathBuf; #[derive(Clone, Debug, Parser)] pub(crate) struct LintcheckConfig { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index de56a6f8275..3a022b343a4 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -15,16 +15,14 @@ use crate::config::LintcheckConfig; use crate::recursive::LintcheckServer; use std::collections::{HashMap, HashSet}; -use std::env; use std::env::consts::EXE_SUFFIX; use std::fmt::{self, Write as _}; -use std::fs; use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; use std::time::Duration; +use std::{env, fs, thread}; use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use cargo_metadata::Message; diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 49072e65192..994fa3c3b23 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -3,8 +3,7 @@ //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running //! clippy on the crate to the server -use crate::ClippyWarning; -use crate::RecursiveOptions; +use crate::{ClippyWarning, RecursiveOptions}; use std::collections::HashSet; use std::io::{BufRead, BufReader, Read, Write}; diff --git a/rust-toolchain b/rust-toolchain index 4475d914ced..b658101a10d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-06-29" +channel = "nightly-2023-07-14" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustfmt.toml b/rustfmt.toml index 18b2a33469d..4248f42f654 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -4,5 +4,6 @@ match_block_trailing_comma = true wrap_comments = true edition = "2021" error_on_line_overflow = true +imports_granularity = "Module" version = "Two" ignore = ["tests/ui/crashes/ice-10912.rs"] diff --git a/src/driver.rs b/src/driver.rs index 1eb288b153a..ee17feed77a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -130,6 +130,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); track_files(parse_sess); + + // Trigger a rebuild if CLIPPY_CONF_DIR changes. The value must be a valid string so + // changes between dirs that are invalid UTF-8 will not trigger rebuilds + parse_sess.env_depinfo.get_mut().insert(( + Symbol::intern("CLIPPY_CONF_DIR"), + env::var("CLIPPY_CONF_DIR").ok().map(|dir| Symbol::intern(&dir)), + )); })); config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0fd37c640ae..d70c4ea34cb 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -3,17 +3,108 @@ #![feature(is_sorted)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] +#![allow(unused_extern_crates)] use compiletest::{status_emitter, CommandBuilder}; use ui_test as compiletest; use ui_test::Mode as TestMode; +use std::collections::BTreeMap; use std::env::{self, remove_var, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fs; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use test_utils::IS_RUSTC_TEST_SUITE; +// Test dependencies may need an `extern crate` here to ensure that they show up +// in the depinfo file (otherwise cargo thinks they are unused) +extern crate clippy_lints; +extern crate clippy_utils; +extern crate derive_new; +extern crate futures; +extern crate if_chain; +extern crate itertools; +extern crate parking_lot; +extern crate quote; +extern crate syn; +extern crate tokio; + +/// All crates used in UI tests are listed here +static TEST_DEPENDENCIES: &[&str] = &[ + "clippy_lints", + "clippy_utils", + "derive_new", + "futures", + "if_chain", + "itertools", + "parking_lot", + "quote", + "regex", + "serde_derive", + "serde", + "syn", + "tokio", +]; + +/// Produces a string with an `--extern` flag for all UI test crate +/// dependencies. +/// +/// The dependency files are located by parsing the depinfo file for this test +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test +/// dependencies must be added to Cargo.toml at the project root. Test +/// dependencies that are not *directly* used by this test module require an +/// `extern crate` declaration. +static EXTERN_FLAGS: LazyLock> = LazyLock::new(|| { + let current_exe_depinfo = { + let mut path = env::current_exe().unwrap(); + path.set_extension("d"); + fs::read_to_string(path).unwrap() + }; + let mut crates = BTreeMap::<&str, &str>::new(); + for line in current_exe_depinfo.lines() { + // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:` + let parse_name_path = || { + if line.starts_with(char::is_whitespace) { + return None; + } + let path_str = line.strip_suffix(':')?; + let path = Path::new(path_str); + if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") { + return None; + } + let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?; + // the "lib" prefix is not present for dll files + let name = name.strip_prefix("lib").unwrap_or(name); + Some((name, path_str)) + }; + if let Some((name, path)) = parse_name_path() { + if TEST_DEPENDENCIES.contains(&name) { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); + } + } + } + let not_found: Vec<&str> = TEST_DEPENDENCIES + .iter() + .copied() + .filter(|n| !crates.contains_key(n)) + .collect(); + assert!( + not_found.is_empty(), + "dependencies not found in depinfo: {not_found:?}\n\ + help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ + help: Try adding to dev-dependencies in Cargo.toml\n\ + help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", + ); + crates + .into_iter() + .map(|(name, path)| format!("--extern={name}={path}")) + .collect() +}); + mod test_utils; // whether to run internal tests or not @@ -29,7 +120,6 @@ fn base_config(test_dir: &str) -> compiletest::Config { } else { compiletest::OutputConflictHandling::Error("cargo test -- -- --bless".into()) }, - dependencies_crate_manifest_path: Some("clippy_test_deps/Cargo.toml".into()), target: None, out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap_or("target".into())).join("ui_test"), ..compiletest::Config::rustc(Path::new("tests").join(test_dir)) @@ -44,10 +134,23 @@ fn base_config(test_dir: &str) -> compiletest::Config { let deps_path = current_exe_path.parent().unwrap(); let profile_path = deps_path.parent().unwrap(); - config.program.args.push("--emit=metadata".into()); - config.program.args.push("-Aunused".into()); - config.program.args.push("-Zui-testing".into()); - config.program.args.push("-Dwarnings".into()); + config.program.args.extend( + [ + "--emit=metadata", + "-Aunused", + "-Zui-testing", + "-Dwarnings", + &format!("-Ldependency={}", deps_path.display()), + ] + .map(OsString::from), + ); + + config.program.args.extend(EXTERN_FLAGS.iter().map(OsString::from)); + + if let Some(host_libs) = option_env!("HOST_LIBS") { + let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display()); + config.program.args.push(dep.into()); + } // Normalize away slashes in windows paths. config.stderr_filter(r"\\", "/"); @@ -105,9 +208,7 @@ fn run_internal_tests() { if !RUN_INTERNAL_TESTS { return; } - let mut config = base_config("ui-internal"); - config.dependency_builder.args.push("--features".into()); - config.dependency_builder.args.push("internal".into()); + let config = base_config("ui-internal"); compiletest::run_tests(config).unwrap(); } @@ -211,12 +312,45 @@ fn main() { } set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); - run_ui(); - run_ui_toml(); - run_ui_cargo(); - run_internal_tests(); - rustfix_coverage_known_exceptions_accuracy(); - ui_cargo_toml_metadata(); + // The SPEEDTEST_* env variables can be used to check Clippy's performance on your PR. It runs the + // affected test 1000 times and gets the average. + if let Ok(speedtest) = std::env::var("SPEEDTEST") { + println!("----------- STARTING SPEEDTEST -----------"); + let f = match speedtest.as_str() { + "ui" => run_ui as fn(), + "cargo" => run_ui_cargo as fn(), + "toml" => run_ui_toml as fn(), + "internal" => run_internal_tests as fn(), + "rustfix-coverage-known-exceptions-accuracy" => rustfix_coverage_known_exceptions_accuracy as fn(), + "ui-cargo-toml-metadata" => ui_cargo_toml_metadata as fn(), + + _ => panic!("unknown speedtest: {speedtest} || accepted speedtests are: [ui, cargo, toml, internal]"), + }; + + let iterations; + if let Ok(iterations_str) = std::env::var("SPEEDTEST_ITERATIONS") { + iterations = iterations_str + .parse::() + .unwrap_or_else(|_| panic!("Couldn't parse `{iterations_str}`, please use a valid u64")); + } else { + iterations = 1000; + } + + let mut sum = 0; + for _ in 0..iterations { + let start = std::time::Instant::now(); + f(); + sum += start.elapsed().as_millis(); + } + println!("average {} time: {} millis.", speedtest.to_uppercase(), sum / 1000); + } else { + run_ui(); + run_ui_toml(); + run_ui_cargo(); + run_internal_tests(); + rustfix_coverage_known_exceptions_accuracy(); + ui_cargo_toml_metadata(); + } } const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 15e5cdd6992..98019c75527 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -18,18 +18,20 @@ impl Message { fn new(path: PathBuf) -> Self { // we don't want the first letter after "error: ", "help: " ... to be capitalized // also no punctuation (except for "?" ?) at the end of a line + // Prefer "try" over "try this". static REGEX_SET: LazyLock = LazyLock::new(|| { RegexSet::new([ "error: [A-Z]", "help: [A-Z]", "warning: [A-Z]", "note: [A-Z]", - "try this: [A-Z]", + "try: [A-Z]", "error: .*[.!]$", "help: .*[.!]$", "warning: .*[.!]$", "note: .*[.!]$", - "try this: .*[.!]$", + "try: .*[.!]$", + "try this", ]) .unwrap() }); diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr index 6ec79a618de..eb1180e60b8 100644 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -30,7 +30,7 @@ LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); | ^^^ | = note: `-D clippy::print-literal` implied by `-D warnings` -help: try this +help: try | LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); LL + println!("Hello x is {:.*}", local_i32, local_f64); diff --git a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs index ebadd4e440a..60fbaaea3d3 100644 --- a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs +++ b/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs @@ -7,13 +7,10 @@ extern crate proc_macro; use core::mem; -use proc_macro::{ - token_stream::IntoIter, - Delimiter::{self, Brace, Parenthesis}, - Group, Ident, Literal, Punct, - Spacing::{self, Alone, Joint}, - Span, TokenStream, TokenTree as TT, -}; +use proc_macro::token_stream::IntoIter; +use proc_macro::Delimiter::{self, Brace, Parenthesis}; +use proc_macro::Spacing::{self, Alone, Joint}; +use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; type Result = core::result::Result; diff --git a/tests/ui-toml/toml_trivially_copy/test.rs b/tests/ui-toml/toml_trivially_copy/test.rs index f267a67f40e..78784bfff0f 100644 --- a/tests/ui-toml/toml_trivially_copy/test.rs +++ b/tests/ui-toml/toml_trivially_copy/test.rs @@ -2,6 +2,7 @@ //@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)" #![warn(clippy::trivially_copy_pass_by_ref)] +#![allow(clippy::needless_pass_by_ref_mut)] #[derive(Copy, Clone)] struct Foo(u8); diff --git a/tests/ui-toml/toml_trivially_copy/test.stderr b/tests/ui-toml/toml_trivially_copy/test.stderr index d2b55eff16d..db5d6805362 100644 --- a/tests/ui-toml/toml_trivially_copy/test.stderr +++ b/tests/ui-toml/toml_trivially_copy/test.stderr @@ -1,5 +1,5 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/test.rs:14:11 + --> $DIR/test.rs:15:11 | LL | fn bad(x: &u16, y: &Foo) {} | ^^^^ help: consider passing by value instead: `u16` @@ -7,7 +7,7 @@ LL | fn bad(x: &u16, y: &Foo) {} = note: `-D clippy::trivially-copy-pass-by-ref` implied by `-D warnings` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/test.rs:14:20 + --> $DIR/test.rs:15:20 | LL | fn bad(x: &u16, y: &Foo) {} | ^^^^ help: consider passing by value instead: `Foo` diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index dde1c6d7c37..e300ba18c33 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -9,9 +9,7 @@ #![warn(clippy::unwrap_used)] #![warn(clippy::get_unwrap)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, VecDeque}; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index eb66a5cf50b..4c9bdfa9dba 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -1,13 +1,13 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:40:17 + --> $DIR/unwrap_used.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` | = note: `-D clippy::get-unwrap` implied by `-D warnings` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:40:17 + --> $DIR/unwrap_used.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,13 +16,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:41:17 + --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:41:17 + --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -30,13 +30,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:42:17 + --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:42:17 + --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,13 +44,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:43:17 + --> $DIR/unwrap_used.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:43:17 + --> $DIR/unwrap_used.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,13 +58,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:44:17 + --> $DIR/unwrap_used.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:44:17 + --> $DIR/unwrap_used.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,13 +72,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:45:17 + --> $DIR/unwrap_used.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:45:17 + --> $DIR/unwrap_used.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,13 +86,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:49:21 + --> $DIR/unwrap_used.rs:47:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:49:22 + --> $DIR/unwrap_used.rs:47:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,13 +100,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:54:9 + --> $DIR/unwrap_used.rs:52:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:54:10 + --> $DIR/unwrap_used.rs:52:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,13 +114,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:55:9 + --> $DIR/unwrap_used.rs:53:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:55:10 + --> $DIR/unwrap_used.rs:53:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,13 +128,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:56:9 + --> $DIR/unwrap_used.rs:54:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:56:10 + --> $DIR/unwrap_used.rs:54:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,13 +142,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:57:9 + --> $DIR/unwrap_used.rs:55:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:57:10 + --> $DIR/unwrap_used.rs:55:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,13 +156,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:69:17 + --> $DIR/unwrap_used.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:69:17 + --> $DIR/unwrap_used.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -170,13 +170,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:70:17 + --> $DIR/unwrap_used.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_used.rs:70:17 + --> $DIR/unwrap_used.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,16 +184,16 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:77:13 + --> $DIR/unwrap_used.rs:75:13 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:95:17 + --> $DIR/unwrap_used.rs:93:17 | LL | let _ = Box::new([0]).get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&Box::new([0])[1]` error: aborting due to 28 previous errors diff --git a/tests/ui/arc_with_non_send_sync.rs b/tests/ui/arc_with_non_send_sync.rs index ac786f68c12..b6fcca0a791 100644 --- a/tests/ui/arc_with_non_send_sync.rs +++ b/tests/ui/arc_with_non_send_sync.rs @@ -7,11 +7,18 @@ fn foo(x: T) { // Should not lint - purposefully ignoring generic args. let a = Arc::new(x); } +fn issue11076() { + let a: Arc> = Arc::new(Vec::new()); +} fn main() { - // This is safe, as `i32` implements `Send` and `Sync`. - let a = Arc::new(42); + let _ = Arc::new(42); - // This is not safe, as `RefCell` does not implement `Sync`. - let b = Arc::new(RefCell::new(42)); + // !Sync + let _ = Arc::new(RefCell::new(42)); + let mutex = Mutex::new(1); + // !Send + let _ = Arc::new(mutex.lock().unwrap()); + // !Send + !Sync + let _ = Arc::new(&42 as *const i32); } diff --git a/tests/ui/arc_with_non_send_sync.stderr b/tests/ui/arc_with_non_send_sync.stderr index fc2fc5f93b1..7633b38dfb5 100644 --- a/tests/ui/arc_with_non_send_sync.stderr +++ b/tests/ui/arc_with_non_send_sync.stderr @@ -1,11 +1,34 @@ -error: usage of `Arc` where `T` is not `Send` or `Sync` - --> $DIR/arc_with_non_send_sync.rs:16:13 +error: usage of an `Arc` that is not `Send` or `Sync` + --> $DIR/arc_with_non_send_sync.rs:18:13 | -LL | let b = Arc::new(RefCell::new(42)); +LL | let _ = Arc::new(RefCell::new(42)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider using `Rc` instead or wrapping `T` in a std::sync type like `Mutex` + = note: the trait `Sync` is not implemented for `RefCell` + = note: required for `Arc>` to implement `Send` and `Sync` + = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` = note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings` -error: aborting due to previous error +error: usage of an `Arc` that is not `Send` or `Sync` + --> $DIR/arc_with_non_send_sync.rs:21:13 + | +LL | let _ = Arc::new(mutex.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the trait `Send` is not implemented for `MutexGuard<'_, i32>` + = note: required for `Arc>` to implement `Send` and `Sync` + = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + +error: usage of an `Arc` that is not `Send` or `Sync` + --> $DIR/arc_with_non_send_sync.rs:23:13 + | +LL | let _ = Arc::new(&42 as *const i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the trait `Send` is not implemented for `*const i32` + = note: the trait `Sync` is not implemented for `*const i32` + = note: required for `Arc<*const i32>` to implement `Send` and `Sync` + = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + +error: aborting due to 3 previous errors diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 4f38e50c81d..ed75acee8a2 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -481,4 +481,9 @@ pub fn issue_10792() { let _ = 10 / TWO.c; } +pub fn issue_11145() { + let mut x: Wrapping = Wrapping(0_u32); + x += 1; +} + fn main() {} diff --git a/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs index 427842a51d9..69f1c541c4e 100644 --- a/tests/ui/as_conversions.rs +++ b/tests/ui/as_conversions.rs @@ -4,8 +4,7 @@ #![allow(clippy::borrow_as_ptr, unused)] extern crate proc_macros; -use proc_macros::external; -use proc_macros::with_span; +use proc_macros::{external, with_span}; fn main() { let i = 0u32 as u64; diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr index ca41d1378aa..54037a64997 100644 --- a/tests/ui/as_conversions.stderr +++ b/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:11:13 + --> $DIR/as_conversions.rs:10:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = note: `-D clippy::as-conversions` implied by `-D warnings` error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:13:13 + --> $DIR/as_conversions.rs:12:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:13:13 + --> $DIR/as_conversions.rs:12:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/macro_use_helper.rs b/tests/ui/auxiliary/macro_use_helper.rs index cab216b51ac..f20df6f0f09 100644 --- a/tests/ui/auxiliary/macro_use_helper.rs +++ b/tests/ui/auxiliary/macro_use_helper.rs @@ -15,8 +15,7 @@ pub mod inner { // RE-EXPORT // this will stick in `inner` module - pub use macro_rules::mut_mut; - pub use macro_rules::try_err; + pub use macro_rules::{mut_mut, try_err}; pub mod nested { pub use macro_rules::string_add; diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index fdfe5fc4181..c5879557516 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -8,11 +8,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; -use syn::parse_macro_input; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, + parse_macro_input, parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, + TraitItem, Type, }; #[proc_macro_attribute] diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 4d008c8cb59..43df654389b 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -5,13 +5,10 @@ extern crate proc_macro; use core::mem; -use proc_macro::{ - token_stream::IntoIter, - Delimiter::{self, Brace, Parenthesis}, - Group, Ident, Literal, Punct, - Spacing::{self, Alone, Joint}, - Span, TokenStream, TokenTree as TT, -}; +use proc_macro::token_stream::IntoIter; +use proc_macro::Delimiter::{self, Brace, Parenthesis}; +use proc_macro::Spacing::{self, Alone, Joint}; +use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; type Result = core::result::Result; diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index b6a81d21bb2..f17fee7460d 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -14,7 +14,7 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed --> $DIR/bind_instead_of_map.rs:10:13 | LL | let _ = x.and_then(|o| Some(o + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op --> $DIR/bind_instead_of_map.rs:16:13 diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index 0152a93feee..cedbca78561 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try this +help: try | LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -20,7 +20,7 @@ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try this +help: try | LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -31,7 +31,7 @@ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try this +help: try | LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ @@ -48,7 +48,7 @@ LL | | } LL | | }); | |______^ | -help: try this +help: try | LL ~ Some("42").map(|s| { LL | if { @@ -82,7 +82,7 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try this +help: try | LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); | ~~~ ~~~~ ~~~~~~~~ diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index d6774c03598..8689f89d2c3 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -2,6 +2,7 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] +#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] fn main() { let x = true; diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index c0483fd7374..a1c94aff94b 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -2,6 +2,7 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] +#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] fn main() { let x = true; diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index f4dded365fb..19bdf301358 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,5 +1,5 @@ error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:8:8 + --> $DIR/bool_comparison.rs:9:8 | LL | if x == true { | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -7,127 +7,127 @@ LL | if x == true { = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:13:8 + --> $DIR/bool_comparison.rs:14:8 | LL | if x == false { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:18:8 + --> $DIR/bool_comparison.rs:19:8 | LL | if true == x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:23:8 + --> $DIR/bool_comparison.rs:24:8 | LL | if false == x { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:28:8 + --> $DIR/bool_comparison.rs:29:8 | LL | if x != true { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:33:8 + --> $DIR/bool_comparison.rs:34:8 | LL | if x != false { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:38:8 + --> $DIR/bool_comparison.rs:39:8 | LL | if true != x { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:43:8 + --> $DIR/bool_comparison.rs:44:8 | LL | if false != x { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:48:8 + --> $DIR/bool_comparison.rs:49:8 | LL | if x < true { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:53:8 + --> $DIR/bool_comparison.rs:54:8 | LL | if false < x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:58:8 + --> $DIR/bool_comparison.rs:59:8 | LL | if x > false { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:63:8 + --> $DIR/bool_comparison.rs:64:8 | LL | if true > x { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:69:8 + --> $DIR/bool_comparison.rs:70:8 | LL | if x < y { | ^^^^^ help: try simplifying it as shown: `!x & y` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:74:8 + --> $DIR/bool_comparison.rs:75:8 | LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:122:8 + --> $DIR/bool_comparison.rs:123:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:123:8 + --> $DIR/bool_comparison.rs:124:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:127:8 + --> $DIR/bool_comparison.rs:128:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:128:8 + --> $DIR/bool_comparison.rs:129:8 | LL | if !b == a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:152:8 + --> $DIR/bool_comparison.rs:153:8 | LL | if false == m!(func) {} | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:153:8 + --> $DIR/bool_comparison.rs:154:8 | LL | if m!(func) == false {} | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:154:8 + --> $DIR/bool_comparison.rs:155:8 | LL | if true == m!(func) {} | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:155:8 + --> $DIR/bool_comparison.rs:156:8 | LL | if m!(func) == true {} | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 3b5b6bf4c95..95b6b0f5038 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -1,6 +1,10 @@ #![deny(clippy::borrowed_box)] #![allow(dead_code, unused_variables)] -#![allow(clippy::uninlined_format_args, clippy::disallowed_names)] +#![allow( + clippy::uninlined_format_args, + clippy::disallowed_names, + clippy::needless_pass_by_ref_mut +)] use std::fmt::Display; diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 99cb60a1ead..90e752211ff 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:20:14 + --> $DIR/borrow_box.rs:24:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,55 +11,55 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:24:10 + --> $DIR/borrow_box.rs:28:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:28:17 + --> $DIR/borrow_box.rs:32:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:94:25 + --> $DIR/borrow_box.rs:98:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:95:25 + --> $DIR/borrow_box.rs:99:25 | LL | pub fn test15(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:96:29 + --> $DIR/borrow_box.rs:100:29 | LL | pub fn test16<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:98:25 + --> $DIR/borrow_box.rs:102:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:99:25 + --> $DIR/borrow_box.rs:103:25 | LL | pub fn test18(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:100:29 + --> $DIR/borrow_box.rs:104:29 | LL | pub fn test19<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:105:25 + --> $DIR/borrow_box.rs:109:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/crashes/ice-7169.stderr b/tests/ui/crashes/ice-7169.stderr index 84e0af3f0d0..0cd02851640 100644 --- a/tests/ui/crashes/ice-7169.stderr +++ b/tests/ui/crashes/ice-7169.stderr @@ -2,7 +2,7 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/ice-7169.rs:10:12 | LL | if let Ok(_) = Ok::<_, ()>(A::::default()) {} - | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::::default()).is_ok()` + | -------^^^^^-------------------------------------- help: try: `if Ok::<_, ()>(A::::default()).is_ok()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 8ed8f3b3a06..e6f3644ef34 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -2,7 +2,7 @@ error: unnecessary use of `splitn` --> $DIR/ice-8250.rs:2:13 | LL | let _ = s[1..].splitn(2, '.').next()?; - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `s[1..].split('.')` | = note: `-D clippy::needless-splitn` implied by `-D warnings` diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index 14eb6d572cf..6e541473cb3 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -7,9 +7,8 @@ extern crate proc_macros; use proc_macros::with_span; -use std::default; use std::default::Default as D2; -use std::string; +use std::{default, string}; fn main() { let s1: String = String::default(); diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index aa2ced0a7f0..2ffeb32fbdc 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -7,9 +7,8 @@ extern crate proc_macros; use proc_macros::with_span; -use std::default; use std::default::Default as D2; -use std::string; +use std::{default, string}; fn main() { let s1: String = Default::default(); diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index e4f73c08d19..103fccf6a1d 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,5 +1,5 @@ error: calling `String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:15:22 + --> $DIR/default_trait_access.rs:14:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` @@ -11,43 +11,43 @@ LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:19:22 + --> $DIR/default_trait_access.rs:18:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:21:22 + --> $DIR/default_trait_access.rs:20:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:25:22 + --> $DIR/default_trait_access.rs:24:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:35:46 + --> $DIR/default_trait_access.rs:34:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:41:36 + --> $DIR/default_trait_access.rs:40:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:43:36 + --> $DIR/default_trait_access.rs:42:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:47:42 + --> $DIR/default_trait_access.rs:46:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index e0287522fc5..9dd1e246b3e 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -2,7 +2,7 @@ error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:24:13 | LL | let b = *&a; - | ^^^ help: try this: `a` + | ^^^ help: try: `a` | = note: `-D clippy::deref-addrof` implied by `-D warnings` @@ -10,49 +10,49 @@ error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:26:13 | LL | let b = *&get_number(); - | ^^^^^^^^^^^^^^ help: try this: `get_number()` + | ^^^^^^^^^^^^^^ help: try: `get_number()` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:31:13 | LL | let b = *&bytes[1..2][0]; - | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]` + | ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:35:13 | LL | let b = *&(a); - | ^^^^^ help: try this: `(a)` + | ^^^^^ help: try: `(a)` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:37:13 | LL | let b = *(&a); - | ^^^^^ help: try this: `a` + | ^^^^^ help: try: `a` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:40:13 | LL | let b = *((&a)); - | ^^^^^^^ help: try this: `a` + | ^^^^^^^ help: try: `a` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:42:13 | LL | let b = *&&a; - | ^^^^ help: try this: `&a` + | ^^^^ help: try: `&a` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:44:14 | LL | let b = **&aref; - | ^^^^^^ help: try this: `aref` + | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:54:17 | LL | inline!(*& $(@expr self)) - | ^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)` + | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -60,7 +60,7 @@ error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:58:17 | LL | inline!(*&mut $(@expr self)) - | ^^^^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)` + | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/deref_addrof_double_trigger.stderr b/tests/ui/deref_addrof_double_trigger.stderr index 2c55a4ed6ac..6fa5069b6b4 100644 --- a/tests/ui/deref_addrof_double_trigger.stderr +++ b/tests/ui/deref_addrof_double_trigger.stderr @@ -2,7 +2,7 @@ error: immediately dereferencing a reference --> $DIR/deref_addrof_double_trigger.rs:10:14 | LL | let b = **&&a; - | ^^^^ help: try this: `&a` + | ^^^^ help: try: `&a` | = note: `-D clippy::deref-addrof` implied by `-D warnings` @@ -10,13 +10,13 @@ error: immediately dereferencing a reference --> $DIR/deref_addrof_double_trigger.rs:14:17 | LL | let y = *&mut x; - | ^^^^^^^ help: try this: `x` + | ^^^^^^^ help: try: `x` error: immediately dereferencing a reference --> $DIR/deref_addrof_double_trigger.rs:21:18 | LL | let y = **&mut &mut x; - | ^^^^^^^^^^^^ help: try this: `&mut x` + | ^^^^^^^^^^^^ help: try: `&mut x` error: aborting due to 3 previous errors diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index e01079bc977..c76711312e1 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -1,4 +1,8 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, dead_code)] +#![allow( + clippy::incorrect_clone_impl_on_copy_type, + clippy::incorrect_partial_ord_impl_on_ord_type, + dead_code +)] #![warn(clippy::expl_impl_clone_on_copy)] diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index e1fbb8dcd1e..5d7ed09188f 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:8:1 + --> $DIR/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | fn clone(&self) -> Self { @@ -9,7 +9,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:8:1 + --> $DIR/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | fn clone(&self) -> Self { @@ -20,7 +20,7 @@ LL | | } = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:32:1 + --> $DIR/derive.rs:36:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | fn clone(&self) -> Self { @@ -30,7 +30,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:32:1 + --> $DIR/derive.rs:36:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | fn clone(&self) -> Self { @@ -40,7 +40,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:43:1 + --> $DIR/derive.rs:47:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -50,7 +50,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:43:1 + --> $DIR/derive.rs:47:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -60,7 +60,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:54:1 + --> $DIR/derive.rs:58:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -70,7 +70,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:54:1 + --> $DIR/derive.rs:58:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -80,7 +80,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:74:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | fn clone(&self) -> Self { @@ -90,7 +90,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:74:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | fn clone(&self) -> Self { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 6f12d36d777..1fb3d51c46d 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,6 @@ #![warn(clippy::derive_ord_xor_partial_ord)] #![allow(clippy::unnecessary_wraps)] +#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] use std::cmp::Ordering; diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr index 58efbb8541f..bd148834814 100644 --- a/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -1,11 +1,11 @@ error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:21:10 + --> $DIR/derive_ord_xor_partial_ord.rs:22:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:24:1 + --> $DIR/derive_ord_xor_partial_ord.rs:25:1 | LL | impl PartialOrd for DeriveOrd { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,20 +13,20 @@ LL | impl PartialOrd for DeriveOrd { = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:30:10 + --> $DIR/derive_ord_xor_partial_ord.rs:31:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:33:1 + --> $DIR/derive_ord_xor_partial_ord.rs:34:1 | LL | impl PartialOrd for DeriveOrdWithExplicitTypeVariable { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:42:1 + --> $DIR/derive_ord_xor_partial_ord.rs:43:1 | LL | / impl std::cmp::Ord for DerivePartialOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -36,14 +36,14 @@ LL | | } | |_^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:39:10 + --> $DIR/derive_ord_xor_partial_ord.rs:40:10 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:62:5 + --> $DIR/derive_ord_xor_partial_ord.rs:63:5 | LL | / impl Ord for DerivePartialOrdInUseOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -53,7 +53,7 @@ LL | | } | |_____^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:59:14 + --> $DIR/derive_ord_xor_partial_ord.rs:60:14 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 2c4c49d2522..e8a003e9cf6 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -4,7 +4,7 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); LL | | } - | |_____^ help: try this: `m.entry(k).or_insert(v);` + | |_____^ help: try: `m.entry(k).or_insert(v);` | = note: `-D clippy::map-entry` implied by `-D warnings` @@ -20,7 +20,7 @@ LL | | } LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + if true { @@ -43,7 +43,7 @@ LL | | }; LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + if true { @@ -66,7 +66,7 @@ LL | | } LL | | } | |_____^ | -help: try this +help: try | LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { LL + if true { @@ -87,7 +87,7 @@ LL | | m.insert(k, v); LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + foo(); @@ -107,7 +107,7 @@ LL | | }; LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + match 0 { @@ -133,7 +133,7 @@ LL | | }; LL | | } | |_____^ | -help: try this +help: try | LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { LL + match 0 { @@ -157,7 +157,7 @@ LL | | } LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + foo(); @@ -192,7 +192,7 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); LL | | } - | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` + | |_____^ help: try: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `HashMap` --> $DIR/entry.rs:152:5 @@ -204,7 +204,7 @@ LL | | m.insert(k, v); LL | | } | |_____^ | -help: try this +help: try | LL ~ m.entry(k).or_insert_with(|| { LL + let x = (String::new(), String::new()); diff --git a/tests/ui/entry_btree.stderr b/tests/ui/entry_btree.stderr index 5c6fcdf1a28..8f41581d6b6 100644 --- a/tests/ui/entry_btree.stderr +++ b/tests/ui/entry_btree.stderr @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::map-entry` implied by `-D warnings` -help: try this +help: try | LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { LL + e.insert(v); diff --git a/tests/ui/entry_with_else.stderr b/tests/ui/entry_with_else.stderr index e0f6671b460..0d0eb964937 100644 --- a/tests/ui/entry_with_else.stderr +++ b/tests/ui/entry_with_else.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::map-entry` implied by `-D warnings` -help: try this +help: try | LL ~ match m.entry(k) { LL + std::collections::hash_map::Entry::Vacant(e) => { @@ -31,7 +31,7 @@ LL | | m.insert(k, v2); LL | | } | |_____^ | -help: try this +help: try | LL ~ match m.entry(k) { LL + std::collections::hash_map::Entry::Occupied(mut e) => { @@ -53,7 +53,7 @@ LL | | foo(); LL | | } | |_____^ | -help: try this +help: try | LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { LL + e.insert(v); @@ -72,7 +72,7 @@ LL | | m.insert(k, v); LL | | } | |_____^ | -help: try this +help: try | LL ~ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { LL + e.insert(v); @@ -91,7 +91,7 @@ LL | | m.insert(k, v2); LL | | } | |_____^ | -help: try this +help: try | LL ~ match m.entry(k) { LL + std::collections::hash_map::Entry::Vacant(e) => { @@ -113,7 +113,7 @@ LL | | m.insert(k, v) LL | | }; | |_____^ | -help: try this +help: try | LL ~ match m.entry(k) { LL + std::collections::hash_map::Entry::Occupied(mut e) => { @@ -137,7 +137,7 @@ LL | | None LL | | }; | |_____^ | -help: try this +help: try | LL ~ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { LL + foo(); diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index 36fb0e5de15..a621f681d98 100644 --- a/tests/ui/expect_fun_call.stderr +++ b/tests/ui/expect_fun_call.stderr @@ -2,7 +2,7 @@ error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:38:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` | = note: `-D clippy::expect-fun-call` implied by `-D warnings` @@ -10,85 +10,85 @@ error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:41:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:44:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:54:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:57:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:69:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:90:21 | LL | Some("foo").expect(&get_string()); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_string().as_ref()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:92:21 | LL | Some("foo").expect(get_string().as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:94:21 | LL | Some("foo").expect(get_static_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:95:21 | LL | Some("foo").expect(get_non_static_str(&0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:99:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:105:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:109:20 | LL | format_capture.expect(&format!("{error_code}")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}"))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))` error: use of `expect` followed by a function call --> $DIR/expect_fun_call.rs:112:30 | LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` error: aborting due to 15 previous errors diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index 91863abcc5d..afc311e3f7c 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -2,7 +2,7 @@ error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:70:19 | LL | let _: &str = &*s; - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` | = note: `-D clippy::explicit-auto-deref` implied by `-D warnings` @@ -10,229 +10,229 @@ error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:71:19 | LL | let _: &str = &*{ String::new() }; - | ^^^^^^^^^^^^^^^^^^^ help: try this: `&{ String::new() }` + | ^^^^^^^^^^^^^^^^^^^ help: try: `&{ String::new() }` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:72:19 | LL | let _: &str = &mut *{ String::new() }; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&mut { String::new() }` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut { String::new() }` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:76:11 | LL | f_str(&*s); - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:80:13 | LL | f_str_t(&*s, &*s); // Don't lint second param. - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:83:24 | LL | let _: &Box = &**b; - | ^^^^ help: try this: `&b` + | ^^^^ help: try: `&b` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:89:7 | LL | c(&*s); - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:95:9 | LL | &**x - | ^^^^ help: try this: `x` + | ^^^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:99:11 | LL | { &**x } - | ^^^^ help: try this: `x` + | ^^^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:103:9 | LL | &**{ x } - | ^^^^^^^^ help: try this: `{ x }` + | ^^^^^^^^ help: try: `{ x }` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:107:9 | LL | &***x - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:124:12 | LL | f1(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:125:12 | LL | f2(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:126:12 | LL | f3(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:127:27 | LL | f4.callable_str()(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:128:12 | LL | f5(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:129:12 | LL | f6(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:130:27 | LL | f7.callable_str()(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:131:25 | LL | f8.callable_t()(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:132:12 | LL | f9(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:133:13 | LL | f10(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:134:26 | LL | f11.callable_t()(&*x); - | ^^^ help: try this: `&x` + | ^^^ help: try: `&x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:138:16 | LL | let _ = S1(&*s); - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:143:21 | LL | let _ = S2 { s: &*s }; - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:159:30 | LL | let _ = Self::S1(&**s); - | ^^^^ help: try this: `s` + | ^^^^ help: try: `s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:160:35 | LL | let _ = Self::S2 { s: &**s }; - | ^^^^ help: try this: `s` + | ^^^^ help: try: `s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:163:20 | LL | let _ = E1::S1(&*s); - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:164:25 | LL | let _ = E1::S2 { s: &*s }; - | ^^^ help: try this: `&s` + | ^^^ help: try: `&s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:182:13 | LL | let _ = (*b).foo; - | ^^^^ help: try this: `b` + | ^^^^ help: try: `b` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:183:13 | LL | let _ = (**b).foo; - | ^^^^^ help: try this: `b` + | ^^^^^ help: try: `b` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:198:19 | LL | let _ = f_str(*ref_str); - | ^^^^^^^^ help: try this: `ref_str` + | ^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:200:19 | LL | let _ = f_str(**ref_ref_str); - | ^^^^^^^^^^^^^ help: try this: `ref_ref_str` + | ^^^^^^^^^^^^^ help: try: `ref_ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:210:13 | LL | f_str(&&*ref_str); // `needless_borrow` will suggest removing both references - | ^^^^^^^^ help: try this: `ref_str` + | ^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:211:12 | LL | f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference - | ^^^^^^^^^^ help: try this: `ref_str` + | ^^^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:220:41 | LL | let _ = || -> &'static str { return *s }; - | ^^ help: try this: `s` + | ^^ help: try: `s` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:239:9 | LL | &**x - | ^^^^ help: try this: `x` + | ^^^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:262:8 | LL | c1(*x); - | ^^ help: try this: `x` + | ^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:265:20 | LL | return *x; - | ^^ help: try this: `x` + | ^^ help: try: `x` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:267:9 | LL | *x - | ^^ help: try this: `x` + | ^^ help: try: `x` error: aborting due to 39 previous errors diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index d025035b789..362e559b21a 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -2,7 +2,7 @@ error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:54:19 | LL | let b: &str = a.deref(); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` | = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` @@ -10,67 +10,67 @@ error: explicit `deref_mut` method call --> $DIR/explicit_deref_methods.rs:56:23 | LL | let b: &mut str = a.deref_mut(); - | ^^^^^^^^^^^^^ help: try this: `&mut **a` + | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:59:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:59:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:61:20 | LL | println!("{}", a.deref()); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:64:11 | LL | match a.deref() { - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:68:28 | LL | let b: String = concat(a.deref()); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:70:13 | LL | let b = just_return(a).deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:72:28 | LL | let b: String = concat(just_return(a).deref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:74:19 | LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` + | ^^^^^^^^^^^^^^^^^ help: try: `&**a` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:77:13 | LL | let b = opt_a.unwrap().deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` error: explicit `deref` method call --> $DIR/explicit_deref_methods.rs:114:31 | LL | let b: &str = expr_deref!(a.deref()); - | ^^^^^^^^^ help: try this: `&*a` + | ^^^^^^^^^ help: try: `&*a` error: aborting due to 12 previous errors diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 457e9c62718..b3aa7274c6d 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -2,7 +2,7 @@ error: use of `write!(stdout(), ...).unwrap()` --> $DIR/explicit_write.rs:24:9 | LL | write!(std::io::stdout(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` | = note: `-D clippy::explicit-write` implied by `-D warnings` @@ -10,73 +10,73 @@ error: use of `write!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:25:9 | LL | write!(std::io::stderr(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` --> $DIR/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:27:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` --> $DIR/explicit_write.rs:28:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` --> $DIR/explicit_write.rs:29:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` --> $DIR/explicit_write.rs:32:9 | LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:33:9 | LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:36:9 | LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {}", value)` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:37:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:39:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:41:9 | LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{:w$}", value, w = width)` error: aborting due to 13 previous errors diff --git a/tests/ui/extend_with_drain.stderr b/tests/ui/extend_with_drain.stderr index da14ddb25b3..eb2dd304d37 100644 --- a/tests/ui/extend_with_drain.stderr +++ b/tests/ui/extend_with_drain.stderr @@ -2,7 +2,7 @@ error: use of `extend` instead of `append` for adding the full range of a second --> $DIR/extend_with_drain.rs:9:5 | LL | vec2.extend(vec1.drain(..)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec2.append(&mut vec1)` | = note: `-D clippy::extend-with-drain` implied by `-D warnings` @@ -10,19 +10,19 @@ error: use of `extend` instead of `append` for adding the full range of a second --> $DIR/extend_with_drain.rs:14:5 | LL | vec4.extend(vec3.drain(..)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec4.append(&mut vec3)` error: use of `extend` instead of `append` for adding the full range of a second vector --> $DIR/extend_with_drain.rs:18:5 | LL | vec11.extend(return_vector().drain(..)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec11.append(&mut return_vector())` error: use of `extend` instead of `append` for adding the full range of a second vector --> $DIR/extend_with_drain.rs:49:5 | LL | y.extend(ref_x.drain(..)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `y.append(ref_x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `y.append(ref_x)` error: aborting due to 4 previous errors diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index a9fc6abe88f..26d9c5e19da 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -2,7 +2,7 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly --> $DIR/filter_map_next_fixable.rs:9:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` @@ -10,7 +10,7 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly --> $DIR/filter_map_next_fixable.rs:22:26 | LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` error: aborting due to 2 previous errors diff --git a/tests/ui/get_first.fixed b/tests/ui/get_first.fixed index a29c0918a6d..bc2f86566bc 100644 --- a/tests/ui/get_first.fixed +++ b/tests/ui/get_first.fixed @@ -1,9 +1,7 @@ //@run-rustfix #![warn(clippy::get_first)] #![allow(clippy::useless_vec)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, VecDeque}; struct Bar { arr: [u32; 3], diff --git a/tests/ui/get_first.rs b/tests/ui/get_first.rs index 2062f3ec23a..bc0e233fdee 100644 --- a/tests/ui/get_first.rs +++ b/tests/ui/get_first.rs @@ -1,9 +1,7 @@ //@run-rustfix #![warn(clippy::get_first)] #![allow(clippy::useless_vec)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, VecDeque}; struct Bar { arr: [u32; 3], diff --git a/tests/ui/get_first.stderr b/tests/ui/get_first.stderr index 4e267ba9a3b..0899a5905f3 100644 --- a/tests/ui/get_first.stderr +++ b/tests/ui/get_first.stderr @@ -1,5 +1,5 @@ error: accessing first element with `x.get(0)` - --> $DIR/get_first.rs:20:13 + --> $DIR/get_first.rs:18:13 | LL | let _ = x.get(0); // Use x.first() | ^^^^^^^^ help: try: `x.first()` @@ -7,13 +7,13 @@ LL | let _ = x.get(0); // Use x.first() = note: `-D clippy::get-first` implied by `-D warnings` error: accessing first element with `y.get(0)` - --> $DIR/get_first.rs:25:13 + --> $DIR/get_first.rs:23:13 | LL | let _ = y.get(0); // Use y.first() | ^^^^^^^^ help: try: `y.first()` error: accessing first element with `z.get(0)` - --> $DIR/get_first.rs:30:13 + --> $DIR/get_first.rs:28:13 | LL | let _ = z.get(0); // Use z.first() | ^^^^^^^^ help: try: `z.first()` diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 56ee37f02d2..fda334407a9 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -9,9 +9,7 @@ #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, VecDeque}; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index af3a619adc5..eaf6b005a36 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -9,9 +9,7 @@ #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, VecDeque}; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index fd961420dc4..c567ed319b5 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -1,8 +1,8 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` | note: the lint level is defined here --> $DIR/get_unwrap.rs:10:9 @@ -11,7 +11,7 @@ LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:42:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:42:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:43:17 + --> $DIR/get_unwrap.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:43:17 + --> $DIR/get_unwrap.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:44:17 + --> $DIR/get_unwrap.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:44:17 + --> $DIR/get_unwrap.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:45:17 + --> $DIR/get_unwrap.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:45:17 + --> $DIR/get_unwrap.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:49:21 + --> $DIR/get_unwrap.rs:47:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:49:22 + --> $DIR/get_unwrap.rs:47:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:54:9 + --> $DIR/get_unwrap.rs:52:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:54:10 + --> $DIR/get_unwrap.rs:52:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:55:9 + --> $DIR/get_unwrap.rs:53:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:55:10 + --> $DIR/get_unwrap.rs:53:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:56:9 + --> $DIR/get_unwrap.rs:54:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:56:10 + --> $DIR/get_unwrap.rs:54:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:57:9 + --> $DIR/get_unwrap.rs:55:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:57:10 + --> $DIR/get_unwrap.rs:55:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:69:17 + --> $DIR/get_unwrap.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:69:17 + --> $DIR/get_unwrap.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,13 +174,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:70:17 + --> $DIR/get_unwrap.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` error: used `unwrap()` on an `Option` value - --> $DIR/get_unwrap.rs:70:17 + --> $DIR/get_unwrap.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,28 +188,28 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:80:24 + --> $DIR/get_unwrap.rs:78:24 | LL | let _x: &i32 = f.get(1 + 2).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `&f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&f[1 + 2]` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:83:18 + --> $DIR/get_unwrap.rs:81:18 | LL | let _x = f.get(1 + 2).unwrap().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:86:18 + --> $DIR/get_unwrap.rs:84:18 | LL | let _x = f.get(1 + 2).unwrap().abs(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:103:33 + --> $DIR/get_unwrap.rs:101:33 | LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&mut rest[linidx(j, k) - linidx(i, k) - 1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut rest[linidx(j, k) - linidx(i, k) - 1]` error: aborting due to 30 previous errors diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/tests/ui/incorrect_clone_impl_on_copy_type.stderr index 0021841aa86..7bcba8ba45a 100644 --- a/tests/ui/incorrect_clone_impl_on_copy_type.stderr +++ b/tests/ui/incorrect_clone_impl_on_copy_type.stderr @@ -16,7 +16,7 @@ LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); LL | | *self = source.clone(); LL | | } - | |_____^ help: remove this + | |_____^ help: remove it error: incorrect implementation of `clone` on a `Copy` type --> $DIR/incorrect_clone_impl_on_copy_type.rs:81:29 @@ -34,7 +34,7 @@ LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); LL | | *self = source.clone(); LL | | } - | |_____^ help: remove this + | |_____^ help: remove it error: aborting due to 4 previous errors diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed new file mode 100644 index 00000000000..dd4fdd98822 --- /dev/null +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed @@ -0,0 +1,114 @@ +//@run-rustfix +#![allow(unused)] +#![no_main] + +use std::cmp::Ordering; + +// lint + +#[derive(Eq, PartialEq)] +struct A(u32); + +impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for A { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +// do not lint + +#[derive(Eq, PartialEq)] +struct B(u32); + +impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// lint, and give `_` a name + +#[derive(Eq, PartialEq)] +struct C(u32); + +impl Ord for C { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for C { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +// do not lint derived + +#[derive(Eq, Ord, PartialEq, PartialOrd)] +struct D(u32); + +// do not lint if ord is not manually implemented + +#[derive(Eq, PartialEq)] +struct E(u32); + +impl PartialOrd for E { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } +} + +// do not lint since ord has more restrictive bounds + +#[derive(Eq, PartialEq)] +struct Uwu(A); + +impl Ord for Uwu { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for Uwu { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } +} + +// do not lint since `Rhs` is not `Self` + +#[derive(Eq, PartialEq)] +struct F(u32); + +impl Ord for F { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for F { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for F { + fn eq(&self, other: &u32) -> bool { + todo!(); + } +} + +impl PartialOrd for F { + fn partial_cmp(&self, other: &u32) -> Option { + todo!(); + } +} diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs new file mode 100644 index 00000000000..522e82299c0 --- /dev/null +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs @@ -0,0 +1,118 @@ +//@run-rustfix +#![allow(unused)] +#![no_main] + +use std::cmp::Ordering; + +// lint + +#[derive(Eq, PartialEq)] +struct A(u32); + +impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for A { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } +} + +// do not lint + +#[derive(Eq, PartialEq)] +struct B(u32); + +impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// lint, and give `_` a name + +#[derive(Eq, PartialEq)] +struct C(u32); + +impl Ord for C { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for C { + fn partial_cmp(&self, _: &Self) -> Option { + todo!(); + } +} + +// do not lint derived + +#[derive(Eq, Ord, PartialEq, PartialOrd)] +struct D(u32); + +// do not lint if ord is not manually implemented + +#[derive(Eq, PartialEq)] +struct E(u32); + +impl PartialOrd for E { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } +} + +// do not lint since ord has more restrictive bounds + +#[derive(Eq, PartialEq)] +struct Uwu(A); + +impl Ord for Uwu { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for Uwu { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } +} + +// do not lint since `Rhs` is not `Self` + +#[derive(Eq, PartialEq)] +struct F(u32); + +impl Ord for F { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for F { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for F { + fn eq(&self, other: &u32) -> bool { + todo!(); + } +} + +impl PartialOrd for F { + fn partial_cmp(&self, other: &u32) -> Option { + todo!(); + } +} diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr new file mode 100644 index 00000000000..0e477798c40 --- /dev/null +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr @@ -0,0 +1,31 @@ +error: incorrect implementation of `partial_cmp` on an `Ord` type + --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:18:1 + | +LL | / impl PartialOrd for A { +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________- +LL | || todo!(); +LL | || } + | ||_____- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__^ + | + = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + +error: incorrect implementation of `partial_cmp` on an `Ord` type + --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:52:1 + | +LL | / impl PartialOrd for C { +LL | | fn partial_cmp(&self, _: &Self) -> Option { +LL | | todo!(); +LL | | } +LL | | } + | |_^ + | +help: change this to + | +LL | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } + | ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/infallible_destructuring_match.stderr b/tests/ui/infallible_destructuring_match.stderr index f8a50f0223d..004260a1d64 100644 --- a/tests/ui/infallible_destructuring_match.stderr +++ b/tests/ui/infallible_destructuring_match.stderr @@ -4,7 +4,7 @@ error: you seem to be trying to use `match` to destructure a single infallible p LL | / let data = match wrapper { LL | | SingleVariantEnum::Variant(i) => i, LL | | }; - | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;` + | |______^ help: try: `let SingleVariantEnum::Variant(data) = wrapper;` | = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` @@ -14,7 +14,7 @@ error: you seem to be trying to use `match` to destructure a single infallible p LL | / let data = match wrapper { LL | | TupleStruct(i) => i, LL | | }; - | |______^ help: try this: `let TupleStruct(data) = wrapper;` + | |______^ help: try: `let TupleStruct(data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` --> $DIR/infallible_destructuring_match.rs:85:5 @@ -22,7 +22,7 @@ error: you seem to be trying to use `match` to destructure a single infallible p LL | / let data = match wrapper { LL | | TupleStructWithNonCopy(ref n) => n, LL | | }; - | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;` + | |______^ help: try: `let TupleStructWithNonCopy(ref data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` --> $DIR/infallible_destructuring_match.rs:104:5 @@ -30,7 +30,7 @@ error: you seem to be trying to use `match` to destructure a single infallible p LL | / let data = match wrapper { LL | | Ok(i) => i, LL | | }; - | |______^ help: try this: `let Ok(data) = wrapper;` + | |______^ help: try: `let Ok(data) = wrapper;` error: aborting due to 4 previous errors diff --git a/tests/ui/infinite_loop.stderr b/tests/ui/infinite_loop.stderr index 85258b9d64f..701b3cd1904 100644 --- a/tests/ui/infinite_loop.stderr +++ b/tests/ui/infinite_loop.stderr @@ -1,3 +1,11 @@ +error: this argument is a mutable reference, but not used mutably + --> $DIR/infinite_loop.rs:7:17 + | +LL | fn fn_mutref(i: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: variables in the condition are not mutated in the loop body --> $DIR/infinite_loop.rs:20:11 | @@ -91,5 +99,5 @@ LL | while y < 10 { = note: this loop contains `return`s or `break`s = help: rewrite it as `if cond { loop { } }` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/issue-7447.rs b/tests/ui/issue-7447.rs index fdb77f32257..de4362c4df7 100644 --- a/tests/ui/issue-7447.rs +++ b/tests/ui/issue-7447.rs @@ -1,4 +1,7 @@ -use std::{borrow::Cow, collections::BTreeMap, marker::PhantomData, sync::Arc}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::marker::PhantomData; +use std::sync::Arc; fn byte_view<'a>(s: &'a ByteView<'_>) -> BTreeMap<&'a str, ByteView<'a>> { panic!() diff --git a/tests/ui/issue-7447.stderr b/tests/ui/issue-7447.stderr index 8d8c29f1385..7a113740c6a 100644 --- a/tests/ui/issue-7447.stderr +++ b/tests/ui/issue-7447.stderr @@ -1,5 +1,5 @@ error: sub-expression diverges - --> $DIR/issue-7447.rs:23:15 + --> $DIR/issue-7447.rs:26:15 | LL | byte_view(panic!()); | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | byte_view(panic!()); = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: sub-expression diverges - --> $DIR/issue-7447.rs:24:19 + --> $DIR/issue-7447.rs:27:19 | LL | group_entries(panic!()); | ^^^^^^^^ diff --git a/tests/ui/iter_cloned_collect.fixed b/tests/ui/iter_cloned_collect.fixed index 2baea06f84b..636f572a343 100644 --- a/tests/ui/iter_cloned_collect.fixed +++ b/tests/ui/iter_cloned_collect.fixed @@ -3,8 +3,7 @@ #![allow(unused)] #![allow(clippy::useless_vec)] -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; fn main() { let v = [1, 2, 3, 4, 5]; diff --git a/tests/ui/iter_cloned_collect.rs b/tests/ui/iter_cloned_collect.rs index 9eac94eb8d9..518cb75affe 100644 --- a/tests/ui/iter_cloned_collect.rs +++ b/tests/ui/iter_cloned_collect.rs @@ -3,8 +3,7 @@ #![allow(unused)] #![allow(clippy::useless_vec)] -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; fn main() { let v = [1, 2, 3, 4, 5]; diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b38cf547dc5..b2cc497bf43 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -1,5 +1,5 @@ error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable - --> $DIR/iter_cloned_collect.rs:11:27 + --> $DIR/iter_cloned_collect.rs:10:27 | LL | let v2: Vec = v.iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` @@ -7,13 +7,13 @@ LL | let v2: Vec = v.iter().cloned().collect(); = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable - --> $DIR/iter_cloned_collect.rs:16:38 + --> $DIR/iter_cloned_collect.rs:15:38 | LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable - --> $DIR/iter_cloned_collect.rs:21:24 + --> $DIR/iter_cloned_collect.rs:20:24 | LL | .to_bytes() | ________________________^ @@ -23,13 +23,13 @@ LL | | .collect(); | |______________________^ help: try: `.to_vec()` error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable - --> $DIR/iter_cloned_collect.rs:29:24 + --> $DIR/iter_cloned_collect.rs:28:24 | LL | let _: Vec<_> = arr.iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable - --> $DIR/iter_cloned_collect.rs:32:26 + --> $DIR/iter_cloned_collect.rs:31:26 | LL | let _: Vec = v.iter().copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index dcae7cecd33..eaac48be880 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -4,7 +4,7 @@ error: unnecessarily eager cloning of iterator items LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^---------------- | | - | help: try this: `.last().cloned()` + | help: try: `.last().cloned()` | = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` @@ -14,7 +14,7 @@ error: unnecessarily eager cloning of iterator items LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- | | - | help: try this: `.next().cloned()` + | help: try: `.next().cloned()` error: unneeded cloning of iterator items --> $DIR/iter_overeager_cloned.rs:12:20 @@ -22,7 +22,7 @@ error: unneeded cloning of iterator items LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- | | - | help: try this: `.count()` + | help: try: `.count()` | = note: `-D clippy::redundant-clone` implied by `-D warnings` @@ -32,7 +32,7 @@ error: unnecessarily eager cloning of iterator items LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^----------------- | | - | help: try this: `.take(2).cloned()` + | help: try: `.take(2).cloned()` error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:16:21 @@ -40,7 +40,7 @@ error: unnecessarily eager cloning of iterator items LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^----------------- | | - | help: try this: `.skip(2).cloned()` + | help: try: `.skip(2).cloned()` error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:18:13 @@ -48,7 +48,7 @@ error: unnecessarily eager cloning of iterator items LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- | | - | help: try this: `.nth(2).cloned()` + | help: try: `.nth(2).cloned()` error: unnecessarily eager cloning of iterator items --> $DIR/iter_overeager_cloned.rs:20:13 @@ -60,7 +60,7 @@ LL | | .cloned() LL | | .flatten(); | |__________________^ | -help: try this +help: try | LL ~ .iter() LL ~ .flatten().cloned(); diff --git a/tests/ui/iter_with_drain.stderr b/tests/ui/iter_with_drain.stderr index aa394439fa6..bfaed29a099 100644 --- a/tests/ui/iter_with_drain.stderr +++ b/tests/ui/iter_with_drain.stderr @@ -2,7 +2,7 @@ error: `drain(..)` used on a `Vec` --> $DIR/iter_with_drain.rs:11:34 | LL | let mut a: BinaryHeap<_> = a.drain(..).collect(); - | ^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^ help: try: `into_iter()` | = note: `-D clippy::iter-with-drain` implied by `-D warnings` @@ -10,31 +10,31 @@ error: `drain(..)` used on a `VecDeque` --> $DIR/iter_with_drain.rs:14:27 | LL | let mut a: Vec<_> = a.drain(..).collect(); - | ^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^ help: try: `into_iter()` error: `drain(..)` used on a `Vec` --> $DIR/iter_with_drain.rs:15:34 | LL | let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect(); - | ^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^ help: try: `into_iter()` error: `drain(..)` used on a `Vec` --> $DIR/iter_with_drain.rs:21:34 | LL | let mut a: BinaryHeap<_> = a.drain(0..).collect(); - | ^^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^^ help: try: `into_iter()` error: `drain(..)` used on a `VecDeque` --> $DIR/iter_with_drain.rs:24:27 | LL | let mut a: Vec<_> = a.drain(..a.len()).collect(); - | ^^^^^^^^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^^^^^^^^ help: try: `into_iter()` error: `drain(..)` used on a `Vec` --> $DIR/iter_with_drain.rs:25:34 | LL | let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect(); - | ^^^^^^^^^^^^^^^^^ help: try this: `into_iter()` + | ^^^^^^^^^^^^^^^^^ help: try: `into_iter()` error: aborting due to 6 previous errors diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index bb162adc9ad..7e4d783a026 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -1,6 +1,8 @@ #![allow(unused)] #![warn(clippy::let_and_return)] +use std::cell::RefCell; + fn test() -> i32 { let _y = 0; // no warning let x = 5; @@ -65,45 +67,46 @@ macro_rules! tuple_encode { ); } +fn issue_3792() -> String { + use std::io::{self, BufRead, Stdin}; + + let stdin = io::stdin(); + // `Stdin::lock` returns `StdinLock<'static>` so `line` doesn't borrow from `stdin` + // https://github.com/rust-lang/rust/pull/93965 + let line = stdin.lock().lines().next().unwrap().unwrap(); + line +} + tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); mod no_lint_if_stmt_borrows { - mod issue_3792 { - use std::io::{self, BufRead, Stdin}; + use std::cell::RefCell; + use std::rc::{Rc, Weak}; + struct Bar; - fn read_line() -> String { - let stdin = io::stdin(); - let line = stdin.lock().lines().next().unwrap().unwrap(); - line + impl Bar { + fn new() -> Self { + Bar {} + } + fn baz(&self) -> u32 { + 0 } } - mod issue_3324 { - use std::cell::RefCell; - use std::rc::{Rc, Weak}; + fn issue_3324(value: Weak>) -> u32 { + let value = value.upgrade().unwrap(); + let ret = value.borrow().baz(); + ret + } - fn test(value: Weak>) -> u32 { - let value = value.upgrade().unwrap(); - let ret = value.borrow().baz(); - ret + fn borrows_in_closure(value: Weak>) -> u32 { + fn f(mut x: impl FnMut() -> u32) -> impl FnMut() -> u32 { + x } - struct Bar; - - impl Bar { - fn new() -> Self { - Bar {} - } - fn baz(&self) -> u32 { - 0 - } - } - - fn main() { - let a = Rc::new(RefCell::new(Bar::new())); - let b = Rc::downgrade(&a); - test(b); - } + let value = value.upgrade().unwrap(); + let ret = f(|| value.borrow().baz())(); + ret } mod free_function { diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index 17fd694bf7a..4ca0a05c858 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -1,5 +1,5 @@ error: returning the result of a `let` binding from a block - --> $DIR/let_and_return.rs:7:5 + --> $DIR/let_and_return.rs:9:5 | LL | let x = 5; | ---------- unnecessary `let` binding @@ -14,7 +14,7 @@ LL ~ 5 | error: returning the result of a `let` binding from a block - --> $DIR/let_and_return.rs:13:9 + --> $DIR/let_and_return.rs:15:9 | LL | let x = 5; | ---------- unnecessary `let` binding @@ -28,7 +28,21 @@ LL ~ 5 | error: returning the result of a `let` binding from a block - --> $DIR/let_and_return.rs:164:13 + --> $DIR/let_and_return.rs:77:5 + | +LL | let line = stdin.lock().lines().next().unwrap().unwrap(); + | --------------------------------------------------------- unnecessary `let` binding +LL | line + | ^^^^ + | +help: return the expression directly + | +LL ~ +LL ~ stdin.lock().lines().next().unwrap().unwrap() + | + +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:167:13 | LL | let clone = Arc::clone(&self.foo); | ---------------------------------- unnecessary `let` binding @@ -41,5 +55,5 @@ LL ~ LL ~ Arc::clone(&self.foo) as _ | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr index 33a748736a8..9e69fb04133 100644 --- a/tests/ui/let_underscore_future.stderr +++ b/tests/ui/let_underscore_future.stderr @@ -1,3 +1,11 @@ +error: this argument is a mutable reference, but not used mutably + --> $DIR/let_underscore_future.rs:11:35 + | +LL | fn do_something_to_future(future: &mut impl Future) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: non-binding `let` on a future --> $DIR/let_underscore_future.rs:14:5 | @@ -23,5 +31,5 @@ LL | let _ = future; | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/let_underscore_lock.rs b/tests/ui/let_underscore_lock.rs index 4dff4d766bc..87f12e2789f 100644 --- a/tests/ui/let_underscore_lock.rs +++ b/tests/ui/let_underscore_lock.rs @@ -3,7 +3,8 @@ extern crate parking_lot; fn main() { - use parking_lot::{lock_api::RawMutex, Mutex, RwLock}; + use parking_lot::lock_api::RawMutex; + use parking_lot::{Mutex, RwLock}; let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ()); let _ = p_m.lock(); diff --git a/tests/ui/let_underscore_lock.stderr b/tests/ui/let_underscore_lock.stderr index f137d411209..5027e6b3cbc 100644 --- a/tests/ui/let_underscore_lock.stderr +++ b/tests/ui/let_underscore_lock.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a synchronization lock - --> $DIR/let_underscore_lock.rs:9:5 + --> $DIR/let_underscore_lock.rs:10:5 | LL | let _ = p_m.lock(); | ^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = p_m.lock(); = note: `-D clippy::let-underscore-lock` implied by `-D warnings` error: non-binding `let` on a synchronization lock - --> $DIR/let_underscore_lock.rs:12:5 + --> $DIR/let_underscore_lock.rs:13:5 | LL | let _ = p_m1.lock(); | ^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = p_m1.lock(); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a synchronization lock - --> $DIR/let_underscore_lock.rs:15:5 + --> $DIR/let_underscore_lock.rs:16:5 | LL | let _ = p_rw.read(); | ^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = p_rw.read(); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a synchronization lock - --> $DIR/let_underscore_lock.rs:16:5 + --> $DIR/let_underscore_lock.rs:17:5 | LL | let _ = p_rw.write(); | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/let_underscore_untyped.rs b/tests/ui/let_underscore_untyped.rs index 431d83778e3..18630c27f41 100644 --- a/tests/ui/let_underscore_untyped.rs +++ b/tests/ui/let_underscore_untyped.rs @@ -7,8 +7,9 @@ extern crate proc_macros; use proc_macros::with_span; use clippy_utils::is_from_proc_macro; +use std::boxed::Box; +use std::fmt::Display; use std::future::Future; -use std::{boxed::Box, fmt::Display}; fn a() -> u32 { 1 diff --git a/tests/ui/let_underscore_untyped.stderr b/tests/ui/let_underscore_untyped.stderr index bbf2508af10..e0c39b6eeaf 100644 --- a/tests/ui/let_underscore_untyped.stderr +++ b/tests/ui/let_underscore_untyped.stderr @@ -1,60 +1,60 @@ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:50:5 + --> $DIR/let_underscore_untyped.rs:51:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:50:10 + --> $DIR/let_underscore_untyped.rs:51:10 | LL | let _ = a(); | ^ = note: `-D clippy::let-underscore-untyped` implied by `-D warnings` error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:51:5 + --> $DIR/let_underscore_untyped.rs:52:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:51:10 + --> $DIR/let_underscore_untyped.rs:52:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:53:5 + --> $DIR/let_underscore_untyped.rs:54:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:53:10 + --> $DIR/let_underscore_untyped.rs:54:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:54:5 + --> $DIR/let_underscore_untyped.rs:55:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:54:10 + --> $DIR/let_underscore_untyped.rs:55:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:55:5 + --> $DIR/let_underscore_untyped.rs:56:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:55:10 + --> $DIR/let_underscore_untyped.rs:56:10 | LL | let _ = f(); | ^ diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr index 53dea922930..f62d3e96059 100644 --- a/tests/ui/manual_filter.stderr +++ b/tests/ui/manual_filter.stderr @@ -8,7 +8,7 @@ LL | | if x > 0 { ... | LL | | }, LL | | }; - | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | |_____^ help: try: `Some(0).filter(|&x| x <= 0)` | = note: `-D clippy::manual-filter` implied by `-D warnings` @@ -22,7 +22,7 @@ LL | | None ... | LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + | |_____^ help: try: `Some(1).filter(|&x| x <= 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:29:5 @@ -34,7 +34,7 @@ LL | | None ... | LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + | |_____^ help: try: `Some(2).filter(|&x| x <= 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:40:5 @@ -46,7 +46,7 @@ LL | | Some(x) ... | LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + | |_____^ help: try: `Some(3).filter(|&x| x > 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:52:5 @@ -58,7 +58,7 @@ LL | | Some(x) => { ... | LL | | }, LL | | }; - | |_____^ help: try this: `y.filter(|&x| x <= 0)` + | |_____^ help: try: `y.filter(|&x| x <= 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:64:5 @@ -70,7 +70,7 @@ LL | | Some(x) ... | LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + | |_____^ help: try: `Some(5).filter(|&x| x > 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:75:5 @@ -82,7 +82,7 @@ LL | | Some(x) ... | LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + | |_____^ help: try: `Some(6).as_ref().filter(|&x| x > &0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:87:5 @@ -94,7 +94,7 @@ LL | | Some(x) ... | LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + | |_____^ help: try: `Some(String::new()).filter(|x| external_cond)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:98:5 @@ -104,7 +104,7 @@ LL | | if external_cond { Some(x) } else { None } LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + | |_____^ help: try: `Some(7).filter(|&x| external_cond)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:104:5 @@ -116,7 +116,7 @@ LL | | Some(x) ... | LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + | |_____^ help: try: `Some(8).filter(|&x| x != 0)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:115:5 @@ -128,7 +128,7 @@ LL | | Some(x) ... | LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + | |_____^ help: try: `Some(9).filter(|&x| x > 10 && x < 100)` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:141:5 @@ -142,7 +142,7 @@ LL | | None => None, LL | | }; | |_____^ | -help: try this +help: try | LL ~ Some(11).filter(|&x| { LL + println!("foo"); @@ -161,7 +161,7 @@ LL | | Some(x) ... | LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + | |_____^ help: try: `Some(14).filter(|&x| unsafe { f(x) })` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:195:13 @@ -173,7 +173,7 @@ LL | | if f(x) { Some(x) } else { None } LL | | }, LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + | |_____^ help: try: `Some(15).filter(|&x| unsafe { f(x) })` error: manual implementation of `Option::filter` --> $DIR/manual_filter.rs:205:12 @@ -185,7 +185,7 @@ LL | | if x % 2 == 0 { Some(x) } else { None } LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + | |_____^ help: try: `{ Some(16).filter(|&x| x % 2 == 0) }` error: aborting due to 15 previous errors diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs new file mode 100644 index 00000000000..af9076cfb71 --- /dev/null +++ b/tests/ui/manual_float_methods.rs @@ -0,0 +1,55 @@ +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::needless_if, unused)] +#![warn(clippy::manual_is_infinite, clippy::manual_is_finite)] +#![feature(inline_const)] + +#[macro_use] +extern crate proc_macros; + +const INFINITE: f32 = f32::INFINITY; +const NEG_INFINITE: f32 = f32::NEG_INFINITY; + +fn fn_test() -> f64 { + f64::NEG_INFINITY +} + +fn fn_test_not_inf() -> f64 { + 112.0 +} + +fn main() { + let x = 1.0f32; + if x == f32::INFINITY || x == f32::NEG_INFINITY {} + if x != f32::INFINITY && x != f32::NEG_INFINITY {} + if x == INFINITE || x == NEG_INFINITE {} + if x != INFINITE && x != NEG_INFINITE {} + let x = 1.0f64; + if x == f64::INFINITY || x == f64::NEG_INFINITY {} + if x != f64::INFINITY && x != f64::NEG_INFINITY {} + // Don't lint + if x.is_infinite() {} + if x.is_finite() {} + if x.abs() < f64::INFINITY {} + if f64::INFINITY > x.abs() {} + if f64::abs(x) < f64::INFINITY {} + if f64::INFINITY > f64::abs(x) {} + // Is not evaluated by `clippy_utils::constant` + if x != f64::INFINITY && x != fn_test() {} + // Not -inf + if x != f64::INFINITY && x != fn_test_not_inf() {} + const X: f64 = 1.0f64; + // Will be linted if `const_float_classify` is enabled + if const { X == f64::INFINITY || X == f64::NEG_INFINITY } {} + if const { X != f64::INFINITY && X != f64::NEG_INFINITY } {} + external! { + let x = 1.0; + if x == f32::INFINITY || x == f32::NEG_INFINITY {} + if x != f32::INFINITY && x != f32::NEG_INFINITY {} + } + with_span! { + span + let x = 1.0; + if x == f32::INFINITY || x == f32::NEG_INFINITY {} + if x != f32::INFINITY && x != f32::NEG_INFINITY {} + } +} diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr new file mode 100644 index 00000000000..a56118b316a --- /dev/null +++ b/tests/ui/manual_float_methods.stderr @@ -0,0 +1,80 @@ +error: manually checking if a float is infinite + --> $DIR/manual_float_methods.rs:22:8 + | +LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` + | + = note: `-D clippy::manual-is-infinite` implied by `-D warnings` + +error: manually checking if a float is finite + --> $DIR/manual_float_methods.rs:23:8 + | +LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-is-finite` implied by `-D warnings` +help: use the dedicated method instead + | +LL | if x.is_finite() {} + | ~~~~~~~~~~~~~ +help: this will alter how it handles NaN; if that is a problem, use instead + | +LL | if x.is_finite() || x.is_nan() {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: or, for conciseness + | +LL | if !x.is_infinite() {} + | ~~~~~~~~~~~~~~~~ + +error: manually checking if a float is infinite + --> $DIR/manual_float_methods.rs:24:8 + | +LL | if x == INFINITE || x == NEG_INFINITE {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` + +error: manually checking if a float is finite + --> $DIR/manual_float_methods.rs:25:8 + | +LL | if x != INFINITE && x != NEG_INFINITE {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the dedicated method instead + | +LL | if x.is_finite() {} + | ~~~~~~~~~~~~~ +help: this will alter how it handles NaN; if that is a problem, use instead + | +LL | if x.is_finite() || x.is_nan() {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: or, for conciseness + | +LL | if !x.is_infinite() {} + | ~~~~~~~~~~~~~~~~ + +error: manually checking if a float is infinite + --> $DIR/manual_float_methods.rs:27:8 + | +LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` + +error: manually checking if a float is finite + --> $DIR/manual_float_methods.rs:28:8 + | +LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the dedicated method instead + | +LL | if x.is_finite() {} + | ~~~~~~~~~~~~~ +help: this will alter how it handles NaN; if that is a problem, use instead + | +LL | if x.is_finite() || x.is_nan() {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: or, for conciseness + | +LL | if !x.is_infinite() {} + | ~~~~~~~~~~~~~~~~ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 46241afec94..381b83409e9 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -279,7 +279,9 @@ fn not_fire() { create_binding_if_some_nf!(v, g()); // Already a let-else - let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else { panic!() }; + let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else { + panic!() + }; // If a type annotation is present, don't lint as // expressing the type might be too hard @@ -304,9 +306,7 @@ fn not_fire() { let _x = if let Some(x) = Some(1) { x } else { - let Some(_z) = Some(3) else { - return - }; + let Some(_z) = Some(3) else { return }; 1 }; diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 1eada4f992e..912302b17a8 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -352,7 +352,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:297:5 + --> $DIR/manual_let_else.rs:299:5 | LL | / let _ = match ff { LL | | Some(value) => value, diff --git a/tests/ui/manual_let_else_question_mark.fixed b/tests/ui/manual_let_else_question_mark.fixed new file mode 100644 index 00000000000..02308bc7c4c --- /dev/null +++ b/tests/ui/manual_let_else_question_mark.fixed @@ -0,0 +1,63 @@ +//@run-rustfix +#![allow(unused_braces, unused_variables, dead_code)] +#![allow( + clippy::collapsible_else_if, + clippy::unused_unit, + clippy::let_unit_value, + clippy::match_single_binding, + clippy::never_loop +)] +#![warn(clippy::manual_let_else, clippy::question_mark)] + +enum Variant { + A(usize, usize), + B(usize), + C, +} + +fn g() -> Option<(u8, u8)> { + None +} + +fn e() -> Variant { + Variant::A(0, 0) +} + +fn main() {} + +fn foo() -> Option<()> { + // Fire here, normal case + let v = g()?; + + // Don't fire here, the pattern is refutable + let Variant::A(v, w) = e() else { return None }; + + // Fire here, the pattern is irrefutable + let (v, w) = g()?; + + // Don't fire manual_let_else in this instance: question mark can be used instead. + let v = g()?; + + // Do fire manual_let_else in this instance: question mark cannot be used here due to the return + // body. + let Some(v) = g() else { + return Some(()); + }; + + // Here we could also fire the question_mark lint, but we don't (as it's a match and not an if let). + // So we still emit manual_let_else here. For the *resulting* code, we *do* emit the question_mark + // lint, so for rustfix reasons, we allow the question_mark lint here. + #[allow(clippy::question_mark)] + { + let Some(v) = g() else { return None }; + } + + // This is a copy of the case above where we'd fire the question_mark lint, but here we have allowed + // it. Make sure that manual_let_else is fired as the fallback. + #[allow(clippy::question_mark)] + { + let Some(v) = g() else { return None }; + } + + Some(()) +} diff --git a/tests/ui/manual_let_else_question_mark.rs b/tests/ui/manual_let_else_question_mark.rs new file mode 100644 index 00000000000..9c7ad386dc9 --- /dev/null +++ b/tests/ui/manual_let_else_question_mark.rs @@ -0,0 +1,68 @@ +//@run-rustfix +#![allow(unused_braces, unused_variables, dead_code)] +#![allow( + clippy::collapsible_else_if, + clippy::unused_unit, + clippy::let_unit_value, + clippy::match_single_binding, + clippy::never_loop +)] +#![warn(clippy::manual_let_else, clippy::question_mark)] + +enum Variant { + A(usize, usize), + B(usize), + C, +} + +fn g() -> Option<(u8, u8)> { + None +} + +fn e() -> Variant { + Variant::A(0, 0) +} + +fn main() {} + +fn foo() -> Option<()> { + // Fire here, normal case + let Some(v) = g() else { return None }; + + // Don't fire here, the pattern is refutable + let Variant::A(v, w) = e() else { return None }; + + // Fire here, the pattern is irrefutable + let Some((v, w)) = g() else { return None }; + + // Don't fire manual_let_else in this instance: question mark can be used instead. + let v = if let Some(v_some) = g() { v_some } else { return None }; + + // Do fire manual_let_else in this instance: question mark cannot be used here due to the return + // body. + let v = if let Some(v_some) = g() { + v_some + } else { + return Some(()); + }; + + // Here we could also fire the question_mark lint, but we don't (as it's a match and not an if let). + // So we still emit manual_let_else here. For the *resulting* code, we *do* emit the question_mark + // lint, so for rustfix reasons, we allow the question_mark lint here. + #[allow(clippy::question_mark)] + { + let v = match g() { + Some(v_some) => v_some, + _ => return None, + }; + } + + // This is a copy of the case above where we'd fire the question_mark lint, but here we have allowed + // it. Make sure that manual_let_else is fired as the fallback. + #[allow(clippy::question_mark)] + { + let v = if let Some(v_some) = g() { v_some } else { return None }; + } + + Some(()) +} diff --git a/tests/ui/manual_let_else_question_mark.stderr b/tests/ui/manual_let_else_question_mark.stderr new file mode 100644 index 00000000000..d7d2e127ea3 --- /dev/null +++ b/tests/ui/manual_let_else_question_mark.stderr @@ -0,0 +1,55 @@ +error: this `let...else` may be rewritten with the `?` operator + --> $DIR/manual_let_else_question_mark.rs:30:5 + | +LL | let Some(v) = g() else { return None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `let v = g()?;` + | + = note: `-D clippy::question-mark` implied by `-D warnings` + +error: this `let...else` may be rewritten with the `?` operator + --> $DIR/manual_let_else_question_mark.rs:36:5 + | +LL | let Some((v, w)) = g() else { return None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `let (v, w) = g()?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/manual_let_else_question_mark.rs:39:13 + | +LL | let v = if let Some(v_some) = g() { v_some } else { return None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `g()?` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_question_mark.rs:43:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | return Some(()); +LL | | }; + | |______^ + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + return Some(()); +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_question_mark.rs:54:9 + | +LL | / let v = match g() { +LL | | Some(v_some) => v_some, +LL | | _ => return None, +LL | | }; + | |__________^ help: consider writing: `let Some(v) = g() else { return None };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_question_mark.rs:64:9 + | +LL | let v = if let Some(v_some) = g() { v_some } else { return None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return None };` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index cdc2c0e62a9..3f9caad4e89 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -5,7 +5,7 @@ LL | / match Some(0) { LL | | Some(_) => Some(2), LL | | None:: => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|_| 2)` + | |_____^ help: try: `Some(0).map(|_| 2)` | = note: `-D clippy::manual-map` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / match Some(0) { LL | | Some(x) => Some(x + 1), LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| x + 1)` + | |_____^ help: try: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:25:5 @@ -25,7 +25,7 @@ LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some("").map(|x| x.is_empty())` + | |_____^ help: try: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:30:5 @@ -35,7 +35,7 @@ LL | | Some(!x) LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `Some(0).map(|x| !x)` + | |_____^ help: try: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:37:5 @@ -44,7 +44,7 @@ LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } LL | | None => { None } LL | | }; - | |_____^ help: try this: `Some(0).map(std::convert::identity)` + | |_____^ help: try: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:42:5 @@ -53,7 +53,7 @@ LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` + | |_____^ help: try: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:52:5 @@ -62,7 +62,7 @@ LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), LL | | &None => None, LL | | }; - | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` + | |_____^ help: try: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:57:5 @@ -71,7 +71,7 @@ LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| x * 2)` + | |_____^ help: try: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:62:5 @@ -80,7 +80,7 @@ LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:67:5 @@ -89,7 +89,7 @@ LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), LL | | _ => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` + | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:72:5 @@ -98,7 +98,7 @@ LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), LL | | &&_ => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| x + x)` + | |_____^ help: try: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:85:9 @@ -107,7 +107,7 @@ LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), LL | | None => None, LL | | }; - | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` + | |_________^ help: try: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:91:5 @@ -116,7 +116,7 @@ LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` + | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:96:5 @@ -125,7 +125,7 @@ LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), LL | | &mut _ => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:101:5 @@ -134,7 +134,7 @@ LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` + | |_____^ help: try: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:106:5 @@ -143,7 +143,7 @@ LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` + | |_____^ help: try: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:111:5 @@ -152,7 +152,7 @@ LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` + | |_____^ help: try: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:169:5 @@ -161,7 +161,7 @@ LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| vec![x])` + | |_____^ help: try: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:174:5 @@ -170,7 +170,7 @@ LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), LL | | None => None, LL | | }; - | |_____^ help: try this: `option_env!("").map(String::from)` + | |_____^ help: try: `option_env!("").map(String::from)` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:194:12 @@ -181,7 +181,7 @@ LL | | Some(x + 1) LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` --> $DIR/manual_map_option.rs:202:12 @@ -192,7 +192,7 @@ LL | | Some(x + 1) LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: aborting due to 21 previous errors diff --git a/tests/ui/manual_map_option_2.stderr b/tests/ui/manual_map_option_2.stderr index d35b6252fb8..8c78fcffca8 100644 --- a/tests/ui/manual_map_option_2.stderr +++ b/tests/ui/manual_map_option_2.stderr @@ -12,7 +12,7 @@ LL | | }; | |_____^ | = note: `-D clippy::manual-map` implied by `-D warnings` -help: try this +help: try | LL ~ let _ = Some(0).map(|x| { LL + let y = (String::new(), String::new()); @@ -32,7 +32,7 @@ LL | | None => None, LL | | }; | |_____^ | -help: try this +help: try | LL ~ let _ = s.as_ref().map(|x| { LL + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } @@ -47,7 +47,7 @@ LL | let _ = match Some(0) { LL | | Some(x) => Some(f(x)), LL | | None => None, LL | | }; - | |_________^ help: try this: `Some(0).map(|x| f(x))` + | |_________^ help: try: `Some(0).map(|x| f(x))` error: manual implementation of `Option::map` --> $DIR/manual_map_option_2.rs:67:13 @@ -57,7 +57,7 @@ LL | let _ = match Some(0) { LL | | Some(x) => unsafe { Some(f(x)) }, LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })` + | |_____^ help: try: `Some(0).map(|x| unsafe { f(x) })` error: manual implementation of `Option::map` --> $DIR/manual_map_option_2.rs:71:13 @@ -67,7 +67,7 @@ LL | let _ = match Some(0) { LL | | Some(x) => Some(unsafe { f(x) }), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })` + | |_____^ help: try: `Some(0).map(|x| unsafe { f(x) })` error: aborting due to 5 previous errors diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index 9eee8f37187..6bfcf263aa5 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -25,6 +25,10 @@ fn main() { 1..=10 => true, _ => false, }; + let _ = matches!(f, -5..=3); + let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing + let _ = matches!(f, -1000001..=1000001); + let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); macro_rules! mac { ($e:expr) => { diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index 10743a7d04c..4a429bb2aed 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -25,6 +25,10 @@ fn main() { 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true, _ => false, }; + let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1 | 2); + let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing + let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); + let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); macro_rules! mac { ($e:expr) => { diff --git a/tests/ui/manual_range_patterns.stderr b/tests/ui/manual_range_patterns.stderr index bc9e3350164..b1b55d483e7 100644 --- a/tests/ui/manual_range_patterns.stderr +++ b/tests/ui/manual_range_patterns.stderr @@ -37,7 +37,19 @@ LL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> $DIR/manual_range_patterns.rs:31:26 + --> $DIR/manual_range_patterns.rs:28:25 + | +LL | let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1 | 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-5..=3` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:30:25 + | +LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1000001..=1000001` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:35:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -47,5 +59,5 @@ LL | mac!(f); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index 09fb0d75852..c95d40fecba 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,12 +1,7 @@ //@run-rustfix #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::BinaryHeap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { binary_heap_retain(); diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 7fee4c95cea..9a3434f489d 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,12 +1,7 @@ //@run-rustfix #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::BinaryHeap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { binary_heap_retain(); diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index 89316ce1d99..0936a23841c 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:51:5 + --> $DIR/manual_retain.rs:46:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` @@ -7,13 +7,13 @@ LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect() = note: `-D clippy::manual-retain` implied by `-D warnings` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:52:5 + --> $DIR/manual_retain.rs:47:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:53:5 + --> $DIR/manual_retain.rs:48:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -22,37 +22,37 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:75:5 + --> $DIR/manual_retain.rs:70:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:76:5 + --> $DIR/manual_retain.rs:71:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:77:5 + --> $DIR/manual_retain.rs:72:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:107:5 + --> $DIR/manual_retain.rs:102:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:108:5 + --> $DIR/manual_retain.rs:103:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:109:5 + --> $DIR/manual_retain.rs:104:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -61,61 +61,61 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:130:5 + --> $DIR/manual_retain.rs:125:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:131:5 + --> $DIR/manual_retain.rs:126:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:132:5 + --> $DIR/manual_retain.rs:127:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:161:5 + --> $DIR/manual_retain.rs:156:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:173:5 + --> $DIR/manual_retain.rs:168:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:174:5 + --> $DIR/manual_retain.rs:169:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:175:5 + --> $DIR/manual_retain.rs:170:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:197:5 + --> $DIR/manual_retain.rs:192:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:198:5 + --> $DIR/manual_retain.rs:193:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:199:5 + --> $DIR/manual_retain.rs:194:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 78da5a16cc5..f454f95b41d 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -2,7 +2,7 @@ error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` | = note: `-D clippy::manual-split-once` implied by `-D warnings` @@ -10,73 +10,73 @@ error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=')` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` --> $DIR/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:44:5 @@ -182,7 +182,7 @@ error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:143:5 diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index bdfee7cab26..331bb6ea575 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -2,7 +2,7 @@ error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:9:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"test".repeat(10)` | = note: `-D clippy::manual-str-repeat` implied by `-D warnings` @@ -10,55 +10,55 @@ error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:16:13 | LL | let _ = repeat(x).take(count + 2).collect::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:25:21 | LL | let _: String = repeat(*x).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:34:21 | LL | let _: String = repeat(x).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:46:21 | LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Cow::Borrowed("test").repeat(count)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:49:21 | LL | let _: String = repeat(x).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators --> $DIR/manual_str_repeat.rs:64:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"test".repeat(10)` error: aborting due to 10 previous errors diff --git a/tests/ui/manual_try_fold.rs b/tests/ui/manual_try_fold.rs index 4521e9fa1a5..05c658579a5 100644 --- a/tests/ui/manual_try_fold.rs +++ b/tests/ui/manual_try_fold.rs @@ -3,9 +3,7 @@ #![warn(clippy::manual_try_fold)] #![feature(try_trait_v2)] -use std::ops::ControlFlow; -use std::ops::FromResidual; -use std::ops::Try; +use std::ops::{ControlFlow, FromResidual, Try}; #[macro_use] extern crate proc_macros; diff --git a/tests/ui/manual_try_fold.stderr b/tests/ui/manual_try_fold.stderr index a0cf5b3b5fc..f1bb97c6d0f 100644 --- a/tests/ui/manual_try_fold.stderr +++ b/tests/ui/manual_try_fold.stderr @@ -1,5 +1,5 @@ error: usage of `Iterator::fold` on a type that implements `Try` - --> $DIR/manual_try_fold.rs:61:10 + --> $DIR/manual_try_fold.rs:59:10 | LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` @@ -7,19 +7,19 @@ LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i)) = note: `-D clippy::manual-try-fold` implied by `-D warnings` error: usage of `Iterator::fold` on a type that implements `Try` - --> $DIR/manual_try_fold.rs:65:10 + --> $DIR/manual_try_fold.rs:63:10 | LL | .fold(NotOption(0i32, 0i32), |sum, i| NotOption(0i32, 0i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(..., |sum, i| ...)` error: usage of `Iterator::fold` on a type that implements `Try` - --> $DIR/manual_try_fold.rs:68:10 + --> $DIR/manual_try_fold.rs:66:10 | LL | .fold(NotOptionButWorse(0i32), |sum, i| NotOptionButWorse(0i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` error: usage of `Iterator::fold` on a type that implements `Try` - --> $DIR/manual_try_fold.rs:98:10 + --> $DIR/manual_try_fold.rs:96:10 | LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr index 8b06e13baa6..596e51e5741 100644 --- a/tests/ui/map_collect_result_unit.stderr +++ b/tests/ui/map_collect_result_unit.stderr @@ -2,7 +2,7 @@ error: `.map().collect()` can be replaced with `.try_for_each()` --> $DIR/map_collect_result_unit.rs:6:17 | LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(0..3).try_for_each(|t| Err(t + 1))` | = note: `-D clippy::map-collect-result-unit` implied by `-D warnings` @@ -10,7 +10,7 @@ error: `.map().collect()` can be replaced with `.try_for_each()` --> $DIR/map_collect_result_unit.rs:7:32 | LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(0..3).try_for_each(|t| Err(t + 1))` error: aborting due to 2 previous errors diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 9f4a4a9ae6b..5b3c61acf74 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -162,7 +162,7 @@ error: called `map().unwrap_or_else()` on a `Result` value. This can be do --> $DIR/map_unwrap_or.rs:99:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or_else(|_e| 0, |x| x + 1)` error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:106:13 diff --git a/tests/ui/map_unwrap_or_fixable.stderr b/tests/ui/map_unwrap_or_fixable.stderr index 1837bc2ca3b..71dc009f2ce 100644 --- a/tests/ui/map_unwrap_or_fixable.stderr +++ b/tests/ui/map_unwrap_or_fixable.stderr @@ -5,7 +5,7 @@ LL | let _ = opt.map(|x| x + 1) | _____________^ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + | |_____________________________^ help: try: `opt.map_or_else(|| 0, |x| x + 1)` | = note: `-D clippy::map-unwrap-or` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | let _ = res.map(|x| x + 1) | _____________^ LL | | // should lint even though this call is on a separate line LL | | .unwrap_or_else(|_e| 0); - | |_______________________________^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + | |_______________________________^ help: try: `res.map_or_else(|_e| 0, |x| x + 1)` error: aborting due to 2 previous errors diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 8fa3f532587..61d414bdf4b 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -12,7 +12,9 @@ fn match_as_ref() { } mod issue4437 { - use std::{error::Error, fmt, num::ParseIntError}; + use std::error::Error; + use std::fmt; + use std::num::ParseIntError; #[derive(Debug)] struct E { diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 02a17791426..cd39514c59a 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -18,7 +18,9 @@ fn match_as_ref() { } mod issue4437 { - use std::{error::Error, fmt, num::ParseIntError}; + use std::error::Error; + use std::fmt; + use std::num::ParseIntError; #[derive(Debug)] struct E { diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index c3b62849cb3..2e6955eb805 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -6,7 +6,7 @@ LL | let borrowed: Option<&()> = match owned { LL | | None => None, LL | | Some(ref v) => Some(v), LL | | }; - | |_____^ help: try this: `owned.as_ref()` + | |_____^ help: try: `owned.as_ref()` | = note: `-D clippy::match-as-ref` implied by `-D warnings` @@ -18,16 +18,16 @@ LL | let borrow_mut: Option<&mut ()> = match mut_owned { LL | | None => None, LL | | Some(ref mut v) => Some(v), LL | | }; - | |_____^ help: try this: `mut_owned.as_mut()` + | |_____^ help: try: `mut_owned.as_mut()` error: use `as_ref()` instead - --> $DIR/match_as_ref.rs:30:13 + --> $DIR/match_as_ref.rs:32:13 | LL | / match self.source { LL | | Some(ref s) => Some(s), LL | | None => None, LL | | } - | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)` + | |_____________^ help: try: `self.source.as_ref().map(|x| x as _)` error: aborting due to 3 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index b72fe10b748..c8c1e5da05f 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -6,7 +6,7 @@ LL | let _y = match x { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `matches!(x, Some(0))` + | |_____^ help: try: `matches!(x, Some(0))` | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` @@ -18,7 +18,7 @@ LL | let _w = match x { LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `x.is_some()` + | |_____^ help: try: `x.is_some()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` @@ -30,7 +30,7 @@ LL | let _z = match x { LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try this: `x.is_none()` + | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:33:15 @@ -40,13 +40,13 @@ LL | let _zz = match x { LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` + | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:39:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:63:20 @@ -57,7 +57,7 @@ LL | | E::A(_) => true, LL | | E::B(_) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` + | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:73:20 @@ -70,7 +70,7 @@ LL | | } LL | | E::B(_) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` + | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:83:20 @@ -81,7 +81,7 @@ LL | | E::B(_) => false, LL | | E::C => false, LL | | _ => true, LL | | }; - | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` + | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:143:18 @@ -91,7 +91,7 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(z, Some(3))` + | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:152:18 @@ -101,7 +101,7 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(&z, Some(3))` + | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:169:21 @@ -111,7 +111,7 @@ LL | let _ = match &z { LL | | AnEnum::X => true, LL | | _ => false, LL | | }; - | |_____________^ help: try this: `matches!(&z, AnEnum::X)` + | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:183:20 @@ -121,7 +121,7 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(&val, &Some(ref _a))` + | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:195:20 @@ -131,7 +131,7 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try this: `matches!(&val, &Some(ref _a))` + | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:253:14 @@ -141,7 +141,7 @@ LL | let _y = match Some(5) { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `matches!(Some(5), Some(0))` + | |_____^ help: try: `matches!(Some(5), Some(0))` error: aborting due to 14 previous errors diff --git a/tests/ui/match_on_vec_items.stderr b/tests/ui/match_on_vec_items.stderr index 9b1f052867e..fc4a3ce1946 100644 --- a/tests/ui/match_on_vec_items.stderr +++ b/tests/ui/match_on_vec_items.stderr @@ -2,7 +2,7 @@ error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:10:11 | LL | match arr[idx] { - | ^^^^^^^^ help: try this: `arr.get(idx)` + | ^^^^^^^^ help: try: `arr.get(idx)` | = note: `-D clippy::match-on-vec-items` implied by `-D warnings` @@ -10,43 +10,43 @@ error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:17:11 | LL | match arr[range] { - | ^^^^^^^^^^ help: try this: `arr.get(range)` + | ^^^^^^^^^^ help: try: `arr.get(range)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:30:11 | LL | match arr[idx] { - | ^^^^^^^^ help: try this: `arr.get(idx)` + | ^^^^^^^^ help: try: `arr.get(idx)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:37:11 | LL | match arr[range] { - | ^^^^^^^^^^ help: try this: `arr.get(range)` + | ^^^^^^^^^^ help: try: `arr.get(range)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:50:11 | LL | match arr[idx] { - | ^^^^^^^^ help: try this: `arr.get(idx)` + | ^^^^^^^^ help: try: `arr.get(idx)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:57:11 | LL | match arr[range] { - | ^^^^^^^^^^ help: try this: `arr.get(range)` + | ^^^^^^^^^^ help: try: `arr.get(range)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:70:11 | LL | match arr[idx] { - | ^^^^^^^^ help: try this: `arr.get(idx)` + | ^^^^^^^^ help: try: `arr.get(idx)` error: indexing into a vector may panic --> $DIR/match_on_vec_items.rs:77:11 | LL | match arr[range] { - | ^^^^^^^^^^ help: try this: `arr.get(range)` + | ^^^^^^^^^^ help: try: `arr.get(range)` error: aborting due to 8 previous errors diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 7d9646c842e..1294e0fe56f 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -35,7 +35,7 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/match_ref_pats.rs:38:12 | LL | if let &None = a { - | -------^^^^^---- help: try this: `if a.is_none()` + | -------^^^^^---- help: try: `if a.is_none()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` @@ -43,7 +43,7 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/match_ref_pats.rs:43:12 | LL | if let &None = &b { - | -------^^^^^----- help: try this: `if b.is_none()` + | -------^^^^^----- help: try: `if b.is_none()` error: you don't need to add `&` to all patterns --> $DIR/match_ref_pats.rs:103:9 diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 7f0c70745ac..a7348187573 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -144,7 +144,7 @@ LL | | E::A => false, LL | | E::B => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `!matches!(x, E::A | E::B)` + | |_____^ help: try: `!matches!(x, E::A | E::B)` | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 105b4c4b41d..40ff4fbd316 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -2,7 +2,7 @@ error: wildcard matches only a single variant and will also match any future add --> $DIR/match_wildcard_for_single_variants.rs:24:13 | LL | _ => (), - | ^ help: try this: `Self::Rgb(..)` + | ^ help: try: `Self::Rgb(..)` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` @@ -10,55 +10,55 @@ error: wildcard matches only a single variant and will also match any future add --> $DIR/match_wildcard_for_single_variants.rs:34:9 | LL | _ => {}, - | ^ help: try this: `Foo::C` + | ^ help: try: `Foo::C` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:44:9 | LL | _ => {}, - | ^ help: try this: `Color::Blue` + | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, - | ^ help: try this: `Color::Blue` + | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, - | ^ help: try this: `Color::Blue` + | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:75:9 | LL | &_ => (), - | ^^ help: try this: `Color::Blue` + | ^^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:84:9 | LL | _ => (), - | ^ help: try this: `C::Blue` + | ^ help: try: `C::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:91:9 | LL | _ => (), - | ^ help: try this: `Color::Blue` + | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:126:13 | LL | _ => (), - | ^ help: try this: `Enum::__Private` + | ^ help: try: `Enum::__Private` error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, - | ^ help: try this: `Foo::B` + | ^ help: try: `Foo::B` error: aborting due to 10 previous errors diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 589eab5cdfc..cb1f695c651 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -25,10 +25,7 @@ #[macro_use] extern crate option_helpers; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 73ec48643e0..6be38b24fbd 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/methods.rs:106:5 + --> $DIR/methods.rs:103:5 | LL | / fn new() -> i32 { LL | | 0 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> $DIR/methods.rs:127:13 + --> $DIR/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 187714c75fb..6f45d100d28 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -2,7 +2,7 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expr --> $DIR/methods_fixable.rs:11:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` | = note: `-D clippy::filter-next` implied by `-D warnings` diff --git a/tests/ui/methods_unfixable.rs b/tests/ui/methods_unfixable.rs new file mode 100644 index 00000000000..3d88ce4b6bb --- /dev/null +++ b/tests/ui/methods_unfixable.rs @@ -0,0 +1,10 @@ +#![warn(clippy::filter_next)] + +fn main() { + issue10029(); +} + +pub fn issue10029() { + let iter = (0..10); + let _ = iter.filter(|_| true).next(); +} diff --git a/tests/ui/methods_unfixable.stderr b/tests/ui/methods_unfixable.stderr new file mode 100644 index 00000000000..6e101fe16b0 --- /dev/null +++ b/tests/ui/methods_unfixable.stderr @@ -0,0 +1,15 @@ +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead + --> $DIR/methods_unfixable.rs:9:13 + | +LL | let _ = iter.filter(|_| true).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` + | +help: you will also need to make `iter` mutable, because `find` takes `&mut self` + --> $DIR/methods_unfixable.rs:8:9 + | +LL | let iter = (0..10); + | ^^^^ + = note: `-D clippy::filter-next` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index b5b9e66aa7a..0fab224a29d 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -3,8 +3,7 @@ #![warn(clippy::min_ident_chars)] extern crate proc_macros; -use proc_macros::external; -use proc_macros::with_span; +use proc_macros::{external, with_span}; struct A { a: u32, diff --git a/tests/ui/min_ident_chars.stderr b/tests/ui/min_ident_chars.stderr index 66a63f65756..4dff6588bb1 100644 --- a/tests/ui/min_ident_chars.stderr +++ b/tests/ui/min_ident_chars.stderr @@ -1,5 +1,5 @@ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:9:8 + --> $DIR/min_ident_chars.rs:8:8 | LL | struct A { | ^ @@ -7,169 +7,169 @@ LL | struct A { = note: `-D clippy::min-ident-chars` implied by `-D warnings` error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:10:5 + --> $DIR/min_ident_chars.rs:9:5 | LL | a: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:12:5 + --> $DIR/min_ident_chars.rs:11:5 | LL | A: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:13:5 + --> $DIR/min_ident_chars.rs:12:5 | LL | I: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:16:8 + --> $DIR/min_ident_chars.rs:15:8 | LL | struct B(u32); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:18:8 + --> $DIR/min_ident_chars.rs:17:8 | LL | struct O { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:19:5 + --> $DIR/min_ident_chars.rs:18:5 | LL | o: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:24:6 + --> $DIR/min_ident_chars.rs:23:6 | LL | enum C { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:25:5 + --> $DIR/min_ident_chars.rs:24:5 | LL | D, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:26:5 + --> $DIR/min_ident_chars.rs:25:5 | LL | E, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:27:5 + --> $DIR/min_ident_chars.rs:26:5 | LL | F, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:51:9 + --> $DIR/min_ident_chars.rs:50:9 | LL | let h = 1; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:52:9 + --> $DIR/min_ident_chars.rs:51:9 | LL | let e = 2; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:53:9 + --> $DIR/min_ident_chars.rs:52:9 | LL | let l = 3; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:54:9 + --> $DIR/min_ident_chars.rs:53:9 | LL | let l = 4; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:55:9 + --> $DIR/min_ident_chars.rs:54:9 | LL | let o = 6; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:59:10 + --> $DIR/min_ident_chars.rs:58:10 | LL | let (h, o, w) = (1, 2, 3); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:59:13 + --> $DIR/min_ident_chars.rs:58:13 | LL | let (h, o, w) = (1, 2, 3); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:60:10 + --> $DIR/min_ident_chars.rs:59:10 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:60:14 + --> $DIR/min_ident_chars.rs:59:14 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:60:17 + --> $DIR/min_ident_chars.rs:59:17 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:62:16 + --> $DIR/min_ident_chars.rs:61:16 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:62:19 + --> $DIR/min_ident_chars.rs:61:19 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:62:29 + --> $DIR/min_ident_chars.rs:61:29 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:66:9 + --> $DIR/min_ident_chars.rs:65:9 | LL | let o = 1; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:67:9 + --> $DIR/min_ident_chars.rs:66:9 | LL | let o = O { o }; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:81:4 + --> $DIR/min_ident_chars.rs:80:4 | LL | fn b() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:82:21 + --> $DIR/min_ident_chars.rs:81:21 | LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:82:29 + --> $DIR/min_ident_chars.rs:81:29 | LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 24e52afd691..1215a02286c 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -1,9 +1,7 @@ #![warn(clippy::all)] #![allow(clippy::manual_clamp)] -use std::cmp::max as my_max; -use std::cmp::min as my_min; -use std::cmp::{max, min}; +use std::cmp::{max as my_max, max, min as my_min, min}; const LARGE: usize = 3; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index 069d9068657..402b094f4f7 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:24:5 + --> $DIR/min_max.rs:22:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,73 +7,73 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:25:5 + --> $DIR/min_max.rs:23:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:26:5 + --> $DIR/min_max.rs:24:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:27:5 + --> $DIR/min_max.rs:25:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:27:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:39:5 + --> $DIR/min_max.rs:37:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:40:5 + --> $DIR/min_max.rs:38:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:45:5 + --> $DIR/min_max.rs:43:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:46:5 + --> $DIR/min_max.rs:44:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:47:5 + --> $DIR/min_max.rs:45:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:53:5 + --> $DIR/min_max.rs:51:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:56:5 + --> $DIR/min_max.rs:54:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:57:5 + --> $DIR/min_max.rs:55:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index cff1706a842..83ebf09c8a3 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -96,10 +96,8 @@ mod internal_impl { } /// dox pub mod public_interface { - pub use crate::internal_impl::documented as foo; pub use crate::internal_impl::globbed::*; - pub use crate::internal_impl::undocumented1 as bar; - pub use crate::internal_impl::{documented, undocumented2}; + pub use crate::internal_impl::{documented as foo, documented, undocumented1 as bar, undocumented2}; } fn main() {} diff --git a/tests/ui/missing_spin_loop.stderr b/tests/ui/missing_spin_loop.stderr index 485da00dc64..5795c2c2190 100644 --- a/tests/ui/missing_spin_loop.stderr +++ b/tests/ui/missing_spin_loop.stderr @@ -2,7 +2,7 @@ error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:11:37 | LL | while b.load(Ordering::Acquire) {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` | = note: `-D clippy::missing-spin-loop` implied by `-D warnings` @@ -10,31 +10,31 @@ error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:13:37 | LL | while !b.load(Ordering::SeqCst) {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:15:46 | LL | while b.load(Ordering::Acquire) == false {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:17:49 | LL | while { true == b.load(Ordering::Acquire) } {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:19:93 | LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop.rs:21:94 | LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {} - | ^^ help: try this: `{ std::hint::spin_loop() }` + | ^^ help: try: `{ std::hint::spin_loop() }` error: aborting due to 6 previous errors diff --git a/tests/ui/missing_spin_loop_no_std.stderr b/tests/ui/missing_spin_loop_no_std.stderr index 2b3b6873c3c..3322a7aae5f 100644 --- a/tests/ui/missing_spin_loop_no_std.stderr +++ b/tests/ui/missing_spin_loop_no_std.stderr @@ -2,7 +2,7 @@ error: busy-waiting loop should at least have a spin loop hint --> $DIR/missing_spin_loop_no_std.rs:13:37 | LL | while b.load(Ordering::Acquire) {} - | ^^ help: try this: `{ core::hint::spin_loop() }` + | ^^ help: try: `{ core::hint::spin_loop() }` | = note: `-D clippy::missing-spin-loop` implied by `-D warnings` diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 0c275504d36..3ca20c07d9b 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -1,6 +1,11 @@ //@run-rustfix #![feature(never_type)] -#![allow(unused_mut, unused_tuple_struct_fields, clippy::redundant_allocation)] +#![allow( + unused_mut, + unused_tuple_struct_fields, + clippy::redundant_allocation, + clippy::needless_pass_by_ref_mut +)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index d1c9267732f..dc4e0118ec7 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -1,6 +1,11 @@ //@run-rustfix #![feature(never_type)] -#![allow(unused_mut, unused_tuple_struct_fields, clippy::redundant_allocation)] +#![allow( + unused_mut, + unused_tuple_struct_fields, + clippy::redundant_allocation, + clippy::needless_pass_by_ref_mut +)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/tests/ui/must_use_candidates.stderr b/tests/ui/must_use_candidates.stderr index 0fa3849d03b..5fb302ccbf1 100644 --- a/tests/ui/must_use_candidates.stderr +++ b/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:12:1 + --> $DIR/must_use_candidates.rs:17:1 | LL | pub fn pure(i: u8) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` @@ -7,25 +7,25 @@ LL | pub fn pure(i: u8) -> u8 { = note: `-D clippy::must-use-candidate` implied by `-D warnings` error: this method could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:17:5 + --> $DIR/must_use_candidates.rs:22:5 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:48:1 + --> $DIR/must_use_candidates.rs:53:1 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:60:1 + --> $DIR/must_use_candidates.rs:65:1 | LL | pub fn rcd(_x: Rc) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc) -> bool` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:68:1 + --> $DIR/must_use_candidates.rs:73:1 | LL | pub fn arcd(_x: Arc) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc) -> bool` diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index 7de15330594..8c0c23b6570 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::needless_lifetimes)] +#![allow(unused, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut)] #![warn(clippy::mut_from_ref)] struct Foo; diff --git a/tests/ui/mut_key.rs b/tests/ui/mut_key.rs index 1c0ba664580..15d68c08984 100644 --- a/tests/ui/mut_key.rs +++ b/tests/ui/mut_key.rs @@ -2,7 +2,8 @@ use std::cell::Cell; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; use std::sync::Arc; struct Key(AtomicUsize); diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index 25dd029b16e..02a0da86a4b 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -1,5 +1,5 @@ error: mutable key type - --> $DIR/mut_key.rs:30:32 + --> $DIR/mut_key.rs:31:32 | LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,100 +7,108 @@ LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> Hash = note: `-D clippy::mutable-key-type` implied by `-D warnings` error: mutable key type - --> $DIR/mut_key.rs:30:72 + --> $DIR/mut_key.rs:31:72 | LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { | ^^^^^^^^^^^^ +error: this argument is a mutable reference, but not used mutably + --> $DIR/mut_key.rs:31:32 + | +LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: mutable key type - --> $DIR/mut_key.rs:31:5 + --> $DIR/mut_key.rs:32:5 | LL | let _other: HashMap = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:58:22 + --> $DIR/mut_key.rs:59:22 | LL | fn tuples_bad(_m: &mut HashMap<(Key, U), bool>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:70:5 + --> $DIR/mut_key.rs:71:5 | LL | let _map = HashMap::, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:71:5 + --> $DIR/mut_key.rs:72:5 | LL | let _map = HashMap::<&mut Cell, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:72:5 + --> $DIR/mut_key.rs:73:5 | LL | let _map = HashMap::<&mut usize, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:74:5 + --> $DIR/mut_key.rs:75:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:75:5 + --> $DIR/mut_key.rs:76:5 | LL | let _map = HashMap::, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:76:5 + --> $DIR/mut_key.rs:77:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:77:5 + --> $DIR/mut_key.rs:78:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:78:5 + --> $DIR/mut_key.rs:79:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:79:5 + --> $DIR/mut_key.rs:80:5 | LL | let _map = HashMap::>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:80:5 + --> $DIR/mut_key.rs:81:5 | LL | let _map = HashMap::, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:82:5 + --> $DIR/mut_key.rs:83:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:83:5 + --> $DIR/mut_key.rs:84:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:84:5 + --> $DIR/mut_key.rs:85:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index b7213428367..fe7d53e8e99 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,7 +1,12 @@ //@aux-build:proc_macros.rs:proc-macro #![warn(clippy::mut_mut)] #![allow(unused)] -#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unnecessary_operation, + clippy::needless_pass_by_ref_mut +)] extern crate proc_macros; use proc_macros::{external, inline_macros}; diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 93b857eb207..58a1c4e683c 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:9:11 + --> $DIR/mut_mut.rs:14:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:26:17 + --> $DIR/mut_mut.rs:31:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:41:25 + --> $DIR/mut_mut.rs:46:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -21,37 +21,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> $DIR/mut_mut.rs:28:21 + --> $DIR/mut_mut.rs:33:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:32:32 + --> $DIR/mut_mut.rs:37:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:32:16 + --> $DIR/mut_mut.rs:37:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:37:37 + --> $DIR/mut_mut.rs:42:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:37:16 + --> $DIR/mut_mut.rs:42:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:37:21 + --> $DIR/mut_mut.rs:42:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index 062d30b262c..23c812475c2 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,3 +1,17 @@ +error: this argument is a mutable reference, but not used mutably + --> $DIR/mut_reference.rs:4:33 + | +LL | fn takes_a_mutable_reference(a: &mut i32) {} + | ^^^^^^^^ help: consider changing to: `&i32` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/mut_reference.rs:11:44 + | +LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} + | ^^^^^^^^ help: consider changing to: `&i32` + error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | @@ -18,5 +32,5 @@ error: the method `takes_an_immutable_reference` doesn't need a mutable referenc LL | my_struct.takes_an_immutable_reference(&mut 42); | ^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 80cdb4e472d..1dfbee150d7 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -492,3 +492,15 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +mod issue_10535 { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 99f735127eb..3c0d73f5f02 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -492,3 +492,15 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +mod issue_10535 { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } +} diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr index db3b52b8850..2d9b8f15902 100644 --- a/tests/ui/needless_borrow_pat.stderr +++ b/tests/ui/needless_borrow_pat.stderr @@ -2,7 +2,7 @@ error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:59:14 | LL | Some(ref x) => x, - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` | = note: `-D clippy::needless-borrow` implied by `-D warnings` @@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference LL | Some(ref x) => *x, | ^^^^^ | -help: try this +help: try | LL | Some(x) => x, | ~ ~ @@ -23,7 +23,7 @@ error: this pattern creates a reference to a reference LL | Some(ref x) => { | ^^^^^ | -help: try this +help: try | LL ~ Some(x) => { LL | f1(x); @@ -34,13 +34,13 @@ error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:81:14 | LL | Some(ref x) => m1!(x), - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:86:15 | LL | let _ = |&ref x: &&String| { - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:91:10 @@ -48,7 +48,7 @@ error: this pattern creates a reference to a reference LL | let (ref y,) = (&x,); | ^^^^^ | -help: try this +help: try | LL ~ let (y,) = (&x,); LL ~ let _: &String = y; @@ -58,7 +58,7 @@ error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:101:14 | LL | Some(ref x) => x.0, - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:111:14 @@ -66,7 +66,7 @@ error: this pattern creates a reference to a reference LL | E::A(ref x) | E::B(ref x) => *x, | ^^^^^ ^^^^^ | -help: try this +help: try | LL | E::A(x) | E::B(x) => x, | ~ ~ ~ @@ -75,7 +75,7 @@ error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:117:21 | LL | if let Some(ref x) = Some(&String::new()); - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:125:12 @@ -83,7 +83,7 @@ error: this pattern creates a reference to a reference LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ | -help: try this +help: try | LL ~ fn f2<'a>(&x: &&'a String) -> &'a String { LL | let _: &String = x; @@ -94,7 +94,7 @@ error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:132:11 | LL | fn f(&ref x: &&String) { - | ^^^^^ help: try this: `x` + | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference --> $DIR/needless_borrow_pat.rs:140:11 @@ -102,7 +102,7 @@ error: this pattern creates a reference to a reference LL | fn f(&ref x: &&String) { | ^^^^^ | -help: try this +help: try | LL ~ fn f(&x: &&String) { LL ~ let _: &String = x; diff --git a/tests/ui/needless_else.stderr b/tests/ui/needless_else.stderr index ea693085164..49cd78501ea 100644 --- a/tests/ui/needless_else.stderr +++ b/tests/ui/needless_else.stderr @@ -1,4 +1,4 @@ -error: this else branch is empty +error: this `else` branch is empty --> $DIR/needless_else.rs:24:7 | LL | } else { diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_if.fixed index 5e6e140c2db..6001c9e9301 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_if.fixed @@ -16,8 +16,7 @@ #![warn(clippy::needless_if)] extern crate proc_macros; -use proc_macros::external; -use proc_macros::with_span; +use proc_macros::{external, with_span}; fn maybe_side_effect() -> bool { true diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_if.rs index eb28ce73be8..c6be4766dd8 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_if.rs @@ -16,8 +16,7 @@ #![warn(clippy::needless_if)] extern crate proc_macros; -use proc_macros::external; -use proc_macros::with_span; +use proc_macros::{external, with_span}; fn maybe_side_effect() -> bool { true diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_if.stderr index 5cb42c36921..14de400953b 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_if.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> $DIR/needless_if.rs:28:5 + --> $DIR/needless_if.rs:27:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -7,13 +7,13 @@ LL | if (true) {} = note: `-D clippy::needless-if` implied by `-D warnings` error: this `if` branch is empty - --> $DIR/needless_if.rs:30:5 + --> $DIR/needless_if.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> $DIR/needless_if.rs:35:5 + --> $DIR/needless_if.rs:34:5 | LL | / if { LL | | return; @@ -28,7 +28,7 @@ LL + }); | error: this `if` branch is empty - --> $DIR/needless_if.rs:47:5 + --> $DIR/needless_if.rs:46:5 | LL | / if { LL | | if let true = true && true { true } else { false } @@ -44,19 +44,19 @@ LL + } && true); | error: this `if` branch is empty - --> $DIR/needless_if.rs:85:5 + --> $DIR/needless_if.rs:84:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> $DIR/needless_if.rs:87:5 + --> $DIR/needless_if.rs:86:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> $DIR/needless_if.rs:91:5 + --> $DIR/needless_if.rs:90:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr index 20d28a968c9..4c0d502a203 100644 --- a/tests/ui/needless_option_as_deref.stderr +++ b/tests/ui/needless_option_as_deref.stderr @@ -2,7 +2,7 @@ error: derefed type is same as origin --> $DIR/needless_option_as_deref.rs:9:29 | LL | let _: Option<&usize> = Some(&1).as_deref(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` + | ^^^^^^^^^^^^^^^^^^^ help: try: `Some(&1)` | = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` @@ -10,13 +10,13 @@ error: derefed type is same as origin --> $DIR/needless_option_as_deref.rs:10:33 | LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&mut 1)` error: derefed type is same as origin --> $DIR/needless_option_as_deref.rs:14:13 | LL | let _ = x.as_deref_mut(); - | ^^^^^^^^^^^^^^^^ help: try this: `x` + | ^^^^^^^^^^^^^^^^ help: try: `x` error: aborting due to 3 previous errors diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs new file mode 100644 index 00000000000..5e7280995c6 --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -0,0 +1,105 @@ +#![allow(unused)] + +use std::ptr::NonNull; + +// Should only warn for `s`. +fn foo(s: &mut Vec, b: &u32, x: &mut u32) { + *x += *b + s.len() as u32; +} + +// Should not warn. +fn foo2(s: &mut Vec) { + s.push(8); +} + +// Should not warn because we return it. +fn foo3(s: &mut Vec) -> &mut Vec { + s +} + +// Should not warn because `s` is used as mutable. +fn foo4(s: &mut Vec) { + Vec::push(s, 4); +} + +// Should not warn. +fn foo5(s: &mut Vec) { + foo2(s); +} + +// Should warn. +fn foo6(s: &mut Vec) { + non_mut_ref(s); +} + +fn non_mut_ref(_: &Vec) {} + +struct Bar; + +impl Bar { + // Should not warn on `&mut self`. + fn bar(&mut self) {} + + // Should warn about `vec` + fn mushroom(&self, vec: &mut Vec) -> usize { + vec.len() + } + + // Should warn about `vec` (and not `self`). + fn badger(&mut self, vec: &mut Vec) -> usize { + vec.len() + } +} + +trait Babar { + // Should not warn here since it's a trait method. + fn method(arg: &mut u32); +} + +impl Babar for Bar { + // Should not warn here since it's a trait method. + fn method(a: &mut u32) {} +} + +// Should not warn (checking variable aliasing). +fn alias_check(s: &mut Vec) { + let mut alias = s; + let mut alias2 = alias; + let mut alias3 = alias2; + alias3.push(0); +} + +// Should not warn (checking variable aliasing). +fn alias_check2(mut s: &mut Vec) { + let mut alias = &mut s; + alias.push(0); +} + +struct Mut { + ptr: NonNull, +} + +impl Mut { + // Should not warn because `NonNull::from` also accepts `&mut`. + fn new(ptr: &mut T) -> Self { + Mut { + ptr: NonNull::from(ptr), + } + } +} + +// Should not warn. +fn unused(_: &mut u32, _b: &mut u8) {} + +fn main() { + let mut u = 0; + let mut v = vec![0]; + foo(&mut v, &0, &mut u); + foo2(&mut v); + foo3(&mut v); + foo4(&mut v); + foo5(&mut v); + alias_check(&mut v); + alias_check2(&mut v); + println!("{u}"); +} diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr new file mode 100644 index 00000000000..5e9d80bb6c4 --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -0,0 +1,28 @@ +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:6:11 + | +LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:31:12 + | +LL | fn foo6(s: &mut Vec) { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:44:29 + | +LL | fn mushroom(&self, vec: &mut Vec) -> usize { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:49:31 + | +LL | fn badger(&mut self, vec: &mut Vec) -> usize { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index f607d8e1ab5..0005f758104 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -2,7 +2,7 @@ error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:14:13 | LL | let _ = str.splitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + | ^^^^^^^^^^^^^^^^^^ help: try: `str.split('=')` | = note: `-D clippy::needless-splitn` implied by `-D warnings` @@ -10,73 +10,73 @@ error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:15:13 | LL | let _ = str.splitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + | ^^^^^^^^^^^^^^^^^^ help: try: `str.split('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:18:18 | LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + | ^^^^^^^^^^^^^^^^^^ help: try: `str.split('=')` error: unnecessary use of `rsplitn` --> $DIR/needless_splitn.rs:21:13 | LL | let _ = str.rsplitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` + | ^^^^^^^^^^^^^^^^^^^ help: try: `str.rsplit('=')` error: unnecessary use of `rsplitn` --> $DIR/needless_splitn.rs:22:13 | LL | let _ = str.rsplitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` + | ^^^^^^^^^^^^^^^^^^^ help: try: `str.rsplit('=')` error: unnecessary use of `rsplitn` --> $DIR/needless_splitn.rs:25:18 | LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` + | ^^^^^^^^^^^^^^^^^^^ help: try: `str.rsplit('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:27:13 | LL | let _ = str.splitn(5, '=').next(); - | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + | ^^^^^^^^^^^^^^^^^^ help: try: `str.split('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:28:13 | LL | let _ = str.splitn(5, '=').nth(3); - | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + | ^^^^^^^^^^^^^^^^^^ help: try: `str.split('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:34:13 | LL | let _ = s.splitn(2, '=').next()?; - | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + | ^^^^^^^^^^^^^^^^ help: try: `s.split('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:35:13 | LL | let _ = s.splitn(2, '=').nth(0)?; - | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + | ^^^^^^^^^^^^^^^^ help: try: `s.split('=')` error: unnecessary use of `rsplitn` --> $DIR/needless_splitn.rs:36:13 | LL | let _ = s.rsplitn(2, '=').next()?; - | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + | ^^^^^^^^^^^^^^^^^ help: try: `s.rsplit('=')` error: unnecessary use of `rsplitn` --> $DIR/needless_splitn.rs:37:13 | LL | let _ = s.rsplitn(2, '=').nth(0)?; - | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + | ^^^^^^^^^^^^^^^^^ help: try: `s.rsplit('=')` error: unnecessary use of `splitn` --> $DIR/needless_splitn.rs:45:13 | LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split('=')` error: aborting due to 13 previous errors diff --git a/tests/ui/numbered_fields.stderr b/tests/ui/numbered_fields.stderr index 60c0d789806..26f7ad9048b 100644 --- a/tests/ui/numbered_fields.stderr +++ b/tests/ui/numbered_fields.stderr @@ -7,7 +7,7 @@ LL | | 0: 1u32, LL | | 1: 42, LL | | 2: 23u8, LL | | }; - | |_____^ help: try this instead: `TupleStruct(1u32, 42, 23u8)` + | |_____^ help: try: `TupleStruct(1u32, 42, 23u8)` | = note: `-D clippy::init-numbered-fields` implied by `-D warnings` @@ -20,7 +20,7 @@ LL | | 0: 1u32, LL | | 2: 2u8, LL | | 1: 3u32, LL | | }; - | |_____^ help: try this instead: `TupleStruct(1u32, 3u32, 2u8)` + | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)` error: aborting due to 2 previous errors diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 0305387b9f8..5be5f10b017 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -4,7 +4,7 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` @@ -14,7 +14,7 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 @@ -22,7 +22,7 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` + | help: try: `if let Some(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 @@ -30,7 +30,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` + | help: try: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 @@ -38,7 +38,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` + | help: try: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 @@ -46,7 +46,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 @@ -54,7 +54,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 @@ -62,7 +62,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 @@ -70,7 +70,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 @@ -78,7 +78,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + | help: try: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 @@ -86,7 +86,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + | help: try: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 @@ -94,7 +94,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + | help: try: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 @@ -102,7 +102,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + | help: try: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 @@ -110,7 +110,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` + | help: try: `if let Some(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 @@ -118,7 +118,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 @@ -126,7 +126,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 @@ -134,7 +134,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Some(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 @@ -142,7 +142,7 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(a) = option() { do_nothing(a) }` + | help: try: `if let Some(a) = option() { do_nothing(a) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:84:5 @@ -150,7 +150,7 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns LL | option().map(|value| println!("{:?}", value)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Some(value) = option() { println!("{:?}", value) }` + | help: try: `if let Some(value) = option() { println!("{:?}", value) }` error: aborting due to 19 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 703debb7a26..6deff0f3240 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -9,8 +9,7 @@ clippy::useless_vec )] -use std::collections::BTreeMap; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::time::Duration; /// Checks implementation of the `OR_FUN_CALL` lint. diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index bb86fe0d45f..b05b33e6ee2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -9,8 +9,7 @@ clippy::useless_vec )] -use std::collections::BTreeMap; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::time::Duration; /// Checks implementation of the `OR_FUN_CALL` lint. diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0b5c686bec0..7342b0c2914 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,172 +1,172 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:54:22 + --> $DIR/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); - | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` + | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` | = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:57:14 + --> $DIR/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:60:21 + --> $DIR/or_fun_call.rs:59:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:63:14 + --> $DIR/or_fun_call.rs:62:14 | LL | with_err.unwrap_or(make()); - | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` + | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:66:19 + --> $DIR/or_fun_call.rs:65:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:69:24 + --> $DIR/or_fun_call.rs:68:24 | LL | with_default_trait.unwrap_or(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:72:23 + --> $DIR/or_fun_call.rs:71:23 | LL | with_default_type.unwrap_or(u64::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:75:18 + --> $DIR/or_fun_call.rs:74:18 | LL | self_default.unwrap_or(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(::default)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:78:18 + --> $DIR/or_fun_call.rs:77:18 | LL | real_default.unwrap_or(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:81:14 + --> $DIR/or_fun_call.rs:80:14 | LL | with_vec.unwrap_or(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:84:21 + --> $DIR/or_fun_call.rs:83:21 | LL | without_default.unwrap_or(Foo::new()); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:87:19 + --> $DIR/or_fun_call.rs:86:19 | LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:90:23 + --> $DIR/or_fun_call.rs:89:23 | LL | map_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` + | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:93:21 + --> $DIR/or_fun_call.rs:92:21 | LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:96:25 + --> $DIR/or_fun_call.rs:95:25 | LL | btree_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` + | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:99:21 + --> $DIR/or_fun_call.rs:98:21 | LL | let _ = stringy.unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:107:21 + --> $DIR/or_fun_call.rs:106:21 | LL | let _ = Some(1).unwrap_or(map[&1]); - | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:109:21 + --> $DIR/or_fun_call.rs:108:21 | LL | let _ = Some(1).unwrap_or(map[&1]); - | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:133:35 + --> $DIR/or_fun_call.rs:132:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:172:14 + --> $DIR/or_fun_call.rs:171:14 | LL | None.unwrap_or(ptr_to_ref(s)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:178:14 + --> $DIR/or_fun_call.rs:177:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:180:14 + --> $DIR/or_fun_call.rs:179:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:194:14 + --> $DIR/or_fun_call.rs:193:14 | LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:207:14 + --> $DIR/or_fun_call.rs:206:14 | LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:219:14 + --> $DIR/or_fun_call.rs:218:14 | LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:230:10 + --> $DIR/or_fun_call.rs:229:10 | LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `map_or` followed by a function call + --> $DIR/or_fun_call.rs:254:25 + | +LL | let _ = Some(4).map_or(g(), |v| v); + | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: use of `map_or` followed by a function call --> $DIR/or_fun_call.rs:255:25 | -LL | let _ = Some(4).map_or(g(), |v| v); - | ^^^^^^^^^^^^^^^^^^ help: try this: `map_or_else(g, |v| v)` - -error: use of `map_or` followed by a function call - --> $DIR/or_fun_call.rs:256:25 - | LL | let _ = Some(4).map_or(g(), f); - | ^^^^^^^^^^^^^^ help: try this: `map_or_else(g, f)` + | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: aborting due to 28 previous errors diff --git a/tests/ui/or_then_unwrap.stderr b/tests/ui/or_then_unwrap.stderr index da88154c59f..2a1a52407dc 100644 --- a/tests/ui/or_then_unwrap.stderr +++ b/tests/ui/or_then_unwrap.stderr @@ -2,7 +2,7 @@ error: found `.or(Some(…)).unwrap()` --> $DIR/or_then_unwrap.rs:24:20 | LL | let _ = option.or(Some("fallback")).unwrap(); // should trigger lint - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` | = note: `-D clippy::or-then-unwrap` implied by `-D warnings` @@ -10,13 +10,13 @@ error: found `.or(Ok(…)).unwrap()` --> $DIR/or_then_unwrap.rs:27:20 | LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` error: found `.or(Some(…)).unwrap()` --> $DIR/or_then_unwrap.rs:31:31 | LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` error: aborting due to 3 previous errors diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index 97787bc84e2..b758fc23812 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` +error: used `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -7,7 +7,7 @@ LL | | panic!("error"); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:8:9 | @@ -15,55 +15,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:11:5 - | -LL | / fn result_with_unimplemented() -> Result // should emit lint -LL | | { -LL | | unimplemented!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:13:9 - | -LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^ - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:16:5 - | -LL | / fn result_with_unreachable() -> Result // should emit lint -LL | | { -LL | | unreachable!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:18:9 - | -LL | unreachable!(); - | ^^^^^^^^^^^^^^ - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:21:5 - | -LL | / fn result_with_todo() -> Result // should emit lint -LL | | { -LL | | todo!("Finish this"); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:23:9 - | -LL | todo!("Finish this"); - | ^^^^^^^^^^^^^^^^^^^^ - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` +error: used `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:52:1 | LL | / fn function_result_with_panic() -> Result // should emit lint @@ -72,28 +24,12 @@ LL | | panic!("error"); LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:54:5 | LL | panic!("error"); | ^^^^^^^^^^^^^^^ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:67:1 - | -LL | / fn main() -> Result<(), String> { -LL | | todo!("finish main method"); -LL | | Ok(()) -LL | | } - | |_^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:68:5 - | -LL | todo!("finish main method"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr index eb0aacbb6a4..0dd213a7eed 100644 --- a/tests/ui/panic_in_result_fn_assertions.stderr +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` +error: used `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:7:5 | LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | Ok(true) LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:9:9 | @@ -16,7 +16,7 @@ LL | assert!(x == 5, "wrong argument"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` +error: used `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:13:5 | LL | / fn result_with_assert_eq(x: i32) -> Result // should emit lint @@ -26,14 +26,14 @@ LL | | Ok(true) LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:15:9 | LL | assert_eq!(x, 5); | ^^^^^^^^^^^^^^^^ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` +error: used `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:19:5 | LL | / fn result_with_assert_ne(x: i32) -> Result // should emit lint @@ -43,7 +43,7 @@ LL | | Ok(true) LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:21:9 | diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 6404dacdafa..71c8d188f16 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -5,7 +5,7 @@ LL | print!("Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::print-literal` implied by `-D warnings` -help: try this +help: try | LL - print!("Hello {}", "world"); LL + print!("Hello world"); @@ -17,7 +17,7 @@ error: literal with an empty format string LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("Hello {} {}", world, "world"); LL + println!("Hello {} world", world); @@ -29,7 +29,7 @@ error: literal with an empty format string LL | println!("Hello {}", "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("Hello {}", "world"); LL + println!("Hello world"); @@ -41,7 +41,7 @@ error: literal with an empty format string LL | println!("{} {:.4}", "a literal", 5); | ^^^^^^^^^^^ | -help: try this +help: try | LL - println!("{} {:.4}", "a literal", 5); LL + println!("a literal {:.4}", 5); @@ -53,7 +53,7 @@ error: literal with an empty format string LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{0} {1}", "hello", "world"); LL + println!("hello {1}", "world"); @@ -65,7 +65,7 @@ error: literal with an empty format string LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{0} {1}", "hello", "world"); LL + println!("{0} world", "hello"); @@ -77,7 +77,7 @@ error: literal with an empty format string LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{1} {0}", "hello", "world"); LL + println!("world {0}", "hello"); @@ -89,7 +89,7 @@ error: literal with an empty format string LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{1} {0}", "hello", "world"); LL + println!("{1} hello", "world"); @@ -101,7 +101,7 @@ error: literal with an empty format string LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{foo} {bar}", foo = "hello", bar = "world"); LL + println!("hello {bar}", bar = "world"); @@ -113,7 +113,7 @@ error: literal with an empty format string LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{foo} {bar}", foo = "hello", bar = "world"); LL + println!("{foo} world", foo = "hello"); @@ -125,7 +125,7 @@ error: literal with an empty format string LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); LL + println!("world {foo}", foo = "hello"); @@ -137,7 +137,7 @@ error: literal with an empty format string LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); LL + println!("{bar} hello", bar = "world"); diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 709f74ee6aa..13e993d247b 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,8 @@ unused, clippy::many_single_char_names, clippy::needless_lifetimes, - clippy::redundant_clone + clippy::redundant_clone, + clippy::needless_pass_by_ref_mut )] #![warn(clippy::ptr_arg)] diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d663b070b9c..0e9dd760f45 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,5 +1,5 @@ error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:13:14 + --> $DIR/ptr_arg.rs:14:14 | LL | fn do_vec(x: &Vec) { | ^^^^^^^^^ help: change this to: `&[i64]` @@ -7,43 +7,43 @@ LL | fn do_vec(x: &Vec) { = note: `-D clippy::ptr-arg` implied by `-D warnings` error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:17:18 + --> $DIR/ptr_arg.rs:18:18 | LL | fn do_vec_mut(x: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [i64]` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:21:14 + --> $DIR/ptr_arg.rs:22:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:25:18 + --> $DIR/ptr_arg.rs:26:18 | LL | fn do_str_mut(x: &mut String) { | ^^^^^^^^^^^ help: change this to: `&mut str` error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:29:15 + --> $DIR/ptr_arg.rs:30:15 | LL | fn do_path(x: &PathBuf) { | ^^^^^^^^ help: change this to: `&Path` error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:33:19 + --> $DIR/ptr_arg.rs:34:19 | LL | fn do_path_mut(x: &mut PathBuf) { | ^^^^^^^^^^^^ help: change this to: `&mut Path` error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:41:18 + --> $DIR/ptr_arg.rs:42:18 | LL | fn do_vec(x: &Vec); | ^^^^^^^^^ help: change this to: `&[i64]` error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:54:14 + --> $DIR/ptr_arg.rs:55:14 | LL | fn cloned(x: &Vec) -> Vec { | ^^^^^^^^ @@ -60,7 +60,7 @@ LL ~ x.to_owned() | error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:63:18 + --> $DIR/ptr_arg.rs:64:18 | LL | fn str_cloned(x: &String) -> String { | ^^^^^^^ @@ -76,7 +76,7 @@ LL ~ x.to_owned() | error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:71:19 + --> $DIR/ptr_arg.rs:72:19 | LL | fn path_cloned(x: &PathBuf) -> PathBuf { | ^^^^^^^^ @@ -92,7 +92,7 @@ LL ~ x.to_path_buf() | error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:79:44 + --> $DIR/ptr_arg.rs:80:44 | LL | fn false_positive_capacity(x: &Vec, y: &String) { | ^^^^^^^ @@ -106,19 +106,19 @@ LL ~ let c = y; | error: using a reference to `Cow` is not recommended - --> $DIR/ptr_arg.rs:93:25 + --> $DIR/ptr_arg.rs:94:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:122:66 + --> $DIR/ptr_arg.rs:123:66 | LL | fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec, _s: &String) {} | ^^^^^^^ help: change this to: `&str` error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:151:21 + --> $DIR/ptr_arg.rs:152:21 | LL | fn foo_vec(vec: &Vec) { | ^^^^^^^^ @@ -131,7 +131,7 @@ LL ~ let _ = vec.to_owned().clone(); | error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:156:23 + --> $DIR/ptr_arg.rs:157:23 | LL | fn foo_path(path: &PathBuf) { | ^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ let _ = path.to_path_buf().clone(); | error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:161:21 + --> $DIR/ptr_arg.rs:162:21 | LL | fn foo_str(str: &PathBuf) { | ^^^^^^^^ @@ -157,43 +157,43 @@ LL ~ let _ = str.to_path_buf().clone(); | error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:167:29 + --> $DIR/ptr_arg.rs:168:29 | LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:229:17 + --> $DIR/ptr_arg.rs:230:17 | LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:229:35 + --> $DIR/ptr_arg.rs:230:35 | LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { | ^^^^^^^^^^^ help: change this to: `&mut str` error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:229:51 + --> $DIR/ptr_arg.rs:230:51 | LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { | ^^^^^^^^^^^^ help: change this to: `&mut Path` error: using a reference to `Cow` is not recommended - --> $DIR/ptr_arg.rs:252:39 + --> $DIR/ptr_arg.rs:253:39 | LL | fn cow_elided_lifetime<'a>(input: &'a Cow) -> &'a str { | ^^^^^^^^^^^^ help: change this to: `&str` error: using a reference to `Cow` is not recommended - --> $DIR/ptr_arg.rs:257:36 + --> $DIR/ptr_arg.rs:258:36 | LL | fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` error: using a reference to `Cow` is not recommended - --> $DIR/ptr_arg.rs:260:40 + --> $DIR/ptr_arg.rs:261:40 | LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` diff --git a/tests/ui/read_line_without_trim.fixed b/tests/ui/read_line_without_trim.fixed new file mode 100644 index 00000000000..cb6aab84e49 --- /dev/null +++ b/tests/ui/read_line_without_trim.fixed @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::read_line_without_trim)] + +fn main() { + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + input.pop(); + let _x: i32 = input.parse().unwrap(); // don't trigger here, newline character is popped + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x: i32 = input.trim_end().parse().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.trim_end().parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.trim_end().parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.trim_end().parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.trim_end().parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + // this is actually ok, so don't lint here + let _x = input.parse::().unwrap(); +} diff --git a/tests/ui/read_line_without_trim.rs b/tests/ui/read_line_without_trim.rs new file mode 100644 index 00000000000..bdc409a7010 --- /dev/null +++ b/tests/ui/read_line_without_trim.rs @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::read_line_without_trim)] + +fn main() { + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + input.pop(); + let _x: i32 = input.parse().unwrap(); // don't trigger here, newline character is popped + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x: i32 = input.parse().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let _x = input.parse::().unwrap(); + + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + // this is actually ok, so don't lint here + let _x = input.parse::().unwrap(); +} diff --git a/tests/ui/read_line_without_trim.stderr b/tests/ui/read_line_without_trim.stderr new file mode 100644 index 00000000000..f3d7b60425f --- /dev/null +++ b/tests/ui/read_line_without_trim.stderr @@ -0,0 +1,73 @@ +error: calling `.parse()` without trimming the trailing newline character + --> $DIR/read_line_without_trim.rs:14:25 + | +LL | let _x: i32 = input.parse().unwrap(); + | ----- ^^^^^^^ + | | + | help: try: `input.trim_end()` + | +note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail + --> $DIR/read_line_without_trim.rs:13:5 + | +LL | std::io::stdin().read_line(&mut input).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::read-line-without-trim` implied by `-D warnings` + +error: calling `.parse()` without trimming the trailing newline character + --> $DIR/read_line_without_trim.rs:18:20 + | +LL | let _x = input.parse::().unwrap(); + | ----- ^^^^^^^^^^^^^^ + | | + | help: try: `input.trim_end()` + | +note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail + --> $DIR/read_line_without_trim.rs:17:5 + | +LL | std::io::stdin().read_line(&mut input).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `.parse()` without trimming the trailing newline character + --> $DIR/read_line_without_trim.rs:22:20 + | +LL | let _x = input.parse::().unwrap(); + | ----- ^^^^^^^^^^^^^^ + | | + | help: try: `input.trim_end()` + | +note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail + --> $DIR/read_line_without_trim.rs:21:5 + | +LL | std::io::stdin().read_line(&mut input).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `.parse()` without trimming the trailing newline character + --> $DIR/read_line_without_trim.rs:26:20 + | +LL | let _x = input.parse::().unwrap(); + | ----- ^^^^^^^^^^^^^^ + | | + | help: try: `input.trim_end()` + | +note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail + --> $DIR/read_line_without_trim.rs:25:5 + | +LL | std::io::stdin().read_line(&mut input).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `.parse()` without trimming the trailing newline character + --> $DIR/read_line_without_trim.rs:30:20 + | +LL | let _x = input.parse::().unwrap(); + | ----- ^^^^^^^^^^^^^^^ + | | + | help: try: `input.trim_end()` + | +note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail + --> $DIR/read_line_without_trim.rs:29:5 + | +LL | std::io::stdin().read_line(&mut input).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 30807e0f8b9..c6025ef1f4d 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -1,5 +1,5 @@ #![warn(clippy::read_zero_byte_vec)] -#![allow(clippy::unused_io_amount)] +#![allow(clippy::unused_io_amount, clippy::needless_pass_by_ref_mut)] use std::fs::File; use std::io; use std::io::prelude::*; diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 574d34aed2d..9eb58a3e53f 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -8,8 +8,7 @@ pub struct SubT { } mod outer_box { - use crate::MyStruct; - use crate::SubT; + use crate::{MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -28,8 +27,7 @@ mod outer_box { } mod outer_rc { - use crate::MyStruct; - use crate::SubT; + use crate::{MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -48,8 +46,7 @@ mod outer_rc { } mod outer_arc { - use crate::MyStruct; - use crate::SubT; + use crate::{MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index e0826fefa6c..a9a1eed702b 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -1,5 +1,5 @@ error: usage of `Box>` - --> $DIR/redundant_allocation.rs:17:30 + --> $DIR/redundant_allocation.rs:16:30 | LL | pub fn box_test6(foo: Box>) {} | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | pub fn box_test6(foo: Box>) {} = note: `-D clippy::redundant-allocation` implied by `-D warnings` error: usage of `Box>` - --> $DIR/redundant_allocation.rs:19:30 + --> $DIR/redundant_allocation.rs:18:30 | LL | pub fn box_test7(foo: Box>) {} | ^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | pub fn box_test7(foo: Box>) {} = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> $DIR/redundant_allocation.rs:21:27 + --> $DIR/redundant_allocation.rs:20:27 | LL | pub fn box_test8() -> Box>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | pub fn box_test8() -> Box>> { = help: consider using just `Box>` or `Rc>` error: usage of `Box>` - --> $DIR/redundant_allocation.rs:25:30 + --> $DIR/redundant_allocation.rs:24:30 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> $DIR/redundant_allocation.rs:25:46 + --> $DIR/redundant_allocation.rs:24:46 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box>` or `Arc>` error: usage of `Rc>` - --> $DIR/redundant_allocation.rs:37:24 + --> $DIR/redundant_allocation.rs:35:24 | LL | pub fn rc_test5(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | pub fn rc_test5(a: Rc>) {} = help: consider using just `Rc` or `Box` error: usage of `Rc>` - --> $DIR/redundant_allocation.rs:39:24 + --> $DIR/redundant_allocation.rs:37:24 | LL | pub fn rc_test7(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | pub fn rc_test7(a: Rc>) {} = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:41:26 + --> $DIR/redundant_allocation.rs:39:26 | LL | pub fn rc_test8() -> Rc>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | pub fn rc_test8() -> Rc>> { = help: consider using just `Rc>` or `Box>` error: usage of `Rc>` - --> $DIR/redundant_allocation.rs:45:29 + --> $DIR/redundant_allocation.rs:43:29 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:45:44 + --> $DIR/redundant_allocation.rs:43:44 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc>` or `Arc>` error: usage of `Arc>` - --> $DIR/redundant_allocation.rs:57:25 + --> $DIR/redundant_allocation.rs:54:25 | LL | pub fn arc_test5(a: Arc>) {} | ^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | pub fn arc_test5(a: Arc>) {} = help: consider using just `Arc` or `Box` error: usage of `Arc>` - --> $DIR/redundant_allocation.rs:59:25 + --> $DIR/redundant_allocation.rs:56:25 | LL | pub fn arc_test6(a: Arc>) {} | ^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | pub fn arc_test6(a: Arc>) {} = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> $DIR/redundant_allocation.rs:61:27 + --> $DIR/redundant_allocation.rs:58:27 | LL | pub fn arc_test8() -> Arc>> { | ^^^^^^^^^^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | pub fn arc_test8() -> Arc>> { = help: consider using just `Arc>` or `Box>` error: usage of `Arc>` - --> $DIR/redundant_allocation.rs:65:30 + --> $DIR/redundant_allocation.rs:62:30 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> $DIR/redundant_allocation.rs:65:45 + --> $DIR/redundant_allocation.rs:62:45 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc>` or `Rc>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:87:27 + --> $DIR/redundant_allocation.rs:84:27 | LL | pub fn test_rc_box(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | pub fn test_rc_box(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:119:31 + --> $DIR/redundant_allocation.rs:116:31 | LL | pub fn test_rc_box_str(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^ @@ -153,7 +153,7 @@ LL | pub fn test_rc_box_str(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:120:33 + --> $DIR/redundant_allocation.rs:117:33 | LL | pub fn test_rc_box_slice(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -162,7 +162,7 @@ LL | pub fn test_rc_box_slice(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:121:32 + --> $DIR/redundant_allocation.rs:118:32 | LL | pub fn test_rc_box_path(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^ @@ -171,7 +171,7 @@ LL | pub fn test_rc_box_path(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:122:34 + --> $DIR/redundant_allocation.rs:119:34 | LL | pub fn test_rc_box_custom(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_allocation_fixable.fixed b/tests/ui/redundant_allocation_fixable.fixed index edb7715f42c..b97863daf22 100644 --- a/tests/ui/redundant_allocation_fixable.fixed +++ b/tests/ui/redundant_allocation_fixable.fixed @@ -16,9 +16,7 @@ pub enum MyEnum { } mod outer_box { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -35,9 +33,7 @@ mod outer_box { } mod outer_rc { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -54,9 +50,7 @@ mod outer_rc { } mod outer_arc { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/redundant_allocation_fixable.rs b/tests/ui/redundant_allocation_fixable.rs index c59422dd966..bffb6f8c000 100644 --- a/tests/ui/redundant_allocation_fixable.rs +++ b/tests/ui/redundant_allocation_fixable.rs @@ -16,9 +16,7 @@ pub enum MyEnum { } mod outer_box { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -35,9 +33,7 @@ mod outer_box { } mod outer_rc { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; @@ -54,9 +50,7 @@ mod outer_rc { } mod outer_arc { - use crate::MyEnum; - use crate::MyStruct; - use crate::SubT; + use crate::{MyEnum, MyStruct, SubT}; use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/redundant_allocation_fixable.stderr b/tests/ui/redundant_allocation_fixable.stderr index 8dd4a6a2687..524ca5bf467 100644 --- a/tests/ui/redundant_allocation_fixable.stderr +++ b/tests/ui/redundant_allocation_fixable.stderr @@ -1,5 +1,5 @@ error: usage of `Box<&T>` - --> $DIR/redundant_allocation_fixable.rs:26:30 + --> $DIR/redundant_allocation_fixable.rs:24:30 | LL | pub fn box_test1(foo: Box<&T>) {} | ^^^^^^^ help: try: `&T` @@ -8,7 +8,7 @@ LL | pub fn box_test1(foo: Box<&T>) {} = note: `-D clippy::redundant-allocation` implied by `-D warnings` error: usage of `Box<&MyStruct>` - --> $DIR/redundant_allocation_fixable.rs:28:27 + --> $DIR/redundant_allocation_fixable.rs:26:27 | LL | pub fn box_test2(foo: Box<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -16,7 +16,7 @@ LL | pub fn box_test2(foo: Box<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap error: usage of `Box<&MyEnum>` - --> $DIR/redundant_allocation_fixable.rs:30:27 + --> $DIR/redundant_allocation_fixable.rs:28:27 | LL | pub fn box_test3(foo: Box<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -24,7 +24,7 @@ LL | pub fn box_test3(foo: Box<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap error: usage of `Box>` - --> $DIR/redundant_allocation_fixable.rs:34:30 + --> $DIR/redundant_allocation_fixable.rs:32:30 | LL | pub fn box_test5(foo: Box>) {} | ^^^^^^^^^^^ help: try: `Box` @@ -32,7 +32,7 @@ LL | pub fn box_test5(foo: Box>) {} = note: `Box` is already on the heap, `Box>` makes an extra allocation error: usage of `Rc<&T>` - --> $DIR/redundant_allocation_fixable.rs:45:29 + --> $DIR/redundant_allocation_fixable.rs:41:29 | LL | pub fn rc_test1(foo: Rc<&T>) {} | ^^^^^^ help: try: `&T` @@ -40,7 +40,7 @@ LL | pub fn rc_test1(foo: Rc<&T>) {} = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap error: usage of `Rc<&MyStruct>` - --> $DIR/redundant_allocation_fixable.rs:47:26 + --> $DIR/redundant_allocation_fixable.rs:43:26 | LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} | ^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -48,7 +48,7 @@ LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap error: usage of `Rc<&MyEnum>` - --> $DIR/redundant_allocation_fixable.rs:49:26 + --> $DIR/redundant_allocation_fixable.rs:45:26 | LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} | ^^^^^^^^^^^ help: try: `&MyEnum` @@ -56,7 +56,7 @@ LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap error: usage of `Rc>` - --> $DIR/redundant_allocation_fixable.rs:53:24 + --> $DIR/redundant_allocation_fixable.rs:49:24 | LL | pub fn rc_test6(a: Rc>) {} | ^^^^^^^^^^^^ help: try: `Rc` @@ -64,7 +64,7 @@ LL | pub fn rc_test6(a: Rc>) {} = note: `Rc` is already on the heap, `Rc>` makes an extra allocation error: usage of `Arc<&T>` - --> $DIR/redundant_allocation_fixable.rs:64:30 + --> $DIR/redundant_allocation_fixable.rs:58:30 | LL | pub fn arc_test1(foo: Arc<&T>) {} | ^^^^^^^ help: try: `&T` @@ -72,7 +72,7 @@ LL | pub fn arc_test1(foo: Arc<&T>) {} = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap error: usage of `Arc<&MyStruct>` - --> $DIR/redundant_allocation_fixable.rs:66:27 + --> $DIR/redundant_allocation_fixable.rs:60:27 | LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -80,7 +80,7 @@ LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap error: usage of `Arc<&MyEnum>` - --> $DIR/redundant_allocation_fixable.rs:68:27 + --> $DIR/redundant_allocation_fixable.rs:62:27 | LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -88,7 +88,7 @@ LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap error: usage of `Arc>` - --> $DIR/redundant_allocation_fixable.rs:72:25 + --> $DIR/redundant_allocation_fixable.rs:66:25 | LL | pub fn arc_test7(a: Arc>) {} | ^^^^^^^^^^^^^^ help: try: `Arc` diff --git a/tests/ui/redundant_pattern_matching_drop_order.stderr b/tests/ui/redundant_pattern_matching_drop_order.stderr index e9ea3f2e688..28f33f0c95d 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.stderr +++ b/tests/ui/redundant_pattern_matching_drop_order.stderr @@ -2,7 +2,7 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_drop_order.rs:17:12 | LL | if let Ok(_) = m.lock() {} - | -------^^^^^----------- help: try this: `if m.lock().is_ok()` + | -------^^^^^----------- help: try: `if m.lock().is_ok()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -12,7 +12,7 @@ error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_drop_order.rs:18:12 | LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} - | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()` + | -------^^^^^^------------------------------------ help: try: `if Err::<(), _>(m.lock().unwrap().0).is_err()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -21,7 +21,7 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_drop_order.rs:21:16 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} - | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -30,7 +30,7 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_drop_order.rs:23:12 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { - | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -39,31 +39,31 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_drop_order.rs:26:12 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} - | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_drop_order.rs:27:12 | LL | if let Err(_) = Err::, _>(()) {} - | -------^^^^^^------------------------------------------ help: try this: `if Err::, _>(()).is_err()` + | -------^^^^^^------------------------------------------ help: try: `if Err::, _>(()).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_drop_order.rs:29:12 | LL | if let Ok(_) = Ok::<_, ()>(String::new()) {} - | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()` + | -------^^^^^----------------------------- help: try: `if Ok::<_, ()>(String::new()).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_drop_order.rs:30:12 | LL | if let Err(_) = Err::<(), _>((String::new(), ())) {} - | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()` + | -------^^^^^^------------------------------------ help: try: `if Err::<(), _>((String::new(), ())).is_err()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_drop_order.rs:33:12 | LL | if let Some(_) = Some(m.lock()) {} - | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()` + | -------^^^^^^^----------------- help: try: `if Some(m.lock()).is_some()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -72,7 +72,7 @@ error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_drop_order.rs:34:12 | LL | if let Some(_) = Some(m.lock().unwrap().0) {} - | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()` + | -------^^^^^^^---------------------------- help: try: `if Some(m.lock().unwrap().0).is_some()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -81,7 +81,7 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_drop_order.rs:37:16 | LL | if let None = None::> {} - | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | -------^^^^------------------------------------ help: try: `if None::>.is_none()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -90,7 +90,7 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_drop_order.rs:39:12 | LL | if let None = None::> { - | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | -------^^^^------------------------------------ help: try: `if None::>.is_none()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -99,25 +99,25 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_drop_order.rs:43:12 | LL | if let None = None::> {} - | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | -------^^^^------------------------------------ help: try: `if None::>.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_drop_order.rs:45:12 | LL | if let Some(_) = Some(String::new()) {} - | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()` + | -------^^^^^^^---------------------- help: try: `if Some(String::new()).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_drop_order.rs:46:12 | LL | if let Some(_) = Some((String::new(), ())) {} - | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()` + | -------^^^^^^^---------------------------- help: try: `if Some((String::new(), ())).is_some()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_drop_order.rs:49:12 | LL | if let Ready(_) = Ready(m.lock()) {} - | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()` + | -------^^^^^^^^------------------ help: try: `if Ready(m.lock()).is_ready()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -126,7 +126,7 @@ error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_drop_order.rs:50:12 | LL | if let Ready(_) = Ready(m.lock().unwrap().0) {} - | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()` + | -------^^^^^^^^----------------------------- help: try: `if Ready(m.lock().unwrap().0).is_ready()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -135,7 +135,7 @@ error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_drop_order.rs:53:16 | LL | if let Pending = Pending::> {} - | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -144,7 +144,7 @@ error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_drop_order.rs:55:12 | LL | if let Pending = Pending::> { - | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important @@ -153,19 +153,19 @@ error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_drop_order.rs:59:12 | LL | if let Pending = Pending::> {} - | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_drop_order.rs:61:12 | LL | if let Ready(_) = Ready(String::new()) {} - | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()` + | -------^^^^^^^^----------------------- help: try: `if Ready(String::new()).is_ready()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_drop_order.rs:62:12 | LL | if let Ready(_) = Ready((String::new(), ())) {} - | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()` + | -------^^^^^^^^----------------------------- help: try: `if Ready((String::new(), ())).is_ready()` error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed index 75ed143446c..02f197aa26a 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -8,10 +8,8 @@ clippy::uninlined_format_args )] -use std::net::{ - IpAddr::{self, V4, V6}, - Ipv4Addr, Ipv6Addr, -}; +use std::net::IpAddr::{self, V4, V6}; +use std::net::{Ipv4Addr, Ipv6Addr}; fn main() { let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs index 9ac77409f79..5c1e1810f55 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -8,10 +8,8 @@ clippy::uninlined_format_args )] -use std::net::{ - IpAddr::{self, V4, V6}, - Ipv4Addr, Ipv6Addr, -}; +use std::net::IpAddr::{self, V4, V6}; +use std::net::{Ipv4Addr, Ipv6Addr}; fn main() { let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr index 6d1fb296463..bec8d30884d 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,130 +1,130 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 | LL | if let V4(_) = &ipaddr {} - | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` + | -------^^^^^---------- help: try: `if ipaddr.is_ipv4()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` --> $DIR/redundant_pattern_matching_ipaddr.rs:20:12 | -LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` - -error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:22:12 - | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` --> $DIR/redundant_pattern_matching_ipaddr.rs:24:15 | -LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` - -error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:26:15 - | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:36:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:34:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:41:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:39:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` + | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:46:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:44:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:51:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:49:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` + | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:56:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:54:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { - | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:64:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:62:20 | LL | let _ = if let V4(_) = gen_ipaddr() { - | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` + | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:66:19 + --> $DIR/redundant_pattern_matching_ipaddr.rs:64:19 | LL | } else if let V6(_) = gen_ipaddr() { - | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` + | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:78:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:80:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:78:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:82:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:84:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:82:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:86:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:84:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:91:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:89:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: aborting due to 18 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 717b603c496..097ca827a42 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -2,7 +2,7 @@ error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:15:12 | LL | if let None = None::<()> {} - | -------^^^^------------- help: try this: `if None::<()>.is_none()` + | -------^^^^------------- help: try: `if None::<()>.is_none()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` @@ -10,37 +10,37 @@ error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:17:12 | LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:19:12 | LL | if let Some(_) = Some(42) { - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:25:15 | LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:27:15 | LL | while let None = Some(42) {} - | ----------^^^^----------- help: try this: `while Some(42).is_none()` + | ----------^^^^----------- help: try: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:29:15 | LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:32:15 | LL | while let Some(_) = v.pop() { - | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` + | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:40:5 @@ -49,7 +49,7 @@ LL | / match Some(42) { LL | | Some(_) => true, LL | | None => false, LL | | }; - | |_____^ help: try this: `Some(42).is_some()` + | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:45:5 @@ -58,7 +58,7 @@ LL | / match None::<()> { LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` + | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:50:13 @@ -68,55 +68,55 @@ LL | let _ = match None::<()> { LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` + | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:56:20 | LL | let _ = if let Some(_) = opt { true } else { false }; - | -------^^^^^^^------ help: try this: `if opt.is_some()` + | -------^^^^^^^------ help: try: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:62:20 | LL | let _ = if let Some(_) = gen_opt() { - | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` + | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:64:19 | LL | } else if let None = gen_opt() { - | -------^^^^------------ help: try this: `if gen_opt().is_none()` + | -------^^^^------------ help: try: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:70:12 | LL | if let Some(..) = gen_opt() {} - | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` + | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:85:12 | LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:87:12 | LL | if let None = None::<()> {} - | -------^^^^------------- help: try this: `if None::<()>.is_none()` + | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:89:15 | LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:91:15 | LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:93:5 @@ -125,7 +125,7 @@ LL | / match Some(42) { LL | | Some(_) => true, LL | | None => false, LL | | }; - | |_____^ help: try this: `Some(42).is_some()` + | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:98:5 @@ -134,19 +134,19 @@ LL | / match None::<()> { LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` + | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:106:12 | LL | if let None = *(&None::<()>) {} - | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` + | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:107:12 | LL | if let None = *&None::<()> {} - | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` + | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:113:5 @@ -155,7 +155,7 @@ LL | / match x { LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `x.is_some()` + | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:118:5 @@ -164,7 +164,7 @@ LL | / match x { LL | | None => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `x.is_none()` + | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:123:5 @@ -173,7 +173,7 @@ LL | / match x { LL | | Some(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `x.is_none()` + | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:128:5 @@ -182,19 +182,19 @@ LL | / match x { LL | | None => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `x.is_some()` + | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:143:13 | LL | let _ = matches!(x, Some(_)); - | ^^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_some()` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` --> $DIR/redundant_pattern_matching_option.rs:145:13 | LL | let _ = matches!(x, None); - | ^^^^^^^^^^^^^^^^^ help: try this: `x.is_none()` + | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` error: aborting due to 28 previous errors diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index b89fde35fcf..28d3606c4fb 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -2,7 +2,7 @@ error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:17:12 | LL | if let Pending = Pending::<()> {} - | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` @@ -10,31 +10,31 @@ error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) {} - | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:21:12 | LL | if let Ready(_) = Ready(42) { - | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:27:15 | LL | while let Ready(_) = Ready(42) {} - | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:29:15 | LL | while let Pending = Ready(42) {} - | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` + | ----------^^^^^^^------------ help: try: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:31:15 | LL | while let Pending = Pending::<()> {} - | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:37:5 @@ -43,7 +43,7 @@ LL | / match Ready(42) { LL | | Ready(_) => true, LL | | Pending => false, LL | | }; - | |_____^ help: try this: `Ready(42).is_ready()` + | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:42:5 @@ -52,7 +52,7 @@ LL | / match Pending::<()> { LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try this: `Pending::<()>.is_pending()` + | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:47:13 @@ -62,49 +62,49 @@ LL | let _ = match Pending::<()> { LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try this: `Pending::<()>.is_pending()` + | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:53:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; - | -------^^^^^^^^------- help: try this: `if poll.is_ready()` + | -------^^^^^^^^------- help: try: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:57:20 | LL | let _ = if let Ready(_) = gen_poll() { - | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` + | -------^^^^^^^^------------- help: try: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:59:19 | LL | } else if let Pending = gen_poll() { - | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` + | -------^^^^^^^------------- help: try: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:75:12 | LL | if let Ready(_) = Ready(42) {} - | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` + | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:77:12 | LL | if let Pending = Pending::<()> {} - | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` + | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:79:15 | LL | while let Ready(_) = Ready(42) {} - | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` + | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:81:15 | LL | while let Pending = Pending::<()> {} - | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` + | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:83:5 @@ -113,7 +113,7 @@ LL | / match Ready(42) { LL | | Ready(_) => true, LL | | Pending => false, LL | | }; - | |_____^ help: try this: `Ready(42).is_ready()` + | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` --> $DIR/redundant_pattern_matching_poll.rs:88:5 @@ -122,7 +122,7 @@ LL | / match Pending::<()> { LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try this: `Pending::<()>.is_pending()` + | |_____^ help: try: `Pending::<()>.is_pending()` error: aborting due to 18 previous errors diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index f6ce666bb4f..2b1ce9f5465 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -2,7 +2,7 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:16:12 | LL | if let Ok(_) = &result {} - | -------^^^^^---------- help: try this: `if result.is_ok()` + | -------^^^^^---------- help: try: `if result.is_ok()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` @@ -10,25 +10,25 @@ error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:24:15 | LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:34:5 @@ -37,7 +37,7 @@ LL | / match Ok::(42) { LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try this: `Ok::(42).is_ok()` + | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:39:5 @@ -46,7 +46,7 @@ LL | / match Ok::(42) { LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try this: `Ok::(42).is_err()` + | |_____^ help: try: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:44:5 @@ -55,7 +55,7 @@ LL | / match Err::(42) { LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` + | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:49:5 @@ -64,73 +64,73 @@ LL | / match Err::(42) { LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try this: `Err::(42).is_ok()` + | |_____^ help: try: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; - | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` + | -------^^^^^--------------------- help: try: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:62:20 | LL | let _ = if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try this: `if gen_res().is_ok()` + | -------^^^^^------------ help: try: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:64:19 | LL | } else if let Err(_) = gen_res() { - | -------^^^^^^------------ help: try this: `if gen_res().is_err()` + | -------^^^^^^------------ help: try: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:87:19 | LL | while let Some(_) = r#try!(result_opt()) {} - | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` + | ----------^^^^^^^----------------------- help: try: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:88:16 | LL | if let Some(_) = r#try!(result_opt()) {} - | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` + | -------^^^^^^^----------------------- help: try: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:94:12 | LL | if let Some(_) = m!() {} - | -------^^^^^^^------- help: try this: `if m!().is_some()` + | -------^^^^^^^------- help: try: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:95:15 | LL | while let Some(_) = m!() {} - | ----------^^^^^^^------- help: try this: `while m!().is_some()` + | ----------^^^^^^^------- help: try: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:115:12 | LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:119:15 | LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:121:5 @@ -139,7 +139,7 @@ LL | / match Ok::(42) { LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try this: `Ok::(42).is_ok()` + | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:126:5 @@ -148,7 +148,7 @@ LL | / match Err::(42) { LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` + | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:136:5 @@ -157,7 +157,7 @@ LL | / match x { LL | | Ok(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `x.is_ok()` + | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:141:5 @@ -166,7 +166,7 @@ LL | / match x { LL | | Ok(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `x.is_err()` + | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:146:5 @@ -175,7 +175,7 @@ LL | / match x { LL | | Err(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `x.is_err()` + | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:151:5 @@ -184,19 +184,19 @@ LL | / match x { LL | | Err(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try this: `x.is_ok()` + | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_ok()` --> $DIR/redundant_pattern_matching_result.rs:172:13 | LL | let _ = matches!(x, Ok(_)); - | ^^^^^^^^^^^^^^^^^^ help: try this: `x.is_ok()` + | ^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` --> $DIR/redundant_pattern_matching_result.rs:174:13 | LL | let _ = matches!(x, Err(_)); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_err()` + | ^^^^^^^^^^^^^^^^^^^ help: try: `x.is_err()` error: aborting due to 28 previous errors diff --git a/tests/ui/ref_binding_to_reference.stderr b/tests/ui/ref_binding_to_reference.stderr index eb36cd516a2..016feb103df 100644 --- a/tests/ui/ref_binding_to_reference.stderr +++ b/tests/ui/ref_binding_to_reference.stderr @@ -5,7 +5,7 @@ LL | Some(ref x) => x, | ^^^^^ | = note: `-D clippy::ref-binding-to-reference` implied by `-D warnings` -help: try this +help: try | LL | Some(x) => &x, | ~ ~~ @@ -16,7 +16,7 @@ error: this pattern creates a reference to a reference LL | Some(ref x) => { | ^^^^^ | -help: try this +help: try | LL ~ Some(x) => { LL | f1(x); @@ -30,7 +30,7 @@ error: this pattern creates a reference to a reference LL | Some(ref x) => m2!(x), | ^^^^^ | -help: try this +help: try | LL | Some(x) => m2!(&x), | ~ ~~ @@ -41,7 +41,7 @@ error: this pattern creates a reference to a reference LL | let _ = |&ref x: &&String| { | ^^^^^ | -help: try this +help: try | LL ~ let _ = |&x: &&String| { LL ~ let _: &&String = &x; @@ -53,7 +53,7 @@ error: this pattern creates a reference to a reference LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ | -help: try this +help: try | LL ~ fn f2<'a>(&x: &&'a String) -> &'a String { LL ~ let _: &&String = &x; @@ -66,7 +66,7 @@ error: this pattern creates a reference to a reference LL | fn f(&ref x: &&String) { | ^^^^^ | -help: try this +help: try | LL ~ fn f(&x: &&String) { LL ~ let _: &&String = &x; @@ -78,7 +78,7 @@ error: this pattern creates a reference to a reference LL | fn f(&ref x: &&String) { | ^^^^^ | -help: try this +help: try | LL ~ fn f(&x: &&String) { LL ~ let _: &&String = &x; diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 782febd5264..ad941fa8bcc 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -4,7 +4,7 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` @@ -14,7 +14,7 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 @@ -22,7 +22,7 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` + | help: try: `if let Ok(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 @@ -30,7 +30,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| x.do_result_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` + | help: try: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 @@ -38,7 +38,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` + | help: try: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 @@ -46,7 +46,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 @@ -54,7 +54,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 @@ -62,7 +62,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 @@ -70,7 +70,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 @@ -78,7 +78,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 @@ -86,7 +86,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 @@ -94,7 +94,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 @@ -102,7 +102,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 @@ -110,7 +110,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` + | help: try: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 @@ -118,7 +118,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 @@ -126,7 +126,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 @@ -134,7 +134,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` + | help: try: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:79:5 @@ -142,7 +142,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| println!("{:?}", value)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }` + | help: try: `if let Ok(value) = x.field { println!("{:?}", value) }` error: aborting due to 18 previous errors diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index d0e534f6356..75ec1ba8024 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -4,7 +4,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { ... }` + | help: try: `if let Ok(value) = x.field { ... }` | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` @@ -14,7 +14,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { ... }` + | help: try: `if let Ok(value) = x.field { ... }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 @@ -23,7 +23,7 @@ LL | // x.field.map(|value| { LL | || do_nothing(value); LL | || do_nothing(value) LL | || }); - | ||______^- help: try this: `if let Ok(value) = x.field { ... }` + | ||______^- help: try: `if let Ok(value) = x.field { ... }` | |______| | @@ -33,7 +33,7 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(value) = x.field { ... }` + | help: try: `if let Ok(value) = x.field { ... }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 @@ -41,7 +41,7 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns LL | "12".parse::().map(diverge); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` + | help: try: `if let Ok(a) = "12".parse::() { diverge(a) }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 @@ -49,7 +49,7 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns LL | y.map(do_nothing); | ^^^^^^^^^^^^^^^^^- | | - | help: try this: `if let Ok(_y) = y { do_nothing(_y) }` + | help: try: `if let Ok(_y) = y { do_nothing(_y) }` error: aborting due to 6 previous errors diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs index ec3ae120942..d6682cc63dc 100644 --- a/tests/ui/self_assignment.rs +++ b/tests/ui/self_assignment.rs @@ -1,5 +1,5 @@ #![warn(clippy::self_assignment)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_pass_by_ref_mut)] pub struct S<'a> { a: i32, diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 4ab7dbab59c..8e7f1d862cf 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -116,5 +116,7 @@ fn function_returning_option() -> Option { // No warning fn let_else_stmts() { - let Some(x) = function_returning_option() else { return; }; + let Some(x) = function_returning_option() else { + return; + }; } diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr index 10bfea68ff5..2ae9fa34d14 100644 --- a/tests/ui/should_impl_trait/method_list_2.stderr +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -39,6 +39,15 @@ LL | | } | = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name +error: this argument is a mutable reference, but not used mutably + --> $DIR/method_list_2.rs:38:31 + | +LL | pub fn hash(&self, state: &mut T) { + | ^^^^^^ help: consider changing to: `&T` + | + = warning: changing this function will impact semver compatibility + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: method `index` can be confused for the standard trait method `std::ops::Index::index` --> $DIR/method_list_2.rs:42:5 | @@ -149,5 +158,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 8c48b21f188..17df9f88fff 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -7,8 +7,7 @@ use std::num::ParseIntError; use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::RwLock; -use std::sync::{Mutex, MutexGuard}; +use std::sync::{Mutex, MutexGuard, RwLock}; struct State {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 75063a8c987..b56ace200a8 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:56:11 + --> $DIR/significant_drop_in_scrutinee.rs:55:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:142:11 + --> $DIR/significant_drop_in_scrutinee.rs:141:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:163:11 + --> $DIR/significant_drop_in_scrutinee.rs:162:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:211:11 + --> $DIR/significant_drop_in_scrutinee.rs:210:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:234:16 + --> $DIR/significant_drop_in_scrutinee.rs:233:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL ~ match (value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:243:22 + --> $DIR/significant_drop_in_scrutinee.rs:242:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL ~ match (true, value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:253:16 + --> $DIR/significant_drop_in_scrutinee.rs:252:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:253:54 + --> $DIR/significant_drop_in_scrutinee.rs:252:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,7 +153,7 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:264:15 + --> $DIR/significant_drop_in_scrutinee.rs:263:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:274:22 + --> $DIR/significant_drop_in_scrutinee.rs:273:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:293:11 + --> $DIR/significant_drop_in_scrutinee.rs:292:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +204,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:300:11 + --> $DIR/significant_drop_in_scrutinee.rs:299:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:318:11 + --> $DIR/significant_drop_in_scrutinee.rs:317:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:329:11 + --> $DIR/significant_drop_in_scrutinee.rs:328:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +265,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:364:11 + --> $DIR/significant_drop_in_scrutinee.rs:363:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:381:11 + --> $DIR/significant_drop_in_scrutinee.rs:380:11 | LL | match match i { | ___________^ @@ -316,7 +316,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:407:11 + --> $DIR/significant_drop_in_scrutinee.rs:406:11 | LL | match if i > 1 { | ___________^ @@ -349,7 +349,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:461:11 + --> $DIR/significant_drop_in_scrutinee.rs:460:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +367,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:489:11 + --> $DIR/significant_drop_in_scrutinee.rs:488:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -380,7 +380,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:508:11 + --> $DIR/significant_drop_in_scrutinee.rs:507:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -399,7 +399,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:514:11 + --> $DIR/significant_drop_in_scrutinee.rs:513:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:520:11 + --> $DIR/significant_drop_in_scrutinee.rs:519:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -437,7 +437,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:526:11 + --> $DIR/significant_drop_in_scrutinee.rs:525:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +456,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:589:11 + --> $DIR/significant_drop_in_scrutinee.rs:588:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> $DIR/significant_drop_in_scrutinee.rs:599:14 + --> $DIR/significant_drop_in_scrutinee.rs:598:14 | LL | for s in rwlock.read().unwrap().iter() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL | } = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:614:11 + --> $DIR/significant_drop_in_scrutinee.rs:613:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/significant_drop_tightening.fixed b/tests/ui/significant_drop_tightening.fixed index 7b848ead784..eb8524167c4 100644 --- a/tests/ui/significant_drop_tightening.fixed +++ b/tests/ui/significant_drop_tightening.fixed @@ -28,6 +28,29 @@ pub fn issue_10413() { } } +pub fn issue_11128() { + use std::mem::drop as unlock; + + struct Foo { + droppable: Option>, + mutex: Mutex>, + } + + impl Drop for Foo { + fn drop(&mut self) { + if let Some(droppable) = self.droppable.take() { + let lock = self.mutex.lock().unwrap(); + let idx_opt = lock.iter().copied().find(|el| Some(el) == droppable.first()); + if let Some(idx) = idx_opt { + let local_droppable = vec![lock.first().copied().unwrap_or_default()]; + unlock(lock); + drop(local_droppable); + } + } + } + } +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.rs b/tests/ui/significant_drop_tightening.rs index 36f77cf1bdb..f7fa65ea922 100644 --- a/tests/ui/significant_drop_tightening.rs +++ b/tests/ui/significant_drop_tightening.rs @@ -27,6 +27,29 @@ pub fn issue_10413() { } } +pub fn issue_11128() { + use std::mem::drop as unlock; + + struct Foo { + droppable: Option>, + mutex: Mutex>, + } + + impl Drop for Foo { + fn drop(&mut self) { + if let Some(droppable) = self.droppable.take() { + let lock = self.mutex.lock().unwrap(); + let idx_opt = lock.iter().copied().find(|el| Some(el) == droppable.first()); + if let Some(idx) = idx_opt { + let local_droppable = vec![lock.first().copied().unwrap_or_default()]; + unlock(lock); + drop(local_droppable); + } + } + } + } +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.stderr b/tests/ui/significant_drop_tightening.stderr index 3bdac0b0a6b..ca4fede17c9 100644 --- a/tests/ui/significant_drop_tightening.stderr +++ b/tests/ui/significant_drop_tightening.stderr @@ -23,7 +23,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:56:13 + --> $DIR/significant_drop_tightening.rs:79:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -43,7 +43,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:77:13 + --> $DIR/significant_drop_tightening.rs:100:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -67,7 +67,7 @@ LL + | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:83:17 + --> $DIR/significant_drop_tightening.rs:106:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index d4d2cbbe57a..b6b6b0288c5 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -4,8 +4,12 @@ use core; + + use serde as edres; + pub use serde; + use std; macro_rules! m { diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 80d72115f43..a8c4d899085 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -3,9 +3,13 @@ #![allow(unused_imports)] use core; + use regex; + use serde as edres; + pub use serde; + use std; macro_rules! m { diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index d69a86470a5..853a2fe0e7b 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,5 +1,5 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:6:1 + --> $DIR/single_component_path_imports.rs:7:1 | LL | use regex; | ^^^^^^^^^^ help: remove it entirely @@ -7,7 +7,7 @@ LL | use regex; = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:29:5 + --> $DIR/single_component_path_imports.rs:33:5 | LL | use regex; | ^^^^^^^^^^ help: remove it entirely diff --git a/tests/ui/single_component_path_imports_nested_first.rs b/tests/ui/single_component_path_imports_nested_first.rs index c75beb74786..d6243c19f55 100644 --- a/tests/ui/single_component_path_imports_nested_first.rs +++ b/tests/ui/single_component_path_imports_nested_first.rs @@ -2,7 +2,9 @@ #![allow(unused_imports)] use regex; + use serde as edres; + pub use serde; fn main() { diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr index 330f285202d..ff148355e12 100644 --- a/tests/ui/single_component_path_imports_nested_first.stderr +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -7,7 +7,7 @@ LL | use regex; = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:13:10 + --> $DIR/single_component_path_imports_nested_first.rs:15:10 | LL | use {regex, serde}; | ^^^^^ @@ -15,7 +15,7 @@ LL | use {regex, serde}; = help: remove this import error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:13:17 + --> $DIR/single_component_path_imports_nested_first.rs:15:17 | LL | use {regex, serde}; | ^^^^^ diff --git a/tests/ui/single_component_path_imports_self_after.rs b/tests/ui/single_component_path_imports_self_after.rs index 48e8e530261..5723d480a2e 100644 --- a/tests/ui/single_component_path_imports_self_after.rs +++ b/tests/ui/single_component_path_imports_self_after.rs @@ -2,6 +2,7 @@ #![allow(unused_imports)] use self::regex::{Regex as xeger, RegexSet as tesxeger}; +#[rustfmt::skip] pub use self::{ regex::{Regex, RegexSet}, some_mod::SomeType, diff --git a/tests/ui/single_component_path_imports_self_before.rs b/tests/ui/single_component_path_imports_self_before.rs index 4fb0cf40b6e..8a4fbf0dc5b 100644 --- a/tests/ui/single_component_path_imports_self_before.rs +++ b/tests/ui/single_component_path_imports_self_before.rs @@ -4,6 +4,7 @@ use regex; use self::regex::{Regex as xeger, RegexSet as tesxeger}; +#[rustfmt::skip] pub use self::{ regex::{Regex, RegexSet}, some_mod::SomeType, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index ef901513240..76f7e789589 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -10,7 +10,7 @@ LL | | }; | |_____^ | = note: `-D clippy::single-match` implied by `-D warnings` -help: try this +help: try | LL ~ if let Some(y) = x { LL + println!("{:?}", y); @@ -27,7 +27,7 @@ LL | | // is expanded before we can do anything. LL | | Some(y) => println!("{:?}", y), LL | | _ => (), LL | | } - | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` + | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:31:5 @@ -36,7 +36,7 @@ LL | / match z { LL | | (2..=3, 7..=9) => dummy(), LL | | _ => {}, LL | | }; - | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` + | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:60:5 @@ -45,7 +45,7 @@ LL | / match x { LL | | Some(y) => dummy(), LL | | None => (), LL | | }; - | |_____^ help: try this: `if let Some(y) = x { dummy() }` + | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:65:5 @@ -54,7 +54,7 @@ LL | / match y { LL | | Ok(y) => dummy(), LL | | Err(..) => (), LL | | }; - | |_____^ help: try this: `if let Ok(y) = y { dummy() }` + | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:72:5 @@ -63,7 +63,7 @@ LL | / match c { LL | | Cow::Borrowed(..) => dummy(), LL | | Cow::Owned(..) => (), LL | | }; - | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` + | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` --> $DIR/single_match.rs:93:5 @@ -72,7 +72,7 @@ LL | / match x { LL | | "test" => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if x == "test" { println!() }` + | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` --> $DIR/single_match.rs:106:5 @@ -81,7 +81,7 @@ LL | / match x { LL | | Foo::A => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if x == Foo::A { println!() }` + | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` --> $DIR/single_match.rs:112:5 @@ -90,7 +90,7 @@ LL | / match x { LL | | FOO_C => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if x == FOO_C { println!() }` + | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` --> $DIR/single_match.rs:117:5 @@ -99,7 +99,7 @@ LL | / match &&x { LL | | Foo::A => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if x == Foo::A { println!() }` + | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` --> $DIR/single_match.rs:123:5 @@ -108,7 +108,7 @@ LL | / match &x { LL | | Foo::A => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if x == &Foo::A { println!() }` + | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:140:5 @@ -117,7 +117,7 @@ LL | / match x { LL | | Bar::A => println!(), LL | | _ => (), LL | | } - | |_____^ help: try this: `if let Bar::A = x { println!() }` + | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:148:5 @@ -126,7 +126,7 @@ LL | / match x { LL | | None => println!(), LL | | _ => (), LL | | }; - | |_____^ help: try this: `if let None = x { println!() }` + | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:170:5 @@ -135,7 +135,7 @@ LL | / match x { LL | | (Some(_), _) => {}, LL | | (None, _) => {}, LL | | } - | |_____^ help: try this: `if let (Some(_), _) = x {}` + | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:176:5 @@ -144,7 +144,7 @@ LL | / match x { LL | | (Some(E::V), _) => todo!(), LL | | (_, _) => {}, LL | | } - | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }` + | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:182:5 @@ -153,7 +153,7 @@ LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, LL | | (..) => {}, LL | | } - | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` + | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:254:5 @@ -167,7 +167,7 @@ LL | | _ => {}, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { unsafe { LL + let r = &v as *const i32; @@ -187,7 +187,7 @@ LL | | _ => {}, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { LL + unsafe { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 228236f3bb8..5e7d4062efe 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -12,7 +12,7 @@ LL | | }; | |_____^ | = note: `-D clippy::single-match-else` implied by `-D warnings` -help: try this +help: try | LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { LL + let x = 5; @@ -32,7 +32,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { LL + println!("else block"); @@ -52,7 +52,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { LL + println!("else block"); @@ -72,7 +72,7 @@ LL | | } LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { LL + println!("else block"); @@ -92,7 +92,7 @@ LL | | } LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else { LL + println!("else block"); @@ -112,7 +112,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { unsafe { LL + let r = &v as *const i32; @@ -135,7 +135,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { LL + println!("Some"); @@ -159,7 +159,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { unsafe { LL + let r = &v as *const i32; @@ -183,7 +183,7 @@ LL | | }, LL | | } | |_____^ | -help: try this +help: try | LL ~ if let Some(v) = bar { LL + unsafe { diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index cb3ce3e95a7..22376680a8e 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -72,5 +72,13 @@ LL | vec1 = Vec::with_capacity(10); LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/slow_vector_initialization.rs:62:18 + | +LL | fn do_stuff(vec: &mut [u8]) {} + | ^^^^^^^^^ help: consider changing to: `&[u8]` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + +error: aborting due to 10 previous errors diff --git a/tests/ui/string_extend.stderr b/tests/ui/string_extend.stderr index b35c77fd961..34b43290147 100644 --- a/tests/ui/string_extend.stderr +++ b/tests/ui/string_extend.stderr @@ -2,7 +2,7 @@ error: calling `.extend(_.chars())` --> $DIR/string_extend.rs:18:5 | LL | s.extend(abc.chars()); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `s.push_str(abc)` | = note: `-D clippy::string-extend-chars` implied by `-D warnings` @@ -10,19 +10,19 @@ error: calling `.extend(_.chars())` --> $DIR/string_extend.rs:21:5 | LL | s.extend("abc".chars()); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.push_str("abc")` error: calling `.extend(_.chars())` --> $DIR/string_extend.rs:24:5 | LL | s.extend(def.chars()); - | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `s.push_str(&def)` error: calling `.extend(_.chars())` --> $DIR/string_extend.rs:34:5 | LL | s.extend(abc[0..2].chars()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&abc[0..2])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.push_str(&abc[0..2])` error: aborting due to 4 previous errors diff --git a/tests/ui/strlen_on_c_strings.stderr b/tests/ui/strlen_on_c_strings.stderr index 296268a5f1d..fcd17f68940 100644 --- a/tests/ui/strlen_on_c_strings.stderr +++ b/tests/ui/strlen_on_c_strings.stderr @@ -2,7 +2,7 @@ error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:15:13 | LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstring.as_bytes().len()` | = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings` @@ -10,37 +10,37 @@ error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:19:13 | LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstr.to_bytes().len()` error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:21:13 | LL | let _ = unsafe { strlen(cstr.as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstr.to_bytes().len()` error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:24:22 | LL | let _ = unsafe { strlen((*pcstr).as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*pcstr).to_bytes().len()` error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:29:22 | LL | let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe_identity(cstr).to_bytes().len()` error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:30:13 | LL | let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe { unsafe_identity(cstr) }.to_bytes().len()` error: using `libc::strlen` on a `CString` or `CStr` value --> $DIR/strlen_on_c_strings.rs:33:22 | LL | let _ = unsafe { strlen(f(cstr).as_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `f(cstr).to_bytes().len()` error: aborting due to 7 previous errors diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index 10a1b393a39..c4718825dc2 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -2,7 +2,7 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:9:13 | LL | let _ = d.to_digit(8).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d.is_digit(8)` | = note: `-D clippy::to-digit-is-some` implied by `-D warnings` @@ -10,7 +10,7 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:10:13 | LL | let _ = char::to_digit(c, 8).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `char::is_digit(c, 8)` error: aborting due to 2 previous errors diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index 48615583156..86f5cc937f4 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -5,7 +5,8 @@ clippy::disallowed_names, clippy::needless_lifetimes, clippy::redundant_field_names, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::needless_pass_by_ref_mut )] #[derive(Copy, Clone)] diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 8c5cfa8a0f1..2af668537f5 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,5 +1,5 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:51:11 + --> $DIR/trivially_copy_pass_by_ref.rs:52:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` @@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:51:20 + --> $DIR/trivially_copy_pass_by_ref.rs:52:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:51:29 + --> $DIR/trivially_copy_pass_by_ref.rs:52:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:12 + --> $DIR/trivially_copy_pass_by_ref.rs:59:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:22 + --> $DIR/trivially_copy_pass_by_ref.rs:59:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:31 + --> $DIR/trivially_copy_pass_by_ref.rs:59:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:40 + --> $DIR/trivially_copy_pass_by_ref.rs:59:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:60:16 + --> $DIR/trivially_copy_pass_by_ref.rs:61:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:60:25 + --> $DIR/trivially_copy_pass_by_ref.rs:61:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:60:34 + --> $DIR/trivially_copy_pass_by_ref.rs:61:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:62:35 + --> $DIR/trivially_copy_pass_by_ref.rs:63:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:16 + --> $DIR/trivially_copy_pass_by_ref.rs:75:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:25 + --> $DIR/trivially_copy_pass_by_ref.rs:75:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:34 + --> $DIR/trivially_copy_pass_by_ref.rs:75:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:78:34 + --> $DIR/trivially_copy_pass_by_ref.rs:79:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:110:21 + --> $DIR/trivially_copy_pass_by_ref.rs:111:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:115:15 + --> $DIR/trivially_copy_pass_by_ref.rs:116:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:142:37 + --> $DIR/trivially_copy_pass_by_ref.rs:143:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 4ad0e2e56a4..79f7b70224a 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -2,7 +2,7 @@ error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:19:9 | LL | Err(err)?; - | ^^^^^^^^^ help: try this: `return Err(err)` + | ^^^^^^^^^ help: try: `return Err(err)` | note: the lint level is defined here --> $DIR/try_err.rs:4:9 @@ -14,25 +14,25 @@ error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:29:9 | LL | Err(err)?; - | ^^^^^^^^^ help: try this: `return Err(err.into())` + | ^^^^^^^^^ help: try: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:49:17 | LL | Err(err)?; - | ^^^^^^^^^ help: try this: `return Err(err)` + | ^^^^^^^^^ help: try: `return Err(err)` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:68:17 | LL | Err(err)?; - | ^^^^^^^^^ help: try this: `return Err(err.into())` + | ^^^^^^^^^ help: try: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:88:23 | LL | Err(_) => Err(1)?, - | ^^^^^^^ help: try this: `return Err(1)` + | ^^^^^^^ help: try: `return Err(1)` | = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -40,7 +40,7 @@ error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:95:23 | LL | Err(_) => Err(inline!(1))?, - | ^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(1))` + | ^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(1))` | = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -48,31 +48,31 @@ error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:122:9 | LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:129:9 | LL | Err(io::ErrorKind::WriteZero)? - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:131:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:139:9 | LL | Err(io::ErrorKind::NotFound)? - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` error: returning an `Err(_)` with the `?` operator --> $DIR/try_err.rs:148:16 | LL | return Err(42)?; - | ^^^^^^^^ help: try this: `Err(42)` + | ^^^^^^^^ help: try: `Err(42)` error: aborting due to 11 previous errors diff --git a/tests/ui/type_id_on_box.fixed b/tests/ui/type_id_on_box.fixed new file mode 100644 index 00000000000..615d809c897 --- /dev/null +++ b/tests/ui/type_id_on_box.fixed @@ -0,0 +1,40 @@ +//@run-rustfix + +#![warn(clippy::type_id_on_box)] + +use std::any::{Any, TypeId}; +use std::ops::Deref; + +type SomeBox = Box; + +struct BadBox(Box); + +impl Deref for BadBox { + type Target = Box; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn existential() -> impl Any { + Box::new(1) as Box +} + +fn main() { + let any_box: Box = Box::new(0usize); + let _ = (*any_box).type_id(); + let _ = TypeId::of::>(); // Don't lint. We explicitly say "do this instead" if this is intentional + let _ = (*any_box).type_id(); + let any_box: &Box = &(Box::new(0usize) as Box); + let _ = (**any_box).type_id(); // 2 derefs are needed here to get to the `dyn Any` + + let b = existential(); + let _ = b.type_id(); // Don't lint. + + let b: SomeBox = Box::new(0usize); + let _ = (*b).type_id(); + + let b = BadBox(Box::new(0usize)); + let _ = b.type_id(); // Don't lint. This is a call to `::type_id`. Not `std::boxed::Box`! +} diff --git a/tests/ui/type_id_on_box.rs b/tests/ui/type_id_on_box.rs new file mode 100644 index 00000000000..74b6c74ae5f --- /dev/null +++ b/tests/ui/type_id_on_box.rs @@ -0,0 +1,40 @@ +//@run-rustfix + +#![warn(clippy::type_id_on_box)] + +use std::any::{Any, TypeId}; +use std::ops::Deref; + +type SomeBox = Box; + +struct BadBox(Box); + +impl Deref for BadBox { + type Target = Box; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn existential() -> impl Any { + Box::new(1) as Box +} + +fn main() { + let any_box: Box = Box::new(0usize); + let _ = any_box.type_id(); + let _ = TypeId::of::>(); // Don't lint. We explicitly say "do this instead" if this is intentional + let _ = (*any_box).type_id(); + let any_box: &Box = &(Box::new(0usize) as Box); + let _ = any_box.type_id(); // 2 derefs are needed here to get to the `dyn Any` + + let b = existential(); + let _ = b.type_id(); // Don't lint. + + let b: SomeBox = Box::new(0usize); + let _ = b.type_id(); + + let b = BadBox(Box::new(0usize)); + let _ = b.type_id(); // Don't lint. This is a call to `::type_id`. Not `std::boxed::Box`! +} diff --git a/tests/ui/type_id_on_box.stderr b/tests/ui/type_id_on_box.stderr new file mode 100644 index 00000000000..1525328c0d0 --- /dev/null +++ b/tests/ui/type_id_on_box.stderr @@ -0,0 +1,36 @@ +error: calling `.type_id()` on a `Box` + --> $DIR/type_id_on_box.rs:26:13 + | +LL | let _ = any_box.type_id(); + | -------^^^^^^^^^^ + | | + | help: consider dereferencing first: `(*any_box)` + | + = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + = note: `-D clippy::type-id-on-box` implied by `-D warnings` + +error: calling `.type_id()` on a `Box` + --> $DIR/type_id_on_box.rs:30:13 + | +LL | let _ = any_box.type_id(); // 2 derefs are needed here to get to the `dyn Any` + | -------^^^^^^^^^^ + | | + | help: consider dereferencing first: `(**any_box)` + | + = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + +error: calling `.type_id()` on a `Box` + --> $DIR/type_id_on_box.rs:36:13 + | +LL | let _ = b.type_id(); + | -^^^^^^^^^^ + | | + | help: consider dereferencing first: `(*b)` + | + = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + +error: aborting due to 3 previous errors + diff --git a/tests/ui/unnecessary_cast_unfixable.rs b/tests/ui/unnecessary_cast_unfixable.rs new file mode 100644 index 00000000000..0e027f6042e --- /dev/null +++ b/tests/ui/unnecessary_cast_unfixable.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unnecessary_cast)] + +fn main() { + let _ = std::ptr::null() as *const u8; +} + +mod issue11113 { + #[repr(C)] + struct Vtbl { + query: unsafe extern "system" fn(), + } + + struct TearOff { + object: *mut std::ffi::c_void, + } + + impl TearOff { + unsafe fn query(&self) { + ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + } + } +} diff --git a/tests/ui/unnecessary_cast_unfixable.stderr b/tests/ui/unnecessary_cast_unfixable.stderr new file mode 100644 index 00000000000..eecf245686a --- /dev/null +++ b/tests/ui/unnecessary_cast_unfixable.stderr @@ -0,0 +1,16 @@ +error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) + --> $DIR/unnecessary_cast_unfixable.rs:4:13 + | +LL | let _ = std::ptr::null() as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null()` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: casting raw pointers to the same type and constness is unnecessary (`*mut issue11113::Vtbl` -> `*mut issue11113::Vtbl`) + --> $DIR/unnecessary_cast_unfixable.rs:19:16 + | +LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5686ab6b453..23639f6d41a 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -2,7 +2,7 @@ error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:23:5 | LL | rc.clone(); - | ^^^^^^^^^^ help: try this: `Rc::::clone(&rc)` + | ^^^^^^^^^^ help: try: `Rc::::clone(&rc)` | = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` @@ -10,25 +10,25 @@ error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:26:5 | LL | arc.clone(); - | ^^^^^^^^^^^ help: try this: `Arc::::clone(&arc)` + | ^^^^^^^^^^^ help: try: `Arc::::clone(&arc)` error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:29:5 | LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&rcweak)` + | ^^^^^^^^^^^^^^ help: try: `Weak::::clone(&rcweak)` error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:32:5 | LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` + | ^^^^^^^^^^^^^^^^ help: try: `Weak::::clone(&arc_weak)` error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:36:33 | LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` + | ^^^^^^^^^ help: try: `Arc::::clone(&x)` error: using `clone` on type `T` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:40:5 @@ -54,7 +54,7 @@ error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:95:14 | LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc::::clone(&try_opt!(Some(rc)))` error: aborting due to 9 previous errors diff --git a/tests/ui/unnecessary_literal_unwrap.fixed b/tests/ui/unnecessary_literal_unwrap.fixed index 630a1bea3c8..276cd800b89 100644 --- a/tests/ui/unnecessary_literal_unwrap.fixed +++ b/tests/ui/unnecessary_literal_unwrap.fixed @@ -68,6 +68,26 @@ fn unwrap_methods_result() { 1; } +fn unwrap_from_binding() { + macro_rules! from_macro { + () => { + Some("") + }; + } + let val = from_macro!(); + let _ = val.unwrap_or(""); +} + +fn unwrap_unchecked() { + let _ = 1; + let _ = unsafe { 1 + *(&1 as *const i32) }; // needs to keep the unsafe block + let _ = 1 + 1; + let _ = 1; + let _ = unsafe { 1 + *(&1 as *const i32) }; + let _ = 1 + 1; + let _ = 123; +} + fn main() { unwrap_option_some(); unwrap_option_none(); @@ -75,4 +95,5 @@ fn main() { unwrap_result_err(); unwrap_methods_option(); unwrap_methods_result(); + unwrap_unchecked(); } diff --git a/tests/ui/unnecessary_literal_unwrap.rs b/tests/ui/unnecessary_literal_unwrap.rs index 14f92cb370f..3065778d779 100644 --- a/tests/ui/unnecessary_literal_unwrap.rs +++ b/tests/ui/unnecessary_literal_unwrap.rs @@ -68,6 +68,26 @@ fn unwrap_methods_result() { Ok::<_, ()>(1).unwrap_or_else(|_| 2); } +fn unwrap_from_binding() { + macro_rules! from_macro { + () => { + Some("") + }; + } + let val = from_macro!(); + let _ = val.unwrap_or(""); +} + +fn unwrap_unchecked() { + let _ = unsafe { Some(1).unwrap_unchecked() }; + let _ = unsafe { Some(1).unwrap_unchecked() + *(&1 as *const i32) }; // needs to keep the unsafe block + let _ = unsafe { Some(1).unwrap_unchecked() } + 1; + let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() }; + let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() + *(&1 as *const i32) }; + let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() } + 1; + let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; +} + fn main() { unwrap_option_some(); unwrap_option_none(); @@ -75,4 +95,5 @@ fn main() { unwrap_result_err(); unwrap_methods_option(); unwrap_methods_result(); + unwrap_unchecked(); } diff --git a/tests/ui/unnecessary_literal_unwrap.stderr b/tests/ui/unnecessary_literal_unwrap.stderr index 0c71ee05323..5823313b736 100644 --- a/tests/ui/unnecessary_literal_unwrap.stderr +++ b/tests/ui/unnecessary_literal_unwrap.stderr @@ -409,5 +409,89 @@ LL - Ok::<_, ()>(1).unwrap_or_else(|_| 2); LL + 1; | -error: aborting due to 36 previous errors +error: used `unwrap_unchecked()` on `Some` value + --> $DIR/unnecessary_literal_unwrap.rs:82:22 + | +LL | let _ = unsafe { Some(1).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Some` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Some(1).unwrap_unchecked() }; +LL + let _ = 1; + | + +error: used `unwrap_unchecked()` on `Some` value + --> $DIR/unnecessary_literal_unwrap.rs:83:22 + | +LL | let _ = unsafe { Some(1).unwrap_unchecked() + *(&1 as *const i32) }; // needs to keep the unsafe block + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Some` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Some(1).unwrap_unchecked() + *(&1 as *const i32) }; // needs to keep the unsafe block +LL + let _ = unsafe { 1 + *(&1 as *const i32) }; // needs to keep the unsafe block + | + +error: used `unwrap_unchecked()` on `Some` value + --> $DIR/unnecessary_literal_unwrap.rs:84:22 + | +LL | let _ = unsafe { Some(1).unwrap_unchecked() } + 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Some` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Some(1).unwrap_unchecked() } + 1; +LL + let _ = 1 + 1; + | + +error: used `unwrap_unchecked()` on `Ok` value + --> $DIR/unnecessary_literal_unwrap.rs:85:22 + | +LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Ok` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() }; +LL + let _ = 1; + | + +error: used `unwrap_unchecked()` on `Ok` value + --> $DIR/unnecessary_literal_unwrap.rs:86:22 + | +LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() + *(&1 as *const i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Ok` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() + *(&1 as *const i32) }; +LL + let _ = unsafe { 1 + *(&1 as *const i32) }; + | + +error: used `unwrap_unchecked()` on `Ok` value + --> $DIR/unnecessary_literal_unwrap.rs:87:22 + | +LL | let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() } + 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Ok` and `unwrap_unchecked()` + | +LL - let _ = unsafe { Ok::<_, ()>(1).unwrap_unchecked() } + 1; +LL + let _ = 1 + 1; + | + +error: used `unwrap_err_unchecked()` on `Err` value + --> $DIR/unnecessary_literal_unwrap.rs:88:22 + | +LL | let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the `Err` and `unwrap_err_unchecked()` + | +LL - let _ = unsafe { Err::<(), i32>(123).unwrap_err_unchecked() }; +LL + let _ = 123; + | + +error: aborting due to 43 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 592a53f3a81..cb7562351e8 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -477,7 +477,8 @@ mod issue_10021 { mod issue_10033 { #![allow(dead_code)] - use std::{fmt::Display, ops::Deref}; + use std::fmt::Display; + use std::ops::Deref; fn _main() { let f = Foo; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index f2e48b1c4a6..f82ddb2d25d 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -477,7 +477,8 @@ mod issue_10021 { mod issue_10033 { #![allow(dead_code)] - use std::{fmt::Display, ops::Deref}; + use std::fmt::Display; + use std::ops::Deref; fn _main() { let f = Foo; diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index d29888ac62f..04f6ef29a9a 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -8,9 +8,13 @@ use std::cell::UnsafeCell as TotallySafeCellAgain; // Shouldn't error use std::cell::RefCell as ProbablyNotUnsafe; + use std::cell::RefCell as RefCellThatCantBeUnsafe; + use std::cell::UnsafeCell as SuperDangerousUnsafeCell; + use std::cell::UnsafeCell as Dangerunsafe; + use std::cell::UnsafeCell as Bombsawayunsafe; mod mod_with_some_unsafe_things { @@ -20,8 +24,12 @@ mod mod_with_some_unsafe_things { use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; +// merged imports +use mod_with_some_unsafe_things::{Unsafe as A, Unsafe as B}; + // Shouldn't error use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; + use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; #[allow(clippy::unsafe_removed_from_name)] diff --git a/tests/ui/unsafe_removed_from_name.stderr b/tests/ui/unsafe_removed_from_name.stderr index 4f871cbe41b..090d917bd38 100644 --- a/tests/ui/unsafe_removed_from_name.stderr +++ b/tests/ui/unsafe_removed_from_name.stderr @@ -13,10 +13,22 @@ LL | use std::cell::UnsafeCell as TotallySafeCellAgain; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety` - --> $DIR/unsafe_removed_from_name.rs:21:1 + --> $DIR/unsafe_removed_from_name.rs:25:1 | LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: removed `unsafe` from the name of `Unsafe` in use as `A` + --> $DIR/unsafe_removed_from_name.rs:28:1 + | +LL | use mod_with_some_unsafe_things::{Unsafe as A, Unsafe as B}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: removed `unsafe` from the name of `Unsafe` in use as `B` + --> $DIR/unsafe_removed_from_name.rs:28:1 + | +LL | use mod_with_some_unsafe_things::{Unsafe as A, Unsafe as B}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 8d3e094b759..e9d1eeb3161 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::needless_pass_by_ref_mut)] #![warn(clippy::unused_io_amount)] extern crate futures; diff --git a/tests/ui/unused_peekable.rs b/tests/ui/unused_peekable.rs index 7374dfdf92e..b227f8660f5 100644 --- a/tests/ui/unused_peekable.rs +++ b/tests/ui/unused_peekable.rs @@ -1,8 +1,7 @@ #![warn(clippy::unused_peekable)] #![allow(clippy::no_effect)] -use std::iter::Empty; -use std::iter::Peekable; +use std::iter::{Empty, Peekable}; fn main() { invalid(); diff --git a/tests/ui/unused_peekable.stderr b/tests/ui/unused_peekable.stderr index 54788f2fa2f..d969232fdf3 100644 --- a/tests/ui/unused_peekable.stderr +++ b/tests/ui/unused_peekable.stderr @@ -1,5 +1,5 @@ error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:14:9 + --> $DIR/unused_peekable.rs:13:9 | LL | let peekable = std::iter::empty::().peekable(); | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | let peekable = std::iter::empty::().peekable(); = note: `-D clippy::unused-peekable` implied by `-D warnings` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:18:9 + --> $DIR/unused_peekable.rs:17:9 | LL | let new_local = old_local; | ^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let new_local = old_local; = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:22:9 + --> $DIR/unused_peekable.rs:21:9 | LL | let by_mut_ref = &mut by_mut_ref_test; | ^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let by_mut_ref = &mut by_mut_ref_test; = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:29:9 + --> $DIR/unused_peekable.rs:28:9 | LL | let peekable_from_fn = returns_peekable(); | ^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let peekable_from_fn = returns_peekable(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:32:13 + --> $DIR/unused_peekable.rs:31:13 | LL | let mut peekable_using_iterator_method = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let mut peekable_using_iterator_method = std::iter::empty::().peek = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:37:9 + --> $DIR/unused_peekable.rs:36:9 | LL | let passed_along_ref = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let passed_along_ref = std::iter::empty::().peekable(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:42:9 + --> $DIR/unused_peekable.rs:41:9 | LL | let _by_ref = by_ref_test.by_ref(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL | let _by_ref = by_ref_test.by_ref(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> $DIR/unused_peekable.rs:44:13 + --> $DIR/unused_peekable.rs:43:13 | LL | let mut peekable_in_for_loop = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unwrap_or.stderr b/tests/ui/unwrap_or.stderr index cf720eaaf05..e384bbbb015 100644 --- a/tests/ui/unwrap_or.stderr +++ b/tests/ui/unwrap_or.stderr @@ -2,7 +2,7 @@ error: use of `unwrap_or` followed by a function call --> $DIR/unwrap_or.rs:5:47 | LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| "Fail".to_string())` | = note: `-D clippy::or-fun-call` implied by `-D warnings` @@ -10,7 +10,7 @@ error: use of `unwrap_or` followed by a function call --> $DIR/unwrap_or.rs:9:47 | LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| "Fail".to_string())` error: aborting due to 2 previous errors diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 490d36ae6d6..e42731f9bcf 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -1,6 +1,10 @@ //@run-rustfix #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] +#![allow( + clippy::explicit_auto_deref, + clippy::uninlined_format_args, + clippy::needless_pass_by_ref_mut +)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index f2681af924d..50c9990bb04 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -1,6 +1,10 @@ //@run-rustfix #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] +#![allow( + clippy::explicit_auto_deref, + clippy::uninlined_format_args, + clippy::needless_pass_by_ref_mut +)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index 67ce8b64e0e..c97851ac6ea 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -1,8 +1,8 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:43:18 + --> $DIR/useless_asref.rs:47:18 | LL | foo_rstr(rstr.as_ref()); - | ^^^^^^^^^^^^^ help: try this: `rstr` + | ^^^^^^^^^^^^^ help: try: `rstr` | note: the lint level is defined here --> $DIR/useless_asref.rs:2:9 @@ -11,64 +11,64 @@ LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:45:20 + --> $DIR/useless_asref.rs:49:20 | LL | foo_rslice(rslice.as_ref()); - | ^^^^^^^^^^^^^^^ help: try this: `rslice` + | ^^^^^^^^^^^^^^^ help: try: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:49:21 + --> $DIR/useless_asref.rs:53:21 | LL | foo_mrslice(mrslice.as_mut()); - | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:51:20 + --> $DIR/useless_asref.rs:55:20 | LL | foo_rslice(mrslice.as_ref()); - | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:58:20 + --> $DIR/useless_asref.rs:62:20 | LL | foo_rslice(rrrrrslice.as_ref()); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` + | ^^^^^^^^^^^^^^^^^^^ help: try: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:60:18 + --> $DIR/useless_asref.rs:64:18 | LL | foo_rstr(rrrrrstr.as_ref()); - | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` + | ^^^^^^^^^^^^^^^^^ help: try: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:65:21 + --> $DIR/useless_asref.rs:69:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:67:20 + --> $DIR/useless_asref.rs:71:20 | LL | foo_rslice(mrrrrrslice.as_ref()); - | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:71:16 + --> $DIR/useless_asref.rs:75:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:121:13 + --> $DIR/useless_asref.rs:125:13 | LL | foo_mrt(mrt.as_mut()); - | ^^^^^^^^^^^^ help: try this: `mrt` + | ^^^^^^^^^^^^ help: try: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:123:12 + --> $DIR/useless_asref.rs:127:12 | LL | foo_rt(mrt.as_ref()); - | ^^^^^^^^^^^^ help: try this: `mrt` + | ^^^^^^^^^^^^ help: try: `mrt` error: aborting due to 11 previous errors diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index fcdc917c1b1..7a7d0026f79 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -115,6 +115,46 @@ fn main() { let _x = vec![1; 201]; } +fn issue11075() { + macro_rules! repro { + ($e:expr) => { + stringify!($e) + }; + } + for _string in [repro!(true), repro!(null)] { + unimplemented!(); + } + + macro_rules! in_macro { + ($e:expr, $vec:expr, $vec2:expr) => {{ + vec![1; 2].fill(3); + vec![1, 2].fill(3); + for _ in vec![1, 2] {} + for _ in vec![1; 2] {} + for _ in vec![$e, $e] {} + for _ in vec![$e; 2] {} + for _ in $vec {} + for _ in $vec2 {} + }}; + } + + in_macro!(1, [1, 2], [1; 2]); + + macro_rules! from_macro { + () => { + vec![1, 2, 3] + }; + } + macro_rules! from_macro_repeat { + () => { + vec![1; 3] + }; + } + + for _ in from_macro!() {} + for _ in from_macro_repeat!() {} +} + #[clippy::msrv = "1.53"] fn above() { for a in [1, 2, 3] { diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 0404d8cdb84..cbe7685b453 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -115,6 +115,46 @@ fn main() { let _x = vec![1; 201]; } +fn issue11075() { + macro_rules! repro { + ($e:expr) => { + stringify!($e) + }; + } + for _string in vec![repro!(true), repro!(null)] { + unimplemented!(); + } + + macro_rules! in_macro { + ($e:expr, $vec:expr, $vec2:expr) => {{ + vec![1; 2].fill(3); + vec![1, 2].fill(3); + for _ in vec![1, 2] {} + for _ in vec![1; 2] {} + for _ in vec![$e, $e] {} + for _ in vec![$e; 2] {} + for _ in $vec {} + for _ in $vec2 {} + }}; + } + + in_macro!(1, vec![1, 2], vec![1; 2]); + + macro_rules! from_macro { + () => { + vec![1, 2, 3] + }; + } + macro_rules! from_macro_repeat { + () => { + vec![1; 3] + }; + } + + for _ in from_macro!() {} + for _ in from_macro_repeat!() {} +} + #[clippy::msrv = "1.53"] fn above() { for a in vec![1, 2, 3] { diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 33d565b2d52..8f6d2a1df80 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -85,16 +85,34 @@ LL | for _ in vec![1, 2, 3] {} | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:120:14 + --> $DIR/vec.rs:124:20 + | +LL | for _string in vec![repro!(true), repro!(null)] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]` + +error: useless use of `vec!` + --> $DIR/vec.rs:141:18 + | +LL | in_macro!(1, vec![1, 2], vec![1; 2]); + | ^^^^^^^^^^ help: you can use an array directly: `[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:141:30 + | +LL | in_macro!(1, vec![1, 2], vec![1; 2]); + | ^^^^^^^^^^ help: you can use an array directly: `[1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:160:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:124:14 + --> $DIR/vec.rs:164:14 | LL | for a in vec![String::new(), String::new()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 30d29aa4e77..5b88ae4ab66 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -2,7 +2,7 @@ error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:40:9 | LL | _ => eprintln!("Not red"), - | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + | ^ help: try: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` | note: the lint level is defined here --> $DIR/wildcard_enum_match_arm.rs:3:9 @@ -14,31 +14,31 @@ error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:44:9 | LL | _not_red => eprintln!("Not red"), - | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` + | ^^^^^^^^ help: try: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:48:9 | LL | not_red => format!("{:?}", not_red), - | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` + | ^^^^^^^ help: try: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:64:9 | LL | _ => "No red", - | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + | ^ help: try: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` error: wildcard matches known variants and will also match future added variants --> $DIR/wildcard_enum_match_arm.rs:81:9 | LL | _ => {}, - | ^ help: try this: `ErrorKind::PermissionDenied | _` + | ^ help: try: `ErrorKind::PermissionDenied | _` error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | Enum::__Private` + | ^ help: try: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 2961b062ec3..67173f40654 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -112,6 +112,7 @@ mod in_fn_test { } fn test_inner_nested() { + #[rustfmt::skip] use self::{inner::inner_foo, inner2::inner_bar}; inner_foo(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 28508a2538b..8223b693018 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -112,6 +112,7 @@ mod in_fn_test { } fn test_inner_nested() { + #[rustfmt::skip] use self::{inner::*, inner2::*}; inner_foo(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index c96b3041a00..f7baf234c2f 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -55,37 +55,37 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:20 + --> $DIR/wildcard_imports.rs:116:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:30 + --> $DIR/wildcard_imports.rs:116:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:122:13 + --> $DIR/wildcard_imports.rs:123:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:151:9 + --> $DIR/wildcard_imports.rs:152:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:161:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:162:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:173:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:17 + --> $DIR/wildcard_imports.rs:208:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:215:13 + --> $DIR/wildcard_imports.rs:216:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:224:17 + --> $DIR/wildcard_imports.rs:225:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:233:13 + --> $DIR/wildcard_imports.rs:234:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:241:13 + --> $DIR/wildcard_imports.rs:242:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed index 3aea013fb3a..8a63375676e 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -106,6 +106,7 @@ mod in_fn_test { } fn test_inner_nested() { + #[rustfmt::skip] use self::{inner::inner_foo, inner2::inner_bar}; inner_foo(); diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr index acca9f651b4..af9ae6e786c 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -55,37 +55,37 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:109:20 + --> $DIR/wildcard_imports_2021.rs:110:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:109:30 + --> $DIR/wildcard_imports_2021.rs:110:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:116:13 + --> $DIR/wildcard_imports_2021.rs:117:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:145:9 + --> $DIR/wildcard_imports_2021.rs:146:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:154:9 + --> $DIR/wildcard_imports_2021.rs:155:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:156:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:166:13 + --> $DIR/wildcard_imports_2021.rs:167:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:201:17 + --> $DIR/wildcard_imports_2021.rs:202:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:209:13 + --> $DIR/wildcard_imports_2021.rs:210:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:218:17 + --> $DIR/wildcard_imports_2021.rs:219:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:227:13 + --> $DIR/wildcard_imports_2021.rs:228:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:235:13 + --> $DIR/wildcard_imports_2021.rs:236:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed index 3aea013fb3a..8a63375676e 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -106,6 +106,7 @@ mod in_fn_test { } fn test_inner_nested() { + #[rustfmt::skip] use self::{inner::inner_foo, inner2::inner_bar}; inner_foo(); diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr index acca9f651b4..af9ae6e786c 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -55,37 +55,37 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:109:20 + --> $DIR/wildcard_imports_2021.rs:110:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:109:30 + --> $DIR/wildcard_imports_2021.rs:110:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:116:13 + --> $DIR/wildcard_imports_2021.rs:117:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:145:9 + --> $DIR/wildcard_imports_2021.rs:146:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:154:9 + --> $DIR/wildcard_imports_2021.rs:155:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:156:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:166:13 + --> $DIR/wildcard_imports_2021.rs:167:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:201:17 + --> $DIR/wildcard_imports_2021.rs:202:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:209:13 + --> $DIR/wildcard_imports_2021.rs:210:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:218:17 + --> $DIR/wildcard_imports_2021.rs:219:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:227:13 + --> $DIR/wildcard_imports_2021.rs:228:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:235:13 + --> $DIR/wildcard_imports_2021.rs:236:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs index 40c2d07527d..52cd2c82854 100644 --- a/tests/ui/wildcard_imports_2021.rs +++ b/tests/ui/wildcard_imports_2021.rs @@ -106,6 +106,7 @@ mod in_fn_test { } fn test_inner_nested() { + #[rustfmt::skip] use self::{inner::*, inner2::*}; inner_foo(); diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 1e306ae28a2..8b72c8bd282 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -5,7 +5,7 @@ LL | write!(v, "Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` -help: try this +help: try | LL - write!(v, "Hello {}", "world"); LL + write!(v, "Hello world"); @@ -17,7 +17,7 @@ error: literal with an empty format string LL | writeln!(v, "Hello {} {}", world, "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "Hello {} {}", world, "world"); LL + writeln!(v, "Hello {} world", world); @@ -29,7 +29,7 @@ error: literal with an empty format string LL | writeln!(v, "Hello {}", "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "Hello {}", "world"); LL + writeln!(v, "Hello world"); @@ -41,7 +41,7 @@ error: literal with an empty format string LL | writeln!(v, "{} {:.4}", "a literal", 5); | ^^^^^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{} {:.4}", "a literal", 5); LL + writeln!(v, "a literal {:.4}", 5); @@ -53,7 +53,7 @@ error: literal with an empty format string LL | writeln!(v, "{0} {1}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{0} {1}", "hello", "world"); LL + writeln!(v, "hello {1}", "world"); @@ -65,7 +65,7 @@ error: literal with an empty format string LL | writeln!(v, "{0} {1}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{0} {1}", "hello", "world"); LL + writeln!(v, "{0} world", "hello"); @@ -77,7 +77,7 @@ error: literal with an empty format string LL | writeln!(v, "{1} {0}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{1} {0}", "hello", "world"); LL + writeln!(v, "world {0}", "hello"); @@ -89,7 +89,7 @@ error: literal with an empty format string LL | writeln!(v, "{1} {0}", "hello", "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{1} {0}", "hello", "world"); LL + writeln!(v, "{1} hello", "world"); @@ -101,7 +101,7 @@ error: literal with an empty format string LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); LL + writeln!(v, "hello {bar}", bar = "world"); @@ -113,7 +113,7 @@ error: literal with an empty format string LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); LL + writeln!(v, "{foo} world", foo = "hello"); @@ -125,7 +125,7 @@ error: literal with an empty format string LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); LL + writeln!(v, "world {foo}", foo = "hello"); @@ -137,7 +137,7 @@ error: literal with an empty format string LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ | -help: try this +help: try | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); LL + writeln!(v, "{bar} hello", bar = "world"); diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index 18591250aad..c30ec385b35 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -13,7 +13,7 @@ LL | writeln!(v, "{}", "{hello}"); | ^^^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` -help: try this +help: try | LL - writeln!(v, "{}", "{hello}"); LL + writeln!(v, "{{hello}}"); @@ -25,7 +25,7 @@ error: literal with an empty format string LL | writeln!(v, r"{}", r"{hello}"); | ^^^^^^^^^^ | -help: try this +help: try | LL - writeln!(v, r"{}", r"{hello}"); LL + writeln!(v, r"{{hello}}"); @@ -37,7 +37,7 @@ error: literal with an empty format string LL | writeln!(v, "{}", '/''); | ^^^^ | -help: try this +help: try | LL - writeln!(v, "{}", '/''); LL + writeln!(v, "'"); @@ -49,7 +49,7 @@ error: literal with an empty format string LL | writeln!(v, "{}", '"'); | ^^^ | -help: try this +help: try | LL - writeln!(v, "{}", '"'); LL + writeln!(v, "/""); @@ -67,7 +67,7 @@ error: literal with an empty format string LL | writeln!(v, r"{}", '/''); | ^^^^ | -help: try this +help: try | LL - writeln!(v, r"{}", '/''); LL + writeln!(v, r"'"); @@ -80,7 +80,7 @@ LL | / "hello / LL | | world!" | |_______________^ | -help: try this +help: try | LL ~ "some hello / LL ~ world!" @@ -92,7 +92,7 @@ error: literal with an empty format string LL | "1", "2", "3", | ^^^ | -help: try this +help: try | LL ~ "some 1/ LL ~ {} // {}", "2", "3", @@ -104,7 +104,7 @@ error: literal with an empty format string LL | "1", "2", "3", | ^^^ | -help: try this +help: try | LL ~ 2 // {}", LL ~ "1", "3", @@ -116,7 +116,7 @@ error: literal with an empty format string LL | "1", "2", "3", | ^^^ | -help: try this +help: try | LL ~ {} // 3", LL ~ "1", "2", @@ -128,7 +128,7 @@ error: literal with an empty format string LL | writeln!(v, "{}", "//"); | ^^^^ | -help: try this +help: try | LL - writeln!(v, "{}", "//"); LL + writeln!(v, "//"); @@ -140,7 +140,7 @@ error: literal with an empty format string LL | writeln!(v, r"{}", "//"); | ^^^^ | -help: try this +help: try | LL - writeln!(v, r"{}", "//"); LL + writeln!(v, r"/"); @@ -152,7 +152,7 @@ error: literal with an empty format string LL | writeln!(v, r#"{}"#, "//"); | ^^^^ | -help: try this +help: try | LL - writeln!(v, r#"{}"#, "//"); LL + writeln!(v, r#"/"#); @@ -164,7 +164,7 @@ error: literal with an empty format string LL | writeln!(v, "{}", r"/"); | ^^^^ | -help: try this +help: try | LL - writeln!(v, "{}", r"/"); LL + writeln!(v, "//"); @@ -176,7 +176,7 @@ error: literal with an empty format string LL | writeln!(v, "{}", "/r"); | ^^^^ | -help: try this +help: try | LL - writeln!(v, "{}", "/r"); LL + writeln!(v, "/r"); diff --git a/util/fetch_prs_between.sh b/util/fetch_prs_between.sh index 6865abf971b..fa7560b6929 100755 --- a/util/fetch_prs_between.sh +++ b/util/fetch_prs_between.sh @@ -6,15 +6,20 @@ # If you want to use this to update the Clippy changelog, be sure to manually # exclude the non-user facing changes like 'rustup' PRs, typo fixes, etc. -first=$1 -last=$2 +set -e IFS=' ' -for pr in $(git log --oneline --grep "Merge #" --grep "Merge pull request" --grep "Auto merge of" --grep "Rollup merge of" "$first...$last" | sort -rn | uniq); do +for pr in $(git log --oneline --merges --first-parent "$1...$2"); do id=$(echo "$pr" | rg -o '#[0-9]{3,5}' | cut -c 2-) commit=$(echo "$pr" | cut -d' ' -f 1) message=$(git --no-pager show --pretty=medium "$commit") + + if [[ -z "$newest_pr" ]]; then + newest_pr="$id" + fi + oldest_pr="$id" + if [[ -n $(echo "$message" | rg "^[\s]{4}changelog: [nN]one\.*$") ]]; then continue fi @@ -25,3 +30,14 @@ for pr in $(git log --oneline --grep "Merge #" --grep "Merge pull request" --gre echo "---------------------------------------------------------" echo done + +newest_merged_at="$(gh pr view -R rust-lang/rust-clippy --json mergedAt $newest_pr -q .mergedAt)" +oldest_merged_at="$(gh pr view -R rust-lang/rust-clippy --json mergedAt $oldest_pr -q .mergedAt)" + +query="merged:$oldest_merged_at..$newest_merged_at base:master" +encoded_query="$(echo $query | sed 's/ /+/g; s/:/%3A/g')" + +pr_link="https://github.com/rust-lang/rust-clippy/pulls?q=$encoded_query" +count="$(gh api -X GET search/issues -f "q=$query repo:rust-lang/rust-clippy" -q .total_count)" + +echo "[View all $count merged pull requests]($pr_link)" From 2feb9a582f50e4322f620823ce195dfb346752fd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 17 Jul 2023 10:22:49 +0200 Subject: [PATCH 12/90] Another fix for incorrect_impls --- clippy_lints/src/incorrect_impls.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs index c5da6039ac3..e6c42ebb832 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/incorrect_impls.rs @@ -4,7 +4,6 @@ use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, path_re use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::EarlyBinder; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -121,7 +120,7 @@ impl LateLintPass<'_> for IncorrectImpls { if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { return; } - let ItemKind::Impl(imp) = item.kind else { + let ItemKind::Impl(_) = item.kind else { return; }; let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { @@ -186,9 +185,9 @@ impl LateLintPass<'_> for IncorrectImpls { .get(&sym::Ord) && implements_trait( cx, - hir_ty_to_ty(cx.tcx, imp.self_ty), + trait_impl.self_ty(), *ord_def_id, - trait_impl.args, + &[], ) { if block.stmts.is_empty() From c3881569afd049d391d6943bff9ee7317fe98211 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:45:37 +0200 Subject: [PATCH 13/90] new lint: `format_collect` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/format_collect.rs | 33 ++++++++++++++++ clippy_lints/src/methods/mod.rs | 35 ++++++++++++++++- src/main.rs | 3 +- tests/ui/format_collect.rs | 26 +++++++++++++ tests/ui/format_collect.stderr | 44 ++++++++++++++++++++++ 7 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/methods/format_collect.rs create mode 100644 tests/ui/format_collect.rs create mode 100644 tests/ui/format_collect.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b6e3b865f..ea8450ed5c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4865,6 +4865,7 @@ Released 2018-09-13 [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref +[`format_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_collect [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 498d657b31f..7472158a81c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -338,6 +338,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::FILTER_NEXT_INFO, crate::methods::FLAT_MAP_IDENTITY_INFO, crate::methods::FLAT_MAP_OPTION_INFO, + crate::methods::FORMAT_COLLECT_INFO, crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, crate::methods::GET_FIRST_INFO, crate::methods::GET_LAST_WITH_LEN_INFO, diff --git a/clippy_lints/src/methods/format_collect.rs b/clippy_lints/src/methods/format_collect.rs new file mode 100644 index 00000000000..74eb80782d6 --- /dev/null +++ b/clippy_lints/src/methods/format_collect.rs @@ -0,0 +1,33 @@ +use super::FORMAT_COLLECT; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::is_format_macro; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::ty::is_type_lang_item; +use rustc_hir::Expr; +use rustc_hir::ExprKind; +use rustc_hir::LangItem; +use rustc_lint::LateContext; +use rustc_span::Span; + +fn tail_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Block(block, _) if !expr.span.from_expansion() => block.expr, + _ => Some(expr), + } +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { + if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + && let ExprKind::Closure(closure) = map_arg.kind + && let body = cx.tcx.hir().body(closure.body) + && let Some(value) = tail_expr(body.value) + && let Some(mac) = root_macro_call_first_node(cx, value) + && is_format_macro(cx, mac.def_id) + { + span_lint_and_then(cx, FORMAT_COLLECT, expr.span, "use of `format!` to build up a string from an iterator", |diag| { + diag.span_help(map_span, "call `fold` instead") + .span_help(value.span.source_callsite(), "... and use the `write!` macro here") + .note("this can be written more efficiently by appending to a `String` directly"); + }); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2747ab5615c..f55a7727fad 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,6 +26,7 @@ mod filter_map_next; mod filter_next; mod flat_map_identity; mod flat_map_option; +mod format_collect; mod from_iter_instead_of_collect; mod get_first; mod get_last_with_len; @@ -3378,6 +3379,36 @@ declare_clippy_lint! { "calling `Stdin::read_line`, then trying to parse it without first trimming" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.map(|_| format!(..)).collect::()`. + /// + /// ### Why is this bad? + /// This allocates a new string for every element in the iterator. + /// This can be done more efficiently by creating the `String` once and appending to it using `Iterator::fold`. + /// + /// ### Example + /// ```rust + /// fn hex_encode(bytes: &[u8]) -> String { + /// bytes.iter().map(|b| format!("{b:02X}")).collect() + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt::Write; + /// fn hex_encode(bytes: &[u8]) -> String { + /// bytes.iter().fold(String::new(), |mut output, b| { + /// let _ = write!(output, "{b:02X}"); + /// output + /// }) + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub FORMAT_COLLECT, + perf, + "`format!`ing every element in a collection, then collecting the strings into a new `String`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3512,6 +3543,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_LITERAL_UNWRAP, DRAIN_COLLECT, MANUAL_TRY_FOLD, + FORMAT_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3733,8 +3765,9 @@ impl Methods { Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, - Some(("map", m_recv, [m_arg], _, _)) => { + Some(("map", m_recv, [m_arg], m_ident_span, _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg); + format_collect::check(cx, expr, m_arg, m_ident_span); }, Some(("take", take_self_arg, [take_arg], _, _)) => { if self.msrv.meets(msrvs::STR_REPEAT) { diff --git a/src/main.rs b/src/main.rs index cdc85cb33ca..26b655076cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,8 +132,7 @@ impl ClippyCmd { let clippy_args: String = self .clippy_args .iter() - .map(|arg| format!("{arg}__CLIPPY_HACKERY__")) - .collect(); + .fold(String::new(), |s, arg| s + arg + "__CLIPPY_HACKERY__"); // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. let terminal_width = termize::dimensions().map_or(0, |(w, _)| w); diff --git a/tests/ui/format_collect.rs b/tests/ui/format_collect.rs new file mode 100644 index 00000000000..ecadb43ecb4 --- /dev/null +++ b/tests/ui/format_collect.rs @@ -0,0 +1,26 @@ +#![allow(unused, dead_code)] +#![warn(clippy::format_collect)] + +fn hex_encode(bytes: &[u8]) -> String { + bytes.iter().map(|b| format!("{b:02X}")).collect() +} + +macro_rules! fmt { + ($x:ident) => { + format!("{x:02X}", x = $x) + }; +} + +fn from_macro(bytes: &[u8]) -> String { + bytes.iter().map(|x| fmt!(x)).collect() +} + +fn with_block() -> String { + (1..10) + .map(|s| { + let y = 1; + format!("{s} {y}") + }) + .collect() +} +fn main() {} diff --git a/tests/ui/format_collect.stderr b/tests/ui/format_collect.stderr new file mode 100644 index 00000000000..cd71a853762 --- /dev/null +++ b/tests/ui/format_collect.stderr @@ -0,0 +1,44 @@ +error: use of `format!` to build up a string from an iterator + --> $DIR/format_collect.rs:5:5 + | +LL | bytes.iter().map(|b| format!("{b:02X}")).collect() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: call `fold` instead + --> $DIR/format_collect.rs:5:18 + | +LL | bytes.iter().map(|b| format!("{b:02X}")).collect() + | ^^^ +help: ... and use the `write!` macro here + --> $DIR/format_collect.rs:5:26 + | +LL | bytes.iter().map(|b| format!("{b:02X}")).collect() + | ^^^^^^^^^^^^^^^^^^ + = note: this can be written more efficiently by appending to a `String` directly + = note: `-D clippy::format-collect` implied by `-D warnings` + +error: use of `format!` to build up a string from an iterator + --> $DIR/format_collect.rs:19:5 + | +LL | / (1..10) +LL | | .map(|s| { +LL | | let y = 1; +LL | | format!("{s} {y}") +LL | | }) +LL | | .collect() + | |__________________^ + | +help: call `fold` instead + --> $DIR/format_collect.rs:20:10 + | +LL | .map(|s| { + | ^^^ +help: ... and use the `write!` macro here + --> $DIR/format_collect.rs:22:13 + | +LL | format!("{s} {y}") + | ^^^^^^^^^^^^^^^^^^ + = note: this can be written more efficiently by appending to a `String` directly + +error: aborting due to 2 previous errors + From c83d58f5076f3a79ea79514395a02babd15687b1 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:20:36 +0200 Subject: [PATCH 14/90] document that `write!`ing into a string never fails --- clippy_lints/src/methods/format_collect.rs | 16 ++++++------- clippy_lints/src/methods/mod.rs | 7 +++++- tests/ui/format_collect.rs | 5 +++++ tests/ui/format_collect.stderr | 26 ++++++++++++++++++---- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/format_collect.rs b/clippy_lints/src/methods/format_collect.rs index 74eb80782d6..1f8863f8521 100644 --- a/clippy_lints/src/methods/format_collect.rs +++ b/clippy_lints/src/methods/format_collect.rs @@ -1,17 +1,17 @@ use super::FORMAT_COLLECT; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::is_format_macro; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{is_format_macro, root_macro_call_first_node}; use clippy_utils::ty::is_type_lang_item; -use rustc_hir::Expr; -use rustc_hir::ExprKind; -use rustc_hir::LangItem; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; -fn tail_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { +/// Same as `peel_blocks` but only actually considers blocks that are not from an expansion. +/// This is needed because always calling `peel_blocks` would otherwise remove parts of the +/// `format!` macro, which would cause `root_macro_call_first_node` to return `None`. +fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { - ExprKind::Block(block, _) if !expr.span.from_expansion() => block.expr, + ExprKind::Block(block, _) if !expr.span.from_expansion() => peel_non_expn_blocks(block.expr?), _ => Some(expr), } } @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, m if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) && let ExprKind::Closure(closure) = map_arg.kind && let body = cx.tcx.hir().body(closure.body) - && let Some(value) = tail_expr(body.value) + && let Some(value) = peel_non_expn_blocks(body.value) && let Some(mac) = root_macro_call_first_node(cx, value) && is_format_macro(cx, mac.def_id) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f55a7727fad..81f59f68ec6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3385,7 +3385,12 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// This allocates a new string for every element in the iterator. - /// This can be done more efficiently by creating the `String` once and appending to it using `Iterator::fold`. + /// This can be done more efficiently by creating the `String` once and appending to it in `Iterator::fold`, + /// using either the `write!` macro which supports exactly the same syntax as the `format!` macro, + /// or concatenating with `+` in case the iterator yields `&str`/`String`. + /// + /// Note also that `write!`-ing into a `String` can never fail, despite the return type of `write!` being `std::fmt::Result`, + /// so it can be safely ignored or unwrapped. /// /// ### Example /// ```rust diff --git a/tests/ui/format_collect.rs b/tests/ui/format_collect.rs index ecadb43ecb4..c7f2b7b6950 100644 --- a/tests/ui/format_collect.rs +++ b/tests/ui/format_collect.rs @@ -5,6 +5,11 @@ fn hex_encode(bytes: &[u8]) -> String { bytes.iter().map(|b| format!("{b:02X}")).collect() } +#[rustfmt::skip] +fn hex_encode_deep(bytes: &[u8]) -> String { + bytes.iter().map(|b| {{{{{ format!("{b:02X}") }}}}}).collect() +} + macro_rules! fmt { ($x:ident) => { format!("{x:02X}", x = $x) diff --git a/tests/ui/format_collect.stderr b/tests/ui/format_collect.stderr index cd71a853762..d918f1ed466 100644 --- a/tests/ui/format_collect.stderr +++ b/tests/ui/format_collect.stderr @@ -18,7 +18,25 @@ LL | bytes.iter().map(|b| format!("{b:02X}")).collect() = note: `-D clippy::format-collect` implied by `-D warnings` error: use of `format!` to build up a string from an iterator - --> $DIR/format_collect.rs:19:5 + --> $DIR/format_collect.rs:10:5 + | +LL | bytes.iter().map(|b| {{{{{ format!("{b:02X}") }}}}}).collect() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: call `fold` instead + --> $DIR/format_collect.rs:10:18 + | +LL | bytes.iter().map(|b| {{{{{ format!("{b:02X}") }}}}}).collect() + | ^^^ +help: ... and use the `write!` macro here + --> $DIR/format_collect.rs:10:32 + | +LL | bytes.iter().map(|b| {{{{{ format!("{b:02X}") }}}}}).collect() + | ^^^^^^^^^^^^^^^^^^ + = note: this can be written more efficiently by appending to a `String` directly + +error: use of `format!` to build up a string from an iterator + --> $DIR/format_collect.rs:24:5 | LL | / (1..10) LL | | .map(|s| { @@ -29,16 +47,16 @@ LL | | .collect() | |__________________^ | help: call `fold` instead - --> $DIR/format_collect.rs:20:10 + --> $DIR/format_collect.rs:25:10 | LL | .map(|s| { | ^^^ help: ... and use the `write!` macro here - --> $DIR/format_collect.rs:22:13 + --> $DIR/format_collect.rs:27:13 | LL | format!("{s} {y}") | ^^^^^^^^^^^^^^^^^^ = note: this can be written more efficiently by appending to a `String` directly -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From 74a77047da404a0b69db4b58957127e415c4d20d Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Mon, 17 Jul 2023 05:33:41 -0500 Subject: [PATCH 15/90] Rewrite [`tuple_array_conversions`] --- clippy_lints/src/tuple_array_conversions.rs | 312 +++++++++----------- tests/ui/tuple_array_conversions.rs | 30 ++ tests/ui/tuple_array_conversions.stderr | 36 +-- 3 files changed, 194 insertions(+), 184 deletions(-) diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 2e00ed042a8..791cbe2fead 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,21 +1,29 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{is_from_proc_macro, path_to_local}; +use itertools::Itertools; use rustc_ast::LitKind; -use rustc_hir::{Expr, ExprKind, HirId, Node, Pat}; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use std::iter::once; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does /// Checks for tuple<=>array conversions that are not done with `.into()`. /// /// ### Why is this bad? - /// It may be unnecessary complexity. `.into()` works for converting tuples - /// <=> arrays of up to 12 elements and may convey intent more clearly. + /// It may be unnecessary complexity. `.into()` works for converting tuples<=> arrays of up to + /// 12 elements and conveys the intent more clearly, while also leaving less room for hard to + /// spot bugs! + /// + /// ### Known issues + /// The suggested code may hide potential asymmetry in some cases. See + /// [#11085](https://github.com/rust-lang/rust-clippy/issues/11085) for more info. /// /// ### Example /// ```rust,ignore @@ -41,130 +49,152 @@ pub struct TupleArrayConversions { impl LateLintPass<'_> for TupleArrayConversions { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) { - match expr.kind { - ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements), - ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements), - _ => {}, - } + if in_external_macro(cx.sess(), expr.span) || !self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) { + return; + } + + match expr.kind { + ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements), + ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements), + _ => {}, } } extract_msrv_attr!(LateContext); } -#[expect( - clippy::blocks_in_if_conditions, - reason = "not a FP, but this is much easier to understand" -)] fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) { - if should_lint( - cx, - elements, - // This is cursed. - Some, - |(first_id, local)| { - if let Node::Pat(pat) = local - && let parent = parent_pat(cx, pat) - && parent.hir_id == first_id - { - return matches!( - cx.typeck_results().pat_ty(parent).peel_refs().kind(), - ty::Tuple(len) if len.len() == elements.len() - ); - } + let (ty::Array(ty, _) | ty::Slice(ty)) = cx.typeck_results().expr_ty(expr).kind() else { + unreachable!("`expr` must be an array or slice due to `ExprKind::Array`"); + }; - false - }, - ) || should_lint( - cx, - elements, - |(i, expr)| { - if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() { - return Some((i, path)); - }; - - None - }, - |(first_id, local)| { - if let Node::Pat(pat) = local - && let parent = parent_pat(cx, pat) - && parent.hir_id == first_id - { - return matches!( - cx.typeck_results().pat_ty(parent).peel_refs().kind(), - ty::Tuple(len) if len.len() == elements.len() - ); - } - - false - }, - ) { - emit_lint(cx, expr, ToType::Array); + if let [first, ..] = elements + && let Some(locals) = (match first.kind { + ExprKind::Field(_, _) => elements + .iter() + .enumerate() + .map(|(i, f)| -> Option<&'tcx Expr<'tcx>> { + let ExprKind::Field(lhs, ident) = f.kind else { + return None; + }; + (ident.name.as_str() == i.to_string()).then_some(lhs) + }) + .collect::>>(), + ExprKind::Path(_) => Some(elements.iter().collect()), + _ => None, + }) + && all_bindings_are_for_conv(cx, &[*ty], expr, elements, &locals, ToType::Array) + && !is_from_proc_macro(cx, expr) + { + span_lint_and_help( + cx, + TUPLE_ARRAY_CONVERSIONS, + expr.span, + "it looks like you're trying to convert a tuple to an array", + None, + "use `.into()` instead, or `<[T; N]>::from` if type annotations are needed", + ); } } -#[expect( - clippy::blocks_in_if_conditions, - reason = "not a FP, but this is much easier to understand" -)] -#[expect(clippy::cast_possible_truncation)] fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) { - if should_lint(cx, elements, Some, |(first_id, local)| { - if let Node::Pat(pat) = local - && let parent = parent_pat(cx, pat) - && parent.hir_id == first_id - { - return matches!( - cx.typeck_results().pat_ty(parent).peel_refs().kind(), - ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len() - ); - } + if let ty::Tuple(tys) = cx.typeck_results().expr_ty(expr).kind() + && let [first, ..] = elements + // Fix #11100 + && tys.iter().all_equal() + && let Some(locals) = (match first.kind { + ExprKind::Index(_, _) => elements + .iter() + .enumerate() + .map(|(i, i_expr)| -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Index(lhs, index) = i_expr.kind + && let ExprKind::Lit(lit) = index.kind + && let LitKind::Int(val, _) = lit.node + { + return (val == i as u128).then_some(lhs); + }; - false - }) || should_lint( - cx, - elements, - |(i, expr)| { - if let ExprKind::Index(path, index) = expr.kind - && let ExprKind::Lit(lit) = index.kind - && let LitKind::Int(val, _) = lit.node - && val as usize == i - { - return Some((i, path)); - }; - - None - }, - |(first_id, local)| { - if let Node::Pat(pat) = local - && let parent = parent_pat(cx, pat) - && parent.hir_id == first_id - { - return matches!( - cx.typeck_results().pat_ty(parent).peel_refs().kind(), - ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len() - ); - } - - false - }, - ) { - emit_lint(cx, expr, ToType::Tuple); + None + }) + .collect::>>(), + ExprKind::Path(_) => Some(elements.iter().collect()), + _ => None, + }) + && all_bindings_are_for_conv(cx, tys, expr, elements, &locals, ToType::Tuple) + && !is_from_proc_macro(cx, expr) + { + span_lint_and_help( + cx, + TUPLE_ARRAY_CONVERSIONS, + expr.span, + "it looks like you're trying to convert an array to a tuple", + None, + "use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed", + ); } } -/// Walks up the `Pat` until it's reached the final containing `Pat`. -fn parent_pat<'tcx>(cx: &LateContext<'tcx>, start: &'tcx Pat<'tcx>) -> &'tcx Pat<'tcx> { - let mut end = start; - for (_, node) in cx.tcx.hir().parent_iter(start.hir_id) { - if let Node::Pat(pat) = node { - end = pat; - } else { - break; - } - } - end +/// Checks that every binding in `elements` comes from the same parent `Pat` with the kind if there +/// is a parent `Pat`. Returns false in any of the following cases: +/// * `kind` does not match `pat.kind` +/// * one or more elements in `elements` is not a binding +/// * one or more bindings does not have the same parent `Pat` +/// * one or more bindings are used after `expr` +/// * the bindings do not all have the same type +#[expect(clippy::cast_possible_truncation)] +fn all_bindings_are_for_conv<'tcx>( + cx: &LateContext<'tcx>, + final_tys: &[Ty<'tcx>], + expr: &Expr<'_>, + elements: &[Expr<'_>], + locals: &[&Expr<'_>], + kind: ToType, +) -> bool { + let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { + return false; + }; + let Some(local_parents) = locals + .iter() + .map(|&l| cx.tcx.hir().find_parent(l)) + .collect::>>() + else { + return false; + }; + + local_parents + .iter() + .map(|node| match node { + Node::Pat(pat) => kind.eq(&pat.kind).then_some(pat.hir_id), + Node::Local(l) => Some(l.hir_id), + _ => None, + }) + .all_equal() + // Fix #11124, very convenient utils function! ❤️ + && locals + .iter() + .all(|&l| for_each_local_use_after_expr(cx, l, expr.hir_id, |_| ControlFlow::Break::<()>(())).is_continue()) + && local_parents.first().is_some_and(|node| { + let Some(ty) = match node { + Node::Pat(pat) => Some(pat.hir_id), + Node::Local(l) => Some(l.hir_id), + _ => None, + } + .map(|hir_id| cx.typeck_results().node_type(hir_id)) else { + return false; + }; + match (kind, ty.kind()) { + // Ensure the final type and the original type have the same length, and that there + // is no implicit `&mut`<=>`&` anywhere (#11100). Bit ugly, I know, but it works. + (ToType::Array, ty::Tuple(tys)) => { + tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal() + }, + (ToType::Tuple, ty::Array(ty, len)) => { + len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len() + && final_tys.iter().chain(once(ty)).all_equal() + }, + _ => false, + } + }) } #[derive(Clone, Copy)] @@ -173,61 +203,11 @@ enum ToType { Tuple, } -impl ToType { - fn msg(self) -> &'static str { +impl PartialEq> for ToType { + fn eq(&self, other: &PatKind<'_>) -> bool { match self { - ToType::Array => "it looks like you're trying to convert a tuple to an array", - ToType::Tuple => "it looks like you're trying to convert an array to a tuple", - } - } - - fn help(self) -> &'static str { - match self { - ToType::Array => "use `.into()` instead, or `<[T; N]>::from` if type annotations are needed", - ToType::Tuple => "use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed", + ToType::Array => matches!(other, PatKind::Tuple(_, _)), + ToType::Tuple => matches!(other, PatKind::Slice(_, _, _)), } } } - -fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToType) -> bool { - if !is_from_proc_macro(cx, expr) { - span_lint_and_help( - cx, - TUPLE_ARRAY_CONVERSIONS, - expr.span, - to_type.msg(), - None, - to_type.help(), - ); - - return true; - } - - false -} - -fn should_lint<'tcx>( - cx: &LateContext<'tcx>, - elements: &'tcx [Expr<'tcx>], - map: impl FnMut((usize, &'tcx Expr<'tcx>)) -> Option<(usize, &Expr<'_>)>, - predicate: impl FnMut((HirId, &Node<'tcx>)) -> bool, -) -> bool { - if let Some(elements) = elements - .iter() - .enumerate() - .map(map) - .collect::>>() - && let Some(locals) = elements - .iter() - .map(|(_, element)| path_to_local(element).and_then(|local| cx.tcx.hir().find(local))) - .collect::>>() - && let [first, rest @ ..] = &*locals - && let Node::Pat(first_pat) = first - && let parent = parent_pat(cx, first_pat).hir_id - && rest.iter().chain(once(first)).map(|i| (parent, i)).all(predicate) - { - return true; - } - - false -} diff --git a/tests/ui/tuple_array_conversions.rs b/tests/ui/tuple_array_conversions.rs index f96a7c97f1a..569415acbce 100644 --- a/tests/ui/tuple_array_conversions.rs +++ b/tests/ui/tuple_array_conversions.rs @@ -52,6 +52,36 @@ fn main() { let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect(); let t2: Vec<(u32, u32)> = v1.iter().map(|&[a, b]| (a, b)).collect(); } + // FP #11082; needs discussion + let (a, b) = (1.0f64, 2.0f64); + let _: &[f64] = &[a, b]; + // FP #11085; impossible to fix + let [src, dest]: [_; 2] = [1, 2]; + (src, dest); + // FP #11100 + fn issue_11100_array_to_tuple(this: [&mut i32; 2]) -> (&i32, &mut i32) { + let [input, output] = this; + (input, output) + } + + fn issue_11100_tuple_to_array<'a>(this: (&'a mut i32, &'a mut i32)) -> [&'a i32; 2] { + let (input, output) = this; + [input, output] + } + // FP #11124 + // tuple=>array + let (a, b) = (1, 2); + [a, b]; + let x = a; + // array=>tuple + let [a, b] = [1, 2]; + (a, b); + let x = a; + // FP #11144 + let (a, (b, c)) = (1, (2, 3)); + [a, c]; + let [[a, b], [c, d]] = [[1, 2], [3, 4]]; + (a, c); } #[clippy::msrv = "1.70.0"] diff --git a/tests/ui/tuple_array_conversions.stderr b/tests/ui/tuple_array_conversions.stderr index be653e8efb7..50bdcf29d1f 100644 --- a/tests/ui/tuple_array_conversions.stderr +++ b/tests/ui/tuple_array_conversions.stderr @@ -15,14 +15,6 @@ LL | let x = [x.0, x.1]; | = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed -error: it looks like you're trying to convert an array to a tuple - --> $DIR/tuple_array_conversions.rs:13:13 - | -LL | let x = (x[0], x[1]); - | ^^^^^^^^^^^^ - | - = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed - error: it looks like you're trying to convert a tuple to an array --> $DIR/tuple_array_conversions.rs:16:53 | @@ -55,8 +47,24 @@ LL | t1.iter().for_each(|&(a, b)| _ = [a, b]); | = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed +error: it looks like you're trying to convert a tuple to an array + --> $DIR/tuple_array_conversions.rs:57:22 + | +LL | let _: &[f64] = &[a, b]; + | ^^^^^^ + | + = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed + error: it looks like you're trying to convert an array to a tuple - --> $DIR/tuple_array_conversions.rs:69:13 + --> $DIR/tuple_array_conversions.rs:60:5 + | +LL | (src, dest); + | ^^^^^^^^^^^ + | + = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed + +error: it looks like you're trying to convert an array to a tuple + --> $DIR/tuple_array_conversions.rs:99:13 | LL | let x = (x[0], x[1]); | ^^^^^^^^^^^^ @@ -64,20 +72,12 @@ LL | let x = (x[0], x[1]); = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed error: it looks like you're trying to convert a tuple to an array - --> $DIR/tuple_array_conversions.rs:70:13 + --> $DIR/tuple_array_conversions.rs:100:13 | LL | let x = [x.0, x.1]; | ^^^^^^^^^^ | = help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed -error: it looks like you're trying to convert an array to a tuple - --> $DIR/tuple_array_conversions.rs:72:13 - | -LL | let x = (x[0], x[1]); - | ^^^^^^^^^^^^ - | - = help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed - error: aborting due to 10 previous errors From 36f84a6176ec2e3df036b0e0938b5395af0bfed1 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 17 Jul 2023 12:09:20 +0000 Subject: [PATCH 16/90] Move tuple_array_conversions to nursery --- clippy_lints/src/tuple_array_conversions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 2e00ed042a8..81270101538 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.72.0"] pub TUPLE_ARRAY_CONVERSIONS, - pedantic, + nursery, "checks for tuple<=>array conversions that are not done with `.into()`" } impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]); From d24f0d056fba88d0b7738f4bb8f3866c052c2b24 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 17 Jul 2023 13:40:55 +0000 Subject: [PATCH 17/90] Don't lint `needless_return` in fns across a macro boundary --- clippy_lints/src/returns.rs | 4 ++++ tests/ui/let_and_return.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 0212deda146..fe797219073 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -174,6 +174,10 @@ impl<'tcx> LateLintPass<'tcx> for Return { sp: Span, _: LocalDefId, ) { + if sp.from_expansion() { + return; + } + match kind { FnKind::Closure => { // when returning without value in closure, replace this `return` diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 7e4d783a026..64665cc906f 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -169,4 +169,14 @@ mod issue_5729 { } } +// https://github.com/rust-lang/rust-clippy/issues/11167 +macro_rules! fn_in_macro { + ($b:block) => { + fn f() -> usize $b + } +} +fn_in_macro!({ + return 1; +}); + fn main() {} From c26801ee927fc00723df60e548af881e9285ddae Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 17 Jul 2023 19:25:28 +0200 Subject: [PATCH 18/90] [`redundant_pattern_matching`]: don't lint if if guards are present --- .../src/matches/redundant_pattern_match.rs | 7 ++- .../redundant_pattern_matching_option.fixed | 4 ++ tests/ui/redundant_pattern_matching_option.rs | 4 ++ .../redundant_pattern_matching_option.stderr | 56 +++++++++---------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 92e8f7dbd43..a7c799f2de5 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -199,8 +199,11 @@ fn find_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + { + let node_pair = (&arm1.pat.kind, &arm2.pat.kind); if let Some(good_method) = found_good_method(cx, arms, node_pair) { let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index a63ba5809e4..d50c204875f 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -11,6 +11,10 @@ clippy::if_same_then_else )] +fn issue_11174(boolean: bool, maybe_some: Option) -> bool { + matches!(maybe_some, None if !boolean) +} + fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 631f9091672..ce365a87aa7 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -11,6 +11,10 @@ clippy::if_same_then_else )] +fn issue_11174(boolean: bool, maybe_some: Option) -> bool { + matches!(maybe_some, None if !boolean) +} + fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 097ca827a42..2d5e1a74e5e 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:15:12 + --> $DIR/redundant_pattern_matching_option.rs:19:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:17:12 + --> $DIR/redundant_pattern_matching_option.rs:21:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:19:12 + --> $DIR/redundant_pattern_matching_option.rs:23:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:25:15 + --> $DIR/redundant_pattern_matching_option.rs:29:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:27:15 + --> $DIR/redundant_pattern_matching_option.rs:31:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:29:15 + --> $DIR/redundant_pattern_matching_option.rs:33:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:32:15 + --> $DIR/redundant_pattern_matching_option.rs:36:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:40:5 + --> $DIR/redundant_pattern_matching_option.rs:44:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:45:5 + --> $DIR/redundant_pattern_matching_option.rs:49:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:50:13 + --> $DIR/redundant_pattern_matching_option.rs:54:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,55 +71,55 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:56:20 + --> $DIR/redundant_pattern_matching_option.rs:60:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:62:20 + --> $DIR/redundant_pattern_matching_option.rs:66:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:64:19 + --> $DIR/redundant_pattern_matching_option.rs:68:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:70:12 + --> $DIR/redundant_pattern_matching_option.rs:74:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:85:12 + --> $DIR/redundant_pattern_matching_option.rs:89:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:87:12 + --> $DIR/redundant_pattern_matching_option.rs:91:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:89:15 + --> $DIR/redundant_pattern_matching_option.rs:93:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:91:15 + --> $DIR/redundant_pattern_matching_option.rs:95:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:93:5 + --> $DIR/redundant_pattern_matching_option.rs:97:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:98:5 + --> $DIR/redundant_pattern_matching_option.rs:102:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,19 +137,19 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:106:12 + --> $DIR/redundant_pattern_matching_option.rs:110:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:107:12 + --> $DIR/redundant_pattern_matching_option.rs:111:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:113:5 + --> $DIR/redundant_pattern_matching_option.rs:117:5 | LL | / match x { LL | | Some(_) => true, @@ -158,7 +158,7 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:118:5 + --> $DIR/redundant_pattern_matching_option.rs:122:5 | LL | / match x { LL | | None => true, @@ -167,7 +167,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:123:5 + --> $DIR/redundant_pattern_matching_option.rs:127:5 | LL | / match x { LL | | Some(_) => false, @@ -176,7 +176,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:128:5 + --> $DIR/redundant_pattern_matching_option.rs:132:5 | LL | / match x { LL | | None => false, @@ -185,13 +185,13 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:143:13 + --> $DIR/redundant_pattern_matching_option.rs:147:13 | LL | let _ = matches!(x, Some(_)); | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:145:13 + --> $DIR/redundant_pattern_matching_option.rs:149:13 | LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` From e7fd44f213df3d8a8d7efcacc8e694a58046b824 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 17 Jul 2023 21:18:11 +0200 Subject: [PATCH 19/90] add guard to suggestion instead of not linting --- .../src/matches/redundant_pattern_match.rs | 77 +++++++++++++------ .../redundant_pattern_matching_option.fixed | 12 ++- tests/ui/redundant_pattern_matching_option.rs | 10 +++ .../redundant_pattern_matching_option.stderr | 74 ++++++++++-------- 4 files changed, 118 insertions(+), 55 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index a7c799f2de5..3994533322d 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -3,18 +3,20 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; -use clippy_utils::visitors::any_temporaries_need_ordered_drop; +use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr}; use clippy_utils::{higher, is_expn_of, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Ty}; use rustc_span::{sym, Symbol}; +use std::fmt::Write; +use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { @@ -199,36 +201,61 @@ fn find_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if let [arm1, arm2] = arms - && arm1.guard.is_none() - && arm2.guard.is_none() - { - let node_pair = (&arm1.pat.kind, &arm2.pat.kind); + if arms.len() == 2 { + let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - if let Some(good_method) = found_good_method(cx, arms, node_pair) { + if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; + let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_")); + + if let Some(guard) = maybe_guard { + let Guard::If(guard) = *guard else { return }; // `...is_none() && let ...` is a syntax error + + // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! + // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, + // counter to the intuition that it should be `Guard::IfLet`, so we need another check + // to see that there aren't any let chains anywhere in the guard, as that would break + // if we suggest `t.is_none() && (let X = y && z)` for: + // `match t { None if let X = y && z => true, _ => false }` + let has_nested_let_chain = for_each_expr(guard, |expr| { + if matches!(expr.kind, ExprKind::Let(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some(); + + if has_nested_let_chain { + return; + } + + let guard = Sugg::hir(cx, guard, ".."); + let _ = write!(sugg, " && {}", guard.maybe_par()); + } + span_lint_and_sugg( cx, REDUNDANT_PATTERN_MATCHING, span, &format!("redundant pattern matching, consider using `{good_method}`"), "try", - format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), + sugg, Applicability::MachineApplicable, ); } } } -fn found_good_method<'a>( +fn found_good_method<'tcx>( cx: &LateContext<'_>, - arms: &[Arm<'_>], + arms: &'tcx [Arm<'tcx>], node: (&PatKind<'_>, &PatKind<'_>), -) -> Option<&'a str> { +) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> { match node { ( PatKind::TupleStruct(ref path_left, patterns_left, _), @@ -314,7 +341,11 @@ fn get_ident(path: &QPath<'_>) -> Option { } } -fn get_good_method<'a>(cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>) -> Option<&'a str> { +fn get_good_method<'tcx>( + cx: &LateContext<'_>, + arms: &'tcx [Arm<'tcx>], + path_left: &QPath<'_>, +) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> { if let Some(name) = get_ident(path_left) { return match name.as_str() { "Ok" => { @@ -380,16 +411,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte } #[expect(clippy::too_many_arguments)] -fn find_good_method_for_match<'a>( +fn find_good_method_for_match<'a, 'tcx>( cx: &LateContext<'_>, - arms: &[Arm<'_>], + arms: &'tcx [Arm<'tcx>], path_left: &QPath<'_>, path_right: &QPath<'_>, expected_item_left: Item, expected_item_right: Item, should_be_left: &'a str, should_be_right: &'a str, -) -> Option<&'a str> { +) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> { let first_pat = arms[0].pat; let second_pat = arms[1].pat; @@ -407,22 +438,22 @@ fn find_good_method_for_match<'a>( match body_node_pair { (ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + (LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())), + (LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())), _ => None, }, _ => None, } } -fn find_good_method_for_matches_macro<'a>( +fn find_good_method_for_matches_macro<'a, 'tcx>( cx: &LateContext<'_>, - arms: &[Arm<'_>], + arms: &'tcx [Arm<'tcx>], path_left: &QPath<'_>, expected_item_left: Item, should_be_left: &'a str, should_be_right: &'a str, -) -> Option<&'a str> { +) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> { let first_pat = arms[0].pat; let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) { @@ -433,8 +464,8 @@ fn find_good_method_for_matches_macro<'a>( match body_node_pair { (ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + (LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())), + (LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())), _ => None, }, _ => None, diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index d50c204875f..d9fcd98c56e 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -10,9 +10,19 @@ clippy::equatable_if_let, clippy::if_same_then_else )] +#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { - matches!(maybe_some, None if !boolean) + maybe_some.is_none() && (!boolean) +} + +fn issue_11174_edge_cases(boolean: bool, boolean2: bool, maybe_some: Option) { + let _ = maybe_some.is_none() && (boolean || boolean2); // guard needs parentheses + let _ = match maybe_some { // can't use `matches!` here + // because `expr` metavars in macros don't allow let exprs + None if let Some(x) = Some(0) && x > 5 => true, + _ => false + }; } fn main() { diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index ce365a87aa7..cbd9494f15a 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -10,11 +10,21 @@ clippy::equatable_if_let, clippy::if_same_then_else )] +#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { matches!(maybe_some, None if !boolean) } +fn issue_11174_edge_cases(boolean: bool, boolean2: bool, maybe_some: Option) { + let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses + let _ = match maybe_some { // can't use `matches!` here + // because `expr` metavars in macros don't allow let exprs + None if let Some(x) = Some(0) && x > 5 => true, + _ => false + }; +} + fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 2d5e1a74e5e..b0e43924dbc 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,49 +1,61 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:19:12 + --> $DIR/redundant_pattern_matching_option.rs:16:5 | -LL | if let None = None::<()> {} - | -------^^^^------------- help: try: `if None::<()>.is_none()` +LL | matches!(maybe_some, None if !boolean) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (!boolean)` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:20:13 + | +LL | let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (boolean || boolean2)` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:29:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try: `if None::<()>.is_none()` + error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:21:12 + --> $DIR/redundant_pattern_matching_option.rs:31:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:23:12 + --> $DIR/redundant_pattern_matching_option.rs:33:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:29:15 + --> $DIR/redundant_pattern_matching_option.rs:39:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:31:15 + --> $DIR/redundant_pattern_matching_option.rs:41:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:33:15 + --> $DIR/redundant_pattern_matching_option.rs:43:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:36:15 + --> $DIR/redundant_pattern_matching_option.rs:46:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:44:5 + --> $DIR/redundant_pattern_matching_option.rs:54:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +64,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:49:5 + --> $DIR/redundant_pattern_matching_option.rs:59:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +73,7 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:54:13 + --> $DIR/redundant_pattern_matching_option.rs:64:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,55 +83,55 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:60:20 + --> $DIR/redundant_pattern_matching_option.rs:70:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:66:20 + --> $DIR/redundant_pattern_matching_option.rs:76:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:68:19 + --> $DIR/redundant_pattern_matching_option.rs:78:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:74:12 + --> $DIR/redundant_pattern_matching_option.rs:84:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:89:12 + --> $DIR/redundant_pattern_matching_option.rs:99:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:91:12 + --> $DIR/redundant_pattern_matching_option.rs:101:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:93:15 + --> $DIR/redundant_pattern_matching_option.rs:103:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:95:15 + --> $DIR/redundant_pattern_matching_option.rs:105:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:97:5 + --> $DIR/redundant_pattern_matching_option.rs:107:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +140,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:102:5 + --> $DIR/redundant_pattern_matching_option.rs:112:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,19 +149,19 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:110:12 + --> $DIR/redundant_pattern_matching_option.rs:120:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:111:12 + --> $DIR/redundant_pattern_matching_option.rs:121:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:117:5 + --> $DIR/redundant_pattern_matching_option.rs:127:5 | LL | / match x { LL | | Some(_) => true, @@ -158,7 +170,7 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:122:5 + --> $DIR/redundant_pattern_matching_option.rs:132:5 | LL | / match x { LL | | None => true, @@ -167,7 +179,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:127:5 + --> $DIR/redundant_pattern_matching_option.rs:137:5 | LL | / match x { LL | | Some(_) => false, @@ -176,7 +188,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:132:5 + --> $DIR/redundant_pattern_matching_option.rs:142:5 | LL | / match x { LL | | None => false, @@ -185,16 +197,16 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:147:13 + --> $DIR/redundant_pattern_matching_option.rs:157:13 | LL | let _ = matches!(x, Some(_)); | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:149:13 + --> $DIR/redundant_pattern_matching_option.rs:159:13 | LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` -error: aborting due to 28 previous errors +error: aborting due to 30 previous errors From d1e1dcb1fbbc3ac2a62a06aa4e37546db05180db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 17 Jul 2023 17:49:47 +0000 Subject: [PATCH 20/90] Rename arg_iter to iter_instantiated --- clippy_lints/src/future_not_send.rs | 2 +- clippy_utils/src/ty.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index e54429aee8e..621415c881c 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; - for (p, _span) in preds.arg_iter_copied(cx.tcx, args) { + for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) { if let Some(trait_pred) = p.as_trait_clause() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index ad79143f4da..fd39a246f48 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -663,7 +663,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option sig_from_bounds( cx, ty, - cx.tcx.item_bounds(def_id).arg_iter(cx.tcx, args), + cx.tcx.item_bounds(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), @@ -739,7 +739,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option let mut output = None; let lang_items = cx.tcx.lang_items(); - for (pred, _) in cx.tcx.explicit_item_bounds(ty.def_id).arg_iter_copied(cx.tcx, ty.args) { + for (pred, _) in cx.tcx.explicit_item_bounds(ty.def_id).iter_instantiated_copied(cx.tcx, ty.args) { match pred.kind().skip_binder() { ty::ClauseKind::Trait(p) if (lang_items.fn_trait() == Some(p.def_id()) From c13cb54e25140afe2845eee3130b2b369d130d48 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:44:17 -0500 Subject: [PATCH 21/90] New lint `needless_return_with_try` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/returns.rs | 74 +++++++++++++++++++++++- tests/ui/needless_return_with_try.fixed | 40 +++++++++++++ tests/ui/needless_return_with_try.rs | 40 +++++++++++++ tests/ui/needless_return_with_try.stderr | 10 ++++ tests/ui/try_err.fixed | 6 +- tests/ui/try_err.rs | 6 +- tests/ui/try_err.stderr | 22 +++---- 9 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 tests/ui/needless_return_with_try.fixed create mode 100644 tests/ui/needless_return_with_try.rs create mode 100644 tests/ui/needless_return_with_try.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ea8450ed5c2..47b64d27ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5093,6 +5093,7 @@ Released 2018-09-13 [`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes [`needless_raw_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_strings [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return +[`needless_return_with_try`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_try [`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7472158a81c..7676e787ee7 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -569,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, crate::returns::NEEDLESS_RETURN_INFO, + crate::returns::NEEDLESS_RETURN_WITH_TRY_INFO, crate::same_name_method::SAME_NAME_METHOD_INFO, crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index fe797219073..58868e97e23 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; @@ -6,7 +6,9 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind}; +use rustc_hir::{ + Block, Body, Expr, ExprKind, FnDecl, ItemKind, LangItem, MatchSource, OwnerNode, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; @@ -77,6 +79,46 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements on `Err` paired with the `?` operator. + /// + /// ### Why is this bad? + /// The `return` is unnecessary. + /// + /// ### Example + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// simplify to + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// if paired with `try_err`, use instead: + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...); + /// } + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub NEEDLESS_RETURN_WITH_TRY, + style, + "using a return statement like `return Err(expr)?;` where removing it would suffice" +} + #[derive(PartialEq, Eq)] enum RetReplacement<'tcx> { Empty, @@ -116,9 +158,35 @@ impl<'tcx> ToString for RetReplacement<'tcx> { } } -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_TRY]); impl<'tcx> LateLintPass<'tcx> for Return { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !in_external_macro(cx.sess(), stmt.span) + && let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Ret(Some(ret)) = expr.kind + && let ExprKind::Match(.., MatchSource::TryDesugar) = ret.kind + // Ensure this is not the final stmt, otherwise removing it would cause a compile error + && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) + && let ItemKind::Fn(_, _, body) = item.kind + && let block = cx.tcx.hir().body(body).value + && let ExprKind::Block(block, _) = block.kind + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id != stmt.hir_id + && !is_from_proc_macro(cx, expr) + { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN_WITH_TRY, + expr.span.until(ret.span), + "unneeded `return` statement with `?` operator", + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + } + } + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { // we need both a let-binding stmt and an expr if_chain! { diff --git a/tests/ui/needless_return_with_try.fixed b/tests/ui/needless_return_with_try.fixed new file mode 100644 index 00000000000..96d3e6df2d1 --- /dev/null +++ b/tests/ui/needless_return_with_try.fixed @@ -0,0 +1,40 @@ +//@run-rustfix +//@aux-build:proc_macros.rs +#![allow( + clippy::needless_return, + clippy::no_effect, + clippy::unit_arg, + clippy::useless_conversion, + unused +)] + +#[macro_use] +extern crate proc_macros; + +fn a() -> u32 { + return 0; +} + +fn b() -> Result { + return Err(0); +} + +// Do not lint +fn c() -> Option<()> { + return None?; +} + +fn main() -> Result<(), ()> { + Err(())?; + return Ok::<(), ()>(()); + Err(())?; + Ok::<(), ()>(()); + return Err(().into()); + external! { + return Err(())?; + } + with_span! { + return Err(())?; + } + Err(()) +} diff --git a/tests/ui/needless_return_with_try.rs b/tests/ui/needless_return_with_try.rs new file mode 100644 index 00000000000..039afba0602 --- /dev/null +++ b/tests/ui/needless_return_with_try.rs @@ -0,0 +1,40 @@ +//@run-rustfix +//@aux-build:proc_macros.rs +#![allow( + clippy::needless_return, + clippy::no_effect, + clippy::unit_arg, + clippy::useless_conversion, + unused +)] + +#[macro_use] +extern crate proc_macros; + +fn a() -> u32 { + return 0; +} + +fn b() -> Result { + return Err(0); +} + +// Do not lint +fn c() -> Option<()> { + return None?; +} + +fn main() -> Result<(), ()> { + return Err(())?; + return Ok::<(), ()>(()); + Err(())?; + Ok::<(), ()>(()); + return Err(().into()); + external! { + return Err(())?; + } + with_span! { + return Err(())?; + } + Err(()) +} diff --git a/tests/ui/needless_return_with_try.stderr b/tests/ui/needless_return_with_try.stderr new file mode 100644 index 00000000000..72f2bba9200 --- /dev/null +++ b/tests/ui/needless_return_with_try.stderr @@ -0,0 +1,10 @@ +error: unneeded `return` statement with `?` operator + --> $DIR/needless_return_with_try.rs:28:5 + | +LL | return Err(())?; + | ^^^^^^^ help: remove it + | + = note: `-D clippy::needless-return-with-try` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 1816740870a..e7e2379fa4c 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,11 @@ //@aux-build:proc_macros.rs:proc-macro #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] +#![allow( + clippy::unnecessary_wraps, + clippy::needless_question_mark, + clippy::needless_return_with_try +)] extern crate proc_macros; use proc_macros::{external, inline_macros}; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 0e47c4d023e..ece21887ed3 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,11 @@ //@aux-build:proc_macros.rs:proc-macro #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] +#![allow( + clippy::unnecessary_wraps, + clippy::needless_question_mark, + clippy::needless_return_with_try +)] extern crate proc_macros; use proc_macros::{external, inline_macros}; diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 79f7b70224a..9968b383ebb 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:19:9 + --> $DIR/try_err.rs:23:9 | LL | Err(err)?; | ^^^^^^^^^ help: try: `return Err(err)` @@ -11,25 +11,25 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:29:9 + --> $DIR/try_err.rs:33:9 | LL | Err(err)?; | ^^^^^^^^^ help: try: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:49:17 + --> $DIR/try_err.rs:53:17 | LL | Err(err)?; | ^^^^^^^^^ help: try: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:68:17 + --> $DIR/try_err.rs:72:17 | LL | Err(err)?; | ^^^^^^^^^ help: try: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:88:23 + --> $DIR/try_err.rs:92:23 | LL | Err(_) => Err(1)?, | ^^^^^^^ help: try: `return Err(1)` @@ -37,7 +37,7 @@ LL | Err(_) => Err(1)?, = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:95:23 + --> $DIR/try_err.rs:99:23 | LL | Err(_) => Err(inline!(1))?, | ^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(1))` @@ -45,31 +45,31 @@ LL | Err(_) => Err(inline!(1))?, = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:122:9 + --> $DIR/try_err.rs:126:9 | LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:129:9 + --> $DIR/try_err.rs:133:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:131:9 + --> $DIR/try_err.rs:135:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:139:9 + --> $DIR/try_err.rs:143:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:148:16 + --> $DIR/try_err.rs:152:16 | LL | return Err(42)?; | ^^^^^^^^ help: try: `Err(42)` From 0d59d1d617531c1fe73cc94a4eaac7db5f23aa72 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Wed, 12 Jul 2023 21:48:22 -0500 Subject: [PATCH 22/90] Rename to `needless_return_with_question_mark` --- CHANGELOG.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/returns.rs | 10 +++++----- ....fixed => needless_return_with_question_mark.fixed} | 2 +- ...th_try.rs => needless_return_with_question_mark.rs} | 2 +- ...tderr => needless_return_with_question_mark.stderr} | 4 ++-- tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) rename tests/ui/{needless_return_with_try.fixed => needless_return_with_question_mark.fixed} (93%) rename tests/ui/{needless_return_with_try.rs => needless_return_with_question_mark.rs} (93%) rename tests/ui/{needless_return_with_try.stderr => needless_return_with_question_mark.stderr} (54%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b64d27ad7..a0a4a541cef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5093,7 +5093,7 @@ Released 2018-09-13 [`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes [`needless_raw_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_strings [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return -[`needless_return_with_try`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_try +[`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark [`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7676e787ee7..a75efd9bbb6 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -569,7 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, crate::returns::NEEDLESS_RETURN_INFO, - crate::returns::NEEDLESS_RETURN_WITH_TRY_INFO, + crate::returns::NEEDLESS_RETURN_WITH_QUESTION_MARK_INFO, crate::same_name_method::SAME_NAME_METHOD_INFO, crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 58868e97e23..e944da6bc81 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; +use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -113,8 +113,8 @@ declare_clippy_lint! { /// Ok(()) /// } /// ``` - #[clippy::version = "1.72.0"] - pub NEEDLESS_RETURN_WITH_TRY, + #[clippy::version = "1.73.0"] + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, style, "using a return statement like `return Err(expr)?;` where removing it would suffice" } @@ -158,7 +158,7 @@ impl<'tcx> ToString for RetReplacement<'tcx> { } } -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_TRY]); +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); impl<'tcx> LateLintPass<'tcx> for Return { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { { span_lint_and_sugg( cx, - NEEDLESS_RETURN_WITH_TRY, + NEEDLESS_RETURN_WITH_QUESTION_MARK, expr.span.until(ret.span), "unneeded `return` statement with `?` operator", "remove it", diff --git a/tests/ui/needless_return_with_try.fixed b/tests/ui/needless_return_with_question_mark.fixed similarity index 93% rename from tests/ui/needless_return_with_try.fixed rename to tests/ui/needless_return_with_question_mark.fixed index 96d3e6df2d1..d6e47d07b0f 100644 --- a/tests/ui/needless_return_with_try.fixed +++ b/tests/ui/needless_return_with_question_mark.fixed @@ -1,5 +1,5 @@ //@run-rustfix -//@aux-build:proc_macros.rs +//@aux-build:proc_macros.rs:proc-macro #![allow( clippy::needless_return, clippy::no_effect, diff --git a/tests/ui/needless_return_with_try.rs b/tests/ui/needless_return_with_question_mark.rs similarity index 93% rename from tests/ui/needless_return_with_try.rs rename to tests/ui/needless_return_with_question_mark.rs index 039afba0602..4fc04d363a9 100644 --- a/tests/ui/needless_return_with_try.rs +++ b/tests/ui/needless_return_with_question_mark.rs @@ -1,5 +1,5 @@ //@run-rustfix -//@aux-build:proc_macros.rs +//@aux-build:proc_macros.rs:proc-macro #![allow( clippy::needless_return, clippy::no_effect, diff --git a/tests/ui/needless_return_with_try.stderr b/tests/ui/needless_return_with_question_mark.stderr similarity index 54% rename from tests/ui/needless_return_with_try.stderr rename to tests/ui/needless_return_with_question_mark.stderr index 72f2bba9200..e1d91638d2c 100644 --- a/tests/ui/needless_return_with_try.stderr +++ b/tests/ui/needless_return_with_question_mark.stderr @@ -1,10 +1,10 @@ error: unneeded `return` statement with `?` operator - --> $DIR/needless_return_with_try.rs:28:5 + --> $DIR/needless_return_with_question_mark.rs:28:5 | LL | return Err(())?; | ^^^^^^^ help: remove it | - = note: `-D clippy::needless-return-with-try` implied by `-D warnings` + = note: `-D clippy::needless-return-with-question-mark` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index e7e2379fa4c..930489fab76 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -5,7 +5,7 @@ #![allow( clippy::unnecessary_wraps, clippy::needless_question_mark, - clippy::needless_return_with_try + clippy::needless_return_with_question_mark )] extern crate proc_macros; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index ece21887ed3..f5baf3d8f74 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -5,7 +5,7 @@ #![allow( clippy::unnecessary_wraps, clippy::needless_question_mark, - clippy::needless_return_with_try + clippy::needless_return_with_question_mark )] extern crate proc_macros; From 75e1329aac850a15f29a86ac685b3cb4794b8b4f Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Tue, 4 Jul 2023 13:36:56 -0500 Subject: [PATCH 23/90] New lint [`error_impl_error`] --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/error_impl_error.rs | 76 ++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/error_impl_error.rs | 71 ++++++++++++++++++++++++++ tests/ui/error_impl_error.stderr | 45 ++++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 clippy_lints/src/error_impl_error.rs create mode 100644 tests/ui/error_impl_error.rs create mode 100644 tests/ui/error_impl_error.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index db18bc296f7..0445e59480c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4819,6 +4819,7 @@ Released 2018-09-13 [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect +[`error_impl_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#error_impl_error [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_nesting`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 51035a9d66b..cd507d8a9e9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -155,6 +155,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::enum_variants::MODULE_INCEPTION_INFO, crate::enum_variants::MODULE_NAME_REPETITIONS_INFO, crate::equatable_if_let::EQUATABLE_IF_LET_INFO, + crate::error_impl_error::ERROR_IMPL_ERROR_INFO, crate::escape::BOXED_LOCAL_INFO, crate::eta_reduction::REDUNDANT_CLOSURE_INFO, crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO, diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs new file mode 100644 index 00000000000..7f361d9ba02 --- /dev/null +++ b/clippy_lints/src/error_impl_error.rs @@ -0,0 +1,76 @@ +use clippy_utils::{ + diagnostics::{span_lint, span_lint_hir_and_then}, + path_res, + ty::implements_trait, +}; +use rustc_hir::{def_id::DefId, Item, ItemKind, Node}; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for types named `Error` that implement `Error`. + /// + /// ### Why is this bad? + /// It can become confusing when a codebase has 20 types all named `Error`, requiring either + /// aliasing them in the `use` statement them or qualifying them like `my_module::Error`. This + /// severely hinders readability. + /// + /// ### Example + /// ```rust,ignore + /// #[derive(Debug)] + /// pub enum Error { ... } + /// + /// impl std::fmt::Display for Error { ... } + /// + /// impl std::error::Error for Error { ... } + /// ``` + #[clippy::version = "1.72.0"] + pub ERROR_IMPL_ERROR, + restriction, + "types named `Error` that implement `Error`" +} +declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); + +impl<'tcx> LateLintPass<'tcx> for ErrorImplError { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { + return; + }; + + match item.kind { + ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) + && item.ident.name == sym::Error => + { + span_lint( + cx, + ERROR_IMPL_ERROR, + item.ident.span, + "type alias named `Error` that implements `Error`", + ); + }, + ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) + && error_def_id == trait_def_id + && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) + && let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id) + && let Node::Item(ty_item) = cx.tcx.hir().get(hir_id) + && ty_item.ident.name == sym::Error => + { + span_lint_hir_and_then( + cx, + ERROR_IMPL_ERROR, + hir_id, + ty_item.ident.span, + "type named `Error` that implements `Error`", + |diag| { + diag.span_note(item.span, "`Error` was implemented here"); + } + ); + } + _ => {}, + } + {} + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ea25e4be9d..1609eb396b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -120,6 +120,7 @@ mod entry; mod enum_clike; mod enum_variants; mod equatable_if_let; +mod error_impl_error; mod escape; mod eta_reduction; mod excessive_bools; @@ -1080,6 +1081,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); + store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/error_impl_error.rs b/tests/ui/error_impl_error.rs new file mode 100644 index 00000000000..d85f9522081 --- /dev/null +++ b/tests/ui/error_impl_error.rs @@ -0,0 +1,71 @@ +#![allow(unused)] +#![warn(clippy::error_impl_error)] +#![no_main] + +mod a { + #[derive(Debug)] + struct Error; + + impl std::fmt::Display for Error { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::error::Error for Error {} +} + +mod b { + #[derive(Debug)] + enum Error {} + + impl std::fmt::Display for Error { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::error::Error for Error {} +} + +mod c { + union Error { + a: u32, + b: u32, + } + + impl std::fmt::Debug for Error { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::fmt::Display for Error { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::error::Error for Error {} +} + +mod d { + type Error = std::fmt::Error; +} + +mod e { + #[derive(Debug)] + struct MyError; + + impl std::fmt::Display for MyError { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::error::Error for MyError {} +} + +mod f { + type MyError = std::fmt::Error; +} diff --git a/tests/ui/error_impl_error.stderr b/tests/ui/error_impl_error.stderr new file mode 100644 index 00000000000..64af0594e0a --- /dev/null +++ b/tests/ui/error_impl_error.stderr @@ -0,0 +1,45 @@ +error: type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:7:12 + | +LL | struct Error; + | ^^^^^ + | +note: `Error` was implemented here + --> $DIR/error_impl_error.rs:15:5 + | +LL | impl std::error::Error for Error {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::error-impl-error` implied by `-D warnings` + +error: type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:20:10 + | +LL | enum Error {} + | ^^^^^ + | +note: `Error` was implemented here + --> $DIR/error_impl_error.rs:28:5 + | +LL | impl std::error::Error for Error {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:32:11 + | +LL | union Error { + | ^^^^^ + | +note: `Error` was implemented here + --> $DIR/error_impl_error.rs:49:5 + | +LL | impl std::error::Error for Error {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type alias named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:53:10 + | +LL | type Error = std::fmt::Error; + | ^^^^^ + +error: aborting due to 4 previous errors + From f4b3bb19c18cbce910875d6fc97de70005b8dcdb Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Thu, 6 Jul 2023 21:58:11 -0500 Subject: [PATCH 24/90] Add `allow_private_error` config option --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++++++ clippy_lints/src/error_impl_error.rs | 31 ++++++++++------- clippy_lints/src/utils/conf.rs | 4 +++ .../allow_private/clippy.toml | 1 + .../disallow_private/clippy.toml | 1 + .../error_impl_error.allow_private.stderr | 33 +++++++++++++++++++ .../error_impl_error.disallow_private.stderr} | 26 +++++++-------- .../error_impl_error}/error_impl_error.rs | 15 +++++---- .../toml_unknown_key/conf_unknown_key.stderr | 2 ++ 10 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 tests/ui-toml/error_impl_error/allow_private/clippy.toml create mode 100644 tests/ui-toml/error_impl_error/disallow_private/clippy.toml create mode 100644 tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr rename tests/{ui/error_impl_error.stderr => ui-toml/error_impl_error/error_impl_error.disallow_private.stderr} (64%) rename tests/{ui => ui-toml/error_impl_error}/error_impl_error.rs (77%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0445e59480c..0676752b766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5465,4 +5465,5 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`allow-private-error`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-error diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f8073dac330..1035f3e7fb6 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -730,3 +730,13 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) +## `allow-private-error` +Whether to allow private types named `Error` that implement `Error` + +**Default Value:** `true` (`bool`) + +--- +**Affected lints:** +* [`error_impl_error`](https://rust-lang.github.io/rust-clippy/master/index.html#error_impl_error) + + diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 7f361d9ba02..585a0ad04c7 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -1,12 +1,11 @@ -use clippy_utils::{ - diagnostics::{span_lint, span_lint_hir_and_then}, - path_res, - ty::implements_trait, -}; -use rustc_hir::{def_id::DefId, Item, ItemKind, Node}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; +use clippy_utils::path_res; +use clippy_utils::ty::implements_trait; +use rustc_hir::def_id::DefId; +use rustc_hir::{Item, ItemKind, Node}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -15,8 +14,9 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// It can become confusing when a codebase has 20 types all named `Error`, requiring either - /// aliasing them in the `use` statement them or qualifying them like `my_module::Error`. This - /// severely hinders readability. + /// aliasing them in the `use` statement or qualifying them like `my_module::Error`. This + /// hinders comprehension, as it requires you to memorize every variation of importing `Error` + /// used across a codebase. /// /// ### Example /// ```rust,ignore @@ -32,14 +32,22 @@ declare_clippy_lint! { restriction, "types named `Error` that implement `Error`" } -declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); +impl_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); + +#[derive(Clone, Copy)] +pub struct ErrorImplError { + pub allow_private_error: bool, +} impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + let Self { allow_private_error } = *self; let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { return; }; - + if allow_private_error && !cx.effective_visibilities.is_exported(item.owner_id.def_id) { + return; + } match item.kind { ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) && item.ident.name == sym::Error => @@ -71,6 +79,5 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { } _ => {}, } - {} } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 76654bfe536..22aef713db6 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -551,6 +551,10 @@ define_Conf! { /// /// Whether to allow `r#""#` when `r""` can be used (allow_one_hash_in_raw_strings: bool = false), + /// Lint: ERROR_IMPL_ERROR. + /// + /// Whether to allow private types named `Error` that implement `Error` + (allow_private_error: bool = true), } /// Search for the configuration file. diff --git a/tests/ui-toml/error_impl_error/allow_private/clippy.toml b/tests/ui-toml/error_impl_error/allow_private/clippy.toml new file mode 100644 index 00000000000..04202148031 --- /dev/null +++ b/tests/ui-toml/error_impl_error/allow_private/clippy.toml @@ -0,0 +1 @@ +allow-private-error = true diff --git a/tests/ui-toml/error_impl_error/disallow_private/clippy.toml b/tests/ui-toml/error_impl_error/disallow_private/clippy.toml new file mode 100644 index 00000000000..d42da3160a9 --- /dev/null +++ b/tests/ui-toml/error_impl_error/disallow_private/clippy.toml @@ -0,0 +1 @@ +allow-private-error = false diff --git a/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr b/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr new file mode 100644 index 00000000000..520c3e0a3e6 --- /dev/null +++ b/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr @@ -0,0 +1,33 @@ +error: type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:10:16 + | +LL | pub struct Error; + | ^^^^^ + | +note: `Error` was implemented here + --> $DIR/error_impl_error.rs:18:5 + | +LL | impl std::error::Error for Error {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::error-impl-error` implied by `-D warnings` + +error: type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:35:15 + | +LL | pub union Error { + | ^^^^^ + | +note: `Error` was implemented here + --> $DIR/error_impl_error.rs:52:5 + | +LL | impl std::error::Error for Error {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type alias named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:56:14 + | +LL | pub type Error = std::fmt::Error; + | ^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/error_impl_error.stderr b/tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr similarity index 64% rename from tests/ui/error_impl_error.stderr rename to tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr index 64af0594e0a..d29c5ef374f 100644 --- a/tests/ui/error_impl_error.stderr +++ b/tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr @@ -1,45 +1,45 @@ error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:7:12 + --> $DIR/error_impl_error.rs:10:16 | -LL | struct Error; - | ^^^^^ +LL | pub struct Error; + | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:15:5 + --> $DIR/error_impl_error.rs:18:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::error-impl-error` implied by `-D warnings` error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:20:10 + --> $DIR/error_impl_error.rs:23:10 | LL | enum Error {} | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:28:5 + --> $DIR/error_impl_error.rs:31:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:32:11 + --> $DIR/error_impl_error.rs:35:15 | -LL | union Error { - | ^^^^^ +LL | pub union Error { + | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:49:5 + --> $DIR/error_impl_error.rs:52:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type alias named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:53:10 + --> $DIR/error_impl_error.rs:56:14 | -LL | type Error = std::fmt::Error; - | ^^^^^ +LL | pub type Error = std::fmt::Error; + | ^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/error_impl_error.rs b/tests/ui-toml/error_impl_error/error_impl_error.rs similarity index 77% rename from tests/ui/error_impl_error.rs rename to tests/ui-toml/error_impl_error/error_impl_error.rs index d85f9522081..3f14b777ee8 100644 --- a/tests/ui/error_impl_error.rs +++ b/tests/ui-toml/error_impl_error/error_impl_error.rs @@ -1,10 +1,13 @@ +//@revisions: allow_private disallow_private +//@[allow_private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/error_impl_error/allow_private +//@[disallow_private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/error_impl_error/disallow_private #![allow(unused)] #![warn(clippy::error_impl_error)] #![no_main] -mod a { +pub mod a { #[derive(Debug)] - struct Error; + pub struct Error; impl std::fmt::Display for Error { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -28,8 +31,8 @@ mod b { impl std::error::Error for Error {} } -mod c { - union Error { +pub mod c { + pub union Error { a: u32, b: u32, } @@ -49,8 +52,8 @@ mod c { impl std::error::Error for Error {} } -mod d { - type Error = std::fmt::Error; +pub mod d { + pub type Error = std::fmt::Error; } mod e { diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ba26e97730..227424f948c 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -6,6 +6,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-print-in-tests + allow-private-error allow-private-module-inception allow-unwrap-in-tests allowed-idents-below-min-chars @@ -75,6 +76,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-print-in-tests + allow-private-error allow-private-module-inception allow-unwrap-in-tests allowed-idents-below-min-chars From 9c6b17e24da64cb8c073943bd4457aafb8470a2c Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 18 Jul 2023 18:43:56 +0000 Subject: [PATCH 25/90] Remove `#![allow(unused)]` and `--crate-name` from new_lint tests --- clippy_dev/src/new_lint.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index f39bc06e6d7..e64cf2c8749 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -96,8 +96,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - let header = format!("//@compile-flags: --crate-name={lint_name}"); - write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; + write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; Ok(()) } @@ -113,7 +112,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); - let test_contents = get_test_file_contents(lint.name, None); + let test_contents = get_test_file_contents(lint.name); write_file(lint.project_root.join(&test_path), test_contents)?; println!("Generated test file: `{test_path}`"); @@ -195,23 +194,16 @@ pub(crate) fn get_stabilization_version() -> String { parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { - let mut contents = formatdoc!( +fn get_test_file_contents(lint_name: &str) -> String { + formatdoc!( r#" - #![allow(unused)] #![warn(clippy::{lint_name})] fn main() {{ // test code goes here }} "# - ); - - if let Some(header) = header_commands { - contents = format!("{header}\n{contents}"); - } - - contents + ) } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { From f539e1a1db6662d374860449fc6cc33fdbe9472f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 18 Jul 2023 18:45:10 +0000 Subject: [PATCH 26/90] Remove unused flags from ui-cargo tests --- tests/ui-cargo/cargo_common_metadata/fail/src/main.rs | 1 - tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs | 1 - .../ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs | 1 - tests/ui-cargo/cargo_common_metadata/pass/src/main.rs | 1 - .../cargo_common_metadata/pass_publish_empty/src/main.rs | 1 - .../cargo_common_metadata/pass_publish_false/src/main.rs | 1 - tests/ui-cargo/feature_name/fail/src/main.rs | 1 - tests/ui-cargo/feature_name/pass/src/main.rs | 1 - tests/ui-cargo/module_style/fail_mod_remap/src/main.rs | 1 + .../multiple_crate_versions/5041_allow_dev_build/src/main.rs | 1 - tests/ui-cargo/multiple_crate_versions/fail/src/main.rs | 1 - tests/ui-cargo/multiple_crate_versions/pass/src/main.rs | 1 - tests/ui-cargo/wildcard_dependencies/fail/src/main.rs | 1 - tests/ui-cargo/wildcard_dependencies/pass/src/main.rs | 1 - 14 files changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs index 1a69bb24101..c67166fc4b0 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/feature_name/fail/src/main.rs b/tests/ui-cargo/feature_name/fail/src/main.rs index 4dd9582aff8..74e40c09ebc 100644 --- a/tests/ui-cargo/feature_name/fail/src/main.rs +++ b/tests/ui-cargo/feature_name/fail/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=feature_name #![warn(clippy::redundant_feature_names)] #![warn(clippy::negative_feature_names)] diff --git a/tests/ui-cargo/feature_name/pass/src/main.rs b/tests/ui-cargo/feature_name/pass/src/main.rs index 4dd9582aff8..74e40c09ebc 100644 --- a/tests/ui-cargo/feature_name/pass/src/main.rs +++ b/tests/ui-cargo/feature_name/pass/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=feature_name #![warn(clippy::redundant_feature_names)] #![warn(clippy::negative_feature_names)] diff --git a/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs b/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs index c70d92e359e..ac21b3a4422 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs +++ b/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs @@ -1,3 +1,4 @@ +// FIXME: find a way to add rustflags to ui-cargo tests //@compile-flags: --remap-path-prefix {{src-base}}=/remapped #![warn(clippy::self_named_module_files)] diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs index ece260b743d..4bc61dd6299 100644 --- a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs index ece260b743d..4bc61dd6299 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs index ece260b743d..4bc61dd6299 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs index bb3a39d0720..3491ccb0d47 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs index bb3a39d0720..3491ccb0d47 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs @@ -1,4 +1,3 @@ -//@compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} From 8b0540bb46467cb4af35eed437994753957a43c6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Jul 2023 21:45:28 +0200 Subject: [PATCH 27/90] Fix async functions handling for `needless_pass_by_ref_mut` lint --- clippy_lints/src/needless_pass_by_ref_mut.rs | 101 ++++++++++++++----- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index cdb7ee48305..c399b43e251 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -3,14 +3,16 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{is_from_proc_macro, is_self}; use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::associated_body; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; @@ -102,17 +104,17 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { } let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id); - - match kind { + let is_async = match kind { FnKind::ItemFn(.., header) => { let attrs = cx.tcx.hir().attrs(hir_id); if header.abi != Abi::Rust || requires_exact_signature(attrs) { return; } + header.is_async() }, - FnKind::Method(..) => (), + FnKind::Method(.., sig) => sig.header.is_async(), FnKind::Closure => return, - } + }; // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { @@ -128,25 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig); // If there are no `&mut` argument, no need to go any further. - if !decl - .inputs - .iter() - .zip(fn_sig.inputs()) - .zip(body.params) - .any(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) - { - return; - } - - // Collect variables mutably used and spans which will need dereferencings from the - // function body. - let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { - let mut ctx = MutablyUsedVariablesCtxt::default(); - let infcx = cx.tcx.infer_ctxt().build(); - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - ctx - }; - let mut it = decl .inputs .iter() @@ -157,6 +140,32 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { if it.peek().is_none() { return; } + + // Collect variables mutably used and spans which will need dereferencings from the + // function body. + let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { + let mut ctx = MutablyUsedVariablesCtxt::default(); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); + if is_async { + let closures = ctx.async_closures.clone(); + let hir = cx.tcx.hir(); + for closure in closures { + ctx.prev_bind = None; + ctx.prev_move_to_closure.clear(); + if let Some(body) = hir + .find_by_def_id(closure) + .and_then(associated_body) + .map(|(_, body_id)| hir.body(body_id)) + { + euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) + .consume_body(body); + } + } + } + ctx + }; + let show_semver_warning = self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(fn_def_id); for ((&input, &_), arg) in it { // Only take `&mut` arguments. @@ -197,7 +206,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { struct MutablyUsedVariablesCtxt { mutably_used_vars: HirIdSet, prev_bind: Option, + prev_move_to_closure: HirIdSet, aliases: HirIdMap, + async_closures: FxHashSet, } impl MutablyUsedVariablesCtxt { @@ -213,16 +224,27 @@ impl MutablyUsedVariablesCtxt { impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { if let euv::Place { - base: euv::PlaceBase::Local(vid), + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), base_ty, .. } = &cmt.place { if let Some(bind_id) = self.prev_bind.take() { - self.aliases.insert(bind_id, *vid); - } else if matches!(base_ty.ref_mutability(), Some(Mutability::Mut)) { + if bind_id != *vid { + self.aliases.insert(bind_id, *vid); + } + } else if !self.prev_move_to_closure.contains(vid) + && matches!(base_ty.ref_mutability(), Some(Mutability::Mut)) + { self.add_mutably_used_var(*vid); } + self.prev_bind = None; + self.prev_move_to_closure.remove(vid); } } @@ -265,7 +287,30 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { self.prev_bind = None; } - fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + cmt: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, + cause: FakeReadCause, + _id: HirId, + ) { + if let euv::Place { + base: + euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), + .. + } = &cmt.place + { + if let FakeReadCause::ForLet(Some(inner)) = cause { + // Seems like we are inside an async function. We need to store the closure `DefId` + // to go through it afterwards. + self.async_closures.insert(inner); + self.aliases.insert(cmt.hir_id, *vid); + self.prev_move_to_closure.insert(*vid); + } + } + } fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { self.prev_bind = Some(id); From 5e9e46278e1e69b0c8379f05f986b548572bdba8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Jul 2023 21:45:35 +0200 Subject: [PATCH 28/90] Update `needless_pass_by_ref_mut` ui test --- tests/ui/needless_pass_by_ref_mut.rs | 86 +++++++++++++++++++++--- tests/ui/needless_pass_by_ref_mut.stderr | 56 ++++++++++++++- 2 files changed, 130 insertions(+), 12 deletions(-) diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 5e7280995c6..27e1fc075bb 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -91,15 +91,79 @@ impl Mut { // Should not warn. fn unused(_: &mut u32, _b: &mut u8) {} -fn main() { - let mut u = 0; - let mut v = vec![0]; - foo(&mut v, &0, &mut u); - foo2(&mut v); - foo3(&mut v); - foo4(&mut v); - foo5(&mut v); - alias_check(&mut v); - alias_check2(&mut v); - println!("{u}"); +// Should not warn. +async fn f1(x: &mut i32) { + *x += 1; +} +// Should not warn. +async fn f2(x: &mut i32, y: String) { + *x += 1; +} +// Should not warn. +async fn f3(x: &mut i32, y: String, z: String) { + *x += 1; +} +// Should not warn. +async fn f4(x: &mut i32, y: i32) { + *x += 1; +} +// Should not warn. +async fn f5(x: i32, y: &mut i32) { + *y += 1; +} +// Should not warn. +async fn f6(x: i32, y: &mut i32, z: &mut i32) { + *y += 1; + *z += 1; +} +// Should not warn. +async fn f7(x: &mut i32, y: i32, z: &mut i32, a: i32) { + *x += 1; + *z += 1; +} + +// Should warn. +async fn a1(x: &mut i32) { + println!("{:?}", x); +} +// Should warn. +async fn a2(x: &mut i32, y: String) { + println!("{:?}", x); +} +// Should warn. +async fn a3(x: &mut i32, y: String, z: String) { + println!("{:?}", x); +} +// Should warn. +async fn a4(x: &mut i32, y: i32) { + println!("{:?}", x); +} +// Should warn. +async fn a5(x: i32, y: &mut i32) { + println!("{:?}", x); +} +// Should warn. +async fn a6(x: i32, y: &mut i32) { + println!("{:?}", x); +} +// Should warn. +async fn a7(x: i32, y: i32, z: &mut i32) { + println!("{:?}", z); +} +// Should warn. +async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { + println!("{:?}", z); +} + +fn main() { + // let mut u = 0; + // let mut v = vec![0]; + // foo(&mut v, &0, &mut u); + // foo2(&mut v); + // foo3(&mut v); + // foo4(&mut v); + // foo5(&mut v); + // alias_check(&mut v); + // alias_check2(&mut v); + // println!("{u}"); } diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 5e9d80bb6c4..caf47bbce86 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -24,5 +24,59 @@ error: this argument is a mutable reference, but not used mutably LL | fn badger(&mut self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` -error: aborting due to 4 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:126:16 + | +LL | async fn a1(x: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:130:16 + | +LL | async fn a2(x: &mut i32, y: String) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:134:16 + | +LL | async fn a3(x: &mut i32, y: String, z: String) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:138:16 + | +LL | async fn a4(x: &mut i32, y: i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:142:24 + | +LL | async fn a5(x: i32, y: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:146:24 + | +LL | async fn a6(x: i32, y: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:150:32 + | +LL | async fn a7(x: i32, y: i32, z: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:154:24 + | +LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:154:45 + | +LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + +error: aborting due to 13 previous errors From 9d08502496f8a61bdc1f8d20ba075b6d0a5a8b87 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:39:17 -0500 Subject: [PATCH 29/90] New lint [`string_lit_chars_any`] --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 39 ++++++++++++- .../src/methods/string_lit_chars_any.rs | 58 +++++++++++++++++++ tests/ui/string_lit_chars_any.fixed | 50 ++++++++++++++++ tests/ui/string_lit_chars_any.rs | 50 ++++++++++++++++ tests/ui/string_lit_chars_any.stderr | 58 +++++++++++++++++++ 7 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/string_lit_chars_any.rs create mode 100644 tests/ui/string_lit_chars_any.fixed create mode 100644 tests/ui/string_lit_chars_any.rs create mode 100644 tests/ui/string_lit_chars_any.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index db18bc296f7..ece1111213c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5256,6 +5256,7 @@ Released 2018-09-13 [`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars [`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes +[`string_lit_chars_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_chars_any [`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 51035a9d66b..168dc9bf4e1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -405,6 +405,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, + crate::methods::STRING_LIT_CHARS_ANY_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 81f59f68ec6..6efeff92091 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -86,6 +86,7 @@ mod skip_while_next; mod stable_sort_primitive; mod str_splitn; mod string_extend_chars; +mod string_lit_chars_any; mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; @@ -115,7 +116,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -3379,6 +3380,34 @@ declare_clippy_lint! { "calling `Stdin::read_line`, then trying to parse it without first trimming" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.chars().any(|i| i == c)`. + /// + /// ### Why is this bad? + /// It's significantly slower than using a pattern instead, like + /// `matches!(c, '\\' | '.' | '+')`. + /// + /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice + /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available + /// for situations where that additional performance is absolutely necessary. + /// + /// ### Example + /// ```rust + /// # let c = 'c'; + /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + /// ``` + /// Use instead: + /// ```rust + /// # let c = 'c'; + /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + /// ``` + #[clippy::version = "1.72.0"] + pub STRING_LIT_CHARS_ANY, + restriction, + "checks for `.chars().any(|i| i == c)`" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `.map(|_| format!(..)).collect::()`. @@ -3549,6 +3578,7 @@ impl_lint_pass!(Methods => [ DRAIN_COLLECT, MANUAL_TRY_FOLD, FORMAT_COLLECT, + STRING_LIT_CHARS_ANY, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3923,6 +3953,13 @@ impl Methods { } } }, + ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params + && let Some(("chars", recv, _, _, _)) = method_call(recv) => + { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs new file mode 100644 index 00000000000..70da6ad58bd --- /dev/null +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -0,0 +1,58 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{Msrv, MATCHES_MACRO}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; +use itertools::Itertools; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, Param, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::STRING_LIT_CHARS_ANY; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &Expr<'_>, + param: &'tcx Param<'tcx>, + body: &Expr<'_>, + msrv: &Msrv, +) { + if msrv.meets(MATCHES_MACRO) + && is_trait_method(cx, expr, sym::Iterator) + && let PatKind::Binding(_, arg, _, _) = param.pat.kind + && let ExprKind::Lit(lit_kind) = recv.kind + && let LitKind::Str(val, _) = lit_kind.node + && let ExprKind::Binary(kind, lhs, rhs) = body.kind + && let BinOpKind::Eq = kind.node + && let Some(lhs_path) = path_to_local(lhs) + && let Some(rhs_path) = path_to_local(rhs) + && let scrutinee = match (lhs_path == arg, rhs_path == arg) { + (true, false) => rhs, + (false, true) => lhs, + _ => return, + } + && !is_from_proc_macro(cx, expr) + && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span) + { + // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then + // something like `r"\"` will become `'\'`, which is of course invalid + let pat_snip = val.as_str().chars().map(|c| format!("{c:?}")).join(" | "); + + span_lint_and_then( + cx, + STRING_LIT_CHARS_ANY, + expr.span, + "usage of `.chars().any(...)` to check if a char matches any from a string literal", + |diag| { + diag.span_suggestion_verbose( + expr.span, + "use `matches!(...)` instead", + format!("matches!({scrutinee_snip}, {pat_snip})"), + Applicability::MachineApplicable, + ); + } + ); + } +} diff --git a/tests/ui/string_lit_chars_any.fixed b/tests/ui/string_lit_chars_any.fixed new file mode 100644 index 00000000000..d7ab9c3397a --- /dev/null +++ b/tests/ui/string_lit_chars_any.fixed @@ -0,0 +1,50 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::eq_op, clippy::needless_raw_string_hashes, clippy::no_effect, unused)] +#![warn(clippy::string_lit_chars_any)] + +#[macro_use] +extern crate proc_macros; + +struct NotStringLit; + +impl NotStringLit { + fn chars(&self) -> impl Iterator { + "c".chars() + } +} + +fn main() { + let c = 'c'; + matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + #[rustfmt::skip] + matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + // Do not lint + NotStringLit.chars().any(|x| x == c); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| { + let c = 'c'; + x == c + }); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| { + 1; + x == c + }); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == x); + "\\.+*?()|[]{}^$#&-~".chars().any(|_x| c == c); + matches!( + c, + '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~' + ); + external! { + let c = 'c'; + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + } + with_span! { + span + let c = 'c'; + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + } +} diff --git a/tests/ui/string_lit_chars_any.rs b/tests/ui/string_lit_chars_any.rs new file mode 100644 index 00000000000..9408d7bb239 --- /dev/null +++ b/tests/ui/string_lit_chars_any.rs @@ -0,0 +1,50 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::eq_op, clippy::needless_raw_string_hashes, clippy::no_effect, unused)] +#![warn(clippy::string_lit_chars_any)] + +#[macro_use] +extern crate proc_macros; + +struct NotStringLit; + +impl NotStringLit { + fn chars(&self) -> impl Iterator { + "c".chars() + } +} + +fn main() { + let c = 'c'; + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| x == c); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| c == x); + r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| c == x); + #[rustfmt::skip] + "\\.+*?()|[]{}^$#&-~".chars().any(|x| { x == c }); + // Do not lint + NotStringLit.chars().any(|x| x == c); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| { + let c = 'c'; + x == c + }); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| { + 1; + x == c + }); + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == x); + "\\.+*?()|[]{}^$#&-~".chars().any(|_x| c == c); + matches!( + c, + '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~' + ); + external! { + let c = 'c'; + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + } + with_span! { + span + let c = 'c'; + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + } +} diff --git a/tests/ui/string_lit_chars_any.stderr b/tests/ui/string_lit_chars_any.stderr new file mode 100644 index 00000000000..ff951b73ded --- /dev/null +++ b/tests/ui/string_lit_chars_any.stderr @@ -0,0 +1,58 @@ +error: usage of `.chars().any(...)` to check if a char matches any from a string literal + --> $DIR/string_lit_chars_any.rs:19:5 + | +LL | "//.+*?()|[]{}^$#&-~".chars().any(|x| x == c); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::string-lit-chars-any` implied by `-D warnings` +help: use `matches!(...)` instead + | +LL | matches!(c, '//' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: usage of `.chars().any(...)` to check if a char matches any from a string literal + --> $DIR/string_lit_chars_any.rs:20:5 + | +LL | r#"/.+*?()|[]{}^$#&-~"#.chars().any(|x| x == c); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!(...)` instead + | +LL | matches!(c, '//' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: usage of `.chars().any(...)` to check if a char matches any from a string literal + --> $DIR/string_lit_chars_any.rs:21:5 + | +LL | "//.+*?()|[]{}^$#&-~".chars().any(|x| c == x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!(...)` instead + | +LL | matches!(c, '//' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: usage of `.chars().any(...)` to check if a char matches any from a string literal + --> $DIR/string_lit_chars_any.rs:22:5 + | +LL | r#"/.+*?()|[]{}^$#&-~"#.chars().any(|x| c == x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!(...)` instead + | +LL | matches!(c, '//' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: usage of `.chars().any(...)` to check if a char matches any from a string literal + --> $DIR/string_lit_chars_any.rs:24:5 + | +LL | "//.+*?()|[]{}^$#&-~".chars().any(|x| { x == c }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!(...)` instead + | +LL | matches!(c, '//' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 5 previous errors + From 19b0e84187fd0c7ada499adc256d671e2d6ad78c Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Tue, 18 Jul 2023 10:25:58 -0500 Subject: [PATCH 30/90] Remove the configuration option Also no longer lints non-exported types now --- CHANGELOG.md | 1 - book/src/lint_configuration.md | 10 ----- clippy_lints/src/error_impl_error.rs | 44 ++++++++++--------- clippy_lints/src/utils/conf.rs | 4 -- .../allow_private/clippy.toml | 1 - .../disallow_private/clippy.toml | 1 - .../error_impl_error.allow_private.stderr | 33 -------------- .../toml_unknown_key/conf_unknown_key.stderr | 2 - .../error_impl_error.rs | 30 ++++++++++--- .../error_impl_error.stderr} | 26 +++++------ 10 files changed, 60 insertions(+), 92 deletions(-) delete mode 100644 tests/ui-toml/error_impl_error/allow_private/clippy.toml delete mode 100644 tests/ui-toml/error_impl_error/disallow_private/clippy.toml delete mode 100644 tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr rename tests/{ui-toml/error_impl_error => ui}/error_impl_error.rs (75%) rename tests/{ui-toml/error_impl_error/error_impl_error.disallow_private.stderr => ui/error_impl_error.stderr} (55%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0676752b766..0445e59480c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5465,5 +5465,4 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings -[`allow-private-error`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-error diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 1035f3e7fb6..f8073dac330 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -730,13 +730,3 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) -## `allow-private-error` -Whether to allow private types named `Error` that implement `Error` - -**Default Value:** `true` (`bool`) - ---- -**Affected lints:** -* [`error_impl_error`](https://rust-lang.github.io/rust-clippy/master/index.html#error_impl_error) - - diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 585a0ad04c7..379af9b2234 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::path_res; use clippy_utils::ty::implements_trait; -use rustc_hir::def_id::DefId; -use rustc_hir::{Item, ItemKind, Node}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{Item, ItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_middle::ty::Visibility; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { @@ -30,48 +31,42 @@ declare_clippy_lint! { #[clippy::version = "1.72.0"] pub ERROR_IMPL_ERROR, restriction, - "types named `Error` that implement `Error`" -} -impl_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); - -#[derive(Clone, Copy)] -pub struct ErrorImplError { - pub allow_private_error: bool, + "exported types named `Error` that implement `Error`" } +declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - let Self { allow_private_error } = *self; let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { return; }; - if allow_private_error && !cx.effective_visibilities.is_exported(item.owner_id.def_id) { - return; - } + match item.kind { ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) - && item.ident.name == sym::Error => + && item.ident.name == sym::Error + && is_visible_outside_module(cx, item.owner_id.def_id) => { span_lint( cx, ERROR_IMPL_ERROR, item.ident.span, - "type alias named `Error` that implements `Error`", + "exported type alias named `Error` that implements `Error`", ); }, ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) && error_def_id == trait_def_id && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) && let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id) - && let Node::Item(ty_item) = cx.tcx.hir().get(hir_id) - && ty_item.ident.name == sym::Error => + && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) + && ident.name == sym::Error + && is_visible_outside_module(cx, def_id) => { span_lint_hir_and_then( cx, ERROR_IMPL_ERROR, hir_id, - ty_item.ident.span, - "type named `Error` that implements `Error`", + ident.span, + "exported type named `Error` that implements `Error`", |diag| { diag.span_note(item.span, "`Error` was implemented here"); } @@ -81,3 +76,12 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { } } } + +/// Do not lint private `Error`s, i.e., ones without any `pub` (minus `pub(self)` of course) and +/// which aren't reexported +fn is_visible_outside_module(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + !matches!( + cx.tcx.visibility(def_id), + Visibility::Restricted(mod_def_id) if cx.tcx.parent_module_from_def_id(def_id).to_def_id() == mod_def_id + ) +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 22aef713db6..76654bfe536 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -551,10 +551,6 @@ define_Conf! { /// /// Whether to allow `r#""#` when `r""` can be used (allow_one_hash_in_raw_strings: bool = false), - /// Lint: ERROR_IMPL_ERROR. - /// - /// Whether to allow private types named `Error` that implement `Error` - (allow_private_error: bool = true), } /// Search for the configuration file. diff --git a/tests/ui-toml/error_impl_error/allow_private/clippy.toml b/tests/ui-toml/error_impl_error/allow_private/clippy.toml deleted file mode 100644 index 04202148031..00000000000 --- a/tests/ui-toml/error_impl_error/allow_private/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -allow-private-error = true diff --git a/tests/ui-toml/error_impl_error/disallow_private/clippy.toml b/tests/ui-toml/error_impl_error/disallow_private/clippy.toml deleted file mode 100644 index d42da3160a9..00000000000 --- a/tests/ui-toml/error_impl_error/disallow_private/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -allow-private-error = false diff --git a/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr b/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr deleted file mode 100644 index 520c3e0a3e6..00000000000 --- a/tests/ui-toml/error_impl_error/error_impl_error.allow_private.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:10:16 - | -LL | pub struct Error; - | ^^^^^ - | -note: `Error` was implemented here - --> $DIR/error_impl_error.rs:18:5 - | -LL | impl std::error::Error for Error {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::error-impl-error` implied by `-D warnings` - -error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:35:15 - | -LL | pub union Error { - | ^^^^^ - | -note: `Error` was implemented here - --> $DIR/error_impl_error.rs:52:5 - | -LL | impl std::error::Error for Error {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: type alias named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:56:14 - | -LL | pub type Error = std::fmt::Error; - | ^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 227424f948c..6ba26e97730 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -6,7 +6,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-print-in-tests - allow-private-error allow-private-module-inception allow-unwrap-in-tests allowed-idents-below-min-chars @@ -76,7 +75,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-print-in-tests - allow-private-error allow-private-module-inception allow-unwrap-in-tests allowed-idents-below-min-chars diff --git a/tests/ui-toml/error_impl_error/error_impl_error.rs b/tests/ui/error_impl_error.rs similarity index 75% rename from tests/ui-toml/error_impl_error/error_impl_error.rs rename to tests/ui/error_impl_error.rs index 3f14b777ee8..40ce4181bf3 100644 --- a/tests/ui-toml/error_impl_error/error_impl_error.rs +++ b/tests/ui/error_impl_error.rs @@ -1,6 +1,3 @@ -//@revisions: allow_private disallow_private -//@[allow_private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/error_impl_error/allow_private -//@[disallow_private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/error_impl_error/disallow_private #![allow(unused)] #![warn(clippy::error_impl_error)] #![no_main] @@ -20,7 +17,7 @@ pub mod a { mod b { #[derive(Debug)] - enum Error {} + pub(super) enum Error {} impl std::fmt::Display for Error { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -58,7 +55,7 @@ pub mod d { mod e { #[derive(Debug)] - struct MyError; + pub(super) struct MyError; impl std::fmt::Display for MyError { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -69,6 +66,25 @@ mod e { impl std::error::Error for MyError {} } -mod f { - type MyError = std::fmt::Error; +pub mod f { + pub type MyError = std::fmt::Error; +} + +// Do not lint module-private types + +mod g { + #[derive(Debug)] + enum Error {} + + impl std::fmt::Display for Error { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + impl std::error::Error for Error {} +} + +mod h { + type Error = std::fmt::Error; } diff --git a/tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr b/tests/ui/error_impl_error.stderr similarity index 55% rename from tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr rename to tests/ui/error_impl_error.stderr index d29c5ef374f..f3e04b64167 100644 --- a/tests/ui-toml/error_impl_error/error_impl_error.disallow_private.stderr +++ b/tests/ui/error_impl_error.stderr @@ -1,42 +1,42 @@ -error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:10:16 +error: exported type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:7:16 | LL | pub struct Error; | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:18:5 + --> $DIR/error_impl_error.rs:15:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::error-impl-error` implied by `-D warnings` -error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:23:10 +error: exported type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:20:21 | -LL | enum Error {} - | ^^^^^ +LL | pub(super) enum Error {} + | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:31:5 + --> $DIR/error_impl_error.rs:28:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: type named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:35:15 +error: exported type named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:32:15 | LL | pub union Error { | ^^^^^ | note: `Error` was implemented here - --> $DIR/error_impl_error.rs:52:5 + --> $DIR/error_impl_error.rs:49:5 | LL | impl std::error::Error for Error {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: type alias named `Error` that implements `Error` - --> $DIR/error_impl_error.rs:56:14 +error: exported type alias named `Error` that implements `Error` + --> $DIR/error_impl_error.rs:53:14 | LL | pub type Error = std::fmt::Error; | ^^^^^ From 4c79d8ace5bf19d6f839b52138af014da570284f Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:55:07 -0500 Subject: [PATCH 31/90] new lint `iter_skip_zero` --- CHANGELOG.md | 1 + clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/iter_skip_zero.rs | 34 +++++++++++++++++ clippy_lints/src/methods/mod.rs | 34 ++++++++++++++++- tests/ui/iter_skip_zero.fixed | 25 +++++++++++++ tests/ui/iter_skip_zero.rs | 25 +++++++++++++ tests/ui/iter_skip_zero.stderr | 43 ++++++++++++++++++++++ 8 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/methods/iter_skip_zero.rs create mode 100644 tests/ui/iter_skip_zero.fixed create mode 100644 tests/ui/iter_skip_zero.rs create mode 100644 tests/ui/iter_skip_zero.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ece1111213c..acc9d8ffbf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4939,6 +4939,7 @@ Released 2018-09-13 [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next +[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero [`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain [`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index ae56f38d9ad..4b45b0c9eac 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -257,7 +257,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx // Will this work for more complex types? Probably not! if !snippet .split("->") - .skip(0) + .skip(1) .map(|s| { s.trim() == cast_from.to_string() || s.split("where").any(|ty| ty.trim() == cast_from.to_string()) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 168dc9bf4e1..47995fea9f9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -360,6 +360,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_ON_SINGLE_ITEMS_INFO, crate::methods::ITER_OVEREAGER_CLONED_INFO, crate::methods::ITER_SKIP_NEXT_INFO, + crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs new file mode 100644 index 00000000000..6b696b42a69 --- /dev/null +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -0,0 +1,34 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_from_proc_macro, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_SKIP_ZERO; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { + if !expr.span.from_expansion() + && is_trait_method(cx, expr, sym::Iterator) + && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| { + if let Constant::Int(arg) = constant { + Some(arg) + } else { + None + } + }) + && arg == 0 + && !is_from_proc_macro(cx, expr) + { + span_lint_and_then(cx, ITER_SKIP_ZERO, arg_expr.span, "usage of `.skip(0)`", |diag| { + diag.span_suggestion( + arg_expr.span, + "if you meant to skip the first element, use", + "1", + Applicability::MaybeIncorrect, + ) + .note("this call to `skip` does nothing and is useless; remove it"); + }); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6efeff92091..be71072acd6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -45,6 +45,7 @@ mod iter_nth_zero; mod iter_on_single_or_empty_collections; mod iter_overeager_cloned; mod iter_skip_next; +mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod manual_next_back; @@ -3443,6 +3444,27 @@ declare_clippy_lint! { "`format!`ing every element in a collection, then collecting the strings into a new `String`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.skip(0)` on iterators. + /// + /// ### Why is this bad? + /// This was likely intended to be `.skip(1)` to skip the first element, as `.skip(0)` does + /// nothing. If not, the call should be removed. + /// + /// ### Example + /// ```rust + /// let v = vec![1, 2, 3]; + /// let x = v.iter().skip(0).collect::>(); + /// let y = v.iter().collect::>(); + /// assert_eq!(x, y); + /// ``` + #[clippy::version = "1.72.0"] + pub ITER_SKIP_ZERO, + correctness, + "disallows `.skip(0)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3579,6 +3601,7 @@ impl_lint_pass!(Methods => [ MANUAL_TRY_FOLD, FORMAT_COLLECT, STRING_LIT_CHARS_ANY, + ITER_SKIP_ZERO, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3901,7 +3924,16 @@ impl Methods { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, - ("last", []) | ("skip", [_]) => { + ("skip", [arg]) => { + iter_skip_zero::check(cx, expr, arg); + + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); + } + } + } + ("last", []) => { if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); diff --git a/tests/ui/iter_skip_zero.fixed b/tests/ui/iter_skip_zero.fixed new file mode 100644 index 00000000000..1eb0984fe07 --- /dev/null +++ b/tests/ui/iter_skip_zero.fixed @@ -0,0 +1,25 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::useless_vec, unused)] +#![warn(clippy::iter_skip_zero)] + +#[macro_use] +extern crate proc_macros; + +use std::iter::once; + +fn main() { + let _ = [1, 2, 3].iter().skip(1); + let _ = vec![1, 2, 3].iter().skip(1); + let _ = once([1, 2, 3]).skip(1); + let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(1)).skip(1); + // Don't lint + let _ = [1, 2, 3].iter().skip(1); + let _ = vec![1, 2, 3].iter().skip(1); + external! { + let _ = [1, 2, 3].iter().skip(0); + } + with_span! { + let _ = [1, 2, 3].iter().skip(0); + } +} diff --git a/tests/ui/iter_skip_zero.rs b/tests/ui/iter_skip_zero.rs new file mode 100644 index 00000000000..8c103ab1d5b --- /dev/null +++ b/tests/ui/iter_skip_zero.rs @@ -0,0 +1,25 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::useless_vec, unused)] +#![warn(clippy::iter_skip_zero)] + +#[macro_use] +extern crate proc_macros; + +use std::iter::once; + +fn main() { + let _ = [1, 2, 3].iter().skip(0); + let _ = vec![1, 2, 3].iter().skip(0); + let _ = once([1, 2, 3]).skip(0); + let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0); + // Don't lint + let _ = [1, 2, 3].iter().skip(1); + let _ = vec![1, 2, 3].iter().skip(1); + external! { + let _ = [1, 2, 3].iter().skip(0); + } + with_span! { + let _ = [1, 2, 3].iter().skip(0); + } +} diff --git a/tests/ui/iter_skip_zero.stderr b/tests/ui/iter_skip_zero.stderr new file mode 100644 index 00000000000..80fecd59e6d --- /dev/null +++ b/tests/ui/iter_skip_zero.stderr @@ -0,0 +1,43 @@ +error: usage of `.skip(0)` + --> $DIR/iter_skip_zero.rs:12:35 + | +LL | let _ = [1, 2, 3].iter().skip(0); + | ^ help: if you meant to skip the first element, use: `1` + | + = note: this call to `skip` does nothing and is useless; remove it + = note: `-D clippy::iter-skip-zero` implied by `-D warnings` + +error: usage of `.skip(0)` + --> $DIR/iter_skip_zero.rs:13:39 + | +LL | let _ = vec![1, 2, 3].iter().skip(0); + | ^ help: if you meant to skip the first element, use: `1` + | + = note: this call to `skip` does nothing and is useless; remove it + +error: usage of `.skip(0)` + --> $DIR/iter_skip_zero.rs:14:34 + | +LL | let _ = once([1, 2, 3]).skip(0); + | ^ help: if you meant to skip the first element, use: `1` + | + = note: this call to `skip` does nothing and is useless; remove it + +error: usage of `.skip(0)` + --> $DIR/iter_skip_zero.rs:15:71 + | +LL | let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0); + | ^ help: if you meant to skip the first element, use: `1` + | + = note: this call to `skip` does nothing and is useless; remove it + +error: usage of `.skip(0)` + --> $DIR/iter_skip_zero.rs:15:62 + | +LL | let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0); + | ^ help: if you meant to skip the first element, use: `1` + | + = note: this call to `skip` does nothing and is useless; remove it + +error: aborting due to 5 previous errors + From dcfc6a20dba6333852c5b55741b47c6f9d2c8db4 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Fri, 16 Jun 2023 22:01:15 -0500 Subject: [PATCH 32/90] check for fully qualified paths in `unnecessary_cast` --- clippy_lints/src/casts/unnecessary_cast.rs | 11 ++- tests/ui/unnecessary_cast.fixed | 17 +++++ tests/ui/unnecessary_cast.rs | 17 +++++ tests/ui/unnecessary_cast.stderr | 88 ++++++++++++---------- 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 4b45b0c9eac..9130f5df02f 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -254,13 +254,20 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx // function's declaration snippet is exactly equal to the `Ty`. That way, we can // see whether it's a type alias. // - // Will this work for more complex types? Probably not! + // FIXME: This won't work if the type is given an alias through `use`, should we + // consider this a type alias as well? if !snippet .split("->") .skip(1) .map(|s| { s.trim() == cast_from.to_string() - || s.split("where").any(|ty| ty.trim() == cast_from.to_string()) + || s.trim().contains(&format!("::{cast_from}")) + || s.split("where").any(|ty| { + ty.trim() == cast_from.to_string() + || ty.trim() == cast_from.to_string() + // Fully qualified path, or something silly like `::u32` + || s.trim().contains(&format!("::{cast_from}")) + }) }) .any(|a| a) { diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 8efd44baf59..2bf02f13414 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -38,6 +38,16 @@ mod fake_libc { } } +fn aaa() -> ::std::primitive::u32 { + 0 +} + +use std::primitive::u32 as UnsignedThirtyTwoBitInteger; + +fn bbb() -> UnsignedThirtyTwoBitInteger { + 0 +} + #[rustfmt::skip] fn main() { // Test cast_unnecessary @@ -105,6 +115,13 @@ fn main() { extern_fake_libc::getpid_SAFE_TRUTH() as i32; let pid = unsafe { fake_libc::getpid() }; pid as i32; + aaa(); + let x = aaa(); + aaa(); + // Will not lint currently. + bbb() as u32; + let x = bbb(); + bbb() as u32; let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index c7723ef51f9..25b6b0f9b07 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -38,6 +38,16 @@ mod fake_libc { } } +fn aaa() -> ::std::primitive::u32 { + 0 +} + +use std::primitive::u32 as UnsignedThirtyTwoBitInteger; + +fn bbb() -> UnsignedThirtyTwoBitInteger { + 0 +} + #[rustfmt::skip] fn main() { // Test cast_unnecessary @@ -105,6 +115,13 @@ fn main() { extern_fake_libc::getpid_SAFE_TRUTH() as i32; let pid = unsafe { fake_libc::getpid() }; pid as i32; + aaa() as u32; + let x = aaa(); + aaa() as u32; + // Will not lint currently. + bbb() as u32; + let x = bbb(); + bbb() as u32; let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index f0443556fb4..19411a01b67 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -7,226 +7,238 @@ LL | ptr as *const T = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:44:5 + --> $DIR/unnecessary_cast.rs:54:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:45:5 + --> $DIR/unnecessary_cast.rs:55:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:46:5 + --> $DIR/unnecessary_cast.rs:56:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:49:5 + --> $DIR/unnecessary_cast.rs:59:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:50:5 + --> $DIR/unnecessary_cast.rs:60:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:51:5 + --> $DIR/unnecessary_cast.rs:61:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:52:5 + --> $DIR/unnecessary_cast.rs:62:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:53:5 + --> $DIR/unnecessary_cast.rs:63:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:55:22 + --> $DIR/unnecessary_cast.rs:65:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:57:5 + --> $DIR/unnecessary_cast.rs:67:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> $DIR/unnecessary_cast.rs:59:5 + --> $DIR/unnecessary_cast.rs:69:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> $DIR/unnecessary_cast.rs:70:5 + --> $DIR/unnecessary_cast.rs:80:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:71:5 + --> $DIR/unnecessary_cast.rs:81:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> $DIR/unnecessary_cast.rs:73:5 + --> $DIR/unnecessary_cast.rs:83:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> $DIR/unnecessary_cast.rs:118:5 + | +LL | aaa() as u32; + | ^^^^^^^^^^^^ help: try: `aaa()` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> $DIR/unnecessary_cast.rs:120:5 + | +LL | aaa() as u32; + | ^^^^^^^^^^^^ help: try: `aaa()` + error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:139:9 + --> $DIR/unnecessary_cast.rs:156:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:140:9 + --> $DIR/unnecessary_cast.rs:157:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:141:9 + --> $DIR/unnecessary_cast.rs:158:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:142:17 + --> $DIR/unnecessary_cast.rs:159:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:143:17 + --> $DIR/unnecessary_cast.rs:160:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:144:17 + --> $DIR/unnecessary_cast.rs:161:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:145:9 + --> $DIR/unnecessary_cast.rs:162:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:146:9 + --> $DIR/unnecessary_cast.rs:163:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:158:9 + --> $DIR/unnecessary_cast.rs:175:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:159:9 + --> $DIR/unnecessary_cast.rs:176:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast.rs:160:9 + --> $DIR/unnecessary_cast.rs:177:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast.rs:161:9 + --> $DIR/unnecessary_cast.rs:178:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:162:9 + --> $DIR/unnecessary_cast.rs:179:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:164:9 + --> $DIR/unnecessary_cast.rs:181:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:165:9 + --> $DIR/unnecessary_cast.rs:182:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:169:17 + --> $DIR/unnecessary_cast.rs:186:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:170:17 + --> $DIR/unnecessary_cast.rs:187:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> $DIR/unnecessary_cast.rs:176:18 + --> $DIR/unnecessary_cast.rs:193:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:182:22 + --> $DIR/unnecessary_cast.rs:199:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:184:22 + --> $DIR/unnecessary_cast.rs:201:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:191:22 + --> $DIR/unnecessary_cast.rs:208:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:193:23 + --> $DIR/unnecessary_cast.rs:210:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:201:20 + --> $DIR/unnecessary_cast.rs:218:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 38 previous errors +error: aborting due to 40 previous errors From f583fd18e4d1ecb7a0c2c838a5ac0a10188f24db Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 9 Jul 2023 18:59:25 -0400 Subject: [PATCH 33/90] Fix `unwrap_or_else_default` false positive --- .../src/methods/unwrap_or_else_default.rs | 6 +- clippy_utils/src/ty.rs | 3 + .../src/ty/type_certainty/certainty.rs | 122 +++++++ clippy_utils/src/ty/type_certainty/mod.rs | 315 ++++++++++++++++++ tests/ui/unwrap_or_else_default.fixed | 37 ++ tests/ui/unwrap_or_else_default.rs | 37 ++ tests/ui/unwrap_or_else_default.stderr | 50 ++- 7 files changed, 568 insertions(+), 2 deletions(-) create mode 100644 clippy_utils/src/ty/type_certainty/certainty.rs create mode 100644 clippy_utils/src/ty/type_certainty/mod.rs diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs index 474a33b67e1..cfb1f7bbba8 100644 --- a/clippy_lints/src/methods/unwrap_or_else_default.rs +++ b/clippy_lints/src/methods/unwrap_or_else_default.rs @@ -4,7 +4,7 @@ use super::UNWRAP_OR_ELSE_DEFAULT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_default_equivalent_call; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{expr_type_is_certain, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -17,6 +17,10 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, u_arg: &'tcx hir::Expr<'_>, ) { + if !expr_type_is_certain(cx, recv) { + return; + } + // something.unwrap_or_else(Default::default) // ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 15a08370b6a..53152ab7e13 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -28,6 +28,9 @@ use std::iter; use crate::{match_def_path, path_res, paths}; +mod type_certainty; +pub use type_certainty::expr_type_is_certain; + /// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_copy_modulo_regions(cx.tcx, cx.param_env) diff --git a/clippy_utils/src/ty/type_certainty/certainty.rs b/clippy_utils/src/ty/type_certainty/certainty.rs new file mode 100644 index 00000000000..0e69ffa2212 --- /dev/null +++ b/clippy_utils/src/ty/type_certainty/certainty.rs @@ -0,0 +1,122 @@ +use rustc_hir::def_id::DefId; +use std::fmt::Debug; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Certainty { + /// Determining the type requires contextual information. + Uncertain, + + /// The type can be determined purely from subexpressions. If the argument is `Some(..)`, the + /// specific `DefId` is known. Such arguments are needed to handle path segments whose `res` is + /// `Res::Err`. + Certain(Option), + + /// The heuristic believes that more than one `DefId` applies to a type---this is a bug. + Contradiction, +} + +pub trait Meet { + fn meet(self, other: Self) -> Self; +} + +pub trait TryJoin: Sized { + fn try_join(self, other: Self) -> Option; +} + +impl Meet for Option { + fn meet(self, other: Self) -> Self { + match (self, other) { + (None, _) | (_, None) => None, + (Some(lhs), Some(rhs)) => (lhs == rhs).then_some(lhs), + } + } +} + +impl TryJoin for Option { + fn try_join(self, other: Self) -> Option { + match (self, other) { + (Some(lhs), Some(rhs)) => (lhs == rhs).then_some(Some(lhs)), + (Some(def_id), _) | (_, Some(def_id)) => Some(Some(def_id)), + (None, None) => Some(None), + } + } +} + +impl Meet for Certainty { + fn meet(self, other: Self) -> Self { + match (self, other) { + (Certainty::Uncertain, _) | (_, Certainty::Uncertain) => Certainty::Uncertain, + (Certainty::Certain(lhs), Certainty::Certain(rhs)) => Certainty::Certain(lhs.meet(rhs)), + (Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner), + (Certainty::Contradiction, Certainty::Contradiction) => Certainty::Contradiction, + } + } +} + +impl Certainty { + /// Join two `Certainty`s preserving their `DefId`s (if any). Generally speaking, this method + /// should be used only when `self` and `other` refer directly to types. Otherwise, + /// `join_clearing_def_ids` should be used. + pub fn join(self, other: Self) -> Self { + match (self, other) { + (Certainty::Contradiction, _) | (_, Certainty::Contradiction) => Certainty::Contradiction, + + (Certainty::Certain(lhs), Certainty::Certain(rhs)) => { + if let Some(inner) = lhs.try_join(rhs) { + Certainty::Certain(inner) + } else { + debug_assert!(false, "Contradiction with {lhs:?} and {rhs:?}"); + Certainty::Contradiction + } + }, + + (Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner), + + (Certainty::Uncertain, Certainty::Uncertain) => Certainty::Uncertain, + } + } + + /// Join two `Certainty`s after clearing their `DefId`s. This method should be used when `self` + /// or `other` do not necessarily refer to types, e.g., when they are aggregations of other + /// `Certainty`s. + pub fn join_clearing_def_ids(self, other: Self) -> Self { + self.clear_def_id().join(other.clear_def_id()) + } + + pub fn clear_def_id(self) -> Certainty { + if matches!(self, Certainty::Certain(_)) { + Certainty::Certain(None) + } else { + self + } + } + + pub fn with_def_id(self, def_id: DefId) -> Certainty { + if matches!(self, Certainty::Certain(_)) { + Certainty::Certain(Some(def_id)) + } else { + self + } + } + + pub fn to_def_id(self) -> Option { + match self { + Certainty::Certain(inner) => inner, + _ => None, + } + } + + pub fn is_certain(self) -> bool { + matches!(self, Self::Certain(_)) + } +} + +/// Think: `iter.all(/* is certain */)` +pub fn meet(iter: impl Iterator) -> Certainty { + iter.fold(Certainty::Certain(None), Certainty::meet) +} + +/// Think: `iter.any(/* is certain */)` +pub fn join(iter: impl Iterator) -> Certainty { + iter.fold(Certainty::Uncertain, Certainty::join) +} diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs new file mode 100644 index 00000000000..67c4a293a86 --- /dev/null +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -0,0 +1,315 @@ +//! A heuristic to tell whether an expression's type can be determined purely from its +//! subexpressions, and the arguments and locals they use. Put another way, `expr_type_is_certain` +//! tries to tell whether an expression's type can be determined without appeal to the surrounding +//! context. +//! +//! This is, in some sense, a counterpart to `let_unit_value`'s `expr_needs_inferred_result`. +//! Intuitively, that function determines whether an expression's type is needed for type inference, +//! whereas `expr_type_is_certain` determines whether type inference is needed for an expression's +//! type. +//! +//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should +//! be considered a bug. + +use crate::def_path_res; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor}; +use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; +use rustc_span::{Span, Symbol}; + +mod certainty; +use certainty::{join, meet, Certainty, Meet}; + +pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + expr_type_certainty(cx, expr).is_certain() +} + +fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { + let certainty = match &expr.kind { + ExprKind::Unary(_, expr) + | ExprKind::Field(expr, _) + | ExprKind::Index(expr, _) + | ExprKind::AddrOf(_, _, expr) => expr_type_certainty(cx, expr), + + ExprKind::Array(exprs) => join(exprs.iter().map(|expr| expr_type_certainty(cx, expr))), + + ExprKind::Call(callee, args) => { + let lhs = expr_type_certainty(cx, callee); + let rhs = if type_is_inferrable_from_arguments(cx, expr) { + meet(args.iter().map(|arg| expr_type_certainty(cx, arg))) + } else { + Certainty::Uncertain + }; + lhs.join_clearing_def_ids(rhs) + }, + + ExprKind::MethodCall(method, receiver, args, _) => { + let mut receiver_type_certainty = expr_type_certainty(cx, receiver); + // Even if `receiver_type_certainty` is `Certain(Some(..))`, the `Self` type in the method + // identified by `type_dependent_def_id(..)` can differ. This can happen as a result of a `deref`, + // for example. So update the `DefId` in `receiver_type_certainty` (if any). + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id)) + { + receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id); + }; + let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false); + let rhs = if type_is_inferrable_from_arguments(cx, expr) { + meet( + std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))), + ) + } else { + Certainty::Uncertain + }; + lhs.join(rhs) + }, + + ExprKind::Tup(exprs) => meet(exprs.iter().map(|expr| expr_type_certainty(cx, expr))), + + ExprKind::Binary(_, lhs, rhs) => expr_type_certainty(cx, lhs).meet(expr_type_certainty(cx, rhs)), + + ExprKind::Lit(_) => Certainty::Certain(None), + + ExprKind::Cast(_, ty) => type_certainty(cx, ty), + + ExprKind::If(_, if_expr, Some(else_expr)) => { + expr_type_certainty(cx, if_expr).join(expr_type_certainty(cx, else_expr)) + }, + + ExprKind::Path(qpath) => qpath_certainty(cx, qpath, false), + + ExprKind::Struct(qpath, _, _) => qpath_certainty(cx, qpath, true), + + _ => Certainty::Uncertain, + }; + + let expr_ty = cx.typeck_results().expr_ty(expr); + if let Some(def_id) = adt_def_id(expr_ty) { + certainty.with_def_id(def_id) + } else { + certainty + } +} + +struct CertaintyVisitor<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + certainty: Certainty, +} + +impl<'cx, 'tcx> CertaintyVisitor<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>) -> Self { + Self { + cx, + certainty: Certainty::Certain(None), + } + } +} + +impl<'cx, 'tcx> Visitor<'cx> for CertaintyVisitor<'cx, 'tcx> { + fn visit_qpath(&mut self, qpath: &'cx QPath<'_>, hir_id: HirId, _: Span) { + self.certainty = self.certainty.meet(qpath_certainty(self.cx, qpath, true)); + if self.certainty != Certainty::Uncertain { + walk_qpath(self, qpath, hir_id); + } + } + + fn visit_ty(&mut self, ty: &'cx hir::Ty<'_>) { + if matches!(ty.kind, TyKind::Infer) { + self.certainty = Certainty::Uncertain; + } + if self.certainty != Certainty::Uncertain { + walk_ty(self, ty); + } + } +} + +fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty { + // Handle `TyKind::Path` specially so that its `DefId` can be preserved. + // + // Note that `CertaintyVisitor::new` initializes the visitor's internal certainty to + // `Certainty::Certain(None)`. Furthermore, if a `TyKind::Path` is encountered while traversing + // `ty`, the result of the call to `qpath_certainty` is combined with the visitor's internal + // certainty using `Certainty::meet`. Thus, if the `TyKind::Path` were not treated specially here, + // the resulting certainty would be `Certainty::Certain(None)`. + if let TyKind::Path(qpath) = &ty.kind { + return qpath_certainty(cx, qpath, true); + } + + let mut visitor = CertaintyVisitor::new(cx); + visitor.visit_ty(ty); + visitor.certainty +} + +fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certainty { + let mut visitor = CertaintyVisitor::new(cx); + visitor.visit_generic_args(args); + visitor.certainty +} + +/// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path +/// segments generic arguments are are instantiated. +/// +/// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a +/// value. So `DefId`s are retained only when `resolves_to_type` is true. +fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bool) -> Certainty { + let certainty = match qpath { + QPath::Resolved(ty, path) => { + let len = path.segments.len(); + path.segments.iter().enumerate().fold( + ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)), + |parent_certainty, (i, path_segment)| { + path_segment_certainty(cx, parent_certainty, path_segment, i != len - 1 || resolves_to_type) + }, + ) + }, + + QPath::TypeRelative(ty, path_segment) => { + path_segment_certainty(cx, type_certainty(cx, ty), path_segment, resolves_to_type) + }, + + QPath::LangItem(lang_item, _, _) => { + cx.tcx + .lang_items() + .get(*lang_item) + .map_or(Certainty::Uncertain, |def_id| { + let generics = cx.tcx.generics_of(def_id); + if generics.parent_count == 0 && generics.params.is_empty() { + Certainty::Certain(if resolves_to_type { Some(def_id) } else { None }) + } else { + Certainty::Uncertain + } + }) + }, + }; + debug_assert!(resolves_to_type || certainty.to_def_id().is_none()); + certainty +} + +fn path_segment_certainty( + cx: &LateContext<'_>, + parent_certainty: Certainty, + path_segment: &PathSegment<'_>, + resolves_to_type: bool, +) -> Certainty { + let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) { + // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or + // an unparameterized type), or the generics are instantiated with arguments that are certain. + // + // If the parent is uncertain, then the current path segment must account for the parent's generic arguments. + // Consider the following examples, where the current path segment is `None`: + // - `Option::None` // uncertain; parent (i.e., `Option`) is uncertain + // - `Option::>::None` // certain; parent (i.e., `Option::<..>`) is certain + // - `Option::None::>` // certain; parent (i.e., `Option`) is uncertain + Res::Def(_, def_id) => { + // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. + if cx.tcx.res_generics_def_id(path_segment.res).is_some() { + let generics = cx.tcx.generics_of(def_id); + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && generics.params.is_empty() + { + Certainty::Certain(None) + } else { + Certainty::Uncertain + }; + let rhs = path_segment + .args + .map_or(Certainty::Uncertain, |args| generic_args_certainty(cx, args)); + // See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value. + let certainty = lhs.join_clearing_def_ids(rhs); + if resolves_to_type { + certainty.with_def_id(def_id) + } else { + certainty + } + } else { + Certainty::Certain(None) + } + }, + + Res::PrimTy(_) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::SelfCtor(_) => { + Certainty::Certain(None) + }, + + // `get_parent` because `hir_id` refers to a `Pat`, and we're interested in the node containing the `Pat`. + Res::Local(hir_id) => match cx.tcx.hir().get_parent(hir_id) { + // An argument's type is always certain. + Node::Param(..) => Certainty::Certain(None), + // A local's type is certain if its type annotation is certain or it has an initializer whose + // type is certain. + Node::Local(local) => { + let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)); + let rhs = local + .init + .map_or(Certainty::Uncertain, |init| expr_type_certainty(cx, init)); + let certainty = lhs.join(rhs); + if resolves_to_type { + certainty + } else { + certainty.clear_def_id() + } + }, + _ => Certainty::Uncertain, + }, + + _ => Certainty::Uncertain, + }; + debug_assert!(resolves_to_type || certainty.to_def_id().is_none()); + certainty +} + +/// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`. +/// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`. +fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option { + if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() { + let mut def_path = cx.get_def_path(def_id); + def_path.push(path_segment.ident.name); + let reses = def_path_res(cx, &def_path.iter().map(Symbol::as_str).collect::>()); + if let [res] = reses.as_slice() { Some(*res) } else { None } + } else { + None + } +} + +#[allow(clippy::cast_possible_truncation)] +fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let Some(callee_def_id) = (match expr.kind { + ExprKind::Call(callee, _) => { + let callee_ty = cx.typeck_results().expr_ty(callee); + if let ty::FnDef(callee_def_id, _) = callee_ty.kind() { + Some(*callee_def_id) + } else { + None + } + }, + ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, + }) else { + return false; + }; + + let generics = cx.tcx.generics_of(callee_def_id); + let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); + + // Check that all type parameters appear in the functions input types. + (0..(generics.parent_count + generics.params.len()) as u32).all(|index| { + fn_sig + .inputs() + .iter() + .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) + }) +} + +fn self_ty<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId) -> Ty<'tcx> { + cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()[0] +} + +fn adt_def_id(ty: Ty<'_>) -> Option { + ty.peel_refs().ty_adt_def().map(AdtDef::did) +} + +fn contains_param(ty: Ty<'_>, index: u32) -> bool { + ty.walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(ty) if ty.is_param(index))) +} diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 08b89a18bbb..e6b6ab359df 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -74,4 +74,41 @@ fn unwrap_or_else_default() { empty_string.unwrap_or_default(); } +fn type_certainty(option: Option>) { + option.unwrap_or_default().push(1); + + let option: std::option::Option> = None; + option.unwrap_or_default().push(1); + + let option: Option> = None; + option.unwrap_or_default().push(1); + + let option = std::option::Option::>::None; + option.unwrap_or_default().push(1); + + let option = Option::>::None; + option.unwrap_or_default().push(1); + + let option = std::option::Option::None::>; + option.unwrap_or_default().push(1); + + let option = Option::None::>; + option.unwrap_or_default().push(1); + + let option = None::>; + option.unwrap_or_default().push(1); + + // should not be changed: type annotation with infer, unconcretized initializer + let option: Option> = None; + option.unwrap_or_else(Vec::new).push(1); + + // should not be changed: no type annotation, unconcretized initializer + let option = Option::None; + option.unwrap_or_else(Vec::new).push(1); + + // should not be changed: no type annotation, unconcretized initializer + let option = None; + option.unwrap_or_else(Vec::new).push(1); +} + fn main() {} diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index ad2a744908f..38fa2e4595d 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -74,4 +74,41 @@ fn unwrap_or_else_default() { empty_string.unwrap_or_else(|| "".to_string()); } +fn type_certainty(option: Option>) { + option.unwrap_or_else(Vec::new).push(1); + + let option: std::option::Option> = None; + option.unwrap_or_else(Vec::new).push(1); + + let option: Option> = None; + option.unwrap_or_else(Vec::new).push(1); + + let option = std::option::Option::>::None; + option.unwrap_or_else(Vec::new).push(1); + + let option = Option::>::None; + option.unwrap_or_else(Vec::new).push(1); + + let option = std::option::Option::None::>; + option.unwrap_or_else(Vec::new).push(1); + + let option = Option::None::>; + option.unwrap_or_else(Vec::new).push(1); + + let option = None::>; + option.unwrap_or_else(Vec::new).push(1); + + // should not be changed: type annotation with infer, unconcretized initializer + let option: Option> = None; + option.unwrap_or_else(Vec::new).push(1); + + // should not be changed: no type annotation, unconcretized initializer + let option = Option::None; + option.unwrap_or_else(Vec::new).push(1); + + // should not be changed: no type annotation, unconcretized initializer + let option = None; + option.unwrap_or_else(Vec::new).push(1); +} + fn main() {} diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index d2b9212223f..a6815e8f916 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -36,5 +36,53 @@ error: use of `.unwrap_or_else(..)` to construct default value LL | empty_string.unwrap_or_else(|| "".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `empty_string.unwrap_or_default()` -error: aborting due to 6 previous errors +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:78:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:81:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:84:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:87:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:90:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:93:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:96:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:99:5 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + +error: aborting due to 14 previous errors From cbca8f9908fd4c4d66d83f0f54221f809db8b1c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 3 Mar 2023 22:25:18 +0000 Subject: [PATCH 34/90] On nightly, dump ICE backtraces to disk Implement rust-lang/compiler-team#578. When an ICE is encountered on nightly releases, the new rustc panic handler will also write the contents of the backtrace to disk. If any `delay_span_bug`s are encountered, their backtrace is also added to the file. The platform and rustc version will also be collected. --- clippy_lints/src/doc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e5f39d102cd..8879c529262 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -729,7 +729,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(false, None, Box::new(emitter), None); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { From 84c411272d4ae8f0269747ce2d1fd7f4a3c2e158 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 19 Jul 2023 20:05:16 -0400 Subject: [PATCH 35/90] Merge `unwrap_or_else_default.rs` into `or_fun_call.rs` --- clippy_lints/src/methods/mod.rs | 33 ++++--- clippy_lints/src/methods/or_fun_call.rs | 93 ++++++++++++++----- .../src/methods/unwrap_or_else_default.rs | 70 -------------- tests/ui/or_fun_call.fixed | 63 ++++++++++++- tests/ui/or_fun_call.rs | 55 +++++++++++ tests/ui/or_fun_call.stderr | 90 +++++++++++------- tests/ui/unwrap_or_else_default.fixed | 19 +++- tests/ui/unwrap_or_else_default.rs | 19 +++- tests/ui/unwrap_or_else_default.stderr | 92 +++++++++--------- 9 files changed, 343 insertions(+), 191 deletions(-) delete mode 100644 clippy_lints/src/methods/unwrap_or_else_default.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index be71072acd6..ac3055f04ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -103,7 +103,6 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_sort_by; mod unnecessary_to_owned; -mod unwrap_or_else_default; mod unwrap_used; mod useless_asref; mod utils; @@ -476,29 +475,40 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.unwrap_or_else(Default::default)` on `Option` and - /// `Result` values. + /// Checks for usages of the following functions with an argument that constructs a default value + /// (e.g., `Default::default` or `String::new`): + /// - `unwrap_or` + /// - `unwrap_or_else` + /// - `or_insert` + /// - `or_insert_with` /// /// ### Why is this bad? - /// Readability, these can be written as `_.unwrap_or_default`, which is - /// simpler and more concise. + /// Readability. Using `unwrap_or_default` in place of `unwrap_or`/`unwrap_or_else`, or `or_default` + /// in place of `or_insert`/`or_insert_with`, is simpler and more concise. + /// + /// ### Known problems + /// In some cases, the argument of `unwrap_or`, etc. is needed for type inference. The lint uses a + /// heuristic to try to identify such cases. However, the heuristic can produce false negatives. /// /// ### Examples /// ```rust /// # let x = Some(1); - /// x.unwrap_or_else(Default::default); - /// x.unwrap_or_else(u32::default); + /// # let mut map = std::collections::HashMap::::new(); + /// x.unwrap_or(Default::default()); + /// map.entry(42).or_insert_with(String::new); /// ``` /// /// Use instead: /// ```rust /// # let x = Some(1); + /// # let mut map = std::collections::HashMap::::new(); /// x.unwrap_or_default(); + /// map.entry(42).or_default(); /// ``` #[clippy::version = "1.56.0"] pub UNWRAP_OR_ELSE_DEFAULT, style, - "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`" + "using `.unwrap_or`, etc. with an argument that constructs a default value" } declare_clippy_lint! { @@ -3756,8 +3766,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()) - .self_ty(); + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3774,8 +3783,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.owner_id); - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()) - .self_ty(); + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); if !ret_ty.contains(self_ty); then { @@ -4134,7 +4142,6 @@ impl Methods { Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {}, _ => { - unwrap_or_else_default::check(cx, expr, recv, u_arg); unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 9165c1248f0..1a5a65dbece 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,16 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{self, sym, Symbol}; +use {rustc_ast as ast, rustc_hir as hir}; -use super::OR_FUN_CALL; +use super::{OR_FUN_CALL, UNWRAP_OR_ELSE_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] @@ -24,44 +25,64 @@ pub(super) fn check<'tcx>( ) { /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, /// `or_insert(T::new())` or `or_insert(T::default())`. + /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, + /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. #[allow(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: &str, + receiver: &hir::Expr<'_>, fun: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - or_has_args: bool, + call_expr: Option<&hir::Expr<'_>>, span: Span, method_span: Span, ) -> bool { - let is_default_default = || is_trait_item(cx, fun, sym::Default); + if !expr_type_is_certain(cx, receiver) { + return false; + } - let implements_default = |arg, default_trait_id| { - let arg_ty = cx.typeck_results().expr_ty(arg); - implements_trait(cx, arg_ty, default_trait_id, &[]) + let is_new = |fun: &hir::Expr<'_>| { + if let hir::ExprKind::Path(ref qpath) = fun.kind { + let path = last_path_segment(qpath).ident.name; + matches!(path, sym::new) + } else { + false + } + }; + + let output_type_implements_default = |fun| { + let fun_ty = cx.typeck_results().expr_ty(fun); + if let ty::FnDef(def_id, substs) = fun_ty.kind() { + let output_ty = cx.tcx.fn_sig(def_id).subst(cx.tcx, substs).skip_binder().output(); + cx.tcx + .get_diagnostic_item(sym::Default) + .map_or(false, |default_trait_id| { + implements_trait(cx, output_ty, default_trait_id, substs) + }) + } else { + false + } }; if_chain! { - if !or_has_args; - if let Some(sugg) = match name { - "unwrap_or" => Some("unwrap_or_default"), - "or_insert" => Some("or_default"), + if let Some(sugg) = match (name, call_expr.is_some()) { + ("unwrap_or", true) | ("unwrap_or_else", false) => Some("unwrap_or_default"), + ("or_insert", true) | ("or_insert_with", false) => Some("or_default"), _ => None, }; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default); - let path = last_path_segment(qpath).ident.name; // needs to target Default::default in particular or be *::new and have a Default impl // available - if (matches!(path, kw::Default) && is_default_default()) - || (matches!(path, sym::new) && implements_default(arg, default_trait_id)); - + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun), + }; then { span_lint_and_sugg( cx, - OR_FUN_CALL, + UNWRAP_OR_ELSE_DEFAULT, method_span.with_hi(span.hi()), - &format!("use of `{name}` followed by a call to `{path}`"), + &format!("use of `{name}` to construct default value"), "try", format!("{sugg}()"), Applicability::MachineApplicable, @@ -168,11 +189,16 @@ pub(super) fn check<'tcx>( match inner_arg.kind { hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); - if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) { + if or_has_args + || !check_unwrap_or_default(cx, name, receiver, fun, Some(inner_arg), expr.span, method_span) + { let fun_span = if or_has_args { None } else { Some(fun.span) }; check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); } }, + hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) => { + check_unwrap_or_default(cx, name, receiver, inner_arg, None, expr.span, method_span); + }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); }, @@ -189,3 +215,22 @@ pub(super) fn check<'tcx>( } } } + +fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { + let body = cx.tcx.hir().body(body); + + if body.params.is_empty() + && let hir::Expr{ kind, .. } = &body.value + && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind + && ident == &symbol::Ident::from_str("to_string") + && let hir::Expr{ kind, .. } = self_arg + && let hir::ExprKind::Lit(lit) = kind + && let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node + { + return true; + } + } + + false +} diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs deleted file mode 100644 index cfb1f7bbba8..00000000000 --- a/clippy_lints/src/methods/unwrap_or_else_default.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Lint for `some_result_or_option.unwrap_or_else(Default::default)` - -use super::UNWRAP_OR_ELSE_DEFAULT; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_default_equivalent_call; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{expr_type_is_certain, is_type_diagnostic_item}; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::{sym, symbol}; - -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - u_arg: &'tcx hir::Expr<'_>, -) { - if !expr_type_is_certain(cx, recv) { - return; - } - - // something.unwrap_or_else(Default::default) - // ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr - let recv_ty = cx.typeck_results().expr_ty(recv); - let is_option = is_type_diagnostic_item(cx, recv_ty, sym::Option); - let is_result = is_type_diagnostic_item(cx, recv_ty, sym::Result); - - if_chain! { - if is_option || is_result; - if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg); - then { - let mut applicability = Applicability::MachineApplicable; - - span_lint_and_sugg( - cx, - UNWRAP_OR_ELSE_DEFAULT, - expr.span, - "use of `.unwrap_or_else(..)` to construct default value", - "try", - format!( - "{}.unwrap_or_default()", - snippet_with_applicability(cx, recv.span, "..", &mut applicability) - ), - applicability, - ); - } - } -} - -fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { - let body = cx.tcx.hir().body(body); - - if body.params.is_empty() - && let hir::Expr{ kind, .. } = &body.value - && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind - && ident == &symbol::Ident::from_str("to_string") - && let hir::Expr{ kind, .. } = self_arg - && let hir::ExprKind::Lit(lit) = kind - && let LitKind::Str(symbol::kw::Empty, _) = lit.node - { - return true; - } - } - - false -} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 6deff0f3240..581f3ad45c7 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -190,7 +190,7 @@ mod issue8239 { acc.push_str(&f); acc }) - .unwrap_or_default(); + .unwrap_or(String::new()); } fn more_to_max_suggestion_highest_lines_1() { @@ -203,7 +203,7 @@ mod issue8239 { acc.push_str(&f); acc }) - .unwrap_or_default(); + .unwrap_or(String::new()); } fn equal_to_max_suggestion_highest_lines() { @@ -215,7 +215,7 @@ mod issue8239 { acc.push_str(&f); acc }) - .unwrap_or_default(); + .unwrap_or(String::new()); } fn less_than_max_suggestion_highest_lines() { @@ -226,7 +226,7 @@ mod issue8239 { acc.push_str(&f); acc }) - .unwrap_or_default(); + .unwrap_or(String::new()); } } @@ -257,4 +257,59 @@ mod issue8993 { } } +mod lazy { + use super::*; + + fn foo() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + struct FakeDefault; + impl FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + impl Default for FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + let with_new = Some(vec![1]); + with_new.unwrap_or_default(); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_default(); + + let with_default_type = Some(1); + with_default_type.unwrap_or_default(); + + let real_default = None::; + real_default.unwrap_or_default(); + + let mut map = HashMap::::new(); + map.entry(42).or_default(); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_default(); + + let stringy = Some(String::new()); + let _ = stringy.unwrap_or_default(); + + // negative tests + let self_default = None::; + self_default.unwrap_or_else(::default); + + let without_default = Some(Foo); + without_default.unwrap_or_else(Foo::new); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index b05b33e6ee2..1f3987eb891 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -257,4 +257,59 @@ mod issue8993 { } } +mod lazy { + use super::*; + + fn foo() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + struct FakeDefault; + impl FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + impl Default for FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + let with_new = Some(vec![1]); + with_new.unwrap_or_else(Vec::new); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_else(Default::default); + + let with_default_type = Some(1); + with_default_type.unwrap_or_else(u64::default); + + let real_default = None::; + real_default.unwrap_or_else(::default); + + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + + let stringy = Some(String::new()); + let _ = stringy.unwrap_or_else(String::new); + + // negative tests + let self_default = None::; + self_default.unwrap_or_else(::default); + + let without_default = Some(Foo); + without_default.unwrap_or_else(Foo::new); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 7342b0c2914..13d392461de 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -6,11 +6,13 @@ LL | with_constructor.unwrap_or(make()); | = note: `-D clippy::or-fun-call` implied by `-D warnings` -error: use of `unwrap_or` followed by a call to `new` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + | + = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` error: use of `unwrap_or` followed by a function call --> $DIR/or_fun_call.rs:59:21 @@ -30,13 +32,13 @@ error: use of `unwrap_or` followed by a function call LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` -error: use of `unwrap_or` followed by a call to `default` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:68:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a call to `default` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:71:23 | LL | with_default_type.unwrap_or(u64::default()); @@ -48,13 +50,13 @@ error: use of `unwrap_or` followed by a function call LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` -error: use of `unwrap_or` followed by a call to `default` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:77:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a call to `new` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:80:14 | LL | with_vec.unwrap_or(vec![]); @@ -66,31 +68,31 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a call to `new` +error: use of `or_insert` to construct default value --> $DIR/or_fun_call.rs:86:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: use of `or_insert` followed by a call to `new` +error: use of `or_insert` to construct default value --> $DIR/or_fun_call.rs:89:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: use of `or_insert` followed by a call to `new` +error: use of `or_insert` to construct default value --> $DIR/or_fun_call.rs:92:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: use of `or_insert` followed by a call to `new` +error: use of `or_insert` to construct default value --> $DIR/or_fun_call.rs:95:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: use of `unwrap_or` followed by a call to `new` +error: use of `unwrap_or` to construct default value --> $DIR/or_fun_call.rs:98:21 | LL | let _ = stringy.unwrap_or(String::new()); @@ -132,30 +134,6 @@ error: use of `unwrap_or` followed by a function call LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` -error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:193:14 - | -LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` - -error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:206:14 - | -LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` - -error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:218:14 - | -LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` - -error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:229:10 - | -LL | .unwrap_or(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` - error: use of `map_or` followed by a function call --> $DIR/or_fun_call.rs:254:25 | @@ -168,5 +146,47 @@ error: use of `map_or` followed by a function call LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` -error: aborting due to 28 previous errors +error: use of `unwrap_or_else` to construct default value + --> $DIR/or_fun_call.rs:286:18 + | +LL | with_new.unwrap_or_else(Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or_else` to construct default value + --> $DIR/or_fun_call.rs:289:28 + | +LL | with_default_trait.unwrap_or_else(Default::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or_else` to construct default value + --> $DIR/or_fun_call.rs:292:27 + | +LL | with_default_type.unwrap_or_else(u64::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or_else` to construct default value + --> $DIR/or_fun_call.rs:295:22 + | +LL | real_default.unwrap_or_else(::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `or_insert_with` to construct default value + --> $DIR/or_fun_call.rs:298:23 + | +LL | map.entry(42).or_insert_with(String::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` + +error: use of `or_insert_with` to construct default value + --> $DIR/or_fun_call.rs:301:25 + | +LL | btree.entry(42).or_insert_with(String::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` + +error: use of `unwrap_or_else` to construct default value + --> $DIR/or_fun_call.rs:304:25 + | +LL | let _ = stringy.unwrap_or_else(String::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: aborting due to 31 previous errors diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index e6b6ab359df..8c038c3647d 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -4,7 +4,7 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] -/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint. +/// Checks implementation of the `UNWRAP_OR_DEFAULT` lint. fn unwrap_or_else_default() { struct Foo; @@ -111,4 +111,21 @@ fn type_certainty(option: Option>) { option.unwrap_or_else(Vec::new).push(1); } +fn method_call_with_deref() { + use std::cell::RefCell; + use std::collections::HashMap; + + let cell = RefCell::new(HashMap::>::new()); + + let mut outer_map = cell.borrow_mut(); + + #[allow(unused_assignments)] + let mut option = None; + option = Some(0); + + let inner_map = outer_map.get_mut(&option.unwrap()).unwrap(); + + let _ = inner_map.entry(0).or_default(); +} + fn main() {} diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index 38fa2e4595d..47aadb68548 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -4,7 +4,7 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] -/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint. +/// Checks implementation of the `UNWRAP_OR_DEFAULT` lint. fn unwrap_or_else_default() { struct Foo; @@ -111,4 +111,21 @@ fn type_certainty(option: Option>) { option.unwrap_or_else(Vec::new).push(1); } +fn method_call_with_deref() { + use std::cell::RefCell; + use std::collections::HashMap; + + let cell = RefCell::new(HashMap::>::new()); + + let mut outer_map = cell.borrow_mut(); + + #[allow(unused_assignments)] + let mut option = None; + option = Some(0); + + let inner_map = outer_map.get_mut(&option.unwrap()).unwrap(); + + let _ = inner_map.entry(0).or_insert_with(Default::default); +} + fn main() {} diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index a6815e8f916..5486d82e8fa 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -1,88 +1,94 @@ -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:48:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:48:14 | LL | with_new.unwrap_or_else(Vec::new); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_new.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` | = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:62:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:62:23 | LL | with_real_default.unwrap_or_else(::default); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:65:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:65:24 | LL | with_default_trait.unwrap_or_else(Default::default); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_trait.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:68:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:68:23 | LL | with_default_type.unwrap_or_else(u64::default); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:71:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:71:23 | LL | with_default_type.unwrap_or_else(Vec::new); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:74:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:74:18 | LL | empty_string.unwrap_or_else(|| "".to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `empty_string.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:78:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:78:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:81:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:81:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:84:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:84:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:87:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:87:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:90:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:90:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:93:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:93:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:96:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:96:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `.unwrap_or_else(..)` to construct default value - --> $DIR/unwrap_or_else_default.rs:99:5 +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:99:12 | LL | option.unwrap_or_else(Vec::new).push(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: aborting due to 14 previous errors +error: use of `or_insert_with` to construct default value + --> $DIR/unwrap_or_else_default.rs:128:32 + | +LL | let _ = inner_map.entry(0).or_insert_with(Default::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` + +error: aborting due to 15 previous errors From e27977b84776b641ec48482f18c1dac7420a5830 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 19 Jul 2023 20:05:16 -0400 Subject: [PATCH 36/90] Rename `unwrap_or_else_default` to `unwrap_or_default` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 +- clippy_lints/src/methods/or_fun_call.rs | 4 +- clippy_lints/src/renamed_lints.rs | 1 + tests/ui/or_fun_call.stderr | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 114 +++++++++++++----------- tests/ui/unwrap_or_else_default.fixed | 2 +- tests/ui/unwrap_or_else_default.rs | 2 +- tests/ui/unwrap_or_else_default.stderr | 2 +- 12 files changed, 75 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acc9d8ffbf7..c50d28273bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5366,6 +5366,7 @@ Released 2018-09-13 [`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 [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result +[`unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_default [`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 47995fea9f9..cb5fed5f109 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -422,7 +422,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, - crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO, + crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, crate::methods::VEC_RESIZE_TO_ZERO_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ac3055f04ee..1058289679b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -506,7 +506,7 @@ declare_clippy_lint! { /// map.entry(42).or_default(); /// ``` #[clippy::version = "1.56.0"] - pub UNWRAP_OR_ELSE_DEFAULT, + pub UNWRAP_OR_DEFAULT, style, "using `.unwrap_or`, etc. with an argument that constructs a default value" } @@ -3505,7 +3505,7 @@ impl_lint_pass!(Methods => [ SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, OK_EXPECT, - UNWRAP_OR_ELSE_DEFAULT, + UNWRAP_OR_DEFAULT, MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1a5a65dbece..d1899658b84 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -11,7 +11,7 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{self, sym, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; -use super::{OR_FUN_CALL, UNWRAP_OR_ELSE_DEFAULT}; +use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] @@ -80,7 +80,7 @@ pub(super) fn check<'tcx>( then { span_lint_and_sugg( cx, - UNWRAP_OR_ELSE_DEFAULT, + UNWRAP_OR_DEFAULT, method_span.with_hi(span.hi()), &format!("use of `{name}` to construct default value"), "try", diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index d24215c2292..13086f996ee 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -30,6 +30,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::single_char_push_str", "clippy::single_char_add_str"), ("clippy::stutter", "clippy::module_name_repetitions"), ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::cast_ref_to_mut", "cast_ref_to_mut"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 13d392461de..519f0916562 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -12,7 +12,7 @@ error: use of `unwrap_or` to construct default value LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` | - = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` + = note: `-D clippy::unwrap-or-default` implied by `-D warnings` error: use of `unwrap_or` followed by a function call --> $DIR/or_fun_call.rs:59:21 diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index cab02bb93c9..6936d526aeb 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -27,6 +27,7 @@ #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] +#![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] #![allow(cast_ref_to_mut)] #![allow(suspicious_double_ref_op)] @@ -78,6 +79,7 @@ #![warn(clippy::single_char_add_str)] #![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] +#![warn(clippy::unwrap_or_default)] #![warn(clippy::invisible_characters)] #![warn(cast_ref_to_mut)] #![warn(suspicious_double_ref_op)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e5e31452149..626e5202025 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -27,6 +27,7 @@ #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] +#![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] #![allow(cast_ref_to_mut)] #![allow(suspicious_double_ref_op)] @@ -78,6 +79,7 @@ #![warn(clippy::single_char_push_str)] #![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] +#![warn(clippy::unwrap_or_else_default)] #![warn(clippy::zero_width_space)] #![warn(clippy::cast_ref_to_mut)] #![warn(clippy::clone_double_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 783608a0841..a0e0a66892e 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,316 +7,322 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` + --> $DIR/rename.rs:82:9 + | +LL | #![warn(clippy::unwrap_or_else_default)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` + error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::fn_null_check` has been renamed to `incorrect_fn_null_checks` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `incorrect_fn_null_checks` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:102:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:104:9 + --> $DIR/rename.rs:106:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:105:9 + --> $DIR/rename.rs:107:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 53 previous errors +error: aborting due to 54 previous errors diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 8c038c3647d..89a6b1395e5 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -1,6 +1,6 @@ //@run-rustfix -#![warn(clippy::unwrap_or_else_default)] +#![warn(clippy::unwrap_or_default)] #![allow(dead_code)] #![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index 47aadb68548..e5ebf35adda 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -1,6 +1,6 @@ //@run-rustfix -#![warn(clippy::unwrap_or_else_default)] +#![warn(clippy::unwrap_or_default)] #![allow(dead_code)] #![allow(clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index 5486d82e8fa..91e48375f18 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -4,7 +4,7 @@ error: use of `unwrap_or_else` to construct default value LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` | - = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` + = note: `-D clippy::unwrap-or-default` implied by `-D warnings` error: use of `unwrap_or_else` to construct default value --> $DIR/unwrap_or_else_default.rs:62:23 From 579f1ec123293f118b594e2d07f30f48c160f8aa Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 20 Jul 2023 09:49:21 +0200 Subject: [PATCH 37/90] Add Centri3 and blyxyas to the reviewer rotation And with that you will now be picked automatically by rustbot to review pull requests. Welcome to the team! --- triagebot.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index c40b71f6ca7..d94fdc2862f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -27,4 +27,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB "@Alexendoo", "@dswij", "@Jarcho", + "@blyxyas", + "@Centri3", ] From 15b68c24b8bccbad6b3c9ec2e60a9540573365ce Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:30:51 -0500 Subject: [PATCH 38/90] Extract the logic for if a snippet equals a type --- clippy_lints/src/casts/mod.rs | 8 +++++++ clippy_lints/src/casts/unnecessary_cast.rs | 25 +++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 0ac6ef6496a..d34de305f5d 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -181,6 +181,14 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's just unnecessary. /// + /// ### Known problems + /// When the expression on the left is a function call, the lint considers the return type to be + /// a type alias if it's aliased through a `use` statement + /// (like `use std::io::Result as IoResult`). It will not lint such cases. + /// + /// This check is also rather primitive. It will only work on primitive types without any + /// intermediate references, raw pointers and trait objects may or may not work. + /// /// ### Example /// ```rust /// let _ = 2i32 as i32; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 9130f5df02f..86057bb74ee 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -85,11 +85,6 @@ pub(super) fn check<'tcx>( } } - // skip cast of fn call that returns type alias - if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) { - return false; - } - // skip cast to non-primitive type if_chain! { if let ExprKind::Cast(_, cast_to) = expr.kind; @@ -101,6 +96,11 @@ pub(super) fn check<'tcx>( } } + // skip cast of fn call that returns type alias + if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) { + return false; + } + if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = &cast_str; @@ -259,16 +259,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx if !snippet .split("->") .skip(1) - .map(|s| { - s.trim() == cast_from.to_string() - || s.trim().contains(&format!("::{cast_from}")) - || s.split("where").any(|ty| { - ty.trim() == cast_from.to_string() - || ty.trim() == cast_from.to_string() - // Fully qualified path, or something silly like `::u32` - || s.trim().contains(&format!("::{cast_from}")) - }) - }) + .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) .any(|a| a) { return ControlFlow::Break(()); @@ -295,3 +286,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx }) .is_some() } + +fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { + snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) +} From 5a6c4d7d43e6bacdfe1373e3d60452a2608d8d0b Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 18 Jul 2023 17:03:22 +0200 Subject: [PATCH 39/90] XSimplifiedType to SimplifiedType::X --- .../src/utils/internal_lints/invalid_paths.rs | 8 ++-- clippy_utils/src/lib.rs | 47 +++++++++---------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 94b56304bca..e4906944c8d 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -74,10 +74,10 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_items = cx.tcx.lang_items(); // This list isn't complete, but good enough for our current list of paths. let incoherent_impls = [ - SimplifiedType::FloatSimplifiedType(FloatTy::F32), - SimplifiedType::FloatSimplifiedType(FloatTy::F64), - SimplifiedType::SliceSimplifiedType, - SimplifiedType::StrSimplifiedType, + SimplifiedType::Float(FloatTy::F32), + SimplifiedType::Float(FloatTy::F64), + SimplifiedType::Slice, + SimplifiedType::Str, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 00e893fbdda..035511e8912 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -100,10 +100,7 @@ use rustc_middle::mir::ConstantKind; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::fast_reject::SimplifiedType::{ - ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, - PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, -}; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, @@ -512,30 +509,30 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { let ty = match name { - "bool" => BoolSimplifiedType, - "char" => CharSimplifiedType, - "str" => StrSimplifiedType, - "array" => ArraySimplifiedType, - "slice" => SliceSimplifiedType, + "bool" => SimplifiedType::Bool, + "char" => SimplifiedType::Char, + "str" => SimplifiedType::Str, + "array" => SimplifiedType::Array, + "slice" => SimplifiedType::Slice, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => PtrSimplifiedType(Mutability::Not), - "mut_ptr" => PtrSimplifiedType(Mutability::Mut), - "isize" => IntSimplifiedType(IntTy::Isize), - "i8" => IntSimplifiedType(IntTy::I8), - "i16" => IntSimplifiedType(IntTy::I16), - "i32" => IntSimplifiedType(IntTy::I32), - "i64" => IntSimplifiedType(IntTy::I64), - "i128" => IntSimplifiedType(IntTy::I128), - "usize" => UintSimplifiedType(UintTy::Usize), - "u8" => UintSimplifiedType(UintTy::U8), - "u16" => UintSimplifiedType(UintTy::U16), - "u32" => UintSimplifiedType(UintTy::U32), - "u64" => UintSimplifiedType(UintTy::U64), - "u128" => UintSimplifiedType(UintTy::U128), - "f32" => FloatSimplifiedType(FloatTy::F32), - "f64" => FloatSimplifiedType(FloatTy::F64), + "const_ptr" => SimplifiedType::Ptr(Mutability::Not), + "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), + "isize" => SimplifiedType::Int(IntTy::Isize), + "i8" => SimplifiedType::Int(IntTy::I8), + "i16" => SimplifiedType::Int(IntTy::I16), + "i32" => SimplifiedType::Int(IntTy::I32), + "i64" => SimplifiedType::Int(IntTy::I64), + "i128" => SimplifiedType::Int(IntTy::I128), + "usize" => SimplifiedType::Uint(UintTy::Usize), + "u8" => SimplifiedType::Uint(UintTy::U8), + "u16" => SimplifiedType::Uint(UintTy::U16), + "u32" => SimplifiedType::Uint(UintTy::U32), + "u64" => SimplifiedType::Uint(UintTy::U64), + "u128" => SimplifiedType::Uint(UintTy::U128), + "f32" => SimplifiedType::Float(FloatTy::F32), + "f64" => SimplifiedType::Float(FloatTy::F64), _ => return [].iter().copied(), }; From f743fec6b0fdeb3c980e350043116bd30e364126 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 20 Jul 2023 11:49:27 +0200 Subject: [PATCH 40/90] fix: false positive for `option_env!` in ifs Clippy had a false positive for with `ifs_same_cond` when two if-let expressions have an `option_env!` macro. The fix is similar to the `env!` macro fix. The following example had a clippy error: ```rust if let Some(env1) = option_env!("ENV1") { // ... } else if let Some(env2) = option_env!("ENV2") { // ... } ``` See https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=01b85c61b56ddd900117fb247af04824 changelog: Fix [`ifs_same_cond`] false positive when using `option_env!` in if-let expressions. --- clippy_utils/src/hir_utils.rs | 11 +++++++---- tests/ui/ifs_same_cond.rs | 4 ++++ tests/ui/ifs_same_cond.stderr | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index fb359ee3bbe..e2540a1a667 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -494,10 +494,13 @@ impl HirEqInterExpr<'_, '_, '_> { loop { use TokenKind::{BlockComment, LineComment, Whitespace}; if left_data.macro_def_id != right_data.macro_def_id - || (matches!(left_data.kind, ExpnKind::Macro(MacroKind::Bang, name) if name == sym::cfg) - && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) - })) + || (matches!( + left_data.kind, + ExpnKind::Macro(MacroKind::Bang, name) + if name == sym::cfg || name == sym::option_env + ) && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) + })) { // Either a different chain of macro calls, or different arguments to the `cfg` macro. return false; diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 5c338e3c5c8..ad77346b75f 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -46,6 +46,10 @@ fn ifs_same_cond() { // ok, functions } else if v.len() == 42 { } + + if let Some(env1) = option_env!("ENV1") { + } else if let Some(env2) = option_env!("ENV2") { + } } fn issue10272() { diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr index 8d70934476c..3f52c10b762 100644 --- a/tests/ui/ifs_same_cond.stderr +++ b/tests/ui/ifs_same_cond.stderr @@ -36,13 +36,13 @@ LL | if 2 * a == 1 { | ^^^^^^^^^^ error: this `if` has the same condition as a previous `if` - --> $DIR/ifs_same_cond.rs:54:15 + --> $DIR/ifs_same_cond.rs:58:15 | LL | } else if a.contains("ah") { | ^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/ifs_same_cond.rs:53:8 + --> $DIR/ifs_same_cond.rs:57:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ From f0a16bb8a3ae4ef0ac24cac0f87eb5ca07005d3c Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 20 Jul 2023 09:04:08 -0300 Subject: [PATCH 41/90] [significant_drop_tightening] Fix #11189 --- .../src/significant_drop_tightening.rs | 30 +++++++++++++------ tests/ui/significant_drop_tightening.fixed | 20 +++++++++++++ tests/ui/significant_drop_tightening.rs | 20 +++++++++++++ tests/ui/significant_drop_tightening.stderr | 6 ++-- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 8f8614ab305..5db061d1cef 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -331,13 +331,13 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o apa.last_method_span = span; } }, - hir::StmtKind::Semi(expr) => { - if has_drop(expr, &apa.first_bind_ident, self.cx) { + hir::StmtKind::Semi(semi_expr) => { + if has_drop(semi_expr, &apa.first_bind_ident, self.cx) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; } - if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind { + if let hir::ExprKind::MethodCall(_, _, _, span) = semi_expr.kind { apa.last_method_span = span; } }, @@ -435,14 +435,26 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_ && let Res::Def(DefKind::Fn, did) = fun_path.res && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) && let [first_arg, ..] = args - && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind - && let [first_arg_ps, .. ] = arg_path.segments { - &first_arg_ps.ident == first_bind_ident - } - else { - false + let has_ident = |local_expr: &hir::Expr<'_>| { + if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind + && let [first_arg_ps, .. ] = arg_path.segments + && &first_arg_ps.ident == first_bind_ident + { + true + } + else { + false + } + }; + if has_ident(first_arg) { + return true; + } + if let hir::ExprKind::Tup(value) = &first_arg.kind && value.iter().any(has_ident) { + return true; + } } + false } fn is_inexpensive_expr(expr: &hir::Expr<'_>) -> bool { diff --git a/tests/ui/significant_drop_tightening.fixed b/tests/ui/significant_drop_tightening.fixed index aa6690b77e1..8065e9e5fbc 100644 --- a/tests/ui/significant_drop_tightening.fixed +++ b/tests/ui/significant_drop_tightening.fixed @@ -58,6 +58,26 @@ pub fn issue_11160() -> bool { true } +pub fn issue_11189() { + struct Number { + pub value: u32, + } + + fn do_something() -> Result<(), ()> { + let number = Mutex::new(Number { value: 1 }); + let number2 = Mutex::new(Number { value: 2 }); + let number3 = Mutex::new(Number { value: 3 }); + let mut lock = number.lock().unwrap(); + let mut lock2 = number2.lock().unwrap(); + let mut lock3 = number3.lock().unwrap(); + lock.value += 1; + lock2.value += 1; + lock3.value += 1; + drop((lock, lock2, lock3)); + Ok(()) + } +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.rs b/tests/ui/significant_drop_tightening.rs index a0bf657df55..1620b76843a 100644 --- a/tests/ui/significant_drop_tightening.rs +++ b/tests/ui/significant_drop_tightening.rs @@ -57,6 +57,26 @@ pub fn issue_11160() -> bool { true } +pub fn issue_11189() { + struct Number { + pub value: u32, + } + + fn do_something() -> Result<(), ()> { + let number = Mutex::new(Number { value: 1 }); + let number2 = Mutex::new(Number { value: 2 }); + let number3 = Mutex::new(Number { value: 3 }); + let mut lock = number.lock().unwrap(); + let mut lock2 = number2.lock().unwrap(); + let mut lock3 = number3.lock().unwrap(); + lock.value += 1; + lock2.value += 1; + lock3.value += 1; + drop((lock, lock2, lock3)); + Ok(()) + } +} + pub fn path_return_can_be_ignored() -> i32 { let mutex = Mutex::new(1); let lock = mutex.lock().unwrap(); diff --git a/tests/ui/significant_drop_tightening.stderr b/tests/ui/significant_drop_tightening.stderr index 45ca73af3a8..b5cad88ad3f 100644 --- a/tests/ui/significant_drop_tightening.stderr +++ b/tests/ui/significant_drop_tightening.stderr @@ -23,7 +23,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:86:13 + --> $DIR/significant_drop_tightening.rs:106:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -43,7 +43,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:107:13 + --> $DIR/significant_drop_tightening.rs:127:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -67,7 +67,7 @@ LL + | error: temporary with significant `Drop` can be early dropped - --> $DIR/significant_drop_tightening.rs:113:17 + --> $DIR/significant_drop_tightening.rs:133:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); From 541d0c8ab7e84689d2b54419e4b9c2748d83220b Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 20 Jul 2023 21:39:32 +0200 Subject: [PATCH 42/90] [`slow_vector_initialization`]: lint `Vec::new()` --- .../src/slow_vector_initialization.rs | 96 ++++++++++++++----- tests/ui/read_zero_byte_vec.rs | 6 +- tests/ui/read_zero_byte_vec.stderr | 20 ++-- tests/ui/slow_vector_initialization.rs | 16 ++++ tests/ui/slow_vector_initialization.stderr | 64 +++++++++---- 5 files changed, 145 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 858135c8d46..b496185cc72 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq, + get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, + path_to_local_id, paths, SpanlessEq, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -60,7 +61,11 @@ struct VecAllocation<'tcx> { /// Reference to the expression used as argument on `with_capacity` call. This is used /// to only match slow zero-filling idioms of the same length than vector initialization. - len_expr: &'tcx Expr<'tcx>, + /// + /// Initially set to `None` if initialized with `Vec::new()`, but will always be `Some(_)` by + /// the time the visitor has looked through the enclosing block and found a slow + /// initialization, so it is safe to unwrap later at lint time. + size_expr: Option<&'tcx Expr<'tcx>>, } /// Type of slow initialization @@ -77,18 +82,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` if_chain! { if let ExprKind::Assign(left, right, _) = expr.kind; - - // Extract variable if let Some(local_id) = path_to_local(left); - - // Extract len argument - if let Some(len_arg) = Self::is_vec_with_capacity(cx, right); + if let Some(size_expr) = Self::as_vec_initializer(cx, right); then { let vi = VecAllocation { local_id, allocation_expr: right, - len_expr: len_arg, + size_expr, }; Self::search_initialization(cx, vi, expr.hir_id); @@ -98,17 +99,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` + // or `Vec::new()` if_chain! { if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind; if let Some(init) = local.init; - if let Some(len_arg) = Self::is_vec_with_capacity(cx, init); + if let Some(size_expr) = Self::as_vec_initializer(cx, init); then { let vi = VecAllocation { local_id, allocation_expr: init, - len_expr: len_arg, + size_expr, }; Self::search_initialization(cx, vi, stmt.hir_id); @@ -118,6 +120,25 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { } impl SlowVectorInit { + /// Given an expression, returns: + /// - `Some(Some(size))` if it is a function call to `Vec::with_capacity(size)` + /// - `Some(None)` if it is a call to `Vec::new()` + /// - `None` if it is neither + #[allow( + clippy::option_option, + reason = "outer option is immediately removed at call site and the inner option is kept around, \ + so extracting into a dedicated enum seems unnecessarily complicated" + )] + fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option>> { + if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) { + Some(Some(len_expr)) + } else if Self::is_vec_new(cx, expr) { + Some(None) + } else { + None + } + } + /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression /// of the first argument of `with_capacity` call if it matches or `None` if it does not. fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { @@ -134,6 +155,14 @@ impl SlowVectorInit { } } + /// Checks if the given expression is `Vec::new()` + fn is_vec_new(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW) + ) + } + /// Search initialization for the given vector fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) { let enclosing_body = get_enclosing_block(cx, parent_node); @@ -169,12 +198,18 @@ impl SlowVectorInit { } fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { - let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); + let len_expr = Sugg::hir( + cx, + vec_alloc + .size_expr + .expect("length expression must be set by this point"), + "len", + ); span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( vec_alloc.allocation_expr.span, - "consider replace allocation with", + "consider replacing this with", format!("vec![0; {len_expr}]"), Applicability::Unspecified, ); @@ -214,36 +249,45 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } /// Checks if the given expression is resizing a vector with 0 - fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { + fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) && path.ident.name == sym!(resize) // Check that is filled with 0 - && is_integer_literal(fill_arg, 0) { - // Check that len expression is equals to `with_capacity` expression - if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { - self.slow_expression = Some(InitializationType::Resize(expr)); - } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" { - self.slow_expression = Some(InitializationType::Resize(expr)); - } + && is_integer_literal(fill_arg, 0) + { + let is_matching_resize = if let Some(size_expr) = self.vec_alloc.size_expr { + // If we have a size expression, check that it is equal to what's passed to `resize` + SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") + } else { + self.vec_alloc.size_expr = Some(len_arg); + true + }; + + if is_matching_resize { + self.slow_expression = Some(InitializationType::Resize(expr)); } + } } /// Returns `true` if give expression is `repeat(0).take(...)` - fn is_repeat_take(&self, expr: &Expr<'_>) -> bool { + fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if_chain! { if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind; if take_path.ident.name == sym!(take); // Check that take is applied to `repeat(0)` if self.is_repeat_zero(recv); then { - // Check that len expression is equals to `with_capacity` expression - if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { - return true; - } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" { - return true; + if let Some(size_expr) = self.vec_alloc.size_expr { + // Check that len expression is equals to `with_capacity` expression + return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") } + + self.vec_alloc.size_expr = Some(len_arg); + return true; } } diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index c6025ef1f4d..ff2ad8644b4 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -1,5 +1,9 @@ #![warn(clippy::read_zero_byte_vec)] -#![allow(clippy::unused_io_amount, clippy::needless_pass_by_ref_mut)] +#![allow( + clippy::unused_io_amount, + clippy::needless_pass_by_ref_mut, + clippy::slow_vector_initialization +)] use std::fs::File; use std::io; use std::io::prelude::*; diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 08ba9753d7c..4c7f605f4c2 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -1,5 +1,5 @@ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:17:5 + --> $DIR/read_zero_byte_vec.rs:21:5 | LL | f.read_exact(&mut data).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();` @@ -7,55 +7,55 @@ LL | f.read_exact(&mut data).unwrap(); = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:21:5 + --> $DIR/read_zero_byte_vec.rs:25:5 | LL | f.read_exact(&mut data2)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;` error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:25:5 + --> $DIR/read_zero_byte_vec.rs:29:5 | LL | f.read_exact(&mut data3)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:29:5 + --> $DIR/read_zero_byte_vec.rs:33:5 | LL | let _ = f.read(&mut data4)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:34:9 + --> $DIR/read_zero_byte_vec.rs:38:9 | LL | f.read(&mut data5) | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:40:9 + --> $DIR/read_zero_byte_vec.rs:44:9 | LL | f.read(&mut data6) | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:70:5 + --> $DIR/read_zero_byte_vec.rs:74:5 | LL | r.read(&mut data).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:74:5 + --> $DIR/read_zero_byte_vec.rs:78:5 | LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:80:5 + --> $DIR/read_zero_byte_vec.rs:84:5 | LL | r.read(&mut data).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:84:5 + --> $DIR/read_zero_byte_vec.rs:88:5 | LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 16be9f6d203..cfb856861b8 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -4,6 +4,7 @@ fn main() { resize_vector(); extend_vector(); mixed_extend_resize_vector(); + from_empty_vec(); } fn extend_vector() { @@ -59,6 +60,21 @@ fn resize_vector() { vec1.resize(10, 0); } +fn from_empty_vec() { + // Resize with constant expression + let len = 300; + let mut vec1 = Vec::new(); + vec1.resize(len, 0); + + // Resize with len expression + let mut vec3 = Vec::new(); + vec3.resize(len - 10, 0); + + // Reinitialization should be warned + vec1 = Vec::new(); + vec1.resize(10, 0); +} + fn do_stuff(vec: &mut [u8]) {} fn extend_vector_with_manipulations_between() { diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index 22376680a8e..532ce4ac191 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -1,84 +1,108 @@ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:13:5 + --> $DIR/slow_vector_initialization.rs:14:5 | LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replace allocation with: `vec![0; len]` + | ----------------------- help: consider replacing this with: `vec![0; len]` LL | vec1.extend(repeat(0).take(len)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:17:5 + --> $DIR/slow_vector_initialization.rs:18:5 | LL | let mut vec2 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` + | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` LL | vec2.extend(repeat(0).take(len - 10)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:24:5 + --> $DIR/slow_vector_initialization.rs:25:5 | LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replace allocation with: `vec![0; len]` + | ----------------------- help: consider replacing this with: `vec![0; len]` LL | vec4.extend(repeat(0).take(vec4.capacity())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:34:5 + --> $DIR/slow_vector_initialization.rs:35:5 | LL | let mut resized_vec = Vec::with_capacity(30); - | ---------------------- help: consider replace allocation with: `vec![0; 30]` + | ---------------------- help: consider replacing this with: `vec![0; 30]` LL | resized_vec.resize(30, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:37:5 + --> $DIR/slow_vector_initialization.rs:38:5 | LL | let mut extend_vec = Vec::with_capacity(30); - | ---------------------- help: consider replace allocation with: `vec![0; 30]` + | ---------------------- help: consider replacing this with: `vec![0; 30]` LL | extend_vec.extend(repeat(0).take(30)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:44:5 + --> $DIR/slow_vector_initialization.rs:45:5 | LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replace allocation with: `vec![0; len]` + | ----------------------- help: consider replacing this with: `vec![0; len]` LL | vec1.resize(len, 0); | ^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:52:5 + --> $DIR/slow_vector_initialization.rs:53:5 | LL | let mut vec3 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` + | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` LL | vec3.resize(len - 10, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:55:5 + --> $DIR/slow_vector_initialization.rs:56:5 | LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replace allocation with: `vec![0; len]` + | ----------------------- help: consider replacing this with: `vec![0; len]` LL | vec4.resize(vec4.capacity(), 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: slow zero-filling initialization - --> $DIR/slow_vector_initialization.rs:59:5 + --> $DIR/slow_vector_initialization.rs:60:5 | LL | vec1 = Vec::with_capacity(10); - | ---------------------- help: consider replace allocation with: `vec![0; 10]` + | ---------------------- help: consider replacing this with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:67:5 + | +LL | let mut vec1 = Vec::new(); + | ---------- help: consider replacing this with: `vec![0; len]` +LL | vec1.resize(len, 0); + | ^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:71:5 + | +LL | let mut vec3 = Vec::new(); + | ---------- help: consider replacing this with: `vec![0; len - 10]` +LL | vec3.resize(len - 10, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:75:5 + | +LL | vec1 = Vec::new(); + | ---------- help: consider replacing this with: `vec![0; 10]` LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:62:18 + --> $DIR/slow_vector_initialization.rs:78:18 | LL | fn do_stuff(vec: &mut [u8]) {} | ^^^^^^^^^ help: consider changing to: `&[u8]` | = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` -error: aborting due to 10 previous errors +error: aborting due to 13 previous errors From a4c367d0e9a11e6eeb10088a1c8adccc77bcbc89 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 19 Jul 2023 02:59:06 -0500 Subject: [PATCH 43/90] Allow `Self::cmp(self, other)` as a correct impl --- clippy_lints/src/incorrect_impls.rs | 72 ++++++++++++++++--- clippy_utils/src/paths.rs | 1 + ...correct_partial_ord_impl_on_ord_type.fixed | 33 ++++++++- .../incorrect_partial_ord_impl_on_ord_type.rs | 33 ++++++++- ...orrect_partial_ord_impl_on_ord_type.stderr | 4 +- ...partial_ord_impl_on_ord_type_fully_qual.rs | 51 +++++++++++++ ...ial_ord_impl_on_ord_type_fully_qual.stderr | 31 ++++++++ 7 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs create mode 100644 tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs index 166908ef4e4..e8a2042e92a 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/incorrect_impls.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::paths::ORD_CMP; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, path_res}; +use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -60,6 +61,10 @@ declare_clippy_lint! { /// wrapping the result of `cmp` in `Some` for `partial_cmp`. Not doing this may silently /// introduce an error upon refactoring. /// + /// ### Known issues + /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()` + /// wrapping it in `Some`. + /// /// ### Limitations /// Will not lint if `Self` and `Rhs` do not have the same type. /// @@ -191,6 +196,11 @@ impl LateLintPass<'_> for IncorrectImpls { trait_impl.substs, ) { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. + let mut needs_fully_qualified = false; + if block.stmts.is_empty() && let Some(expr) = block.expr && let ExprKind::Call( @@ -202,9 +212,8 @@ impl LateLintPass<'_> for IncorrectImpls { [cmp_expr], ) = expr.kind && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - && let ExprKind::MethodCall(cmp_path, _, [other_expr], ..) = cmp_expr.kind - && cmp_path.ident.name == sym::cmp - && let Res::Local(..) = path_res(cx, other_expr) + // Fix #11178, allow `Self::cmp(self, ..)` too + && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified) {} else { // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid // suggestion tons more complex. @@ -221,14 +230,29 @@ impl LateLintPass<'_> for IncorrectImpls { let [_, other] = body.params else { return; }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; - let suggs = if let Some(other_ident) = other.pat.simple_ident() { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - } else { - vec![ + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ (block.span, "{ Some(self.cmp(other)) }".to_owned()), (other.pat.span, "other".to_owned()), - ] + ], }; diag.multipart_suggestion( @@ -242,3 +266,31 @@ impl LateLintPass<'_> for IncorrectImpls { } } } + +/// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. +fn self_cmp_call<'tcx>( + cx: &LateContext<'tcx>, + cmp_expr: &'tcx Expr<'tcx>, + def_id: LocalDefId, + needs_fully_qualified: &mut bool, +) -> bool { + match cmp_expr.kind { + ExprKind::Call(path, [_self, _other]) => path_res(cx, path) + .opt_def_id() + .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)), + ExprKind::MethodCall(_, _, [_other], ..) => { + // We can set this to true here no matter what as if it's a `MethodCall` and goes to the + // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp` + *needs_fully_qualified = true; + + // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we + // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid + // an immediate ICE + cx.tcx + .typeck(def_id) + .type_dependent_def_id(cmp_expr.hir_id) + .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)) + }, + _ => false, + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index f3677e6f614..8fc13f23ef9 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -161,3 +161,4 @@ pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"]; pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; +pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"]; diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed index dd4fdd98822..2f51bf27480 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed @@ -1,5 +1,4 @@ //@run-rustfix -#![allow(unused)] #![no_main] use std::cmp::Ordering; @@ -112,3 +111,35 @@ impl PartialOrd for F { todo!(); } } + +// #11178, do not lint + +#[derive(Eq, PartialEq)] +struct G(u32); + +impl Ord for G { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for G { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Self::cmp(self, other)) + } +} + +#[derive(Eq, PartialEq)] +struct H(u32); + +impl Ord for H { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for H { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } +} diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs index 522e82299c0..47127bdaec2 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs @@ -1,5 +1,4 @@ //@run-rustfix -#![allow(unused)] #![no_main] use std::cmp::Ordering; @@ -116,3 +115,35 @@ impl PartialOrd for F { todo!(); } } + +// #11178, do not lint + +#[derive(Eq, PartialEq)] +struct G(u32); + +impl Ord for G { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for G { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Self::cmp(self, other)) + } +} + +#[derive(Eq, PartialEq)] +struct H(u32); + +impl Ord for H { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for H { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } +} diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr index 0e477798c40..66048fc9000 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr @@ -1,5 +1,5 @@ error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:18:1 + --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:17:1 | LL | / impl PartialOrd for A { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -13,7 +13,7 @@ LL | | } = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:52:1 + --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:51:1 | LL | / impl PartialOrd for C { LL | | fn partial_cmp(&self, _: &Self) -> Option { diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs b/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs new file mode 100644 index 00000000000..3a3b84f93c4 --- /dev/null +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs @@ -0,0 +1,51 @@ +// This test's filename is... a bit verbose. But it ensures we suggest the correct code when `Ord` +// is not in scope. +#![no_main] +#![no_implicit_prelude] + +extern crate std; + +use std::cmp::{self, Eq, Ordering, PartialEq, PartialOrd}; +use std::option::Option::{self, Some}; +use std::todo; + +// lint + +#[derive(Eq, PartialEq)] +struct A(u32); + +impl cmp::Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for A { + fn partial_cmp(&self, other: &Self) -> Option { + // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't + // automatically applied + todo!(); + } +} + +#[derive(Eq, PartialEq)] +struct B(u32); + +impl B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl cmp::Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + // This calls `B.cmp`, not `Ord::cmp`! + Some(self.cmp(other)) + } +} diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr b/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr new file mode 100644 index 00000000000..f4374c28128 --- /dev/null +++ b/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr @@ -0,0 +1,31 @@ +error: incorrect implementation of `partial_cmp` on an `Ord` type + --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:23:1 + | +LL | / impl PartialOrd for A { +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________- +LL | || // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't +LL | || // automatically applied +LL | || todo!(); +LL | || } + | ||_____- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__^ + | + = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + +error: incorrect implementation of `partial_cmp` on an `Ord` type + --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:46:1 + | +LL | / impl PartialOrd for B { +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________- +LL | || // This calls `B.cmp`, not `Ord::cmp`! +LL | || Some(self.cmp(other)) +LL | || } + | ||_____- help: change this to: `{ Some(std::cmp::Ord::cmp(self, other)) }` +LL | | } + | |__^ + +error: aborting due to 2 previous errors + From 37b83660bc2501f6e90cb48689304aed73229967 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 21 Jul 2023 00:50:01 +0200 Subject: [PATCH 44/90] [`unused_async`]: don't lint if paths reference async fn without call --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unused_async.rs | 103 +++++++++++++++++++++++++------ tests/ui/unused_async.rs | 17 +++++ tests/ui/unused_async.stderr | 34 ++++++---- 4 files changed, 123 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1609eb396b4..e6f6212508e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -911,7 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); - store.register_late_pass(|_| Box::new(unused_async::UnusedAsync)); + store.register_late_pass(|_| Box::::default()); let disallowed_types = conf.disallowed_types.clone(); store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); let import_renames = conf.enforced_import_renames.clone(); diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 5e42cf7e4f3..5c8c6e58914 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_def_id_trait_method; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::DefKind; use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::def_id::LocalDefId; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::{LocalDefId, LocalDefIdSet}; use rustc_span::Span; declare_clippy_lint! { @@ -38,7 +40,23 @@ declare_clippy_lint! { "finds async functions with no await statements" } -declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); +#[derive(Default)] +pub struct UnusedAsync { + /// Keeps track of async functions used as values (i.e. path expressions to async functions that + /// are not immediately called) + async_fns_as_value: LocalDefIdSet, + /// Functions with unused `async`, linted post-crate after we've found all uses of local async + /// functions + unused_async_fns: FxHashMap, +} + +#[derive(Copy, Clone)] +struct UnusedAsyncFn { + fn_span: Span, + await_in_async_block: Option, +} + +impl_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); struct AsyncFnVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -101,24 +119,71 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { - span_lint_and_then( - cx, - UNUSED_ASYNC, - span, - "unused `async` for function with no await statements", - |diag| { - diag.help("consider removing the `async` from this function"); - - if let Some(span) = visitor.await_in_async_block { - diag.span_note( - span, - "`await` used in an async block, which does not require \ - the enclosing function to be `async`", - ); - } + // Don't lint just yet, but store the necessary information for later. + // The actual linting happens in `check_crate_post`, once we've found all + // uses of local async functions that do require asyncness to pass typeck + self.unused_async_fns.insert( + def_id, + UnusedAsyncFn { + await_in_async_block: visitor.await_in_async_block, + fn_span: span, }, ); } } } + + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: rustc_hir::HirId) { + fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool { + matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Call(Expr { span, .. }, _) | ExprKind::MethodCall(_, Expr { span, .. }, ..), + .. + }) if *span == expected_receiver + ) + } + + // Find paths to local async functions that aren't immediately called. + // E.g. `async fn f() {}; let x = f;` + // Depending on how `x` is used, f's asyncness might be required despite not having any `await` + // statements, so don't lint at all if there are any such paths. + if let Some(def_id) = path.res.opt_def_id() + && let Some(local_def_id) = def_id.as_local() + && let Some(DefKind::Fn) = cx.tcx.opt_def_kind(def_id) + && cx.tcx.asyncness(def_id).is_async() + && !is_node_func_call(cx.tcx.hir().get_parent(hir_id), path.span) + { + self.async_fns_as_value.insert(local_def_id); + } + } + + // After collecting all unused `async` and problematic paths to such functions, + // lint those unused ones that didn't have any path expressions to them. + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + let iter = self + .unused_async_fns + .iter() + .filter_map(|(did, item)| (!self.async_fns_as_value.contains(did)).then_some(item)); + + for fun in iter { + span_lint_and_then( + cx, + UNUSED_ASYNC, + fun.fn_span, + "unused `async` for function with no await statements", + |diag| { + diag.help("consider removing the `async` from this function"); + + if let Some(span) = fun.await_in_async_block { + diag.span_note( + span, + "`await` used in an async block, which does not require \ + the enclosing function to be `async`", + ); + } + }, + ); + } + } } diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 69e46ab4736..1d188025e41 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -37,6 +37,23 @@ mod issue10459 { } } +mod issue9695 { + use std::future::Future; + + async fn f() {} + async fn f2() {} + async fn f3() {} + + fn needs_async_fn>(_: fn() -> F) {} + + fn test() { + let x = f; + needs_async_fn(x); // async needed in f + needs_async_fn(f2); // async needed in f2 + f3(); // async not needed in f3 + } +} + async fn foo() -> i32 { 4 } diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr index ffae8366b88..035dffa7f13 100644 --- a/tests/ui/unused_async.stderr +++ b/tests/ui/unused_async.stderr @@ -1,3 +1,22 @@ +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:45:5 + | +LL | async fn f3() {} + | ^^^^^^^^^^^^^^^^ + | + = help: consider removing the `async` from this function + = note: `-D clippy::unused-async` implied by `-D warnings` + +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:57:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = help: consider removing the `async` from this function + error: unused `async` for function with no await statements --> $DIR/unused_async.rs:13:5 | @@ -14,20 +33,9 @@ note: `await` used in an async block, which does not require the enclosing funct | LL | ready(()).await; | ^^^^^ - = note: `-D clippy::unused-async` implied by `-D warnings` error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:40:1 - | -LL | / async fn foo() -> i32 { -LL | | 4 -LL | | } - | |_^ - | - = help: consider removing the `async` from this function - -error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:51:5 + --> $DIR/unused_async.rs:68:5 | LL | / async fn unused(&self) -> i32 { LL | | 1 @@ -36,5 +44,5 @@ LL | | } | = help: consider removing the `async` from this function -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From f3f7f63c16ba173acefe43859516d0af9afc34f6 Mon Sep 17 00:00:00 2001 From: Catherine Flores Date: Fri, 21 Jul 2023 06:45:30 -0500 Subject: [PATCH 45/90] [`inherent_to_string`]: Don't lint `unsafe` or `extern` fns --- clippy_lints/src/inherent_to_string.rs | 35 +++++++++++++------------- tests/ui/inherent_to_string.rs | 26 +++++++++++++++++-- tests/ui/inherent_to_string.stderr | 10 +++----- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index d43e5cc9b2c..bc4ec33b733 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::{return_ty, trait_ref_of_method}; -use if_chain::if_chain; -use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; +use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -95,24 +95,23 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { return; } - if_chain! { - // Check if item is a method, called to_string and has a parameter 'self' - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; - if impl_item.ident.name == sym::to_string; - let decl = &signature.decl; - if decl.implicit_self.has_implicit_self(); - if decl.inputs.len() == 1; - if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })); - + // Check if item is a method called `to_string` and has a parameter 'self' + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind + // #11201 + && let header = signature.header + && header.unsafety == Unsafety::Normal + && header.abi == Abi::Rust + && impl_item.ident.name == sym::to_string + && let decl = signature.decl + && decl.implicit_self.has_implicit_self() + && decl.inputs.len() == 1 + && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) // Check if return type is String - if is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String); - + && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) // Filters instances of to_string which are required by a trait - if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none(); - - then { - show_lint(cx, impl_item); - } + && trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none() + { + show_lint(cx, impl_item); } } } diff --git a/tests/ui/inherent_to_string.rs b/tests/ui/inherent_to_string.rs index aeb0a0c1e2e..adb0389a043 100644 --- a/tests/ui/inherent_to_string.rs +++ b/tests/ui/inherent_to_string.rs @@ -1,5 +1,4 @@ -#![warn(clippy::inherent_to_string)] -#![deny(clippy::inherent_to_string_shadow_display)] +#![allow(improper_ctypes_definitions)] use std::fmt; @@ -14,6 +13,9 @@ struct D; struct E; struct F; struct G; +struct H; +struct I; +struct J; impl A { // Should be detected; emit warning @@ -80,6 +82,26 @@ impl G { } } +// Issue #11201 + +impl H { + unsafe fn to_string(&self) -> String { + "G.to_string()".to_string() + } +} + +impl I { + extern "C" fn to_string(&self) -> String { + "G.to_string()".to_string() + } +} + +impl J { + unsafe extern "C" fn to_string(&self) -> String { + "G.to_string()".to_string() + } +} + fn main() { let a = A; a.to_string(); diff --git a/tests/ui/inherent_to_string.stderr b/tests/ui/inherent_to_string.stderr index 443fecae1aa..579b3c8c56f 100644 --- a/tests/ui/inherent_to_string.stderr +++ b/tests/ui/inherent_to_string.stderr @@ -1,5 +1,5 @@ error: implementation of inherent method `to_string(&self) -> String` for type `A` - --> $DIR/inherent_to_string.rs:20:5 + --> $DIR/inherent_to_string.rs:22:5 | LL | / fn to_string(&self) -> String { LL | | "A.to_string()".to_string() @@ -10,7 +10,7 @@ LL | | } = note: `-D clippy::inherent-to-string` implied by `-D warnings` error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` - --> $DIR/inherent_to_string.rs:44:5 + --> $DIR/inherent_to_string.rs:46:5 | LL | / fn to_string(&self) -> String { LL | | "C.to_string()".to_string() @@ -18,11 +18,7 @@ LL | | } | |_____^ | = help: remove the inherent method from type `C` -note: the lint level is defined here - --> $DIR/inherent_to_string.rs:2:9 - | -LL | #![deny(clippy::inherent_to_string_shadow_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::inherent_to_string_shadow_display)]` on by default error: aborting due to 2 previous errors From 9cf1509b25889fad9743777602d472ffc8bd3ec2 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Wed, 21 Jun 2023 21:15:20 -0500 Subject: [PATCH 46/90] New lint `absolute_paths` --- CHANGELOG.md | 3 + book/src/lint_configuration.md | 21 ++++ clippy_dev/src/lib.rs | 2 +- clippy_lints/src/absolute_paths.rs | 100 ++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 9 ++ clippy_lints/src/lifetimes.rs | 3 +- .../src/redundant_static_lifetimes.rs | 3 +- clippy_lints/src/utils/conf.rs | 10 ++ clippy_utils/src/mir/possible_origin.rs | 2 +- clippy_utils/src/usage.rs | 3 +- .../absolute_paths.allow_crates.stderr | 28 +++++ .../absolute_paths.disallow_crates.stderr | 70 ++++++++++++ .../ui-toml/absolute_paths/absolute_paths.rs | 97 +++++++++++++++++ .../absolute_paths/allow_crates/clippy.toml | 2 + .../absolute_paths/auxiliary/helper.rs | 11 ++ .../disallow_crates/clippy.toml | 1 + .../toml_unknown_key/conf_unknown_key.stderr | 4 + 18 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 clippy_lints/src/absolute_paths.rs create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.rs create mode 100644 tests/ui-toml/absolute_paths/allow_crates/clippy.toml create mode 100644 tests/ui-toml/absolute_paths/auxiliary/helper.rs create mode 100644 tests/ui-toml/absolute_paths/disallow_crates/clippy.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 437f08dd777..fe879712130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4680,6 +4680,7 @@ Released 2018-09-13 +[`absolute_paths`]: https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core [`allow_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes @@ -5467,4 +5468,6 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments +[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f8073dac330..caaad6d1173 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -730,3 +730,24 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) +## `absolute-paths-max-segments` +The maximum number of segments a path can have before being linted, anything above this will +be linted. + +**Default Value:** `2` (`u64`) + +--- +**Affected lints:** +* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) + + +## `absolute-paths-allowed-crates` +Which crates to allow absolute paths from + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +--- +**Affected lints:** +* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) + + diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 4624451cff4..c4ae4f0e2bd 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -51,7 +51,7 @@ pub fn clippy_project_root() -> PathBuf { for path in current_dir.ancestors() { let result = std::fs::read_to_string(path.join("Cargo.toml")); if let Err(err) = &result { - if err.kind() == std::io::ErrorKind::NotFound { + if err.kind() == io::ErrorKind::NotFound { continue; } } diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs new file mode 100644 index 00000000000..04417c4c460 --- /dev/null +++ b/clippy_lints/src/absolute_paths.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet_opt; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_hir::{HirId, ItemKind, Node, Path}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of items through absolute paths, like `std::env::current_dir`. + /// + /// ### Why is this bad? + /// Many codebases have their own style when it comes to importing, but one that is seldom used + /// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you + /// should add a `use` statement. + /// + /// The default maximum segments (2) is pretty strict, you may want to increase this in + /// `clippy.toml`. + /// + /// Note: One exception to this is code from macro expansion - this does not lint such cases, as + /// using absolute paths is the proper way of referencing items in one. + /// + /// ### Example + /// ```rust + /// let x = std::f64::consts::PI; + /// ``` + /// Use any of the below instead, or anything else: + /// ```rust + /// use std::f64; + /// use std::f64::consts; + /// use std::f64::consts::PI; + /// let x = f64::consts::PI; + /// let x = consts::PI; + /// let x = PI; + /// use std::f64::consts as f64_consts; + /// let x = f64_consts::PI; + /// ``` + #[clippy::version = "1.73.0"] + pub ABSOLUTE_PATHS, + restriction, + "checks for usage of an item without a `use` statement" +} +impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); + +pub struct AbsolutePaths { + pub absolute_paths_max_segments: u64, + pub absolute_paths_allowed_crates: FxHashSet, +} + +impl LateLintPass<'_> for AbsolutePaths { + // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath` + // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't + // a `Use` + #[expect(clippy::cast_possible_truncation)] + fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { + let Self { + absolute_paths_max_segments, + absolute_paths_allowed_crates, + } = self; + + if !path.span.from_expansion() + && let Some(node) = cx.tcx.hir().find(hir_id) + && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _))) + && let [first, rest @ ..] = path.segments + // Handle `::std` + && let (segment, len) = if first.ident.name == kw::PathRoot { + // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1` + // is fine here for the same reason + (&rest[0], path.segments.len() - 1) + } else { + (first, path.segments.len()) + } + && len > *absolute_paths_max_segments as usize + && let Some(segment_snippet) = snippet_opt(cx, segment.ident.span) + && segment_snippet == segment.ident.as_str() + { + let is_abs_external = + matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX); + let is_abs_crate = segment.ident.name == kw::Crate; + + if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str()) + || is_abs_crate && absolute_paths_allowed_crates.contains("crate") + { + return; + } + + if is_abs_external || is_abs_crate { + span_lint( + cx, + ABSOLUTE_PATHS, + path.span, + "consider bringing this path into scope with the `use` keyword", + ); + } + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a0d181be3b1..830714ce7b0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -37,6 +37,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, + crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::allow_attributes::ALLOW_ATTRIBUTES_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1609eb396b4..0fa5c2f1828 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -65,6 +65,7 @@ mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` +mod absolute_paths; mod allow_attributes; mod almost_complete_range; mod approx_const; @@ -1082,6 +1083,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); + let absolute_paths_max_segments = conf.absolute_paths_max_segments; + let absolute_paths_allowed_crates = conf.absolute_paths_allowed_crates.clone(); + store.register_late_pass(move |_| { + Box::new(absolute_paths::AbsolutePaths { + absolute_paths_max_segments, + absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(), + }) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 852f6736585..0004a150d51 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -15,6 +15,7 @@ use rustc_hir::{ PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -620,7 +621,7 @@ impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> where F: NestedFilter<'tcx>, { - type Map = rustc_middle::hir::map::Map<'tcx>; + type Map = Map<'tcx>; type NestedFilter = F; // for lifetimes as parameters of generics diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 038dfe8e480..ed42a422b4b 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does @@ -64,7 +65,7 @@ impl RedundantStaticLifetimes { if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { - if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { + if lifetime.ident.name == kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); span_lint_and_then( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 76654bfe536..58ae0656db7 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -551,6 +551,16 @@ define_Conf! { /// /// Whether to allow `r#""#` when `r""` can be used (allow_one_hash_in_raw_strings: bool = false), + /// Lint: ABSOLUTE_PATHS. + /// + /// The maximum number of segments a path can have before being linted, anything above this will + /// be linted. + (absolute_paths_max_segments: u64 = 2), + /// Lint: ABSOLUTE_PATHS. + /// + /// Which crates to allow absolute paths from + (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = + rustc_data_structures::fx::FxHashSet::default()), } /// Search for the configuration file. diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index 8e7513d740a..da04266863f 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { let lhs = place.local; match rvalue { // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, borrowed) | // _2: &mut _; // _3 = move _2 mir::Rvalue::Use(mir::Operand::Move(borrowed)) | diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 20993398d1b..8a7ea1e6cad 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,5 +1,6 @@ use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; use core::ops::ControlFlow; +use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Node}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -127,7 +128,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { - if let hir::def::Res::Local(id) = path.res { + if let Res::Local(id) = path.res { if self.binding_ids.contains(&id) { self.usage_found = true; } diff --git a/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr new file mode 100644 index 00000000000..a8900da4e6e --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr @@ -0,0 +1,28 @@ +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:40:5 + | +LL | std::f32::MAX; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::absolute-paths` implied by `-D warnings` + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:41:5 + | +LL | core::f32::MAX; + | ^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:42:5 + | +LL | ::core::f32::MAX; + | ^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:58:5 + | +LL | ::std::f32::MAX; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr new file mode 100644 index 00000000000..41b70644be8 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr @@ -0,0 +1,70 @@ +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:40:5 + | +LL | std::f32::MAX; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::absolute-paths` implied by `-D warnings` + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:41:5 + | +LL | core::f32::MAX; + | ^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:42:5 + | +LL | ::core::f32::MAX; + | ^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:43:5 + | +LL | crate::a::b::c::C; + | ^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:44:5 + | +LL | crate::a::b::c::d::e::f::F; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:45:5 + | +LL | crate::a::A; + | ^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:46:5 + | +LL | crate::a::b::B; + | ^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:47:5 + | +LL | crate::a::b::c::C::ZERO; + | ^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:48:5 + | +LL | helper::b::c::d::e::f(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:49:5 + | +LL | ::helper::b::c::d::e::f(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> $DIR/absolute_paths.rs:58:5 + | +LL | ::std::f32::MAX; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui-toml/absolute_paths/absolute_paths.rs b/tests/ui-toml/absolute_paths/absolute_paths.rs new file mode 100644 index 00000000000..d4c250a8ff2 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.rs @@ -0,0 +1,97 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs:proc-macro +//@aux-build:helper.rs +//@revisions: allow_crates disallow_crates +//@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates +//@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates +#![allow(clippy::no_effect, unused)] +#![warn(clippy::absolute_paths)] +#![feature(decl_macro)] + +extern crate helper; +#[macro_use] +extern crate proc_macros; + +pub mod a { + pub mod b { + pub mod c { + pub struct C; + + impl C { + pub const ZERO: u32 = 0; + } + + pub mod d { + pub mod e { + pub mod f { + pub struct F; + } + } + } + } + + pub struct B; + } + + pub struct A; +} + +fn main() { + f32::max(1.0, 2.0); + std::f32::MAX; + core::f32::MAX; + ::core::f32::MAX; + crate::a::b::c::C; + crate::a::b::c::d::e::f::F; + crate::a::A; + crate::a::b::B; + crate::a::b::c::C::ZERO; + helper::b::c::d::e::f(); + ::helper::b::c::d::e::f(); + fn b() -> a::b::B { + todo!() + } + std::println!("a"); + let x = 1; + std::ptr::addr_of!(x); + // Test we handle max segments with `PathRoot` properly; this has 4 segments but we should say it + // has 3 + ::std::f32::MAX; + // Do not lint due to the above + ::helper::a(); + // Do not lint + helper::a(); + use crate::a::b::c::C; + use a::b; + use std::f32::MAX; + a::b::c::d::e::f::F; + b::c::C; + fn a() -> a::A { + todo!() + } + use a::b::c; + + fn c() -> c::C { + todo!() + } + fn d() -> Result<(), ()> { + todo!() + } + external! { + crate::a::b::c::C::ZERO; + } + // For some reason, `path.span.from_expansion()` takes care of this for us + with_span! { + span + crate::a::b::c::C::ZERO; + } + macro_rules! local_crate { + () => { + crate::a::b::c::C::ZERO; + }; + } + macro local_crate_2_0() { + crate::a::b::c::C::ZERO; + } + local_crate!(); + local_crate_2_0!(); +} diff --git a/tests/ui-toml/absolute_paths/allow_crates/clippy.toml b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml new file mode 100644 index 00000000000..59a621e9d1d --- /dev/null +++ b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml @@ -0,0 +1,2 @@ +absolute-paths-max-segments = 2 +absolute-paths-allowed-crates = ["crate", "helper"] diff --git a/tests/ui-toml/absolute_paths/auxiliary/helper.rs b/tests/ui-toml/absolute_paths/auxiliary/helper.rs new file mode 100644 index 00000000000..8e2678f5fe6 --- /dev/null +++ b/tests/ui-toml/absolute_paths/auxiliary/helper.rs @@ -0,0 +1,11 @@ +pub fn a() {} + +pub mod b { + pub mod c { + pub mod d { + pub mod e { + pub fn f() {} + } + } + } +} diff --git a/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml b/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml new file mode 100644 index 00000000000..d44d648c641 --- /dev/null +++ b/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml @@ -0,0 +1 @@ +absolute-paths-max-segments = 2 diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ba26e97730..cdabe6460cd 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expected one of + absolute-paths-allowed-crates + absolute-paths-max-segments accept-comment-above-attributes accept-comment-above-statement allow-dbg-in-tests @@ -68,6 +70,8 @@ LL | foobar = 42 | ^^^^^^ error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of + absolute-paths-allowed-crates + absolute-paths-max-segments accept-comment-above-attributes accept-comment-above-statement allow-dbg-in-tests From 008ba2b8bb711b8ee725a808f3d9ee65c2a1dce3 Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Sat, 3 Jun 2023 19:58:27 -0700 Subject: [PATCH 47/90] new lint: redundant_local fix dogfood lints in `redundant_local` keep `redundant_local` from running in proc macros rewrite `redundant_local` as late pass make redundant_local's `find_binding` more readable pluralize `redundant_locals` name add test for `redundant_locals` in macros test `redundant_locals` in proc macros use more destructuring in `redundant_locals` fix: format redundant_locals.rs ignore needless_pass_by_mut_ref in redundant_locals test --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/redundant_locals.rs | 103 ++++++++++++++++++++ tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 46 ++++----- tests/ui/redundant_locals.rs | 111 ++++++++++++++++++++++ tests/ui/redundant_locals.stderr | 136 +++++++++++++++++++++++++++ tests/ui/shadow.rs | 2 +- tests/ui/swap.fixed | 3 +- tests/ui/swap.rs | 3 +- tests/ui/swap.stderr | 34 +++---- 13 files changed, 403 insertions(+), 45 deletions(-) create mode 100644 clippy_lints/src/redundant_locals.rs create mode 100644 tests/ui/redundant_locals.rs create mode 100644 tests/ui/redundant_locals.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index fe879712130..2b53f1df7c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5190,6 +5190,7 @@ Released 2018-09-13 [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names +[`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 830714ce7b0..e92d9fa7f63 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -561,6 +561,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, crate::redundant_else::REDUNDANT_ELSE_INFO, crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO, + crate::redundant_locals::REDUNDANT_LOCALS_INFO, crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO, crate::redundant_slicing::DEREF_BY_SLICING_INFO, crate::redundant_slicing::REDUNDANT_SLICING_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0fa5c2f1828..ca2f5191cdf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -275,6 +275,7 @@ mod redundant_clone; mod redundant_closure_call; mod redundant_else; mod redundant_field_names; +mod redundant_locals; mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; @@ -1091,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(), }) }); + store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs new file mode 100644 index 00000000000..140ae837a17 --- /dev/null +++ b/clippy_lints/src/redundant_locals.rs @@ -0,0 +1,103 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; +use clippy_utils::ty::needs_ordered_drop; +use rustc_hir::def::Res; +use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// ### What it does + /// Checks for redundant redefinitions of local bindings. + /// + /// ### Why is this bad? + /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. + /// + /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. + /// + /// ### Example + /// ```rust + /// let a = 0; + /// let a = a; + /// + /// fn foo(b: i32) { + /// let b = b; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let a = 0; + /// // no redefinition with the same name + /// + /// fn foo(b: i32) { + /// // no redefinition with the same name + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub REDUNDANT_LOCALS, + correctness, + "redundant redefinition of a local binding" +} +declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); + +impl<'tcx> LateLintPass<'tcx> for RedundantLocals { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + if_chain! { + // the pattern is a single by-value binding + if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind; + // the binding is not type-ascribed + if local.ty.is_none(); + // the expression is a resolved path + if let Some(expr) = local.init; + if let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind; + // the path is a single segment equal to the local's name + if let [last_segment] = path.segments; + if last_segment.ident == ident; + // resolve the path to its defining binding pattern + if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id); + if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); + // the previous binding has the same mutability + if find_binding(binding_pat, ident).unwrap().1 == mutability; + // the local does not affect the code's drop behavior + if !affects_drop_behavior(cx, binding_id, local.hir_id, expr); + // the local is user-controlled + if !in_external_macro(cx.sess(), local.span); + if !is_from_proc_macro(cx, expr); + then { + span_lint_and_help( + cx, + REDUNDANT_LOCALS, + vec![binding_pat.span, local.span], + "redundant redefinition of a binding", + None, + &format!("remove the redefinition of `{ident}`"), + ); + } + } + } +} + +/// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced. +fn find_binding(pat: &Pat<'_>, name: Ident) -> Option { + let mut ret = None; + + pat.each_binding_or_first(&mut |annotation, _, _, ident| { + if ident == name { + ret = Some(annotation); + } + }); + + ret +} + +/// Check if a rebinding of a local affects the code's drop behavior. +fn affects_drop_behavior<'tcx>(cx: &LateContext<'tcx>, bind: HirId, rebind: HirId, rebind_expr: &Expr<'tcx>) -> bool { + let hir = cx.tcx.hir(); + + // the rebinding is in a different scope than the original binding + // and the type of the binding cares about drop order + hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) + && needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr)) +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 8e59e4375d2..6fee3cce619 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,7 +5,8 @@ clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let, - clippy::let_unit_value + clippy::let_unit_value, + clippy::redundant_locals )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index e72edf2a8e3..4b3cf948a1b 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -5,7 +5,8 @@ clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let, - clippy::let_unit_value + clippy::let_unit_value, + clippy::redundant_locals )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index aa2da217400..350f0f07e13 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:12:5 + --> $DIR/option_if_let_else.rs:13:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:30:13 + --> $DIR/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:38:13 + --> $DIR/option_if_let_else.rs:39:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:45:13 + --> $DIR/option_if_let_else.rs:46:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:54:5 + --> $DIR/option_if_let_else.rs:55:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:67:13 + --> $DIR/option_if_let_else.rs:68:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:76:13 + --> $DIR/option_if_let_else.rs:77:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,7 +143,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:109:13 + --> $DIR/option_if_let_else.rs:110:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -153,7 +153,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:120:5 + --> $DIR/option_if_let_else.rs:121:5 | LL | / if let Ok(binding) = variable { LL | | println!("Ok {binding}"); @@ -172,13 +172,13 @@ LL + }) | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:142:13 + --> $DIR/option_if_let_else.rs:143:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:152:13 + --> $DIR/option_if_let_else.rs:153:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -200,13 +200,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:180:13 + --> $DIR/option_if_let_else.rs:181:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:184:13 + --> $DIR/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -226,7 +226,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:223:13 + --> $DIR/option_if_let_else.rs:224:13 | LL | let _ = match s { | _____________^ @@ -236,7 +236,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:227:13 + --> $DIR/option_if_let_else.rs:228:13 | LL | let _ = match Some(10) { | _____________^ @@ -246,7 +246,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:233:13 + --> $DIR/option_if_let_else.rs:234:13 | LL | let _ = match res { | _____________^ @@ -256,7 +256,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:237:13 + --> $DIR/option_if_let_else.rs:238:13 | LL | let _ = match res { | _____________^ @@ -266,13 +266,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:241:13 + --> $DIR/option_if_let_else.rs:242:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:258:9 + --> $DIR/option_if_let_else.rs:259:9 | LL | / match initial { LL | | Some(value) => do_something(value), @@ -281,7 +281,7 @@ LL | | } | |_________^ help: try: `initial.as_ref().map_or({}, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:265:9 + --> $DIR/option_if_let_else.rs:266:9 | LL | / match initial { LL | | Some(value) => do_something2(value), diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs new file mode 100644 index 00000000000..e74c78a50b6 --- /dev/null +++ b/tests/ui/redundant_locals.rs @@ -0,0 +1,111 @@ +//@aux-build:proc_macros.rs:proc-macro +#![allow(unused, clippy::no_effect, clippy::needless_pass_by_ref_mut)] +#![warn(clippy::redundant_locals)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +fn main() {} + +fn immutable() { + let x = 1; + let x = x; +} + +fn mutable() { + let mut x = 1; + let mut x = x; +} + +fn upgraded_mutability() { + let x = 1; + let mut x = x; +} + +fn downgraded_mutability() { + let mut x = 1; + let x = x; +} + +fn coercion(par: &mut i32) { + let par: &i32 = par; + + let x: &mut i32 = &mut 1; + let x: &i32 = x; +} + +fn parameter(x: i32) { + let x = x; +} + +fn many() { + let x = 1; + let x = x; + let x = x; + let x = x; + let x = x; +} + +fn interleaved() { + let a = 1; + let b = 2; + let a = a; + let b = b; +} + +fn block() { + { + let x = 1; + let x = x; + } +} + +fn closure() { + || { + let x = 1; + let x = x; + }; + |x: i32| { + let x = x; + }; +} + +fn consequential_drop_order() { + use std::sync::Mutex; + + let mutex = Mutex::new(1); + let guard = mutex.lock().unwrap(); + + { + let guard = guard; + } +} + +fn inconsequential_drop_order() { + let x = 1; + + { + let x = x; + } +} + +fn macros() { + macro_rules! rebind { + ($x:ident) => { + let $x = 1; + let $x = $x; + }; + } + + rebind!(x); + + external! { + let x = 1; + let x = x; + } + with_span! { + span + let x = 1; + let x = x; + } +} diff --git a/tests/ui/redundant_locals.stderr b/tests/ui/redundant_locals.stderr new file mode 100644 index 00000000000..df07dd2f453 --- /dev/null +++ b/tests/ui/redundant_locals.stderr @@ -0,0 +1,136 @@ +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:11:9 + | +LL | let x = 1; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + = note: `-D clippy::redundant-locals` implied by `-D warnings` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:16:9 + | +LL | let mut x = 1; + | ^^^^^ +LL | let mut x = x; + | ^^^^^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:37:14 + | +LL | fn parameter(x: i32) { + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:42:9 + | +LL | let x = 1; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:43:9 + | +LL | let x = x; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:44:9 + | +LL | let x = x; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:45:9 + | +LL | let x = x; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:50:9 + | +LL | let a = 1; + | ^ +LL | let b = 2; +LL | let a = a; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `a` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:51:9 + | +LL | let b = 2; + | ^ +LL | let a = a; +LL | let b = b; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `b` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:58:13 + | +LL | let x = 1; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:65:13 + | +LL | let x = 1; + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:68:6 + | +LL | |x: i32| { + | ^ +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:85:9 + | +LL | let x = 1; + | ^ +... +LL | let x = x; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `x` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 9be8c5e59d0..4d22b7d2ad0 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macro_derive.rs:proc-macro #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] -#![allow(clippy::let_unit_value, clippy::needless_if)] +#![allow(clippy::let_unit_value, clippy::needless_if, clippy::redundant_locals)] extern crate proc_macro_derive; diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 22f904e3fd9..7b74a83b6df 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -11,7 +11,8 @@ unused_assignments, unused_variables, clippy::let_and_return, - clippy::useless_vec + clippy::useless_vec, + clippy::redundant_locals )] struct Foo(u32); diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index ada64f89e6d..93855cd7b5c 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -11,7 +11,8 @@ unused_assignments, unused_variables, clippy::let_and_return, - clippy::useless_vec + clippy::useless_vec, + clippy::redundant_locals )] struct Foo(u32); diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index a3b9c2b744c..1097b29bba0 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:28:5 + --> $DIR/swap.rs:29:5 | LL | / let temp = bar.a; LL | | bar.a = bar.b; @@ -10,7 +10,7 @@ LL | | bar.b = temp; = note: `-D clippy::manual-swap` implied by `-D warnings` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:40:5 + --> $DIR/swap.rs:41:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -18,7 +18,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:49:5 + --> $DIR/swap.rs:50:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -26,7 +26,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:68:5 + --> $DIR/swap.rs:69:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -34,7 +34,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:79:5 + --> $DIR/swap.rs:80:5 | LL | / a ^= b; LL | | b ^= a; @@ -42,7 +42,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:87:5 + --> $DIR/swap.rs:88:5 | LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; @@ -50,7 +50,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:95:5 + --> $DIR/swap.rs:96:5 | LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; @@ -58,7 +58,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> $DIR/swap.rs:124:5 + --> $DIR/swap.rs:125:5 | LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; @@ -68,7 +68,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:138:7 + --> $DIR/swap.rs:139:7 | LL | ; let t = a; | _______^ @@ -79,7 +79,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> $DIR/swap.rs:147:7 + --> $DIR/swap.rs:148:7 | LL | ; let t = c.0; | _______^ @@ -90,7 +90,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `b` and `a` manually - --> $DIR/swap.rs:173:5 + --> $DIR/swap.rs:174:5 | LL | / let t = b; LL | | b = a; @@ -100,7 +100,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:135:5 + --> $DIR/swap.rs:136:5 | LL | / a = b; LL | | b = a; @@ -110,7 +110,7 @@ LL | | b = a; = note: `-D clippy::almost-swapped` implied by `-D warnings` error: this looks like you are trying to swap `c.0` and `a` - --> $DIR/swap.rs:144:5 + --> $DIR/swap.rs:145:5 | LL | / c.0 = a; LL | | a = c.0; @@ -119,7 +119,7 @@ LL | | a = c.0; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:151:5 + --> $DIR/swap.rs:152:5 | LL | / let a = b; LL | | let b = a; @@ -128,7 +128,7 @@ LL | | let b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `d` and `c` - --> $DIR/swap.rs:156:5 + --> $DIR/swap.rs:157:5 | LL | / d = c; LL | | c = d; @@ -137,7 +137,7 @@ LL | | c = d; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:160:5 + --> $DIR/swap.rs:161:5 | LL | / let a = b; LL | | b = a; @@ -146,7 +146,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> $DIR/swap.rs:208:5 + --> $DIR/swap.rs:209:5 | LL | / let t = s.0.x; LL | | s.0.x = s.0.y; From da93ee86e5708cd991d7e21012ff2a2a980b0b2d Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:20:31 -0500 Subject: [PATCH 48/90] Make `comparison_to_empty` work on `if let`/`let` chains refactor it --- clippy_lints/src/len_zero.rs | 30 +++++++++- clippy_utils/src/higher.rs | 92 +++++++++++++++++++++++++++++ tests/ui/comparison_to_empty.fixed | 12 +++- tests/ui/comparison_to_empty.rs | 12 +++- tests/ui/comparison_to_empty.stderr | 40 +++++++++++-- 5 files changed, 176 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e64d46bd6e3..a707921ce7e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -7,11 +7,10 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::lang_items::LangItem; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, - ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy, QPath, TraitItemRef, TyKind, - TypeBindingKind, + ImplicitSelfKind, Item, ItemKind, LangItem, Mutability, Node, PatKind, PathSegment, PrimTy, QPath, TraitItemRef, + TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -168,6 +167,31 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } + if let ExprKind::Let(lt) = expr.kind + && has_is_empty(cx, lt.init) + && match lt.pat.kind { + PatKind::Slice([], _, []) => true, + PatKind::Lit(lit) if is_empty_string(lit) => true, + _ => false, + } + { + let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lt.init); + let lit_str = + Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par(); + + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + lt.span, + "comparison to empty slice using `if let`", + "using `is_empty` is clearer and more explicit", + format!("{lit_str}.is_empty()"), + applicability, + ); + } + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { // expr.span might contains parenthesis, see issue #10529 let actual_span = left.span.with_hi(right.span.hi()); diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 802adbd4d2d..312e6ea40c5 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -5,6 +5,7 @@ use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; +use hir::BinOpKind; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; @@ -137,6 +138,97 @@ impl<'hir> IfLet<'hir> { } } +/// A `let` chain, like `if true && let Some(true) = x {}` +#[derive(Debug)] +pub struct LetChain<'hir> { + pub conds: Vec>, + pub if_then: &'hir Expr<'hir>, + pub if_else: Option<&'hir Expr<'hir>>, +} + +impl<'hir> LetChain<'hir> { + pub fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::If(cond, if_then, if_else) = expr.kind { + let mut conds = vec![]; + let mut cursor = cond; + while let ExprKind::Binary(binop, lhs, rhs) = cursor.kind + && let BinOpKind::And = binop.node + { + cursor = lhs; + conds.push(IfOrIfLetInChain::hir(rhs)?); + } + + // The final lhs cannot be `&&` + conds.push(IfOrIfLetInChain::hir(cursor)?); + + return Some(Self { + conds, + if_then, + if_else, + }); + } + + None + } +} + +/// An `if let` or `if` expression in a let chain. +#[derive(Debug)] +pub enum IfOrIfLetInChain<'hir> { + If(IfInChain<'hir>), + IfLet(IfLetInChain<'hir>), +} + +impl<'hir> IfOrIfLetInChain<'hir> { + pub fn hir(expr: &Expr<'hir>) -> Option { + match expr.kind { + ExprKind::DropTemps(cond) => Some(IfInChain { cond }.into()), + ExprKind::Let(hir::Let { + pat: let_pat, + init: let_expr, + span: let_span, + .. + }) => Some( + IfLetInChain { + let_pat, + let_expr, + let_span: *let_span, + } + .into(), + ), + _ => None, + } + } +} + +impl<'hir> From> for IfOrIfLetInChain<'hir> { + fn from(value: IfInChain<'hir>) -> Self { + Self::If(value) + } +} + +impl<'hir> From> for IfOrIfLetInChain<'hir> { + fn from(value: IfLetInChain<'hir>) -> Self { + Self::IfLet(value) + } +} + +/// An `if` expression in a let chain. +#[derive(Debug)] +pub struct IfInChain<'hir> { + pub cond: &'hir Expr<'hir>, +} + +/// An `if let` expression in a let chain. +#[derive(Debug)] +pub struct IfLetInChain<'hir> { + pub let_span: Span, + /// `if let` pattern + pub let_pat: &'hir Pat<'hir>, + /// `if let` scrutinee + pub let_expr: &'hir Expr<'hir>, +} + /// An `if let` or `match` expression. Useful for lints that trigger on one or the other. #[derive(Debug)] pub enum IfLetOrMatch<'hir> { diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed index c92dd509ebb..af219eed0b8 100644 --- a/tests/ui/comparison_to_empty.fixed +++ b/tests/ui/comparison_to_empty.fixed @@ -1,7 +1,8 @@ //@run-rustfix #![warn(clippy::comparison_to_empty)] -#![allow(clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![feature(let_chains)] fn main() { // Disallow comparisons to empty @@ -12,6 +13,11 @@ fn main() { let v = vec![0]; let _ = v.is_empty(); let _ = !v.is_empty(); + if (*v).is_empty() {} + let s = [0].as_slice(); + if s.is_empty() {} + if s.is_empty() {} + if s.is_empty() && s.is_empty() {} // Allow comparisons to non-empty let s = String::new(); @@ -21,4 +27,8 @@ fn main() { let v = vec![0]; let _ = v == [0]; let _ = v != [0]; + if let [0] = &*v {} + let s = [0].as_slice(); + if let [0] = s {} + if let [0] = &*s && s == [0] {} } diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs index b3489714380..21e65184d50 100644 --- a/tests/ui/comparison_to_empty.rs +++ b/tests/ui/comparison_to_empty.rs @@ -1,7 +1,8 @@ //@run-rustfix #![warn(clippy::comparison_to_empty)] -#![allow(clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![feature(let_chains)] fn main() { // Disallow comparisons to empty @@ -12,6 +13,11 @@ fn main() { let v = vec![0]; let _ = v == []; let _ = v != []; + if let [] = &*v {} + let s = [0].as_slice(); + if let [] = s {} + if let [] = &*s {} + if let [] = &*s && s == [] {} // Allow comparisons to non-empty let s = String::new(); @@ -21,4 +27,8 @@ fn main() { let v = vec![0]; let _ = v == [0]; let _ = v != [0]; + if let [0] = &*v {} + let s = [0].as_slice(); + if let [0] = s {} + if let [0] = &*s && s == [0] {} } diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr index cc09b17eb89..f29782ed80d 100644 --- a/tests/ui/comparison_to_empty.stderr +++ b/tests/ui/comparison_to_empty.stderr @@ -1,5 +1,5 @@ error: comparison to empty slice - --> $DIR/comparison_to_empty.rs:9:13 + --> $DIR/comparison_to_empty.rs:10:13 | LL | let _ = s == ""; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` @@ -7,22 +7,52 @@ LL | let _ = s == ""; = note: `-D clippy::comparison-to-empty` implied by `-D warnings` error: comparison to empty slice - --> $DIR/comparison_to_empty.rs:10:13 + --> $DIR/comparison_to_empty.rs:11:13 | LL | let _ = s != ""; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> $DIR/comparison_to_empty.rs:13:13 + --> $DIR/comparison_to_empty.rs:14:13 | LL | let _ = v == []; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> $DIR/comparison_to_empty.rs:14:13 + --> $DIR/comparison_to_empty.rs:15:13 | LL | let _ = v != []; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` -error: aborting due to 4 previous errors +error: comparison to empty slice using `if let` + --> $DIR/comparison_to_empty.rs:16:8 + | +LL | if let [] = &*v {} + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(*v).is_empty()` + +error: comparison to empty slice using `if let` + --> $DIR/comparison_to_empty.rs:18:8 + | +LL | if let [] = s {} + | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: comparison to empty slice using `if let` + --> $DIR/comparison_to_empty.rs:19:8 + | +LL | if let [] = &*s {} + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: comparison to empty slice using `if let` + --> $DIR/comparison_to_empty.rs:20:8 + | +LL | if let [] = &*s && s == [] {} + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:20:24 + | +LL | if let [] = &*s && s == [] {} + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: aborting due to 9 previous errors From ae5d391d21fb227da007da44286e43ce61f10f38 Mon Sep 17 00:00:00 2001 From: Catherine Flores Date: Sat, 22 Jul 2023 05:57:39 -0500 Subject: [PATCH 49/90] Remove `LetChain` --- clippy_lints/src/len_zero.rs | 2 +- clippy_utils/src/higher.rs | 92 ------------------------------------ 2 files changed, 1 insertion(+), 93 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index a707921ce7e..d4236926a6c 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && has_is_empty(cx, lt.init) && match lt.pat.kind { - PatKind::Slice([], _, []) => true, + PatKind::Slice([], None, []) => true, PatKind::Lit(lit) if is_empty_string(lit) => true, _ => false, } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 312e6ea40c5..802adbd4d2d 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -5,7 +5,6 @@ use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; -use hir::BinOpKind; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; @@ -138,97 +137,6 @@ impl<'hir> IfLet<'hir> { } } -/// A `let` chain, like `if true && let Some(true) = x {}` -#[derive(Debug)] -pub struct LetChain<'hir> { - pub conds: Vec>, - pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, -} - -impl<'hir> LetChain<'hir> { - pub fn hir(expr: &Expr<'hir>) -> Option { - if let ExprKind::If(cond, if_then, if_else) = expr.kind { - let mut conds = vec![]; - let mut cursor = cond; - while let ExprKind::Binary(binop, lhs, rhs) = cursor.kind - && let BinOpKind::And = binop.node - { - cursor = lhs; - conds.push(IfOrIfLetInChain::hir(rhs)?); - } - - // The final lhs cannot be `&&` - conds.push(IfOrIfLetInChain::hir(cursor)?); - - return Some(Self { - conds, - if_then, - if_else, - }); - } - - None - } -} - -/// An `if let` or `if` expression in a let chain. -#[derive(Debug)] -pub enum IfOrIfLetInChain<'hir> { - If(IfInChain<'hir>), - IfLet(IfLetInChain<'hir>), -} - -impl<'hir> IfOrIfLetInChain<'hir> { - pub fn hir(expr: &Expr<'hir>) -> Option { - match expr.kind { - ExprKind::DropTemps(cond) => Some(IfInChain { cond }.into()), - ExprKind::Let(hir::Let { - pat: let_pat, - init: let_expr, - span: let_span, - .. - }) => Some( - IfLetInChain { - let_pat, - let_expr, - let_span: *let_span, - } - .into(), - ), - _ => None, - } - } -} - -impl<'hir> From> for IfOrIfLetInChain<'hir> { - fn from(value: IfInChain<'hir>) -> Self { - Self::If(value) - } -} - -impl<'hir> From> for IfOrIfLetInChain<'hir> { - fn from(value: IfLetInChain<'hir>) -> Self { - Self::IfLet(value) - } -} - -/// An `if` expression in a let chain. -#[derive(Debug)] -pub struct IfInChain<'hir> { - pub cond: &'hir Expr<'hir>, -} - -/// An `if let` expression in a let chain. -#[derive(Debug)] -pub struct IfLetInChain<'hir> { - pub let_span: Span, - /// `if let` pattern - pub let_pat: &'hir Pat<'hir>, - /// `if let` scrutinee - pub let_expr: &'hir Expr<'hir>, -} - /// An `if let` or `match` expression. Useful for lints that trigger on one or the other. #[derive(Debug)] pub enum IfLetOrMatch<'hir> { From 51b57723d18aec1b9dd26bbb1482d1f29f6d18cf Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Wed, 14 Jun 2023 13:51:31 -0500 Subject: [PATCH 50/90] new lint `redundant_guards` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/loops/utils.rs | 4 +- clippy_lints/src/matches/mod.rs | 33 +++ clippy_lints/src/matches/redundant_guards.rs | 190 ++++++++++++++++++ tests/ui/match_expr_like_matches_macro.fixed | 3 +- tests/ui/match_expr_like_matches_macro.rs | 3 +- tests/ui/match_expr_like_matches_macro.stderr | 28 +-- tests/ui/redundant_guards.fixed | 133 ++++++++++++ tests/ui/redundant_guards.rs | 133 ++++++++++++ tests/ui/redundant_guards.stderr | 98 +++++++++ tests/ui/shadow.rs | 7 +- tests/ui/shadow.stderr | 92 ++++----- tests/ui/single_match.fixed | 1 + tests/ui/single_match.rs | 1 + tests/ui/single_match.stderr | 36 ++-- 16 files changed, 681 insertions(+), 83 deletions(-) create mode 100644 clippy_lints/src/matches/redundant_guards.rs create mode 100644 tests/ui/redundant_guards.fixed create mode 100644 tests/ui/redundant_guards.rs create mode 100644 tests/ui/redundant_guards.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b53f1df7c0..9118c0e634f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5190,6 +5190,7 @@ Released 2018-09-13 [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names +[`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e92d9fa7f63..189ca5b052e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -308,6 +308,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::MATCH_WILD_ERR_ARM_INFO, crate::matches::NEEDLESS_MATCH_INFO, + crate::matches::REDUNDANT_GUARDS_INFO, crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO, crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO, diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 28ee24309cc..6edca2d55f6 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -76,7 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { *state = IncrementVisitorVarState::DontWarn; }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => { *state = IncrementVisitorVarState::DontWarn; }, _ => (), @@ -226,7 +226,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { InitializeVisitorState::DontWarn } }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => { self.state = InitializeVisitorState::DontWarn; }, _ => (), diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index d1061171e4d..6d16d188754 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -16,6 +16,7 @@ mod match_wild_enum; mod match_wild_err_arm; mod needless_match; mod overlapping_arms; +mod redundant_guards; mod redundant_pattern_match; mod rest_pat_in_fully_bound_struct; mod significant_drop_in_scrutinee; @@ -936,6 +937,36 @@ declare_clippy_lint! { "reimplementation of `filter`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary guards in match expressions. + /// + /// ### Why is this bad? + /// It's more complex and much less readable. Making it part of the pattern can improve + /// exhaustiveness checking as well. + /// + /// ### Example + /// ```rust,ignore + /// match x { + /// Some(x) if matches!(x, Some(1)) => .., + /// Some(x) if x == Some(2) => .., + /// _ => todo!(), + /// } + /// ``` + /// Use instead: + /// ```rust,ignore + /// match x { + /// Some(Some(1)) => .., + /// Some(Some(2)) => .., + /// _ => todo!(), + /// } + /// ``` + #[clippy::version = "1.72.0"] + pub REDUNDANT_GUARDS, + complexity, + "checks for unnecessary guards in match expressions" +} + #[derive(Default)] pub struct Matches { msrv: Msrv, @@ -978,6 +1009,7 @@ impl_lint_pass!(Matches => [ TRY_ERR, MANUAL_MAP, MANUAL_FILTER, + REDUNDANT_GUARDS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -1025,6 +1057,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { needless_match::check_match(cx, ex, arms, expr); match_on_vec_items::check(cx, ex); match_str_case_mismatch::check(cx, ex, arms); + redundant_guards::check(cx, arms); if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs new file mode 100644 index 00000000000..6383326aa38 --- /dev/null +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -0,0 +1,190 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::path_to_local; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::visitors::{for_each_expr, is_local_used}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_span::Span; +use std::ops::ControlFlow; + +use super::REDUNDANT_GUARDS; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { + for outer_arm in arms { + let Some(guard) = outer_arm.guard else { + continue; + }; + + // `Some(x) if matches!(x, y)` + if let Guard::If(if_expr) = guard + && let ExprKind::Match( + scrutinee, + [ + arm, + Arm { + pat: Pat { + kind: PatKind::Wild, + .. + }, + .. + }, + ], + MatchSource::Normal, + ) = if_expr.kind + { + emit_redundant_guards( + cx, + outer_arm, + if_expr.span, + scrutinee, + arm.pat.span, + arm.guard, + ); + } + // `Some(x) if let Some(2) = x` + else if let Guard::IfLet(let_expr) = guard { + emit_redundant_guards( + cx, + outer_arm, + let_expr.span, + let_expr.init, + let_expr.pat.span, + None, + ); + } + // `Some(x) if x == Some(2)` + else if let Guard::If(if_expr) = guard + && let ExprKind::Binary(bin_op, local, pat) = if_expr.kind + && matches!(bin_op.node, BinOpKind::Eq) + && expr_can_be_pat(cx, pat) + // Ensure they have the same type. If they don't, we'd need deref coercion which isn't + // possible (currently) in a pattern. In some cases, you can use something like + // `as_deref` or similar but in general, we shouldn't lint this as it'd create an + // extraordinary amount of FPs. + // + // This isn't necessary in the other two checks, as they must be a pattern already. + && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) + { + emit_redundant_guards( + cx, + outer_arm, + if_expr.span, + local, + pat.span, + None, + ); + } + } +} + +fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> { + if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { + let mut span = None; + let mut multiple_bindings = false; + // `each_binding` gives the `HirId` of the `Pat` itself, not the binding + outer_arm.pat.walk(|pat| { + if let PatKind::Binding(_, hir_id, _, _) = pat.kind + && hir_id == local + && span.replace(pat.span).is_some() + { + multiple_bindings = true; + return false; + } + + true + }); + + // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)` + if !multiple_bindings { + return span.map(|span| { + ( + span, + !matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + ) + }); + } + } + + None +} + +fn emit_redundant_guards<'tcx>( + cx: &LateContext<'tcx>, + outer_arm: &Arm<'tcx>, + guard_span: Span, + local: &Expr<'_>, + pat_span: Span, + inner_guard: Option>, +) { + let mut app = Applicability::MaybeIncorrect; + let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else { + return; + }; + + span_lint_and_then( + cx, + REDUNDANT_GUARDS, + guard_span.source_callsite(), + "redundant guard", + |diag| { + let binding_replacement = snippet_with_applicability(cx, pat_span, "", &mut app); + diag.multipart_suggestion_verbose( + "try", + vec![ + if can_use_shorthand { + (pat_binding, binding_replacement.into_owned()) + } else { + (pat_binding.shrink_to_hi(), format!(": {binding_replacement}")) + }, + ( + guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), + inner_guard.map_or_else(String::new, |guard| { + let (prefix, span) = match guard { + Guard::If(e) => ("if", e.span), + Guard::IfLet(l) => ("if let", l.span), + }; + + format!( + " {prefix} {}", + snippet_with_applicability(cx, span, "", &mut app), + ) + }), + ), + ], + app, + ); + }, + ); +} + +/// Checks if the given `Expr` can also be represented as a `Pat`. +fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + for_each_expr(expr, |expr| { + if match expr.kind { + ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat, + ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { + // Allow ctors + matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..)) + }, + ExprKind::Path(qpath) => { + matches!( + cx.qpath_res(&qpath, expr.hir_id), + Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..), + ) + }, + ExprKind::AddrOf(..) + | ExprKind::Array(..) + | ExprKind::Tup(..) + | ExprKind::Struct(..) + | ExprKind::Lit(..) => true, + _ => false, + } { + return ControlFlow::Continue(()); + } + + ControlFlow::Break(()) + }) + .is_none() +} diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 60f59066173..f19149cf9de 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -5,7 +5,8 @@ unreachable_patterns, dead_code, clippy::equatable_if_let, - clippy::needless_borrowed_reference + clippy::needless_borrowed_reference, + clippy::redundant_guards )] fn main() { diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index afdf1069f5e..8f4e58981ea 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -5,7 +5,8 @@ unreachable_patterns, dead_code, clippy::equatable_if_let, - clippy::needless_borrowed_reference + clippy::needless_borrowed_reference, + clippy::redundant_guards )] fn main() { diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index c8c1e5da05f..b57b26284ff 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:15:14 + --> $DIR/match_expr_like_matches_macro.rs:16:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/match_expr_like_matches_macro.rs:21:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _w = match x { | ______________^ @@ -23,7 +23,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:27:14 + --> $DIR/match_expr_like_matches_macro.rs:28:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:33:15 + --> $DIR/match_expr_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:39:16 + --> $DIR/match_expr_like_matches_macro.rs:40:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:63:20 + --> $DIR/match_expr_like_matches_macro.rs:64:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:73:20 + --> $DIR/match_expr_like_matches_macro.rs:74:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:83:20 + --> $DIR/match_expr_like_matches_macro.rs:84:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:143:18 + --> $DIR/match_expr_like_matches_macro.rs:144:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:152:18 + --> $DIR/match_expr_like_matches_macro.rs:153:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:169:21 + --> $DIR/match_expr_like_matches_macro.rs:170:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:183:20 + --> $DIR/match_expr_like_matches_macro.rs:184:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:195:20 + --> $DIR/match_expr_like_matches_macro.rs:196:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:253:14 + --> $DIR/match_expr_like_matches_macro.rs:254:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed new file mode 100644 index 00000000000..77ac7666864 --- /dev/null +++ b/tests/ui/redundant_guards.fixed @@ -0,0 +1,133 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![feature(if_let_guard)] +#![allow(clippy::no_effect, unused)] +#![warn(clippy::redundant_guards)] + +#[macro_use] +extern crate proc_macros; + +struct A(u32); + +struct B { + e: Option, +} + +struct C(u32, u32); + +fn main() { + let c = C(1, 2); + match c { + C(x, 1) => .., + _ => todo!(), + }; + + let x = Some(Some(1)); + match x { + Some(Some(1)) if true => .., + Some(Some(1)) => { + println!("a"); + .. + }, + Some(Some(1)) => .., + Some(Some(2)) => .., + // Don't lint, since x is used in the body + Some(x) if let Some(1) = x => { + x; + .. + } + _ => todo!(), + }; + let y = 1; + match x { + // Don't inline these, since y is not from the pat + Some(x) if matches!(y, 1 if true) => .., + Some(x) if let 1 = y => .., + Some(x) if y == 2 => .., + _ => todo!(), + }; + let a = A(1); + match a { + _ if a.0 == 1 => {}, + _ => todo!(), + } + let b = B { e: Some(A(0)) }; + match b { + B { e: Some(A(2)) } => .., + _ => todo!(), + }; + // Do not lint, since we cannot represent this as a pattern (at least, without a conversion) + let v = Some(vec![1u8, 2, 3]); + match v { + Some(x) if x == [1] => {}, + _ => {}, + } + + external! { + let x = Some(Some(1)); + match x { + Some(x) if let Some(1) = x => .., + _ => todo!(), + }; + } + with_span! { + span + let x = Some(Some(1)); + match x { + Some(x) if let Some(1) = x => .., + _ => todo!(), + }; + } +} + +enum E { + A(&'static str), + B(&'static str), + C(&'static str), +} + +fn i() { + match E::A("") { + // Do not lint + E::A(x) | E::B(x) | E::C(x) if x == "from an or pattern" => {}, + E::A("not from an or pattern") => {}, + _ => {}, + }; +} + +fn h(v: Option) { + match v { + Some(0) => .., + _ => .., + }; +} + +// Do not lint + +fn f(s: Option) { + match s { + Some(x) if x == "a" => {}, + _ => {}, + } +} + +struct S { + a: usize, +} + +impl PartialEq for S { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Eq for S {} + +static CONST_S: S = S { a: 1 }; + +fn g(opt_s: Option) { + match opt_s { + Some(x) if x == CONST_S => {}, + _ => {}, + } +} diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs new file mode 100644 index 00000000000..b072e4ea14d --- /dev/null +++ b/tests/ui/redundant_guards.rs @@ -0,0 +1,133 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![feature(if_let_guard)] +#![allow(clippy::no_effect, unused)] +#![warn(clippy::redundant_guards)] + +#[macro_use] +extern crate proc_macros; + +struct A(u32); + +struct B { + e: Option, +} + +struct C(u32, u32); + +fn main() { + let c = C(1, 2); + match c { + C(x, y) if let 1 = y => .., + _ => todo!(), + }; + + let x = Some(Some(1)); + match x { + Some(x) if matches!(x, Some(1) if true) => .., + Some(x) if matches!(x, Some(1)) => { + println!("a"); + .. + }, + Some(x) if let Some(1) = x => .., + Some(x) if x == Some(2) => .., + // Don't lint, since x is used in the body + Some(x) if let Some(1) = x => { + x; + .. + } + _ => todo!(), + }; + let y = 1; + match x { + // Don't inline these, since y is not from the pat + Some(x) if matches!(y, 1 if true) => .., + Some(x) if let 1 = y => .., + Some(x) if y == 2 => .., + _ => todo!(), + }; + let a = A(1); + match a { + _ if a.0 == 1 => {}, + _ => todo!(), + } + let b = B { e: Some(A(0)) }; + match b { + B { e } if matches!(e, Some(A(2))) => .., + _ => todo!(), + }; + // Do not lint, since we cannot represent this as a pattern (at least, without a conversion) + let v = Some(vec![1u8, 2, 3]); + match v { + Some(x) if x == [1] => {}, + _ => {}, + } + + external! { + let x = Some(Some(1)); + match x { + Some(x) if let Some(1) = x => .., + _ => todo!(), + }; + } + with_span! { + span + let x = Some(Some(1)); + match x { + Some(x) if let Some(1) = x => .., + _ => todo!(), + }; + } +} + +enum E { + A(&'static str), + B(&'static str), + C(&'static str), +} + +fn i() { + match E::A("") { + // Do not lint + E::A(x) | E::B(x) | E::C(x) if x == "from an or pattern" => {}, + E::A(y) if y == "not from an or pattern" => {}, + _ => {}, + }; +} + +fn h(v: Option) { + match v { + x if matches!(x, Some(0)) => .., + _ => .., + }; +} + +// Do not lint + +fn f(s: Option) { + match s { + Some(x) if x == "a" => {}, + _ => {}, + } +} + +struct S { + a: usize, +} + +impl PartialEq for S { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Eq for S {} + +static CONST_S: S = S { a: 1 }; + +fn g(opt_s: Option) { + match opt_s { + Some(x) if x == CONST_S => {}, + _ => {}, + } +} diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr new file mode 100644 index 00000000000..c2a92071d1d --- /dev/null +++ b/tests/ui/redundant_guards.stderr @@ -0,0 +1,98 @@ +error: redundant guard + --> $DIR/redundant_guards.rs:21:20 + | +LL | C(x, y) if let 1 = y => .., + | ^^^^^^^^^ + | + = note: `-D clippy::redundant-guards` implied by `-D warnings` +help: try + | +LL - C(x, y) if let 1 = y => .., +LL + C(x, 1) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:27:20 + | +LL | Some(x) if matches!(x, Some(1) if true) => .., + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | Some(Some(1)) if true => .., + | ~~~~~~~ ~~~~~~~ + +error: redundant guard + --> $DIR/redundant_guards.rs:28:20 + | +LL | Some(x) if matches!(x, Some(1)) => { + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if matches!(x, Some(1)) => { +LL + Some(Some(1)) => { + | + +error: redundant guard + --> $DIR/redundant_guards.rs:32:20 + | +LL | Some(x) if let Some(1) = x => .., + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if let Some(1) = x => .., +LL + Some(Some(1)) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:33:20 + | +LL | Some(x) if x == Some(2) => .., + | ^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if x == Some(2) => .., +LL + Some(Some(2)) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:56:20 + | +LL | B { e } if matches!(e, Some(A(2))) => .., + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - B { e } if matches!(e, Some(A(2))) => .., +LL + B { e: Some(A(2)) } => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:93:20 + | +LL | E::A(y) if y == "not from an or pattern" => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - E::A(y) if y == "not from an or pattern" => {}, +LL + E::A("not from an or pattern") => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:100:14 + | +LL | x if matches!(x, Some(0)) => .., + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - x if matches!(x, Some(0)) => .., +LL + Some(0) => .., + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 4d22b7d2ad0..1b40a43d019 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,7 +1,12 @@ //@aux-build:proc_macro_derive.rs:proc-macro #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] -#![allow(clippy::let_unit_value, clippy::needless_if, clippy::redundant_locals)] +#![allow( + clippy::let_unit_value, + clippy::needless_if, + clippy::redundant_guards, + clippy::redundant_locals +)] extern crate proc_macro_derive; diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8321f6df224..88b02f53be1 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,278 +1,278 @@ error: `x` is shadowed by itself in `x` - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:24:9 | LL | let x = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:23:9 | LL | let x = 1; | ^ = note: `-D clippy::shadow-same` implied by `-D warnings` error: `mut x` is shadowed by itself in `&x` - --> $DIR/shadow.rs:20:13 + --> $DIR/shadow.rs:25:13 | LL | let mut x = &x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:24:9 | LL | let x = x; | ^ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:26:9 | LL | let x = &mut x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:20:9 + --> $DIR/shadow.rs:25:9 | LL | let mut x = &x; | ^^^^^ error: `x` is shadowed by itself in `*x` - --> $DIR/shadow.rs:22:9 + --> $DIR/shadow.rs:27:9 | LL | let x = *x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:26:9 | LL | let x = &mut x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:27:9 + --> $DIR/shadow.rs:32:9 | LL | let x = x.0; | ^ | note: previous binding is here - --> $DIR/shadow.rs:26:9 + --> $DIR/shadow.rs:31:9 | LL | let x = ([[0]], ()); | ^ = note: `-D clippy::shadow-reuse` implied by `-D warnings` error: `x` is shadowed - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:33:9 | LL | let x = x[0]; | ^ | note: previous binding is here - --> $DIR/shadow.rs:27:9 + --> $DIR/shadow.rs:32:9 | LL | let x = x.0; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:29:10 + --> $DIR/shadow.rs:34:10 | LL | let [x] = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:33:9 | LL | let x = x[0]; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:35:9 | LL | let x = Some(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:29:10 + --> $DIR/shadow.rs:34:10 | LL | let [x] = x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:36:9 | LL | let x = foo(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:35:9 | LL | let x = Some(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = || x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:36:9 | LL | let x = foo(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:33:9 + --> $DIR/shadow.rs:38:9 | LL | let x = Some(1).map(|_| x)?; | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = || x; | ^ error: `y` is shadowed - --> $DIR/shadow.rs:35:9 + --> $DIR/shadow.rs:40:9 | LL | let y = match y { | ^ | note: previous binding is here - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:39:9 | LL | let y = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:50:9 + --> $DIR/shadow.rs:55:9 | LL | let x = 2; | ^ | note: previous binding is here - --> $DIR/shadow.rs:49:9 + --> $DIR/shadow.rs:54:9 | LL | let x = 1; | ^ = note: `-D clippy::shadow-unrelated` implied by `-D warnings` error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:55:13 + --> $DIR/shadow.rs:60:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:54:10 + --> $DIR/shadow.rs:59:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:60:14 + --> $DIR/shadow.rs:65:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:57:9 + --> $DIR/shadow.rs:62:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:61:17 + --> $DIR/shadow.rs:66:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:60:14 + --> $DIR/shadow.rs:65:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:65:17 + --> $DIR/shadow.rs:70:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:57:9 + --> $DIR/shadow.rs:62:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:66:20 + --> $DIR/shadow.rs:71:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:57:9 + --> $DIR/shadow.rs:62:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:67:15 + --> $DIR/shadow.rs:72:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:57:9 + --> $DIR/shadow.rs:62:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:68:13 + --> $DIR/shadow.rs:73:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:67:15 + --> $DIR/shadow.rs:72:15 | LL | let _ = |[x]: [u32; 1]| { | ^ error: `y` is shadowed - --> $DIR/shadow.rs:71:17 + --> $DIR/shadow.rs:76:17 | LL | if let Some(y) = y {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:70:9 + --> $DIR/shadow.rs:75:9 | LL | let y = Some(1); | ^ error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:107:9 + --> $DIR/shadow.rs:112:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:106:28 + --> $DIR/shadow.rs:111:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:113:21 + --> $DIR/shadow.rs:118:21 | LL | if let Some(x) = Some(1) { x } else { 1 } | ^ | note: previous binding is here - --> $DIR/shadow.rs:112:13 + --> $DIR/shadow.rs:117:13 | LL | let x = 1; | ^ diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index e7b1fd6a85f..163ba94aff8 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -4,6 +4,7 @@ unused, clippy::uninlined_format_args, clippy::needless_if, + clippy::redundant_guards, clippy::redundant_pattern_matching )] fn dummy() {} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 1515a7053e5..0dcdb125ffd 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -4,6 +4,7 @@ unused, clippy::uninlined_format_args, clippy::needless_if, + clippy::redundant_guards, clippy::redundant_pattern_matching )] fn dummy() {} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 76f7e789589..d3536159949 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:14:5 + --> $DIR/single_match.rs:15:5 | LL | / match x { LL | | Some(y) => { @@ -18,7 +18,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:22:5 + --> $DIR/single_match.rs:23:5 | LL | / match x { LL | | // Note the missing block braces. @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:31:5 + --> $DIR/single_match.rs:32:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:60:5 + --> $DIR/single_match.rs:61:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:65:5 + --> $DIR/single_match.rs:66:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:72:5 + --> $DIR/single_match.rs:73:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:93:5 + --> $DIR/single_match.rs:94:5 | LL | / match x { LL | | "test" => println!(), @@ -75,7 +75,7 @@ LL | | } | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:106:5 + --> $DIR/single_match.rs:107:5 | LL | / match x { LL | | Foo::A => println!(), @@ -84,7 +84,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:112:5 + --> $DIR/single_match.rs:113:5 | LL | / match x { LL | | FOO_C => println!(), @@ -93,7 +93,7 @@ LL | | } | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:117:5 + --> $DIR/single_match.rs:118:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -102,7 +102,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:123:5 + --> $DIR/single_match.rs:124:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -111,7 +111,7 @@ LL | | } | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:140:5 + --> $DIR/single_match.rs:141:5 | LL | / match x { LL | | Bar::A => println!(), @@ -120,7 +120,7 @@ LL | | } | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:148:5 + --> $DIR/single_match.rs:149:5 | LL | / match x { LL | | None => println!(), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:170:5 + --> $DIR/single_match.rs:171:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -138,7 +138,7 @@ LL | | } | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:176:5 + --> $DIR/single_match.rs:177:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -147,7 +147,7 @@ LL | | } | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:182:5 + --> $DIR/single_match.rs:183:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -156,7 +156,7 @@ LL | | } | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:254:5 + --> $DIR/single_match.rs:255:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -176,7 +176,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:262:5 + --> $DIR/single_match.rs:263:5 | LL | / match bar { LL | | #[rustfmt::skip] From 482d5fafc9ffc7edf9236ea186883ee0346e7938 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 22 Jul 2023 14:33:36 +0200 Subject: [PATCH 51/90] replace HashMap with Vec, use span_lint_hir_and_then --- clippy_lints/src/unused_async.rs | 21 +++++++++--------- tests/ui/unused_async.stderr | 38 ++++++++++++++++---------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 5c8c6e58914..bc7c3897a6e 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; -use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; @@ -47,11 +46,12 @@ pub struct UnusedAsync { async_fns_as_value: LocalDefIdSet, /// Functions with unused `async`, linted post-crate after we've found all uses of local async /// functions - unused_async_fns: FxHashMap, + unused_async_fns: Vec, } #[derive(Copy, Clone)] struct UnusedAsyncFn { + def_id: LocalDefId, fn_span: Span, await_in_async_block: Option, } @@ -122,13 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // Don't lint just yet, but store the necessary information for later. // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck - self.unused_async_fns.insert( + self.unused_async_fns.push(UnusedAsyncFn { + await_in_async_block: visitor.await_in_async_block, + fn_span: span, def_id, - UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, - }, - ); + }); } } } @@ -164,12 +162,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let iter = self .unused_async_fns .iter() - .filter_map(|(did, item)| (!self.async_fns_as_value.contains(did)).then_some(item)); + .filter(|UnusedAsyncFn { def_id, .. }| (!self.async_fns_as_value.contains(def_id))); for fun in iter { - span_lint_and_then( + span_lint_hir_and_then( cx, UNUSED_ASYNC, + cx.tcx.local_def_id_to_hir_id(fun.def_id), fun.fn_span, "unused `async` for function with no await statements", |diag| { diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr index 035dffa7f13..8d9b72c4886 100644 --- a/tests/ui/unused_async.stderr +++ b/tests/ui/unused_async.stderr @@ -1,22 +1,3 @@ -error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:45:5 - | -LL | async fn f3() {} - | ^^^^^^^^^^^^^^^^ - | - = help: consider removing the `async` from this function - = note: `-D clippy::unused-async` implied by `-D warnings` - -error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:57:1 - | -LL | / async fn foo() -> i32 { -LL | | 4 -LL | | } - | |_^ - | - = help: consider removing the `async` from this function - error: unused `async` for function with no await statements --> $DIR/unused_async.rs:13:5 | @@ -33,6 +14,25 @@ note: `await` used in an async block, which does not require the enclosing funct | LL | ready(()).await; | ^^^^^ + = note: `-D clippy::unused-async` implied by `-D warnings` + +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:45:5 + | +LL | async fn f3() {} + | ^^^^^^^^^^^^^^^^ + | + = help: consider removing the `async` from this function + +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:57:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = help: consider removing the `async` from this function error: unused `async` for function with no await statements --> $DIR/unused_async.rs:68:5 From 99202a0b56e2ac801dfc8d6fe1a5ae539e6dbc15 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Jul 2023 11:51:44 -0400 Subject: [PATCH 52/90] Start addressing review comments --- clippy_lints/src/methods/or_fun_call.rs | 53 ++++++++++++------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index d1899658b84..9fe863ecc0d 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -64,34 +64,33 @@ pub(super) fn check<'tcx>( } }; - if_chain! { - if let Some(sugg) = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => Some("unwrap_or_default"), - ("or_insert", true) | ("or_insert_with", false) => Some("or_default"), - _ => None, - }; - // needs to target Default::default in particular or be *::new and have a Default impl - // available - if (is_new(fun) && output_type_implements_default(fun)) - || match call_expr { - Some(call_expr) => is_default_equivalent(cx, call_expr), - None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun), - }; - then { - span_lint_and_sugg( - cx, - UNWRAP_OR_DEFAULT, - method_span.with_hi(span.hi()), - &format!("use of `{name}` to construct default value"), - "try", - format!("{sugg}()"), - Applicability::MachineApplicable, - ); + let sugg = match (name, call_expr.is_some()) { + ("unwrap_or", true) | ("unwrap_or_else", false) => "unwrap_or_default", + ("or_insert", true) | ("or_insert_with", false) => "or_default", + _ => return false, + }; - true - } else { - false + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun), } + { + span_lint_and_sugg( + cx, + UNWRAP_OR_DEFAULT, + method_span.with_hi(span.hi()), + &format!("use of `{name}` to construct default value"), + "try", + format!("{sugg}()"), + Applicability::MachineApplicable, + ); + + true + } else { + false } } @@ -223,7 +222,7 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) if body.params.is_empty() && let hir::Expr{ kind, .. } = &body.value && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind - && ident == &symbol::Ident::from_str("to_string") + && ident.name == sym::to_string && let hir::Expr{ kind, .. } = self_arg && let hir::ExprKind::Lit(lit) = kind && let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node From 55dd8a97175e75a47e83f2255ba99ddf2eb07ab8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 4 Jul 2023 21:49:05 -0400 Subject: [PATCH 53/90] Refactor `dereference.rs` Extract getting an expression's use context and the context's defined type as util functions. --- clippy_lints/src/dereference.rs | 1044 ++++++++++++------------------- clippy_utils/src/lib.rs | 270 +++++++- 2 files changed, 673 insertions(+), 641 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b2f78ddf989..63db22a2e86 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,21 +3,23 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adt_and_variant_of_res, expr_sig, is_copy, peel_mid_ty_refs, ty_sig}; +use clippy_utils::ty::{is_copy, peel_mid_ty_refs}; use clippy_utils::{ - fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, + expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; +use hir::def::DefKind; +use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, - ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, - TraitItemKind, TyKind, UnOp, + self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, + Path, QPath, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; @@ -25,8 +27,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, Binder, BoundVariableKind, ClauseKind, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy, - ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults, + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty, + TyCtxt, TypeVisitableExt, TypeckResults, }; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; @@ -163,7 +165,7 @@ impl_lint_pass!(Dereferencing<'_> => [ #[derive(Default)] pub struct Dereferencing<'tcx> { - state: Option<(State, StateData)>, + state: Option<(State, StateData<'tcx>)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an // expression. This is to store the id of that expression so it can be skipped when @@ -203,29 +205,28 @@ impl<'tcx> Dereferencing<'tcx> { } #[derive(Debug)] -struct StateData { +struct StateData<'tcx> { /// Span of the top level expression span: Span, hir_id: HirId, - position: Position, + adjusted_ty: Ty<'tcx>, } -#[derive(Debug)] struct DerefedBorrow { count: usize, msg: &'static str, - snip_expr: Option, + stability: TyCoercionStability, + for_field_access: Option, } -#[derive(Debug)] enum State { // Any number of deref method calls. DerefMethod { // The number of calls in a sequence which changed the referenced type ty_changed_count: usize, - is_final_ufcs: bool, + is_ufcs: bool, /// The required mutability - target_mut: Mutability, + mutbl: Mutability, }, DerefedBorrow(DerefedBorrow), ExplicitDeref { @@ -244,7 +245,7 @@ enum State { // A reference operation considered by this lint pass enum RefOp { - Method(Mutability), + Method { mutbl: Mutability, is_ufcs: bool }, Deref, AddrOf(Mutability), } @@ -294,48 +295,115 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); - match kind { - RefOp::Deref => { + let use_cx = expr_use_ctxt(cx, expr); + let adjusted_ty = match &use_cx { + Some(use_cx) => match use_cx.adjustments { + [.., a] => a.target, + _ => expr_ty, + }, + _ => typeck.expr_ty_adjusted(expr), + }; + + match (use_cx, kind) { + (Some(use_cx), RefOp::Deref) => { let sub_ty = typeck.expr_ty(sub_expr); - if let Position::FieldAccess { - name, - of_union: false, - } = position - && !ty_contains_field(sub_ty, name) + if let ExprUseNode::FieldAccess(name) = use_cx.node + && adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()) + && !ty_contains_field(sub_ty, name.name) { self.state = Some(( - State::ExplicitDerefField { name }, - StateData { span: expr.span, hir_id: expr.hir_id, position }, + State::ExplicitDerefField { name: name.name }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + adjusted_ty, + }, )); - } else if position.is_deref_stable() && sub_ty.is_ref() { + } else if sub_ty.is_ref() + // Linting method receivers would require verifying that name lookup + // would resolve the same way. This is complicated by trait methods. + && !use_cx.node.is_recv() + && let Some(ty) = use_cx.node.defined_ty(cx) + && TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()).is_deref_stable() + { self.state = Some(( State::ExplicitDeref { mutability: None }, - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + adjusted_ty, + }, )); } }, - RefOp::Method(target_mut) + (_, RefOp::Method { mutbl, is_ufcs }) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) - && position.lint_explicit_deref() => + // Allow explicit deref in method chains. e.g. `foo.deref().bar()` + && (is_ufcs || !in_postfix_position(cx, expr)) => { let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr))); self.state = Some(( State::DerefMethod { ty_changed_count, - is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), - target_mut, + is_ufcs, + mutbl, }, StateData { span: expr.span, hir_id: expr.hir_id, - position, + adjusted_ty, }, )); }, - RefOp::AddrOf(mutability) => { + (Some(use_cx), RefOp::AddrOf(mutability)) => { + let defined_ty = use_cx.node.defined_ty(cx); + + // Check needless_borrow for generic arguments. + if !use_cx.is_ty_unified + && let Some(DefinedTy::Mir(ty)) = defined_ty + && let ty::Param(ty) = *ty.value.skip_binder().kind() + && let Some((hir_id, fn_id, i)) = match use_cx.node { + ExprUseNode::MethodArg(_, _, 0) => None, + ExprUseNode::MethodArg(hir_id, None, i) => { + typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) + }, + ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) + if !path_has_args(p) => match typeck.qpath_res(p, hir_id) { + Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { + Some((hir_id, id, i)) + }, + _ => None, + }, + _ => None, + } && let count = needless_borrow_generic_arg_count( + cx, + &mut self.possible_borrowers, + fn_id, + typeck.node_substs(hir_id), + i, + ty, + expr, + &self.msrv, + ) && count != 0 + { + self.state = Some(( + State::DerefedBorrow(DerefedBorrow { + count: count - 1, + msg: "the borrowed expression implements the required traits", + stability: TyCoercionStability::None, + for_field_access: None, + }), + StateData { + span: expr.span, + hir_id: expr.hir_id, + adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), + }, + )); + return; + } + // Find the number of times the borrow is auto-derefed. - let mut iter = adjustments.iter(); + let mut iter = use_cx.adjustments.iter(); let mut deref_count = 0usize; let next_adjust = loop { match iter.next() { @@ -352,6 +420,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; }; + let stability = defined_ty.map_or(TyCoercionStability::None, |ty| { + TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) + }); + let can_auto_borrow = match use_cx.node { + ExprUseNode::Callee => true, + ExprUseNode::FieldAccess(_) => adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()), + ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => { + // Check for calls to trait methods where the trait is implemented + // on a reference. + // Two cases need to be handled: + // * `self` methods on `&T` will never have auto-borrow + // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take + // priority. + if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let arg_ty + = cx.tcx.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target)) + && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() + && let subs = cx + .typeck_results() + .node_substs_opt(hir_id).map(|subs| &subs[1..]).unwrap_or_default() + && let impl_ty = if cx.tcx.fn_sig(fn_id) + .subst_identity() + .skip_binder() + .inputs()[0].is_ref() + { + // Trait methods taking `&self` + sub_ty + } else { + // Trait methods taking `self` + arg_ty + } && impl_ty.is_ref() + && cx.tcx.infer_ctxt().build() + .type_implements_trait( + trait_id, + [impl_ty.into()].into_iter().chain(subs.iter().copied()), + cx.param_env, + ) + .must_apply_modulo_regions() + { + false + } else { + true + } + }, + _ => false, + }; + + let deref_msg = + "this expression creates a reference which is immediately dereferenced by the compiler"; + let borrow_msg = "this expression borrows a value the compiler would automatically borrow"; + // Determine the required number of references before any can be removed. In all cases the // reference made by the current expression will be removed. After that there are four cases to // handle. @@ -374,26 +494,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // }; // } // ``` - let deref_msg = - "this expression creates a reference which is immediately dereferenced by the compiler"; - let borrow_msg = "this expression borrows a value the compiler would automatically borrow"; - let impl_msg = "the borrowed expression implements the required traits"; - - let (required_refs, msg, snip_expr) = if position.can_auto_borrow() { - (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None) - } else if let Position::ImplArg(hir_id) = position { - (0, impl_msg, Some(hir_id)) - } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) = - next_adjust.map(|a| &a.kind) + let (required_refs, msg) = if can_auto_borrow { + (1, if deref_count == 1 { borrow_msg } else { deref_msg }) + } else if let Some(&Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)), + .. + }) = next_adjust + && matches!(mutability, AutoBorrowMutability::Mut { .. }) + && !stability.is_reborrow_stable() { - if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable() - { - (3, deref_msg, None) - } else { - (2, deref_msg, None) - } + (3, deref_msg) } else { - (2, deref_msg, None) + (2, deref_msg) }; if deref_count >= required_refs { @@ -403,15 +515,19 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // can't be removed without breaking the code. See earlier comment. count: deref_count - required_refs, msg, - snip_expr, + stability, + for_field_access: match use_cx.node { + ExprUseNode::FieldAccess(name) => Some(name.name), + _ => None, + }, }), StateData { span: expr.span, hir_id: expr.hir_id, - position, + adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); - } else if position.is_deref_stable() + } else if stability.is_deref_stable() // Auto-deref doesn't combine with other adjustments && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) @@ -421,24 +537,24 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position, + adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); } }, - RefOp::Method(..) => (), + (None, _) | (_, RefOp::Method { .. }) => (), } }, ( Some(( State::DerefMethod { - target_mut, + mutbl, ty_changed_count, .. }, data, )), - RefOp::Method(_), + RefOp::Method { is_ufcs, .. }, ) => { self.state = Some(( State::DerefMethod { @@ -447,8 +563,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } else { ty_changed_count + 1 }, - is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), - target_mut, + is_ufcs, + mutbl, }, data, )); @@ -463,33 +579,44 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { - let position = data.position; + let adjusted_ty = data.adjusted_ty; + let stability = state.stability; report(cx, expr, State::DerefedBorrow(state), data); - if position.is_deref_stable() { + if stability.is_deref_stable() { self.state = Some(( State::Borrow { mutability }, StateData { span: expr.span, hir_id: expr.hir_id, - position, + adjusted_ty, }, )); } }, (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { - let position = data.position; + let adjusted_ty = data.adjusted_ty; + let stability = state.stability; + let for_field_access = state.for_field_access; report(cx, expr, State::DerefedBorrow(state), data); - if let Position::FieldAccess{name, ..} = position + if let Some(name) = for_field_access && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( State::ExplicitDerefField { name }, - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + adjusted_ty, + }, )); - } else if position.is_deref_stable() { + } else if stability.is_deref_stable() { self.state = Some(( State::ExplicitDeref { mutability: None }, - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + adjusted_ty, + }, )); } }, @@ -611,8 +738,8 @@ fn try_parse_ref_op<'tcx>( typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, ) -> Option<(RefOp, &'tcx Expr<'tcx>)> { - let (def_id, arg) = match expr.kind { - ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg), + let (is_ufcs, def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg), ExprKind::Call( Expr { kind: ExprKind::Path(path), @@ -620,7 +747,7 @@ fn try_parse_ref_op<'tcx>( .. }, [arg], - ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => { return Some((RefOp::Deref, sub_expr)); }, @@ -628,9 +755,21 @@ fn try_parse_ref_op<'tcx>( _ => return None, }; if tcx.is_diagnostic_item(sym::deref_method, def_id) { - Some((RefOp::Method(Mutability::Not), arg)) + Some(( + RefOp::Method { + mutbl: Mutability::Not, + is_ufcs, + }, + arg, + )) } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { - Some((RefOp::Method(Mutability::Mut), arg)) + Some(( + RefOp::Method { + mutbl: Mutability::Mut, + is_ufcs, + }, + arg, + )) } else { None } @@ -649,420 +788,164 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -/// The position of an expression relative to it's parent. -#[derive(Clone, Copy, Debug)] -enum Position { - MethodReceiver, - /// The method is defined on a reference type. e.g. `impl Foo for &T` - MethodReceiverRefImpl, - Callee, - ImplArg(HirId), - FieldAccess { - name: Symbol, - of_union: bool, - }, // union fields cannot be auto borrowed - Postfix, - Deref, - /// Any other location which will trigger auto-deref to a specific time. - /// Contains the precedence of the parent expression and whether the target type is sized. - DerefStable(i8, bool), - /// Any other location which will trigger auto-reborrowing. - /// Contains the precedence of the parent expression. - ReborrowStable(i8), - /// Contains the precedence of the parent expression. - Other(i8), +fn path_has_args(p: &QPath<'_>) -> bool { + match *p { + QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), + _ => false, + } } -impl Position { + +fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + if let Some(parent) = get_parent_expr(cx, e) + && parent.span.ctxt() == e.span.ctxt() + { + match parent.kind { + ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _) + if child.hir_id == e.hir_id => true, + ExprKind::Field(_, _) | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) => true, + _ => false, + } + } else { + false + } +} + +#[derive(Clone, Copy)] +enum TyCoercionStability { + Deref, + Reborrow, + None, +} +impl TyCoercionStability { fn is_deref_stable(self) -> bool { - matches!(self, Self::DerefStable(..)) + matches!(self, Self::Deref) } fn is_reborrow_stable(self) -> bool { - matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_)) + matches!(self, Self::Deref | Self::Reborrow) } - fn can_auto_borrow(self) -> bool { - matches!( - self, - Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee - ) - } - - fn lint_explicit_deref(self) -> bool { - matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_)) - } - - fn precedence(self) -> i8 { - match self { - Self::MethodReceiver - | Self::MethodReceiverRefImpl - | Self::Callee - | Self::FieldAccess { .. } - | Self::Postfix => PREC_POSTFIX, - Self::ImplArg(_) | Self::Deref => PREC_PREFIX, - Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, - } - } -} - -/// Walks up the parent expressions attempting to determine both how stable the auto-deref result -/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow -/// locations as those follow different rules. -#[expect(clippy::too_many_lines)] -fn walk_parents<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - e: &'tcx Expr<'_>, - msrv: &Msrv, -) -> (Position, &'tcx [Adjustment<'tcx>]) { - let mut adjustments = [].as_slice(); - let mut precedence = 0i8; - let ctxt = e.span.ctxt(); - let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| { - // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead. - if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) { - adjustments = cx.typeck_results().expr_adjustments(e); - } - match parent { - Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => { - Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty())) - }, - Node::Item(&Item { - kind: ItemKind::Static(..) | ItemKind::Const(..), - owner_id, - span, - .. - }) - | Node::TraitItem(&TraitItem { - kind: TraitItemKind::Const(..), - owner_id, - span, - .. - }) - | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Const(..), - owner_id, - span, - .. - }) if span.ctxt() == ctxt => { - let ty = cx.tcx.type_of(owner_id.def_id).subst_identity(); - Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx)) - }, - - Node::Item(&Item { - kind: ItemKind::Fn(..), - owner_id, - span, - .. - }) - | Node::TraitItem(&TraitItem { - kind: TraitItemKind::Fn(..), - owner_id, - span, - .. - }) - | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Fn(..), - owner_id, - span, - .. - }) if span.ctxt() == ctxt => { - let output = cx - .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); - Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)) - }, - - Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) { - Some(Expr { - hir_id, - kind: ExprKind::Struct(path, ..), - .. - }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id)) - .and_then(|(adt, variant)| { - variant - .fields - .iter() - .find(|f| f.name == field.ident.name) - .map(|f| (adt, f)) - }) - .map(|(adt, field_def)| { - ty_auto_deref_stability( - cx.tcx, - // Use the param_env of the target type. - cx.tcx.param_env(adt.did()), - cx.tcx.type_of(field_def.did).subst_identity(), - precedence, - ) - .position_for_arg() - }), - _ => None, - }, - - Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind { - ExprKind::Ret(_) => { - let owner_id = cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()); - Some( - if let Node::Expr( - closure_expr @ Expr { - kind: ExprKind::Closure(closure), - .. - }, - ) = cx.tcx.hir().get_by_def_id(owner_id) - { - closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence) - } else { - let output = cx - .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); - ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx) - }, - ) - }, - ExprKind::Closure(closure) => Some(closure_result_position( - cx, - closure, - cx.typeck_results().expr_ty(parent), - precedence, - )), - ExprKind::Call(func, _) if func.hir_id == child_id => { - (child_id == e.hir_id).then_some(Position::Callee) - }, - ExprKind::Call(func, args) => args - .iter() - .position(|arg| arg.hir_id == child_id) - .zip(expr_sig(cx, func)) - .and_then(|(i, sig)| { - sig.input_with_hir(i).map(|(hir_ty, ty)| { - match hir_ty { - // Type inference for closures can depend on how they're called. Only go by the explicit - // types here. - Some(hir_ty) => { - binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()) - }, - None => { - // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 - // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782 - if e.hir_id == child_id - && !call_is_qualified(func) - && let ty::Param(param_ty) = ty.skip_binder().kind() - { - needless_borrow_impl_arg_position( - cx, - possible_borrowers, - parent, - i, - *param_ty, - e, - precedence, - msrv, - ) - } else { - ty_auto_deref_stability( - cx.tcx, - // Use the param_env of the target function. - sig.predicates_id().map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)), - cx.tcx.erase_late_bound_regions(ty), - precedence - ).position_for_arg() - } - }, - } - }) - }), - ExprKind::MethodCall(method, receiver, args, _) => { - let fn_id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); - if receiver.hir_id == child_id { - // Check for calls to trait methods where the trait is implemented on a reference. - // Two cases need to be handled: - // * `self` methods on `&T` will never have auto-borrow - // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take - // priority. - if e.hir_id != child_id { - return Some(Position::ReborrowStable(precedence)) - } else if let Some(trait_id) = cx.tcx.trait_of_item(fn_id) - && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e)) - && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() - && let subs = cx - .typeck_results() - .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() - && let impl_ty = if cx.tcx.fn_sig(fn_id) - .subst_identity() - .skip_binder() - .inputs()[0].is_ref() - { - // Trait methods taking `&self` - sub_ty - } else { - // Trait methods taking `self` - arg_ty - } && impl_ty.is_ref() - && let infcx = cx.tcx.infer_ctxt().build() - && infcx - .type_implements_trait( - trait_id, - [impl_ty.into()].into_iter().chain(subs.iter().copied()), - cx.param_env, - ) - .must_apply_modulo_regions() - { - return Some(Position::MethodReceiverRefImpl) - } - return Some(Position::MethodReceiver); - } - args.iter().position(|arg| arg.hir_id == child_id).map(|i| { - let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1); - // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 - // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 - if e.hir_id == child_id - && method.args.is_none() - && let ty::Param(param_ty) = ty.skip_binder().kind() - { - needless_borrow_impl_arg_position( - cx, - possible_borrowers, - parent, - i + 1, - *param_ty, - e, - precedence, - msrv, - ) - } else { - ty_auto_deref_stability( - cx.tcx, - // Use the param_env of the target function. - cx.tcx.param_env(fn_id), - cx.tcx.erase_late_bound_regions(ty), - precedence, - ) - .position_for_arg() - } - }) - }, - ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess { - name: name.name, - of_union: is_union(cx.typeck_results(), child), - }), - ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), - ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) - | ExprKind::Index(child, _) - if child.hir_id == e.hir_id => - { - Some(Position::Postfix) - }, - _ if child_id == e.hir_id => { - precedence = parent.precedence().order(); - None - }, - _ => None, - }, - _ => None, - } - }) - .unwrap_or(Position::Other(precedence)); - (position, adjustments) -} - -fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool { - typeck - .expr_ty_adjusted(path_expr) - .ty_adt_def() - .map_or(false, rustc_middle::ty::AdtDef::is_union) -} - -fn closure_result_position<'tcx>( - cx: &LateContext<'tcx>, - closure: &'tcx Closure<'_>, - ty: Ty<'tcx>, - precedence: i8, -) -> Position { - match closure.fn_decl.output { - FnRetTy::Return(hir_ty) => { - if let Some(sig) = ty_sig(cx, ty) - && let Some(output) = sig.output() - { - binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars()) - } else { - Position::Other(precedence) - } - }, - FnRetTy::DefaultReturn(_) => Position::Other(precedence), - } -} - -// Checks the stability of auto-deref when assigned to a binding with the given explicit type. -// -// e.g. -// let x = Box::new(Box::new(0u32)); -// let y1: &Box<_> = x.deref(); -// let y2: &Box<_> = &x; -// -// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when -// switching to auto-dereferencing. -fn binding_ty_auto_deref_stability<'tcx>( - cx: &LateContext<'tcx>, - ty: &'tcx hir::Ty<'_>, - precedence: i8, - binder_args: &'tcx List, -) -> Position { - let TyKind::Ref(_, ty) = &ty.kind else { - return Position::Other(precedence); - }; - let mut ty = ty; - - loop { - break match ty.ty.kind { - TyKind::Ref(_, ref ref_ty) => { - ty = ref_ty; - continue; - }, - TyKind::Path( - QPath::TypeRelative(_, path) - | QPath::Resolved( - _, - Path { - segments: [.., path], .. - }, - ), - ) => { - if let Some(args) = path.args - && args.args.iter().any(|arg| match arg { - GenericArg::Infer(_) => true, - GenericArg::Type(ty) => ty_contains_infer(ty), - _ => false, - }) - { - Position::ReborrowStable(precedence) - } else { - Position::DerefStable( - precedence, - cx.tcx - .erase_late_bound_regions(Binder::bind_with_vars( - cx.typeck_results().node_type(ty.ty.hir_id), - binder_args, - )) - .is_sized(cx.tcx, cx.param_env.without_caller_bounds()), - ) - } - }, - TyKind::Slice(_) => Position::DerefStable(precedence, false), - TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true), - TyKind::Never - | TyKind::Tup(_) - | TyKind::Path(_) => Position::DerefStable( - precedence, - cx.tcx - .erase_late_bound_regions(Binder::bind_with_vars( - cx.typeck_results().node_type(ty.ty.hir_id), - binder_args, - )) - .is_sized(cx.tcx, cx.param_env.without_caller_bounds()), + fn for_defined_ty<'tcx>(cx: &LateContext<'tcx>, ty: DefinedTy<'tcx>, for_return: bool) -> Self { + match ty { + DefinedTy::Hir(ty) => Self::for_hir_ty(ty), + DefinedTy::Mir(ty) => Self::for_mir_ty( + cx.tcx, + ty.param_env, + cx.tcx.erase_late_bound_regions(ty.value), + for_return, ), - TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err(_) => { - Position::ReborrowStable(precedence) - }, + } + } + + // Checks the stability of type coercions when assigned to a binding with the given explicit type. + // + // e.g. + // let x = Box::new(Box::new(0u32)); + // let y1: &Box<_> = x.deref(); + // let y2: &Box<_> = &x; + // + // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when + // switching to auto-dereferencing. + fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { + let TyKind::Ref(_, ty) = &ty.kind else { + return Self::None; }; + let mut ty = ty; + + loop { + break match ty.ty.kind { + TyKind::Ref(_, ref ref_ty) => { + ty = ref_ty; + continue; + }, + TyKind::Path( + QPath::TypeRelative(_, path) + | QPath::Resolved( + _, + Path { + segments: [.., path], .. + }, + ), + ) => { + if let Some(args) = path.args + && args.args.iter().any(|arg| match arg { + hir::GenericArg::Infer(_) => true, + hir::GenericArg::Type(ty) => ty_contains_infer(ty), + _ => false, + }) + { + Self::Reborrow + } else { + Self::Deref + } + }, + TyKind::Slice(_) + | TyKind::Array(..) + | TyKind::Ptr(_) + | TyKind::BareFn(_) + | TyKind::Never + | TyKind::Tup(_) + | TyKind::Path(_) => Self::Deref, + TyKind::OpaqueDef(..) + | TyKind::Infer + | TyKind::Typeof(..) + | TyKind::TraitObject(..) + | TyKind::Err(_) => Self::Reborrow, + }; + } + } + + fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self { + let ty::Ref(_, mut ty, _) = *ty.kind() else { + return Self::None; + }; + + ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); + loop { + break match *ty.kind() { + ty::Ref(_, ref_ty, _) => { + ty = ref_ty; + continue; + }, + ty::Param(_) if for_return => Self::Deref, + ty::Alias(ty::Weak | ty::Inherent, _) => unreachable!("should have been normalized away above"), + ty::Alias(ty::Projection, _) if !for_return && ty.has_non_region_param() => Self::Reborrow, + ty::Infer(_) + | ty::Error(_) + | ty::Bound(..) + | ty::Alias(ty::Opaque, ..) + | ty::Placeholder(_) + | ty::Dynamic(..) + | ty::Param(_) => Self::Reborrow, + ty::Adt(_, substs) + if ty.has_placeholders() + || ty.has_opaque_types() + || (!for_return && substs.has_non_region_param()) => + { + Self::Reborrow + }, + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Array(..) + | ty::Float(_) + | ty::RawPtr(..) + | ty::FnPtr(_) + | ty::Str + | ty::Slice(..) + | ty::Adt(..) + | ty::Foreign(_) + | ty::FnDef(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Closure(..) + | ty::Never + | ty::Tuple(_) + | ty::Alias(ty::Projection, _) => Self::Deref, + }; + } } } @@ -1084,10 +967,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { } } - fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) { - if self.0 || matches!(arg, GenericArg::Infer(_)) { + fn visit_generic_arg(&mut self, arg: &hir::GenericArg<'_>) { + if self.0 || matches!(arg, hir::GenericArg::Infer(_)) { self.0 = true; - } else if let GenericArg::Type(ty) = arg { + } else if let hir::GenericArg::Type(ty) = arg { self.visit_ty(ty); } } @@ -1097,51 +980,29 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } -fn call_is_qualified(expr: &Expr<'_>) -> bool { - if let ExprKind::Path(path) = &expr.kind { - match path { - QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()), - QPath::TypeRelative(_, segment) => segment.args.is_some(), - QPath::LangItem(..) => false, - } - } else { - false - } -} - -// Checks whether: -// * child is an expression of the form `&e` in an argument position requiring an `impl Trait` -// * `e`'s type implements `Trait` and is copyable -// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. -// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to -// be moved, but it cannot be. -#[expect(clippy::too_many_arguments, clippy::too_many_lines)] -fn needless_borrow_impl_arg_position<'tcx>( +/// Checks for the number of borrow expressions which can be removed from the given expression +/// where the expression is used as an argument to a function expecting a generic type. +/// +/// The following constraints will be checked: +/// * The borrowed expression meets all the generic type's constraints. +/// * The generic type appears only once in the functions signature. +/// * The borrowed value will not be moved if it is used later in the function. +#[expect(clippy::too_many_arguments)] +fn needless_borrow_generic_arg_count<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - parent: &Expr<'tcx>, + fn_id: DefId, + callee_substs: &'tcx List>, arg_index: usize, param_ty: ParamTy, mut expr: &Expr<'tcx>, - precedence: i8, msrv: &Msrv, -) -> Position { +) -> usize { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - let Some(callee_def_id) = fn_def_id(cx, parent) else { - return Position::Other(precedence); - }; - let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder(); - let substs_with_expr_ty = cx - .typeck_results() - .node_substs(if let ExprKind::Call(callee, _) = parent.kind { - callee.hir_id - } else { - parent.hir_id - }); - - let predicates = cx.tcx.param_env(callee_def_id).caller_bounds(); + let fn_sig = cx.tcx.fn_sig(fn_id).subst_identity().skip_binder(); + let predicates = cx.tcx.param_env(fn_id).caller_bounds(); let projection_predicates = predicates .iter() .filter_map(|predicate| { @@ -1176,7 +1037,7 @@ fn needless_borrow_impl_arg_position<'tcx>( || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) }) { - return Position::Other(precedence); + return 0; } // See: @@ -1184,14 +1045,14 @@ fn needless_borrow_impl_arg_position<'tcx>( // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 if projection_predicates .iter() - .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate)) + .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) { - return Position::Other(precedence); + return 0; } // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same // elements are modified each time `check_referent` is called. - let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); + let mut substs_with_referent_ty = callee_substs.to_vec(); let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); @@ -1238,20 +1099,15 @@ fn needless_borrow_impl_arg_position<'tcx>( }) }; - let mut needless_borrow = false; + let mut count = 0; while let ExprKind::AddrOf(_, _, referent) = expr.kind { if !check_reference_and_referent(expr, referent) { break; } expr = referent; - needless_borrow = true; - } - - if needless_borrow { - Position::ImplArg(expr.hir_id) - } else { - Position::Other(precedence) + count += 1; } + count } fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { @@ -1387,95 +1243,6 @@ fn replace_types<'tcx>( true } -struct TyPosition<'tcx> { - position: Position, - ty: Option>, -} -impl From for TyPosition<'_> { - fn from(position: Position) -> Self { - Self { position, ty: None } - } -} -impl<'tcx> TyPosition<'tcx> { - fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self { - Self { - position: Position::ReborrowStable(precedence), - ty: Some(ty), - } - } - fn position_for_result(self, cx: &LateContext<'tcx>) -> Position { - match (self.position, self.ty) { - (Position::ReborrowStable(precedence), Some(ty)) => { - Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env)) - }, - (position, _) => position, - } - } - fn position_for_arg(self) -> Position { - self.position - } -} - -// Checks whether a type is stable when switching to auto dereferencing, -fn ty_auto_deref_stability<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - precedence: i8, -) -> TyPosition<'tcx> { - let ty::Ref(_, mut ty, _) = *ty.kind() else { - return Position::Other(precedence).into(); - }; - - ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); - - loop { - break match *ty.kind() { - ty::Ref(_, ref_ty, _) => { - ty = ref_ty; - continue; - }, - ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Alias(ty::Weak, _) => unreachable!("should have been normalized away above"), - ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"), - ty::Alias(ty::Projection, _) if ty.has_non_region_param() => { - TyPosition::new_deref_stable_for_result(precedence, ty) - }, - ty::Infer(_) - | ty::Error(_) - | ty::Bound(..) - | ty::Alias(ty::Opaque, ..) - | ty::Placeholder(_) - | ty::Dynamic(..) => Position::ReborrowStable(precedence).into(), - ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { - Position::ReborrowStable(precedence).into() - }, - ty::Adt(_, substs) if substs.has_non_region_param() => { - TyPosition::new_deref_stable_for_result(precedence, ty) - }, - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Array(..) - | ty::Float(_) - | ty::RawPtr(..) - | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(), - ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(), - ty::Adt(..) - | ty::Foreign(_) - | ty::FnDef(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) - | ty::Closure(..) - | ty::Never - | ty::Tuple(_) - | ty::Alias(ty::Projection, _) => Position::DerefStable(precedence, ty.is_sized(tcx, param_env)).into(), - }; - } -} - fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { if let ty::Adt(adt, _) = *ty.kind() { adt.is_struct() && adt.all_fields().any(|f| f.name == name) @@ -1485,12 +1252,12 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { } #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData<'tcx>) { match state { State::DerefMethod { ty_changed_count, - is_final_ufcs, - target_mut, + is_ufcs, + mutbl, } => { let mut app = Applicability::MachineApplicable; let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); @@ -1505,12 +1272,12 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }; let addr_of_str = if ty_changed_count < ref_count { // Check if a reborrow from &mut T -> &T is required. - if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { "&*" } else { "" } - } else if target_mut == Mutability::Mut { + } else if mutbl == Mutability::Mut { "&mut " } else { "&" @@ -1527,7 +1294,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data */ // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`. - if is_final_ufcs { + if is_ufcs { return; } @@ -1535,7 +1302,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data cx, EXPLICIT_DEREF_METHODS, data.span, - match target_mut { + match mutbl { Mutability::Not => "explicit `deref` method call", Mutability::Mut => "explicit `deref_mut` method call", }, @@ -1546,13 +1313,19 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }, State::DerefedBorrow(state) => { let mut app = Applicability::MachineApplicable; - let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id)); - let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app); + let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { - let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee); + let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) { + Some(Node::Expr(e)) => match e.kind { + ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false), + ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), + _ => (e.precedence().order(), false), + }, + _ => (0, false), + }; let sugg = if !snip_is_macro + && (calls_field || expr.precedence().order() < precedence) && !has_enclosing_paren(&snip) - && (expr.precedence().order() < data.position.precedence() || calls_field) { format!("({snip})") } else { @@ -1569,7 +1342,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) - ) && matches!(data.position, Position::DerefStable(_, true)) + ) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() + && ty.is_sized(cx.tcx, cx.param_env) { // Rustc bug: auto deref doesn't work on block expression when targeting sized types. return; @@ -1582,9 +1356,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data Mutability::Not => "&", Mutability::Mut => "&mut ", }; - (prefix, 0) + (prefix, PREC_PREFIX) } else { - ("", data.position.precedence()) + ("", 0) }; span_lint_hir_and_then( cx, @@ -1613,7 +1387,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) - ) && matches!(data.position, Position::DerefStable(_, true)) + ) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env) { // Rustc bug: auto deref doesn't work on block expression when targeting sized types. return; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index cf33076d356..8429192437c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -89,15 +89,14 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, - ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local, - MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, - TraitItem, TraitItemRef, TraitRef, TyKind, UnOp, + ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, + ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::ConstantKind; -use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::fast_reject::SimplifiedType::{ @@ -106,7 +105,8 @@ use rustc_middle::ty::fast_reject::SimplifiedType::{ }; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, FloatTy, IntTy, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeAndMut, + TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -116,7 +116,10 @@ use rustc_target::abi::Integer; use crate::consts::{constant, miri_to_const, Constant}; use crate::higher::Range; -use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; +use crate::ty::{ + adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, + ty_is_fn_once_param, +}; use crate::visitors::for_each_expr; use rustc_middle::hir::nested_filter; @@ -2502,6 +2505,261 @@ pub fn walk_to_expr_usage<'tcx, T>( None } +/// A type definition as it would be viewed from within a function. +#[derive(Clone, Copy)] +pub enum DefinedTy<'tcx> { + // Used for locals and closures defined within the function. + Hir(&'tcx hir::Ty<'tcx>), + /// Used for function signatures, and constant and static values. This includes the `ParamEnv` + /// from the definition site. + Mir(ParamEnvAnd<'tcx, Binder<'tcx, Ty<'tcx>>>), +} + +/// The context an expressions value is used in. +pub struct ExprUseCtxt<'tcx> { + /// The parent node which consumes the value. + pub node: ExprUseNode<'tcx>, + /// Any adjustments applied to the type. + pub adjustments: &'tcx [Adjustment<'tcx>], + /// Whether or not the type must unify with another code path. + pub is_ty_unified: bool, + /// Whether or not the value will be moved before it's used. + pub moved_before_use: bool, +} + +/// The node which consumes a value. +pub enum ExprUseNode<'tcx> { + /// Assignment to, or initializer for, a local + Local(&'tcx Local<'tcx>), + /// Initializer for a const or static item. + ConstStatic(OwnerId), + /// Implicit or explicit return from a function. + Return(OwnerId), + /// Initialization of a struct field. + Field(&'tcx ExprField<'tcx>), + /// An argument to a function. + FnArg(&'tcx Expr<'tcx>, usize), + /// An argument to a method. + MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize), + /// The callee of a function call. + Callee, + /// Access of a field. + FieldAccess(Ident), +} +impl<'tcx> ExprUseNode<'tcx> { + /// Checks if the value is returned from the function. + pub fn is_return(&self) -> bool { + matches!(self, Self::Return(_)) + } + + /// Checks if the value is used as a method call receiver. + pub fn is_recv(&self) -> bool { + matches!(self, Self::MethodArg(_, _, 0)) + } + + /// Gets the needed type as it's defined without any type inference. + pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { + match *self { + Self::Local(Local { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), + Self::ConstStatic(id) => Some(DefinedTy::Mir( + cx.param_env.and(Binder::dummy(cx.tcx.type_of(id).subst_identity())), + )), + Self::Return(id) => { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(id.def_id); + if let Some(Node::Expr(Expr { + kind: ExprKind::Closure(c), + .. + })) = cx.tcx.hir().find(hir_id) + { + match c.fn_decl.output { + FnRetTy::DefaultReturn(_) => None, + FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)), + } + } else { + Some(DefinedTy::Mir( + cx.param_env.and(cx.tcx.fn_sig(id).subst_identity().output()), + )) + } + }, + Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) { + Some(Expr { + hir_id, + kind: ExprKind::Struct(path, ..), + .. + }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id)) + .and_then(|(adt, variant)| { + variant + .fields + .iter() + .find(|f| f.name == field.ident.name) + .map(|f| (adt, f)) + }) + .map(|(adt, field_def)| { + DefinedTy::Mir( + cx.tcx + .param_env(adt.did()) + .and(Binder::dummy(cx.tcx.type_of(field_def.did).subst_identity())), + ) + }), + _ => None, + }, + Self::FnArg(callee, i) => { + let sig = expr_sig(cx, callee)?; + let (hir_ty, ty) = sig.input_with_hir(i)?; + Some(match hir_ty { + Some(hir_ty) => DefinedTy::Hir(hir_ty), + None => DefinedTy::Mir( + sig.predicates_id() + .map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)) + .and(ty), + ), + }) + }, + Self::MethodArg(id, _, i) => { + let id = cx.typeck_results().type_dependent_def_id(id)?; + let sig = cx.tcx.fn_sig(id).skip_binder(); + Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i)))) + }, + Self::Local(_) | Self::FieldAccess(..) | Self::Callee => None, + } + } +} + +/// Gets the context an expression's value is used in. +#[expect(clippy::too_many_lines)] +pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option> { + let mut adjustments = [].as_slice(); + let mut is_ty_unified = false; + let mut moved_before_use = false; + let ctxt = e.span.ctxt(); + walk_to_expr_usage(cx, e, &mut |parent, child_id| { + // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead. + if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) { + adjustments = cx.typeck_results().expr_adjustments(e); + } + match parent { + Node::Local(l) if l.span.ctxt() == ctxt => Some(ExprUseCtxt { + node: ExprUseNode::Local(l), + adjustments, + is_ty_unified, + moved_before_use, + }), + Node::Item(&Item { + kind: ItemKind::Static(..) | ItemKind::Const(..), + owner_id, + span, + .. + }) + | Node::TraitItem(&TraitItem { + kind: TraitItemKind::Const(..), + owner_id, + span, + .. + }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Const(..), + owner_id, + span, + .. + }) if span.ctxt() == ctxt => Some(ExprUseCtxt { + node: ExprUseNode::ConstStatic(owner_id), + adjustments, + is_ty_unified, + moved_before_use, + }), + + Node::Item(&Item { + kind: ItemKind::Fn(..), + owner_id, + span, + .. + }) + | Node::TraitItem(&TraitItem { + kind: TraitItemKind::Fn(..), + owner_id, + span, + .. + }) + | Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(..), + owner_id, + span, + .. + }) if span.ctxt() == ctxt => Some(ExprUseCtxt { + node: ExprUseNode::Return(owner_id), + adjustments, + is_ty_unified, + moved_before_use, + }), + + Node::ExprField(field) if field.span.ctxt() == ctxt => Some(ExprUseCtxt { + node: ExprUseNode::Field(field), + adjustments, + is_ty_unified, + moved_before_use, + }), + + Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind { + ExprKind::Ret(_) => Some(ExprUseCtxt { + node: ExprUseNode::Return(OwnerId { + def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + }), + adjustments, + is_ty_unified, + moved_before_use, + }), + ExprKind::Closure(closure) => Some(ExprUseCtxt { + node: ExprUseNode::Return(OwnerId { def_id: closure.def_id }), + adjustments, + is_ty_unified, + moved_before_use, + }), + ExprKind::Call(func, args) => Some(ExprUseCtxt { + node: match args.iter().position(|arg| arg.hir_id == child_id) { + Some(i) => ExprUseNode::FnArg(func, i), + None => ExprUseNode::Callee, + }, + adjustments, + is_ty_unified, + moved_before_use, + }), + ExprKind::MethodCall(name, _, args, _) => Some(ExprUseCtxt { + node: ExprUseNode::MethodArg( + parent.hir_id, + name.args, + args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1), + ), + adjustments, + is_ty_unified, + moved_before_use, + }), + ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(ExprUseCtxt { + node: ExprUseNode::FieldAccess(name), + adjustments, + is_ty_unified, + moved_before_use, + }), + ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { + is_ty_unified = true; + moved_before_use = true; + None + }, + ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => { + is_ty_unified = true; + moved_before_use = true; + None + }, + ExprKind::Block(..) => { + moved_before_use = true; + None + }, + _ => None, + }, + _ => None, + } + }) +} + /// Tokenizes the input while keeping the text associated with each token. pub fn tokenize_with_text(s: &str) -> impl Iterator { let mut pos = 0; From e975d05cdeb41c51c89a9ac385ad1a5980f324a3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 23 Jul 2023 15:51:11 +0200 Subject: [PATCH 54/90] check that the types are equal in `SpanlessEq::eq_expr` --- clippy_utils/src/hir_utils.rs | 14 +++++------ tests/ui/if_same_then_else.rs | 41 +++++++++++++++++++++++++++++++ tests/ui/if_same_then_else.stderr | 20 ++++++++++++++- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index e2540a1a667..85b3b005f93 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -252,15 +252,15 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } - if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { - if let (Some(l), Some(r)) = ( + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results + && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) + && let (Some(l), Some(r)) = ( constant_simple(self.inner.cx, typeck_lhs, left), constant_simple(self.inner.cx, typeck_rhs, right), - ) { - if l == r { - return true; - } - } + ) + && l == r + { + return true; } let is_eq = match ( diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index dad4543f84c..e84b20e9fef 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -214,4 +214,45 @@ mod issue_8836 { } } +mod issue_11213 { + fn reproducer(x: bool) -> bool { + if x { + 0_u8.is_power_of_two() + } else { + 0_u16.is_power_of_two() + } + } + + // a more obvious reproducer that shows + // why the code above is problematic: + fn v2(x: bool) -> bool { + trait Helper { + fn is_u8(&self) -> bool; + } + impl Helper for u8 { + fn is_u8(&self) -> bool { + true + } + } + impl Helper for u16 { + fn is_u8(&self) -> bool { + false + } + } + + // this is certainly not the same code in both branches + // it returns a different bool depending on the branch. + if x { 0_u8.is_u8() } else { 0_u16.is_u8() } + } + + fn do_lint(x: bool) -> bool { + // but do lint if the type of the literal is the same + if x { + 0_u8.is_power_of_two() + } else { + 0_u8.is_power_of_two() + } + } +} + fn main() {} diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index a34fc565590..774cc08685a 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -108,5 +108,23 @@ LL | | bar + 1; LL | | } | |_____^ -error: aborting due to 5 previous errors +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:250:14 + | +LL | if x { + | ______________^ +LL | | 0_u8.is_power_of_two() +LL | | } else { + | |_________^ + | +note: same as this + --> $DIR/if_same_then_else.rs:252:16 + | +LL | } else { + | ________________^ +LL | | 0_u8.is_power_of_two() +LL | | } + | |_________^ + +error: aborting due to 6 previous errors From 30d06a810c8b8967d9d0ea2cb64d5adf8b357914 Mon Sep 17 00:00:00 2001 From: Milo Moisson Date: Sun, 23 Jul 2023 17:05:54 +0200 Subject: [PATCH 55/90] ptr_arg should ignore extern functions --- clippy_lints/src/ptr.rs | 11 +++++++++++ tests/ui/ptr_arg.rs | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 82a55166aea..4534b2ab28a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -26,6 +26,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::{fmt, iter}; @@ -162,6 +163,11 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } + if !matches!(sig.header.abi, Abi::Rust) { + // Ignore `extern` functions with non-Rust calling conventions + return; + } + check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, @@ -217,6 +223,11 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; + if !matches!(sig.header.abi, Abi::Rust) { + // Ignore `extern` functions with non-Rust calling conventions + return; + } + check_mut_from_ref(cx, sig, Some(body)); let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder(); diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 13e993d247b..08075c382a2 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -267,3 +267,16 @@ mod issue_9218 { todo!() } } + +mod issue_11181 { + extern "C" fn allowed(_v: &Vec) {} + + struct S; + impl S { + extern "C" fn allowed(_v: &Vec) {} + } + + trait T { + extern "C" fn allowed(_v: &Vec) {} + } +} From 7b8598d6c0c28c4e60387f18fae8d5b021ce5907 Mon Sep 17 00:00:00 2001 From: Milo Moisson Date: Mon, 24 Jul 2023 01:23:35 +0200 Subject: [PATCH 56/90] ptr lint: check_mut_from_ref is checked independently of the function's ABI --- clippy_lints/src/ptr.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 4534b2ab28a..d32e58c1af9 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -163,12 +163,13 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } + check_mut_from_ref(cx, sig, None); + if !matches!(sig.header.abi, Abi::Rust) { // Ignore `extern` functions with non-Rust calling conventions return; } - check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(), @@ -223,12 +224,13 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; + check_mut_from_ref(cx, sig, Some(body)); + if !matches!(sig.header.abi, Abi::Rust) { // Ignore `extern` functions with non-Rust calling conventions return; } - check_mut_from_ref(cx, sig, Some(body)); let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params) From 82982133a9c5ee663b26013ac8ebd3cfb0e0913f Mon Sep 17 00:00:00 2001 From: Milo Moisson Date: Mon, 24 Jul 2023 14:54:55 +0200 Subject: [PATCH 57/90] changelog: [`min_ident_chars`]: don't lint const generics --- clippy_lints/src/min_ident_chars.rs | 8 ++++++++ tests/ui/min_ident_chars.rs | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 2a60f2faca0..c79a1a7b9d4 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -129,6 +129,14 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { return; } + // `struct Array([T; N])` + // ^ + if let Node::GenericParam(generic_param) = node + && let GenericParamKind::Const { .. } = generic_param.kind + { + return; + } + if is_from_proc_macro(cx, &ident) { return; } diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index 0fab224a29d..03784442e2c 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -81,3 +81,7 @@ fn b() {} fn wrong_pythagoras(a: f32, b: f32) -> f32 { a * a + a * b } + +mod issue_11163 { + struct Array([T; N]); +} From 5c26e82d80b4c6f2f1eb54b98edd832f00cbd0a6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 23 Jul 2023 17:19:52 +0000 Subject: [PATCH 58/90] Remove Gha status emitter in compile-test --- clippy_lints/src/utils/mod.rs | 2 +- tests/compile-test.rs | 36 +++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index fb08256931f..4fef8c0717d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -18,7 +18,7 @@ const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configura // ================================================================== // Configuration // ================================================================== -#[derive(Debug, Clone, Default)] //~ ERROR no such field +#[derive(Debug, Clone, Default)] pub struct ClippyConfiguration { pub name: String, #[allow(dead_code)] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index d70c4ea34cb..596b04fe8a5 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -5,7 +5,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use compiletest::{status_emitter, CommandBuilder}; +use compiletest::{status_emitter, CommandBuilder, OutputConflictHandling}; use ui_test as compiletest; use ui_test::Mode as TestMode; @@ -116,9 +116,9 @@ fn base_config(test_dir: &str) -> compiletest::Config { stderr_filters: vec![], stdout_filters: vec![], output_conflict_handling: if var_os("BLESS").is_some() || env::args().any(|arg| arg == "--bless") { - compiletest::OutputConflictHandling::Bless + OutputConflictHandling::Bless } else { - compiletest::OutputConflictHandling::Error("cargo test -- -- --bless".into()) + OutputConflictHandling::Error("cargo uibless".into()) }, target: None, out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap_or("target".into())).join("ui_test"), @@ -187,9 +187,6 @@ fn run_ui() { .to_string() }), ); - eprintln!(" Compiler: {}", config.program.display()); - - let name = config.root_dir.display().to_string(); let test_filter = test_filter(); @@ -197,7 +194,7 @@ fn run_ui() { config, move |path| compiletest::default_file_filter(path) && test_filter(path), compiletest::default_per_file_config, - (status_emitter::Text, status_emitter::Gha:: { name }), + status_emitter::Text, ) .unwrap(); check_rustfix_coverage(); @@ -208,8 +205,19 @@ fn run_internal_tests() { if !RUN_INTERNAL_TESTS { return; } - let config = base_config("ui-internal"); - compiletest::run_tests(config).unwrap(); + let mut config = base_config("ui-internal"); + if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { + *err = "cargo uitest --features internal -- -- --bless".into(); + } + let test_filter = test_filter(); + + compiletest::run_tests_generic( + config, + move |path| compiletest::default_file_filter(path) && test_filter(path), + compiletest::default_per_file_config, + status_emitter::Text, + ) + .unwrap(); } fn run_ui_toml() { @@ -228,13 +236,11 @@ fn run_ui_toml() { "$$DIR", ); - let name = config.root_dir.display().to_string(); - let test_filter = test_filter(); ui_test::run_tests_generic( config, - |path| test_filter(path) && path.extension() == Some("rs".as_ref()), + |path| compiletest::default_file_filter(path) && test_filter(path), |config, path| { let mut config = config.clone(); config @@ -243,7 +249,7 @@ fn run_ui_toml() { .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); Some(config) }, - (status_emitter::Text, status_emitter::Gha:: { name }), + status_emitter::Text, ) .unwrap(); } @@ -284,8 +290,6 @@ fn run_ui_cargo() { "$$DIR", ); - let name = config.root_dir.display().to_string(); - let test_filter = test_filter(); ui_test::run_tests_generic( @@ -296,7 +300,7 @@ fn run_ui_cargo() { config.out_dir = PathBuf::from("target/ui_test_cargo/").join(path.parent().unwrap()); Some(config) }, - (status_emitter::Text, status_emitter::Gha:: { name }), + status_emitter::Text, ) .unwrap(); } From ef482d17f2f990ac1a9de49c03e771bb0dc1cf3e Mon Sep 17 00:00:00 2001 From: Catherine Flores Date: Fri, 21 Jul 2023 09:14:13 -0500 Subject: [PATCH 59/90] Do not lint if used as a `fn`-like argument --- clippy_lints/src/needless_pass_by_ref_mut.rs | 113 +++++++++++++----- tests/ui/infinite_loop.stderr | 16 +-- tests/ui/let_underscore_future.stderr | 16 +-- tests/ui/mut_key.stderr | 16 +-- tests/ui/mut_reference.stderr | 24 ++-- tests/ui/needless_pass_by_ref_mut.rs | 58 +++++++-- tests/ui/needless_pass_by_ref_mut.stderr | 26 ++-- .../ui/should_impl_trait/method_list_2.stderr | 18 +-- 8 files changed, 187 insertions(+), 100 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index c399b43e251..3299dca09c3 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,16 +1,16 @@ use super::needless_pass_by_value::requires_exact_signature; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; -use clippy_utils::{is_from_proc_macro, is_self}; -use if_chain::if_chain; -use rustc_data_structures::fx::FxHashSet; +use clippy_utils::{get_parent_node, is_from_proc_macro, is_self}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind}; +use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -48,20 +48,24 @@ declare_clippy_lint! { "using a `&mut` argument when it's not mutated" } -#[derive(Copy, Clone)] -pub struct NeedlessPassByRefMut { +#[derive(Clone)] +pub struct NeedlessPassByRefMut<'tcx> { avoid_breaking_exported_api: bool, + used_fn_def_ids: FxHashSet, + fn_def_ids_to_maybe_unused_mut: FxIndexMap>>, } -impl NeedlessPassByRefMut { +impl NeedlessPassByRefMut<'_> { pub fn new(avoid_breaking_exported_api: bool) -> Self { Self { avoid_breaking_exported_api, + used_fn_def_ids: FxHashSet::default(), + fn_def_ids_to_maybe_unused_mut: FxIndexMap::default(), } } } -impl_lint_pass!(NeedlessPassByRefMut => [NEEDLESS_PASS_BY_REF_MUT]); +impl_lint_pass!(NeedlessPassByRefMut<'_> => [NEEDLESS_PASS_BY_REF_MUT]); fn should_skip<'tcx>( cx: &LateContext<'tcx>, @@ -89,12 +93,12 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } -impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { +impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, + decl: &'tcx FnDecl<'tcx>, body: &'tcx Body<'_>, span: Span, fn_def_id: LocalDefId, @@ -140,7 +144,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { if it.peek().is_none() { return; } - // Collect variables mutably used and spans which will need dereferencings from the // function body. let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { @@ -165,30 +168,45 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut { } ctx }; - - let show_semver_warning = self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(fn_def_id); for ((&input, &_), arg) in it { // Only take `&mut` arguments. - if_chain! { - if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind; - if !mutably_used_vars.contains(&canonical_id); - if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind; - then { - // If the argument is never used mutably, we emit the warning. - let sp = input.span; - span_lint_and_then( + if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind + && !mutably_used_vars.contains(&canonical_id) + { + self.fn_def_ids_to_maybe_unused_mut.entry(fn_def_id).or_default().push(input); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor { + cx, + used_fn_def_ids: &mut self.used_fn_def_ids, + }); + + for (fn_def_id, unused) in self + .fn_def_ids_to_maybe_unused_mut + .iter() + .filter(|(def_id, _)| !self.used_fn_def_ids.contains(def_id)) + { + let show_semver_warning = + self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(*fn_def_id); + + for input in unused { + // If the argument is never used mutably, we emit the warning. + let sp = input.span; + if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { + span_lint_hir_and_then( cx, NEEDLESS_PASS_BY_REF_MUT, + cx.tcx.hir().local_def_id_to_hir_id(*fn_def_id), sp, "this argument is a mutable reference, but not used mutably", |diag| { diag.span_suggestion( sp, "consider changing to".to_string(), - format!( - "&{}", - snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"), - ), + format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); if show_semver_warning { @@ -316,3 +334,44 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt { self.prev_bind = Some(id); } } + +/// A final pass to check for paths referencing this function that require the argument to be +/// `&mut`, basically if the function is ever used as a `fn`-like argument. +struct FnNeedsMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + used_fn_def_ids: &'a mut FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) { + walk_qpath(self, qpath, hir_id); + + let Self { cx, used_fn_def_ids } = self; + + // #11182; do not lint if mutability is required elsewhere + if let Node::Expr(expr) = cx.tcx.hir().get(hir_id) + && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id) + && let ty::FnDef(def_id, _) = cx.tcx.typeck(cx.tcx.hir().enclosing_body_owner(hir_id)).expr_ty(expr).kind() + && let Some(def_id) = def_id.as_local() + { + if let Node::Expr(e) = parent + && let ExprKind::Call(call, _) = e.kind + && call.hir_id == expr.hir_id + { + return; + } + + // We don't need to check each argument individually as you cannot coerce a function + // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's + // passed as a `fn`-like argument (or is unified) and should ignore every "unused" + // argument entirely + used_fn_def_ids.insert(def_id); + } + } +} diff --git a/tests/ui/infinite_loop.stderr b/tests/ui/infinite_loop.stderr index 701b3cd1904..04559f9ada4 100644 --- a/tests/ui/infinite_loop.stderr +++ b/tests/ui/infinite_loop.stderr @@ -1,11 +1,3 @@ -error: this argument is a mutable reference, but not used mutably - --> $DIR/infinite_loop.rs:7:17 - | -LL | fn fn_mutref(i: &mut i32) { - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - error: variables in the condition are not mutated in the loop body --> $DIR/infinite_loop.rs:20:11 | @@ -99,5 +91,13 @@ LL | while y < 10 { = note: this loop contains `return`s or `break`s = help: rewrite it as `if cond { loop { } }` +error: this argument is a mutable reference, but not used mutably + --> $DIR/infinite_loop.rs:7:17 + | +LL | fn fn_mutref(i: &mut i32) { + | ^^^^^^^^ help: consider changing to: `&i32` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: aborting due to 12 previous errors diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr index 9e69fb04133..ff1e2b8c901 100644 --- a/tests/ui/let_underscore_future.stderr +++ b/tests/ui/let_underscore_future.stderr @@ -1,11 +1,3 @@ -error: this argument is a mutable reference, but not used mutably - --> $DIR/let_underscore_future.rs:11:35 - | -LL | fn do_something_to_future(future: &mut impl Future) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - error: non-binding `let` on a future --> $DIR/let_underscore_future.rs:14:5 | @@ -31,5 +23,13 @@ LL | let _ = future; | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` +error: this argument is a mutable reference, but not used mutably + --> $DIR/let_underscore_future.rs:11:35 + | +LL | fn do_something_to_future(future: &mut impl Future) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: aborting due to 4 previous errors diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index 02a0da86a4b..3f756f5f0e5 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -12,14 +12,6 @@ error: mutable key type LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { | ^^^^^^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_key.rs:31:32 - | -LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - error: mutable key type --> $DIR/mut_key.rs:32:5 | @@ -110,5 +102,13 @@ error: mutable key type LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: this argument is a mutable reference, but not used mutably + --> $DIR/mut_key.rs:31:32 + | +LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: aborting due to 18 previous errors diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index 23c812475c2..1fdfbf9227e 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,17 +1,3 @@ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_reference.rs:4:33 - | -LL | fn takes_a_mutable_reference(a: &mut i32) {} - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_reference.rs:11:44 - | -LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} - | ^^^^^^^^ help: consider changing to: `&i32` - error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | @@ -32,5 +18,13 @@ error: the method `takes_an_immutable_reference` doesn't need a mutable referenc LL | my_struct.takes_an_immutable_reference(&mut 42); | ^^^^^^^ -error: aborting due to 5 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/mut_reference.rs:11:44 + | +LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} + | ^^^^^^^^ help: consider changing to: `&i32` + | + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 27e1fc075bb..49f88978bff 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,5 @@ -#![allow(unused)] +#![allow(clippy::if_same_then_else, clippy::no_effect)] +#![feature(lint_reasons)] use std::ptr::NonNull; @@ -155,15 +156,48 @@ async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { println!("{:?}", z); } -fn main() { - // let mut u = 0; - // let mut v = vec![0]; - // foo(&mut v, &0, &mut u); - // foo2(&mut v); - // foo3(&mut v); - // foo4(&mut v); - // foo5(&mut v); - // alias_check(&mut v); - // alias_check2(&mut v); - // println!("{u}"); +// Should not warn (passed as closure which takes `&mut`). +fn passed_as_closure(s: &mut u32) {} + +// Should not warn. +fn passed_as_local(s: &mut u32) {} + +// Should not warn. +fn ty_unify_1(s: &mut u32) {} + +// Should not warn. +fn ty_unify_2(s: &mut u32) {} + +// Should not warn. +fn passed_as_field(s: &mut u32) {} + +fn closure_takes_mut(s: fn(&mut u32)) {} + +struct A { + s: fn(&mut u32), +} + +// Should warn. +fn used_as_path(s: &mut u32) {} + +// Make sure lint attributes work fine +#[expect(clippy::needless_pass_by_ref_mut)] +fn lint_attr(s: &mut u32) {} + +fn main() { + let mut u = 0; + let mut v = vec![0]; + foo(&mut v, &0, &mut u); + foo2(&mut v); + foo3(&mut v); + foo4(&mut v); + foo5(&mut v); + alias_check(&mut v); + alias_check2(&mut v); + println!("{u}"); + closure_takes_mut(passed_as_closure); + A { s: passed_as_field }; + used_as_path; + let _: fn(&mut u32) = passed_as_local; + let _ = if v[0] == 0 { ty_unify_1 } else { ty_unify_2 }; } diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index caf47bbce86..4ab5b5675ca 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:6:11 + --> $DIR/needless_pass_by_ref_mut.rs:7:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -7,73 +7,73 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:31:12 + --> $DIR/needless_pass_by_ref_mut.rs:32:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:44:29 + --> $DIR/needless_pass_by_ref_mut.rs:45:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:49:31 + --> $DIR/needless_pass_by_ref_mut.rs:50:31 | LL | fn badger(&mut self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:126:16 + --> $DIR/needless_pass_by_ref_mut.rs:127:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:130:16 + --> $DIR/needless_pass_by_ref_mut.rs:131:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:134:16 + --> $DIR/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:138:16 + --> $DIR/needless_pass_by_ref_mut.rs:139:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:142:24 + --> $DIR/needless_pass_by_ref_mut.rs:143:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:146:24 + --> $DIR/needless_pass_by_ref_mut.rs:147:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:150:32 + --> $DIR/needless_pass_by_ref_mut.rs:151:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:24 + --> $DIR/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:45 + --> $DIR/needless_pass_by_ref_mut.rs:155:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr index 2ae9fa34d14..a3bb05bf176 100644 --- a/tests/ui/should_impl_trait/method_list_2.stderr +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -39,15 +39,6 @@ LL | | } | = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name -error: this argument is a mutable reference, but not used mutably - --> $DIR/method_list_2.rs:38:31 - | -LL | pub fn hash(&self, state: &mut T) { - | ^^^^^^ help: consider changing to: `&T` - | - = warning: changing this function will impact semver compatibility - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - error: method `index` can be confused for the standard trait method `std::ops::Index::index` --> $DIR/method_list_2.rs:42:5 | @@ -158,5 +149,14 @@ LL | | } | = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name +error: this argument is a mutable reference, but not used mutably + --> $DIR/method_list_2.rs:38:31 + | +LL | pub fn hash(&self, state: &mut T) { + | ^^^^^^ help: consider changing to: `&T` + | + = warning: changing this function will impact semver compatibility + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + error: aborting due to 16 previous errors From a1473721b0e5a69fa885386701a4eb2be14d8359 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 25 Jul 2023 11:56:54 +0100 Subject: [PATCH 60/90] clippy: `env!` invocations can't be b"" literals Signed-off-by: David Wood --- clippy_lints/src/strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 58724852b3c..4d45091f4b5 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -328,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { { // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces // `&[u8]`. This change would prevent matching with different sized slices. - } else { + } else if !callsite.starts_with("env!") { span_lint_and_sugg( cx, STRING_LIT_AS_BYTES, From 2b16c37b65f1437768e7379e3fec9425eb96dcd4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Jul 2023 19:42:21 +0200 Subject: [PATCH 61/90] bless more --- tests/ui/self_assignment.rs | 2 +- tests/ui/self_assignment.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs index d6682cc63dc..a7f9fbaae7c 100644 --- a/tests/ui/self_assignment.rs +++ b/tests/ui/self_assignment.rs @@ -14,7 +14,7 @@ pub fn positives(mut a: usize, b: &mut u32, mut s: S) { *b = *b; s = s; s.a = s.a; - s.b[10] = s.b[5 + 5]; + s.b[9] = s.b[5 + 4]; s.c[0][1] = s.c[0][1]; s.b[a] = s.b[a]; *s.e = *s.e; diff --git a/tests/ui/self_assignment.stderr b/tests/ui/self_assignment.stderr index bed88244eea..25b8569fa3d 100644 --- a/tests/ui/self_assignment.stderr +++ b/tests/ui/self_assignment.stderr @@ -24,11 +24,11 @@ error: self-assignment of `s.a` to `s.a` LL | s.a = s.a; | ^^^^^^^^^ -error: self-assignment of `s.b[5 + 5]` to `s.b[10]` +error: self-assignment of `s.b[5 + 4]` to `s.b[9]` --> $DIR/self_assignment.rs:17:5 | -LL | s.b[10] = s.b[5 + 5]; - | ^^^^^^^^^^^^^^^^^^^^ +LL | s.b[9] = s.b[5 + 4]; + | ^^^^^^^^^^^^^^^^^^^ error: self-assignment of `s.c[0][1]` to `s.c[0][1]` --> $DIR/self_assignment.rs:18:5 From 3fb714d8281541d1ac872baa3b9c7a23a3d0f0eb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 25 Jul 2023 10:27:34 +0000 Subject: [PATCH 62/90] Use a builder instead of boolean/option arguments --- clippy_lints/src/doc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 8879c529262..00f70e586f4 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -729,7 +729,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter), None); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { From 15de3dd2afcedef99ef6a1dd485734d94b00bb99 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Jul 2023 18:08:16 +0200 Subject: [PATCH 63/90] Add a note if the function is behind a cfg --- clippy_lints/src/needless_pass_by_ref_mut.rs | 21 +++++++++++++++++--- tests/ui/needless_pass_by_ref_mut.rs | 10 ++++++++++ tests/ui/needless_pass_by_ref_mut.stderr | 18 ++++++++++++++++- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 3299dca09c3..b4be910c8ad 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -12,11 +12,11 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath}; +use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_span::symbol::kw; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -93,6 +93,16 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } +fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + if def_id == CRATE_DEF_ID { + false + } else if tcx.has_attr(def_id, sym::cfg) { + true + } else { + inherits_cfg(tcx, tcx.parent_module_from_def_id(def_id)) + } +} + impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, @@ -192,10 +202,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { let show_semver_warning = self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(*fn_def_id); + let mut is_cfged = None; for input in unused { // If the argument is never used mutably, we emit the warning. let sp = input.span; if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { + let is_cfged = is_cfged.get_or_insert_with(|| inherits_cfg(cx.tcx, *fn_def_id)); span_lint_hir_and_then( cx, NEEDLESS_PASS_BY_REF_MUT, @@ -212,6 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { if show_semver_warning { diag.warn("changing this function will impact semver compatibility"); } + if *is_cfged { + diag.note("this is cfg-gated and may require further changes"); + } }, ); } diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 49f88978bff..b3933bb9b4a 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -184,6 +184,16 @@ fn used_as_path(s: &mut u32) {} #[expect(clippy::needless_pass_by_ref_mut)] fn lint_attr(s: &mut u32) {} +#[cfg(not(feature = "a"))] +// Should warn with note. +fn cfg_warn(s: &mut u32) {} + +#[cfg(not(feature = "a"))] +mod foo { + // Should warn with note. + fn cfg_warn(s: &mut u32) {} +} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 4ab5b5675ca..977dd5154b0 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -78,5 +78,21 @@ error: this argument is a mutable reference, but not used mutably LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` -error: aborting due to 13 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:189:16 + | +LL | fn cfg_warn(s: &mut u32) {} + | ^^^^^^^^ help: consider changing to: `&u32` + | + = note: this is cfg-gated and may require further changes + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:194:20 + | +LL | fn cfg_warn(s: &mut u32) {} + | ^^^^^^^^ help: consider changing to: `&u32` + | + = note: this is cfg-gated and may require further changes + +error: aborting due to 15 previous errors From f20a174c4fcd6965746287be882274f5c99e57f4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 24 Jul 2023 22:02:52 +0000 Subject: [PATCH 64/90] Make everything builtin! --- clippy_utils/src/qualify_min_const_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 1c29d614fa3..e563e41ab2a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_middle::traits::{ImplSource, ObligationCause}; +use rustc_middle::traits::{ImplSource, ObligationCause, BuiltinImplSource}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_semver::RustcVersion; @@ -411,7 +411,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { return false; } From 1fe27629005f15d51bfa770ab4ba330b47e7d9ed Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:21:00 +0200 Subject: [PATCH 65/90] use a dedicated enum for vec initializer --- .../src/slow_vector_initialization.rs | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index b496185cc72..01da63d2b00 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -61,11 +61,24 @@ struct VecAllocation<'tcx> { /// Reference to the expression used as argument on `with_capacity` call. This is used /// to only match slow zero-filling idioms of the same length than vector initialization. - /// - /// Initially set to `None` if initialized with `Vec::new()`, but will always be `Some(_)` by - /// the time the visitor has looked through the enclosing block and found a slow - /// initialization, so it is safe to unwrap later at lint time. - size_expr: Option<&'tcx Expr<'tcx>>, + size_expr: InitializedSize<'tcx>, +} + +/// Initializer for the creation of the vector. +/// +/// When `Vec::with_capacity(size)` is found, the `size` expression will be in +/// `InitializedSize::Initialized`. +/// +/// Otherwise, for `Vec::new()` calls, there is no allocation initializer yet, so +/// `InitializedSize::Uninitialized` is used. +/// Later, when a call to `.resize(size, 0)` or similar is found, it's set +/// to `InitializedSize::Initialized(size)`. +/// +/// Since it will be set to `InitializedSize::Initialized(size)` when a slow initialization is +/// found, it is always safe to "unwrap" it at lint time. +enum InitializedSize<'tcx> { + Initialized(&'tcx Expr<'tcx>), + Uninitialized, } /// Type of slow initialization @@ -120,20 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { } impl SlowVectorInit { - /// Given an expression, returns: - /// - `Some(Some(size))` if it is a function call to `Vec::with_capacity(size)` - /// - `Some(None)` if it is a call to `Vec::new()` - /// - `None` if it is neither - #[allow( - clippy::option_option, - reason = "outer option is immediately removed at call site and the inner option is kept around, \ - so extracting into a dedicated enum seems unnecessarily complicated" - )] - fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option>> { + fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> { if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) { - Some(Some(len_expr)) - } else if Self::is_vec_new(cx, expr) { - Some(None) + Some(InitializedSize::Initialized(len_expr)) + } else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) { + Some(InitializedSize::Uninitialized) } else { None } @@ -155,14 +159,6 @@ impl SlowVectorInit { } } - /// Checks if the given expression is `Vec::new()` - fn is_vec_new(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!( - expr.kind, - ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW) - ) - } - /// Search initialization for the given vector fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) { let enclosing_body = get_enclosing_block(cx, parent_node); @@ -200,9 +196,10 @@ impl SlowVectorInit { fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir( cx, - vec_alloc - .size_expr - .expect("length expression must be set by this point"), + match vec_alloc.size_expr { + InitializedSize::Initialized(expr) => expr, + InitializedSize::Uninitialized => unreachable!("size expression must be set by this point"), + }, "len", ); @@ -257,12 +254,12 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { - let is_matching_resize = if let Some(size_expr) = self.vec_alloc.size_expr { + let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // If we have a size expression, check that it is equal to what's passed to `resize` SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") } else { - self.vec_alloc.size_expr = Some(len_arg); + self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); true }; @@ -280,13 +277,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { // Check that take is applied to `repeat(0)` if self.is_repeat_zero(recv); then { - if let Some(size_expr) = self.vec_alloc.size_expr { + if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // Check that len expression is equals to `with_capacity` expression return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") } - self.vec_alloc.size_expr = Some(len_arg); + self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); return true; } } From 04b710b5695a80b7265c92b1ff7fdaf4be81efa6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Jul 2023 18:26:56 +0200 Subject: [PATCH 66/90] Use libtest errors check to better enforce UI testing --- tests/ui/needless_pass_by_ref_mut.rs | 30 +++++++++++++----------- tests/ui/needless_pass_by_ref_mut.stderr | 28 +++++++++++----------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index b3933bb9b4a..ae7b018d0e2 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -3,8 +3,8 @@ use std::ptr::NonNull; -// Should only warn for `s`. fn foo(s: &mut Vec, b: &u32, x: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably *x += *b + s.len() as u32; } @@ -28,8 +28,8 @@ fn foo5(s: &mut Vec) { foo2(s); } -// Should warn. fn foo6(s: &mut Vec) { + //~^ ERROR: this argument is a mutable reference, but not used mutably non_mut_ref(s); } @@ -41,13 +41,13 @@ impl Bar { // Should not warn on `&mut self`. fn bar(&mut self) {} - // Should warn about `vec` fn mushroom(&self, vec: &mut Vec) -> usize { + //~^ ERROR: this argument is a mutable reference, but not used mutably vec.len() } - // Should warn about `vec` (and not `self`). fn badger(&mut self, vec: &mut Vec) -> usize { + //~^ ERROR: this argument is a mutable reference, but not used mutably vec.len() } } @@ -123,36 +123,36 @@ async fn f7(x: &mut i32, y: i32, z: &mut i32, a: i32) { *z += 1; } -// Should warn. async fn a1(x: &mut i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a2(x: &mut i32, y: String) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a3(x: &mut i32, y: String, z: String) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a4(x: &mut i32, y: i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a5(x: i32, y: &mut i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a6(x: i32, y: &mut i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", x); } -// Should warn. async fn a7(x: i32, y: i32, z: &mut i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", z); } -// Should warn. async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably println!("{:?}", z); } @@ -185,13 +185,15 @@ fn used_as_path(s: &mut u32) {} fn lint_attr(s: &mut u32) {} #[cfg(not(feature = "a"))] -// Should warn with note. fn cfg_warn(s: &mut u32) {} +//~^ ERROR: this argument is a mutable reference, but not used mutably +//~| NOTE: this is cfg-gated and may require further changes #[cfg(not(feature = "a"))] mod foo { - // Should warn with note. fn cfg_warn(s: &mut u32) {} + //~^ ERROR: this argument is a mutable reference, but not used mutably + //~| NOTE: this is cfg-gated and may require further changes } fn main() { diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 977dd5154b0..0d426ce32f9 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:7:11 + --> $DIR/needless_pass_by_ref_mut.rs:6:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -7,79 +7,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:32:12 + --> $DIR/needless_pass_by_ref_mut.rs:31:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:45:29 + --> $DIR/needless_pass_by_ref_mut.rs:44:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:50:31 + --> $DIR/needless_pass_by_ref_mut.rs:49:31 | LL | fn badger(&mut self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:127:16 + --> $DIR/needless_pass_by_ref_mut.rs:126:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:131:16 + --> $DIR/needless_pass_by_ref_mut.rs:130:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:135:16 + --> $DIR/needless_pass_by_ref_mut.rs:134:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:139:16 + --> $DIR/needless_pass_by_ref_mut.rs:138:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:143:24 + --> $DIR/needless_pass_by_ref_mut.rs:142:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:147:24 + --> $DIR/needless_pass_by_ref_mut.rs:146:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:151:32 + --> $DIR/needless_pass_by_ref_mut.rs:150:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:155:24 + --> $DIR/needless_pass_by_ref_mut.rs:154:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:155:45 + --> $DIR/needless_pass_by_ref_mut.rs:154:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:189:16 + --> $DIR/needless_pass_by_ref_mut.rs:188:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` From 1c9772c7737e5937f71be1d1a27699bf226d89f1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Jul 2023 18:43:51 +0200 Subject: [PATCH 67/90] Move `inherits_cfg` function into clippy_utils --- clippy_lints/src/needless_pass_by_ref_mut.rs | 18 ++++-------------- clippy_utils/src/lib.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index b4be910c8ad..0e2a101a977 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,7 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; -use clippy_utils::{get_parent_node, is_from_proc_macro, is_self}; +use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; @@ -12,11 +12,11 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; +use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -93,16 +93,6 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } -fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - if def_id == CRATE_DEF_ID { - false - } else if tcx.has_attr(def_id, sym::cfg) { - true - } else { - inherits_cfg(tcx, tcx.parent_module_from_def_id(def_id)) - } -} - impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8429192437c..beff0e6e1ab 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2451,6 +2451,17 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { .any(is_cfg_test) } +/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. +pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let hir = tcx.hir(); + + tcx.has_attr(def_id, sym::cfg) + || hir + .parent_iter(hir.local_def_id_to_hir_id(def_id)) + .flat_map(|(parent_id, _)| hir.attrs(parent_id)) + .any(|attr| attr.has_name(sym::cfg)) +} + /// Checks whether item either has `test` attribute applied, or /// is a module with `test` in its name. /// From c0484b74f79d2e648083a6c52a697aae66202707 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:56:57 +0200 Subject: [PATCH 68/90] simplify looking for `Vec::with_capacity` exprs --- .../src/slow_vector_initialization.rs | 28 ++++++------------- clippy_utils/src/paths.rs | 1 + 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 01da63d2b00..54a33eb2986 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, paths, SpanlessEq, @@ -8,7 +7,7 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -133,8 +132,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { } impl SlowVectorInit { + /// Looks for `Vec::with_capacity(size)` or `Vec::new()` calls and returns the initialized size, + /// if any. More specifically, it returns: + /// - `Some(InitializedSize::Initialized(size))` for `Vec::with_capacity(size)` + /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()` + /// - `None` for other, unrelated kinds of expressions fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> { - if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) { + if let ExprKind::Call(func, [len_expr]) = expr.kind + && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY) + { Some(InitializedSize::Initialized(len_expr)) } else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) { Some(InitializedSize::Uninitialized) @@ -143,22 +149,6 @@ impl SlowVectorInit { } } - /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression - /// of the first argument of `with_capacity` call if it matches or `None` if it does not. - fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Call(func, [arg]) = expr.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind; - if name.ident.as_str() == "with_capacity"; - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec); - then { - Some(arg) - } else { - None - } - } - } - /// Search initialization for the given vector fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) { let enclosing_body = get_enclosing_block(cx, parent_node); diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index f3677e6f614..ad3e1ce8108 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -149,6 +149,7 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; +pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; From c3079a98560efd4bd3995aab898e404e14855ce8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 17 Jun 2023 10:32:31 +0200 Subject: [PATCH 69/90] Fix integration tests It sees like the `integration` test binary was no longer uploaded. I wonder how it was the successfully "run". First attempt to fix this. Also updates the artifacts actions to v3. --- .github/workflows/clippy_bors.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 4eb11a3ac85..5869cd0b1a4 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -187,16 +187,14 @@ jobs: - name: Extract Binaries run: | DIR=$CARGO_TARGET_DIR/debug - rm $DIR/deps/integration-*.d - mv $DIR/deps/integration-* $DIR/integration + find $DIR/deps/integration-* -executable ! -type d | xargs -I {} mv {} $DIR/integration find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf - rm -rf $CARGO_TARGET_DIR/release - name: Upload Binaries - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: - name: target - path: target + name: binaries + path: target/debug integration: needs: integration_build @@ -206,16 +204,13 @@ jobs: matrix: integration: - 'rust-lang/cargo' - # FIXME: re-enable once fmt_macros is renamed in RLS - # - 'rust-lang/rls' - 'rust-lang/chalk' - 'rust-lang/rustfmt' - 'Marwes/combine' - 'Geal/nom' - 'rust-lang/stdarch' - 'serde-rs/serde' - # FIXME: chrono currently cannot be compiled with `--all-targets` - # - 'chronotope/chrono' + - 'chronotope/chrono' - 'hyperium/hyper' - 'rust-random/rand' - 'rust-lang/futures-rs' @@ -239,10 +234,10 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v3 with: - name: target - path: target + name: binaries + path: target/debug - name: Make Binaries Executable run: chmod +x $CARGO_TARGET_DIR/debug/* From 12b63f5c35532ea265d7ac9198c9668f5e640668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 25 Jul 2023 19:19:59 +0200 Subject: [PATCH 70/90] ci: integration tests: print clippy run output and set LD_LIBRARY_PATH to rustc sysroot --- .github/workflows/clippy_bors.yml | 7 ++++++- tests/integration.rs | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 5869cd0b1a4..a686198f781 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -232,6 +232,11 @@ jobs: - name: Install toolchain run: rustup show active-toolchain + - name: Set LD_LIBRARY_PATH + run: | + SYSROOT=$(rustc --print sysroot) + echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV + # Download - name: Download target dir uses: actions/download-artifact@v3 @@ -246,7 +251,7 @@ jobs: - name: Test ${{ matrix.integration }} run: | RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \ - $CARGO_TARGET_DIR/debug/integration + $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/tests/integration.rs b/tests/integration.rs index a771d8b87c8..e72891c44c2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -65,6 +65,10 @@ fn integration_test() { .expect("unable to run clippy"); let stderr = String::from_utf8_lossy(&output.stderr); + + // debug: + eprintln!("{stderr}"); + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { static BACKTRACE_END_MSG: &str = "end of query stack"; let backtrace_end = stderr[backtrace_start..] From 5976fda20d2e00980bc3be657a68f53594e69c90 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 26 Jul 2023 00:13:06 +0200 Subject: [PATCH 71/90] Add *no merge policy* note via rustbot --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index c40b71f6ca7..eecae04ea51 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -9,6 +9,9 @@ allow-unauthenticated = [ # See https://github.com/rust-lang/triagebot/wiki/Shortcuts [shortcut] +# Have rustbot inform users about the *No Merge Policy* +[no-merges] + [autolabel."S-waiting-on-review"] new_pr = true From 978b1daf99d8326718684381704902fdaaf71b18 Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:56:52 -0500 Subject: [PATCH 72/90] New lint [`filter_map_bool_then`] --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/filter_map_bool_then.rs | 39 +++++++++++++++++++ clippy_lints/src/methods/mod.rs | 34 ++++++++++++++++ clippy_utils/src/paths.rs | 2 + tests/ui/filter_map_bool_then.fixed | 33 ++++++++++++++++ tests/ui/filter_map_bool_then.rs | 33 ++++++++++++++++ tests/ui/filter_map_bool_then.stderr | 28 +++++++++++++ 8 files changed, 171 insertions(+) create mode 100644 clippy_lints/src/methods/filter_map_bool_then.rs create mode 100644 tests/ui/filter_map_bool_then.fixed create mode 100644 tests/ui/filter_map_bool_then.rs create mode 100644 tests/ui/filter_map_bool_then.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 02d12d60e73..251f32d2e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4844,6 +4844,7 @@ Released 2018-09-13 [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file [`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map +[`filter_map_bool_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_bool_then [`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity [`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 9b99a255423..e084471433d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -337,6 +337,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::EXPECT_USED_INFO, crate::methods::EXTEND_WITH_DRAIN_INFO, crate::methods::FILETYPE_IS_FILE_INFO, + crate::methods::FILTER_MAP_BOOL_THEN_INFO, crate::methods::FILTER_MAP_IDENTITY_INFO, crate::methods::FILTER_MAP_NEXT_INFO, crate::methods::FILTER_NEXT_INFO, diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs new file mode 100644 index 00000000000..e5d8be21f3d --- /dev/null +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::BOOL_THEN; +use clippy_utils::source::snippet_opt; +use clippy_utils::{is_from_proc_macro, match_def_path, peel_blocks}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::Span; + +use super::FILTER_MAP_BOOL_THEN; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Closure(closure) = arg.kind + && let body = peel_blocks(cx.tcx.hir().body(closure.body).value) + && let ExprKind::MethodCall(_, recv, [then_arg], _) = body.kind + && let ExprKind::Closure(then_closure) = then_arg.kind + && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value) + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(body.hir_id) + && match_def_path(cx, def_id, &BOOL_THEN) + && !is_from_proc_macro(cx, expr) + && let Some(decl_snippet) = closure.fn_arg_span.and_then(|s| snippet_opt(cx, s)) + // NOTE: This will include the `()` (parenthesis) around it. Maybe there's some utils method + // to remove them? `unused_parens` will already take care of this but it may be nice. + && let Some(filter) = snippet_opt(cx, recv.span) + && let Some(map) = snippet_opt(cx, then_body.span) + { + span_lint_and_sugg( + cx, + FILTER_MAP_BOOL_THEN, + call_span, + "usage of `bool::then` in `filter_map`", + "use `filter` then `map` instead", + format!("filter({decl_snippet} {filter}).map({decl_snippet} {map})"), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1058289679b..e774450018e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -21,6 +21,7 @@ mod expect_used; mod extend_with_drain; mod filetype_is_file; mod filter_map; +mod filter_map_bool_then; mod filter_map_identity; mod filter_map_next; mod filter_next; @@ -3475,6 +3476,37 @@ declare_clippy_lint! { "disallows `.skip(0)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `bool::then` in `Iterator::filter_map`. + /// + /// ### Why is this bad? + /// This can be written with `filter` then `map` instead, which would reduce nesting and + /// separates the filtering from the transformation phase. This comes with no cost to + /// performance and is just cleaner. + /// + /// ### Limitations + /// Does not lint `bool::then_some`, as it eagerly evaluates its arguments rather than lazily. + /// This can create differing behavior, so better safe than sorry. + /// + /// ### Example + /// ```rust + /// # fn really_expensive_fn(i: i32) -> i32 { i } + /// # let v = vec![]; + /// _ = v.into_iter().filter_map(|i| (i % 2 == 0).then(|| really_expensive_fn(i))); + /// ``` + /// Use instead: + /// ```rust + /// # fn really_expensive_fn(i: i32) -> i32 { i } + /// # let v = vec![]; + /// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i)); + /// ``` + #[clippy::version = "1.72.0"] + pub FILTER_MAP_BOOL_THEN, + style, + "checks for usage of `bool::then` in `Iterator::filter_map`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3612,6 +3644,7 @@ impl_lint_pass!(Methods => [ FORMAT_COLLECT, STRING_LIT_CHARS_ANY, ITER_SKIP_ZERO, + FILTER_MAP_BOOL_THEN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3889,6 +3922,7 @@ impl Methods { }, ("filter_map", [arg]) => { unnecessary_filter_map::check(cx, expr, arg, name); + filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, ("find_map", [arg]) => { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 8d96d3cfe50..914ea85ac28 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -163,3 +163,5 @@ pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"]; pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"]; +#[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so +pub const BOOL_THEN: [&str; 4] = ["core", "bool", "", "then"]; diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed new file mode 100644 index 00000000000..0db0b0a8a28 --- /dev/null +++ b/tests/ui/filter_map_bool_then.fixed @@ -0,0 +1,33 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::clone_on_copy, clippy::unnecessary_lazy_evaluations, unused)] +#![warn(clippy::filter_map_bool_then)] + +#[macro_use] +extern crate proc_macros; + +fn main() { + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter(|i| (i % 2 == 0)).map(|i| i + 1); + v.clone() + .into_iter() + .filter(|i| (i % 2 == 0)).map(|i| i + 1); + v.clone() + .into_iter() + .filter(|&i| i != 1000) + .filter(|i| (i % 2 == 0)).map(|i| i + 1); + v.iter() + .copied() + .filter(|&i| i != 1000) + .filter(|i| (i.clone() % 2 == 0)).map(|i| i + 1); + // Do not lint + external! { + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + } + with_span! { + span + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + } +} diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs new file mode 100644 index 00000000000..764f9c4a85e --- /dev/null +++ b/tests/ui/filter_map_bool_then.rs @@ -0,0 +1,33 @@ +//@run-rustfix +//@aux-build:proc_macros.rs:proc-macro +#![allow(clippy::clone_on_copy, clippy::unnecessary_lazy_evaluations, unused)] +#![warn(clippy::filter_map_bool_then)] + +#[macro_use] +extern crate proc_macros; + +fn main() { + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + v.clone() + .into_iter() + .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) }); + v.clone() + .into_iter() + .filter(|&i| i != 1000) + .filter_map(|i| (i % 2 == 0).then(|| i + 1)); + v.iter() + .copied() + .filter(|&i| i != 1000) + .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); + // Do not lint + external! { + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + } + with_span! { + span + let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + } +} diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr new file mode 100644 index 00000000000..79bc7ba02f7 --- /dev/null +++ b/tests/ui/filter_map_bool_then.stderr @@ -0,0 +1,28 @@ +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:11:27 + | +LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` + | + = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:14:10 + | +LL | .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:18:10 + | +LL | .filter_map(|i| (i % 2 == 0).then(|| i + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:22:10 + | +LL | .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i.clone() % 2 == 0)).map(|i| i + 1)` + +error: aborting due to 4 previous errors + From 3235d9d612909bc64550eea3a0d387e1187e93dd Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Sun, 16 Jul 2023 05:30:01 -0500 Subject: [PATCH 73/90] Only lint `Copy` types --- .../src/methods/filter_map_bool_then.rs | 24 ++++++++++------- tests/ui/filter_map_bool_then.fixed | 21 +++++++++++---- tests/ui/filter_map_bool_then.rs | 13 +++++++++- tests/ui/filter_map_bool_then.stderr | 26 ++++++++++++------- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index e5d8be21f3d..4aee22a4afc 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,28 +1,34 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_from_proc_macro, match_def_path, peel_blocks}; +use clippy_utils::ty::is_copy; +use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::Span; +use rustc_span::{sym, Span}; use super::FILTER_MAP_BOOL_THEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !in_external_macro(cx.sess(), expr.span) + && is_trait_method(cx, expr, sym::Iterator) && let ExprKind::Closure(closure) = arg.kind - && let body = peel_blocks(cx.tcx.hir().body(closure.body).value) - && let ExprKind::MethodCall(_, recv, [then_arg], _) = body.kind + && let body = cx.tcx.hir().body(closure.body) + && let value = peel_blocks(body.value) + // Indexing should be fine as `filter_map` always has 1 input, we unfortunately need both + // `inputs` and `params` here as we need both the type and the span + && let param_ty = closure.fn_decl.inputs[0] + && let param = body.params[0] + && is_copy(cx, cx.typeck_results().node_type(param_ty.hir_id).peel_refs()) + && let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind && let ExprKind::Closure(then_closure) = then_arg.kind && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value) - && let Some(def_id) = cx.typeck_results().type_dependent_def_id(body.hir_id) + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) - && let Some(decl_snippet) = closure.fn_arg_span.and_then(|s| snippet_opt(cx, s)) - // NOTE: This will include the `()` (parenthesis) around it. Maybe there's some utils method - // to remove them? `unused_parens` will already take care of this but it may be nice. + && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) { @@ -32,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter({decl_snippet} {filter}).map({decl_snippet} {map})"), + format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 0db0b0a8a28..3e72fee4b07 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -1,26 +1,37 @@ //@run-rustfix //@aux-build:proc_macros.rs:proc-macro -#![allow(clippy::clone_on_copy, clippy::unnecessary_lazy_evaluations, unused)] +#![allow( + clippy::clone_on_copy, + clippy::map_identity, + clippy::unnecessary_lazy_evaluations, + unused +)] #![warn(clippy::filter_map_bool_then)] #[macro_use] extern crate proc_macros; +#[derive(Clone, PartialEq)] +struct NonCopy; + fn main() { let v = vec![1, 2, 3, 4, 5, 6]; - v.clone().into_iter().filter(|i| (i % 2 == 0)).map(|i| i + 1); + v.clone().iter().filter(|&i| (i % 2 == 0)).map(|i| i + 1); + v.clone().into_iter().filter(|&i| (i % 2 == 0)).map(|i| i + 1); v.clone() .into_iter() - .filter(|i| (i % 2 == 0)).map(|i| i + 1); + .filter(|&i| (i % 2 == 0)).map(|i| i + 1); v.clone() .into_iter() .filter(|&i| i != 1000) - .filter(|i| (i % 2 == 0)).map(|i| i + 1); + .filter(|&i| (i % 2 == 0)).map(|i| i + 1); v.iter() .copied() .filter(|&i| i != 1000) - .filter(|i| (i.clone() % 2 == 0)).map(|i| i + 1); + .filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1); // Do not lint + let v = vec![NonCopy, NonCopy]; + v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); external! { let v = vec![1, 2, 3, 4, 5, 6]; v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 764f9c4a85e..38a04e57de4 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -1,13 +1,22 @@ //@run-rustfix //@aux-build:proc_macros.rs:proc-macro -#![allow(clippy::clone_on_copy, clippy::unnecessary_lazy_evaluations, unused)] +#![allow( + clippy::clone_on_copy, + clippy::map_identity, + clippy::unnecessary_lazy_evaluations, + unused +)] #![warn(clippy::filter_map_bool_then)] #[macro_use] extern crate proc_macros; +#[derive(Clone, PartialEq)] +struct NonCopy; + fn main() { let v = vec![1, 2, 3, 4, 5, 6]; + v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); v.clone() .into_iter() @@ -21,6 +30,8 @@ fn main() { .filter(|&i| i != 1000) .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); // Do not lint + let v = vec![NonCopy, NonCopy]; + v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); external! { let v = vec![1, 2, 3, 4, 5, 6]; v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 79bc7ba02f7..b411cd83dfd 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -1,28 +1,34 @@ error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:11:27 + --> $DIR/filter_map_bool_then.rs:19:22 | -LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` +LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` | = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:14:10 + --> $DIR/filter_map_bool_then.rs:20:27 + | +LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:23:10 | LL | .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:18:10 + --> $DIR/filter_map_bool_then.rs:27:10 | LL | .filter_map(|i| (i % 2 == 0).then(|| i + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i % 2 == 0)).map(|i| i + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:22:10 + --> $DIR/filter_map_bool_then.rs:31:10 | LL | .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|i| (i.clone() % 2 == 0)).map(|i| i + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From 90947e95ad8c26156739566c7ad1e52013b63a93 Mon Sep 17 00:00:00 2001 From: Catherine Flores Date: Tue, 25 Jul 2023 17:56:43 -0500 Subject: [PATCH 74/90] [`arc_with_non_send_sync`]: Check if it's macro-generated --- clippy_lints/src/arc_with_non_send_sync.rs | 12 +++++---- tests/ui/arc_with_non_send_sync.rs | 30 +++++++++++++++++++--- tests/ui/arc_with_non_send_sync.stderr | 6 ++--- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 7adcd9ad055..35a04b5e44a 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::last_path_segment; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{is_from_proc_macro, last_path_segment}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -38,10 +38,11 @@ declare_clippy_lint! { } declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); -impl LateLintPass<'_> for ArcWithNonSendSync { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, ty, sym::Arc) +impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !expr.span.from_expansion() + && let ty = cx.typeck_results().expr_ty(expr) + && is_type_diagnostic_item(cx, ty, sym::Arc) && let ExprKind::Call(func, [arg]) = expr.kind && let ExprKind::Path(func_path) = func.kind && last_path_segment(&func_path).ident.name == sym::new @@ -54,6 +55,7 @@ impl LateLintPass<'_> for ArcWithNonSendSync { && let Some(sync) = cx.tcx.lang_items().sync_trait() && let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[])) && !(is_send && is_sync) + && !is_from_proc_macro(cx, expr) { span_lint_and_then( cx, diff --git a/tests/ui/arc_with_non_send_sync.rs b/tests/ui/arc_with_non_send_sync.rs index b6fcca0a791..2940c273255 100644 --- a/tests/ui/arc_with_non_send_sync.rs +++ b/tests/ui/arc_with_non_send_sync.rs @@ -1,6 +1,12 @@ +//@aux-build:proc_macros.rs:proc-macro #![warn(clippy::arc_with_non_send_sync)] #![allow(unused_variables)] + +#[macro_use] +extern crate proc_macros; + use std::cell::RefCell; +use std::ptr::{null, null_mut}; use std::sync::{Arc, Mutex}; fn foo(x: T) { @@ -11,14 +17,32 @@ fn issue11076() { let a: Arc> = Arc::new(Vec::new()); } +fn issue11232() { + external! { + let a: Arc<*const u8> = Arc::new(null()); + let a: Arc<*mut u8> = Arc::new(null_mut()); + } + with_span! { + span + let a: Arc<*const u8> = Arc::new(null()); + let a: Arc<*mut u8> = Arc::new(null_mut()); + } +} + fn main() { let _ = Arc::new(42); - // !Sync let _ = Arc::new(RefCell::new(42)); + //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~| NOTE: the trait `Sync` is not implemented for `RefCell` + let mutex = Mutex::new(1); - // !Send let _ = Arc::new(mutex.lock().unwrap()); - // !Send + !Sync + //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~| NOTE: the trait `Send` is not implemented for `MutexGuard<'_, i32>` + let _ = Arc::new(&42 as *const i32); + //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~| NOTE: the trait `Send` is not implemented for `*const i32` + //~| NOTE: the trait `Sync` is not implemented for `*const i32` } diff --git a/tests/ui/arc_with_non_send_sync.stderr b/tests/ui/arc_with_non_send_sync.stderr index 7633b38dfb5..de3f2fb9e16 100644 --- a/tests/ui/arc_with_non_send_sync.stderr +++ b/tests/ui/arc_with_non_send_sync.stderr @@ -1,5 +1,5 @@ error: usage of an `Arc` that is not `Send` or `Sync` - --> $DIR/arc_with_non_send_sync.rs:18:13 + --> $DIR/arc_with_non_send_sync.rs:35:13 | LL | let _ = Arc::new(RefCell::new(42)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | let _ = Arc::new(RefCell::new(42)); = note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings` error: usage of an `Arc` that is not `Send` or `Sync` - --> $DIR/arc_with_non_send_sync.rs:21:13 + --> $DIR/arc_with_non_send_sync.rs:40:13 | LL | let _ = Arc::new(mutex.lock().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | let _ = Arc::new(mutex.lock().unwrap()); = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` error: usage of an `Arc` that is not `Send` or `Sync` - --> $DIR/arc_with_non_send_sync.rs:23:13 + --> $DIR/arc_with_non_send_sync.rs:44:13 | LL | let _ = Arc::new(&42 as *const i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 0404b6b6c29b9b64827a2b08cd9b55341c8b2c7a Mon Sep 17 00:00:00 2001 From: blyxyas Date: Tue, 25 Jul 2023 20:22:41 +0000 Subject: [PATCH 75/90] Add `sym::iter_mut` + `sym::as_mut_ptr` --- clippy_lints/src/methods/bytecount.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index fef90f6eba4..f490a717554 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = filter_recv.kind { let p = path.ident.name; - if p == sym::iter || p == sym!(iter_mut) { + if p == sym::iter || p == sym::iter_mut { receiver } else { filter_recv From 01b9f9d9dae466511e1eac2ca26fac5f325fe566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 25 Jul 2023 20:34:18 +0200 Subject: [PATCH 76/90] ci: test that we fail CI if we don't find clippy panicking --- .github/workflows/clippy_bors.yml | 1 + tests/integration.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a686198f781..5c69714bc1e 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -217,6 +217,7 @@ jobs: - 'rust-itertools/itertools' - 'rust-lang-nursery/failure' - 'rust-lang/log' + - 'matthiaskrgr/clippy_ci_panic_test' runs-on: ubuntu-latest diff --git a/tests/integration.rs b/tests/integration.rs index e72891c44c2..031982edbe9 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -69,6 +69,26 @@ fn integration_test() { // debug: eprintln!("{stderr}"); + // this is an internal test to make sure we would correctly panic on a delay_span_bug + if repo_name == "matthiaskrgr/clippy_ci_panic_test" { + // we need to kind of switch around our logic here: + // if we find a panic, everything is fine, if we don't panic, SOMETHING is broken about our testing + + // the repo basically just contains a delay_span_bug that forces rustc/clippy to panic: + /* + #![feature(rustc_attrs)] + #[rustc_error(delay_span_bug_from_inside_query)] + fn main() {} + */ + + if stderr.find("error: internal compiler error").is_some() { + eprintln!("we saw that we intentionally panicked, yay"); + return; + } + + panic!("panic caused by delay_span_bug was NOT detected! Something is broken!"); + } + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { static BACKTRACE_END_MSG: &str = "end of query stack"; let backtrace_end = stderr[backtrace_start..] From 4e1db44404bbd11f3644c0402e5b1f5358c1b532 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sun, 7 May 2023 00:08:46 +0200 Subject: [PATCH 77/90] Now `option_env_unwrap` warns even if a variable isn't set at compile time. --- clippy_lints/src/option_env_unwrap.rs | 36 ++++++++++++++------------- tests/ui/option_env_unwrap.rs | 1 + tests/ui/option_env_unwrap.stderr | 18 ++++++++++---- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index 377bddeaa5f..0885bb40cb8 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_direct_expn_of; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -36,21 +35,24 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if_chain! { - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap); - if let ExprKind::Call(caller, _) = &receiver.kind; - if is_direct_expn_of(caller.span, "option_env").is_some(); - then { - span_lint_and_help( - cx, - OPTION_ENV_UNWRAP, - expr.span, - "this will panic at run-time if the environment variable doesn't exist at compile-time", - None, - "consider using the `env!` macro instead" - ); - } + fn lint(cx: &EarlyContext<'_>, span: Span) { + span_lint_and_help( + cx, + OPTION_ENV_UNWRAP, + span, + "this will panic at run-time if the environment variable doesn't exist at compile-time", + None, + "consider using the `env!` macro instead", + ); } + + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind && + matches!(seg.ident.name, sym::expect | sym::unwrap) { + if let ExprKind::Call(caller, _) = &receiver.kind && is_direct_expn_of(caller.span, "option_env").is_some() { + lint(cx, expr.span); + } else if let ExprKind::Path(_, caller) = &receiver.kind && is_direct_expn_of(caller.span, "option_env").is_some() { + lint(cx, expr.span); + } + } } } diff --git a/tests/ui/option_env_unwrap.rs b/tests/ui/option_env_unwrap.rs index ee1fe3f1fc0..f8d382340f2 100644 --- a/tests/ui/option_env_unwrap.rs +++ b/tests/ui/option_env_unwrap.rs @@ -9,6 +9,7 @@ use proc_macros::{external, inline_macros}; fn main() { let _ = option_env!("PATH").unwrap(); let _ = option_env!("PATH").expect("environment variable PATH isn't set"); + let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your environment. let _ = inline!(option_env!($"PATH").unwrap()); let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set")); let _ = external!(option_env!($"PATH").unwrap()); diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr index 7bba62686ee..7698b4b0216 100644 --- a/tests/ui/option_env_unwrap.stderr +++ b/tests/ui/option_env_unwrap.stderr @@ -16,7 +16,15 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set = help: consider using the `env!` macro instead error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:12:21 + --> $DIR/option_env_unwrap.rs:12:13 + | +LL | let _ = option_env!("Y").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your environment. + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:13:21 | LL | let _ = inline!(option_env!($"PATH").unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +33,7 @@ LL | let _ = inline!(option_env!($"PATH").unwrap()); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:13:21 + --> $DIR/option_env_unwrap.rs:14:21 | LL | let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +42,7 @@ LL | let _ = inline!(option_env!($"PATH").expect($"environment variable PATH = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:14:13 + --> $DIR/option_env_unwrap.rs:15:13 | LL | let _ = external!(option_env!($"PATH").unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +51,7 @@ LL | let _ = external!(option_env!($"PATH").unwrap()); = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:15:13 + --> $DIR/option_env_unwrap.rs:16:13 | LL | let _ = external!(option_env!($"PATH").expect($"environment variable PATH isn't set")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,5 +59,5 @@ LL | let _ = external!(option_env!($"PATH").expect($"environment variable PA = help: consider using the `env!` macro instead = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From 6ca6be6f2bfe063def8900a42c2f734be796a8c9 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 3 Jul 2023 13:40:14 -0400 Subject: [PATCH 78/90] Unite bless environment variables under `RUSTC_BLESS` Currently, Clippy, Miri, Rustfmt, and rustc all use an environment variable to indicate that output should be blessed, but they use different variable names. In order to improve consistency, this patch applies the following changes: - Emit `RUSTC_BLESS` within `prepare_cargo_test` so it is always available - Change usage of `MIRI_BLESS` in the Miri subtree to use `RUSTC_BLESS` - Change usage of `BLESS` in the Clippy subtree to `RUSTC_BLESS` - Change usage of `BLESS` in the Rustfmt subtree to `RUSTC_BLESS` - Adjust the blessable test in `rustc_errors` to use this same convention - Update documentation where applicable Any tools that uses `RUSTC_BLESS` should check that it is set to any value other than `"0"`. --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index d70c4ea34cb..f714b296233 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -115,7 +115,9 @@ fn base_config(test_dir: &str) -> compiletest::Config { mode: TestMode::Yolo, stderr_filters: vec![], stdout_filters: vec![], - output_conflict_handling: if var_os("BLESS").is_some() || env::args().any(|arg| arg == "--bless") { + // FIXME(tgross35): deduplicate bless env once clippy can update + output_conflict_handling: if var_os("RUSTC_BLESS").is_some_and(|v| v != "0") + || env::args().any(|arg| arg == "--bless") { compiletest::OutputConflictHandling::Bless } else { compiletest::OutputConflictHandling::Error("cargo test -- -- --bless".into()) From 3bfccacca97cb7d4d7751fc90b89b3381b8b8b35 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Wed, 26 Jul 2023 23:16:24 +0200 Subject: [PATCH 79/90] Add comments + Very minor Refactor --- clippy_lints/src/option_env_unwrap.rs | 7 +++++-- tests/ui/option_env_unwrap.stderr | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index 0885bb40cb8..9c7f7e1cd7f 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -48,9 +48,12 @@ impl EarlyLintPass for OptionEnvUnwrap { if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind && matches!(seg.ident.name, sym::expect | sym::unwrap) { - if let ExprKind::Call(caller, _) = &receiver.kind && is_direct_expn_of(caller.span, "option_env").is_some() { + if let ExprKind::Call(caller, _) = &receiver.kind && + // If it exists, it will be ::core::option::Option::Some("").unwrap() (A method call in the HIR) + is_direct_expn_of(caller.span, "option_env").is_some() { lint(cx, expr.span); - } else if let ExprKind::Path(_, caller) = &receiver.kind && is_direct_expn_of(caller.span, "option_env").is_some() { + } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR) + is_direct_expn_of(caller.span, "option_env").is_some() { lint(cx, expr.span); } } diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr index 7698b4b0216..cfa9dd58a30 100644 --- a/tests/ui/option_env_unwrap.stderr +++ b/tests/ui/option_env_unwrap.stderr @@ -18,8 +18,8 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set error: this will panic at run-time if the environment variable doesn't exist at compile-time --> $DIR/option_env_unwrap.rs:12:13 | -LL | let _ = option_env!("Y").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your environment. - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your env... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead From 136339f2d307cb819b61573c37e4bb5d155a6d9c Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 22 Jul 2023 02:17:51 +0200 Subject: [PATCH 80/90] new lint: [`readonly_write_lock`] --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 36 +++++++++++++ .../src/methods/readonly_write_lock.rs | 51 +++++++++++++++++++ tests/ui/readonly_write_lock.rs | 42 +++++++++++++++ tests/ui/readonly_write_lock.stderr | 16 ++++++ 6 files changed, 147 insertions(+) create mode 100644 clippy_lints/src/methods/readonly_write_lock.rs create mode 100644 tests/ui/readonly_write_lock.rs create mode 100644 tests/ui/readonly_write_lock.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 251f32d2e6e..2655d93599e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5181,6 +5181,7 @@ Released 2018-09-13 [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex [`read_line_without_trim`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_line_without_trim [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec +[`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e084471433d..d4e0d286334 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -398,6 +398,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, + crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e774450018e..0e02c6d8e1b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -76,6 +76,7 @@ mod or_then_unwrap; mod path_buf_push_overwrite; mod range_zip_with_len; mod read_line_without_trim; +mod readonly_write_lock; mod repeat_once; mod search_is_some; mod seek_from_current; @@ -3507,6 +3508,37 @@ declare_clippy_lint! { "checks for usage of `bool::then` in `Iterator::filter_map`" } +declare_clippy_lint! { + /// ### What it does + /// Looks for calls to `RwLock::write` where the lock is only used for reading. + /// + /// ### Why is this bad? + /// The write portion of `RwLock` is exclusive, meaning that no other thread + /// can access the lock while this writer is active. + /// + /// ### Example + /// ```rust + /// use std::sync::RwLock; + /// fn assert_is_zero(lock: &RwLock) { + /// let num = lock.write().unwrap(); + /// assert_eq!(*num, 0); + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// use std::sync::RwLock; + /// fn assert_is_zero(lock: &RwLock) { + /// let num = lock.read().unwrap(); + /// assert_eq!(*num, 0); + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub READONLY_WRITE_LOCK, + nursery, + "acquiring a writer lock when a reader lock would work" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3645,6 +3677,7 @@ impl_lint_pass!(Methods => [ STRING_LIT_CHARS_ANY, ITER_SKIP_ZERO, FILTER_MAP_BOOL_THEN, + READONLY_WRITE_LOCK ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4188,6 +4221,9 @@ impl Methods { range_zip_with_len::check(cx, expr, iter_recv, arg); } }, + ("write", []) => { + readonly_write_lock::check(cx, expr, recv); + } _ => {}, } } diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs new file mode 100644 index 00000000000..404ded2b013 --- /dev/null +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -0,0 +1,51 @@ +use super::READONLY_WRITE_LOCK; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::mir::{enclosing_mir, visit_local_usage}; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node}; +use rustc_lint::LateContext; +use rustc_middle::mir::{Location, START_BLOCK}; +use rustc_span::sym; + +fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::MethodCall(path, receiver, ..) = expr.kind + && path.ident.name == sym::unwrap + { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) + } else { + false + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) + && let Node::Expr(unwrap_call_expr) = cx.tcx.hir().get_parent(expr.hir_id) + && is_unwrap_call(cx, unwrap_call_expr) + && let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id) + && let Node::Local(local) = parent + && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) + && let Some((local, _)) = mir.local_decls.iter_enumerated().find(|(_, decl)| { + local.span.contains(decl.source_info.span) + }) + && let Some(usage) = visit_local_usage(&[local], mir, Location { + block: START_BLOCK, + statement_index: 0, + }) + { + let writer_never_mutated = usage[0].local_consume_or_mutate_locs.is_empty(); + + if writer_never_mutated { + span_lint_and_sugg( + cx, + READONLY_WRITE_LOCK, + expr.span, + "this write lock is used only for reading", + "consider using a read lock instead", + format!("{}.read()", snippet(cx, receiver.span, "")), + Applicability::MaybeIncorrect // write lock might be intentional for enforcing exclusiveness + ); + } + } +} diff --git a/tests/ui/readonly_write_lock.rs b/tests/ui/readonly_write_lock.rs new file mode 100644 index 00000000000..656b45787e8 --- /dev/null +++ b/tests/ui/readonly_write_lock.rs @@ -0,0 +1,42 @@ +#![warn(clippy::readonly_write_lock)] + +use std::sync::RwLock; + +fn mutate_i32(x: &mut i32) { + *x += 1; +} + +fn accept_i32(_: i32) {} + +fn main() { + let lock = RwLock::new(42); + let lock2 = RwLock::new(1234); + + { + let writer = lock.write().unwrap(); + dbg!(&writer); + } + + { + let writer = lock.write().unwrap(); + accept_i32(*writer); + } + + { + let mut writer = lock.write().unwrap(); + mutate_i32(&mut writer); + dbg!(&writer); + } + + { + let mut writer = lock.write().unwrap(); + *writer += 1; + } + + { + let mut writer1 = lock.write().unwrap(); + let mut writer2 = lock2.write().unwrap(); + *writer2 += 1; + *writer1 = *writer2; + } +} diff --git a/tests/ui/readonly_write_lock.stderr b/tests/ui/readonly_write_lock.stderr new file mode 100644 index 00000000000..e3d8fce7b2c --- /dev/null +++ b/tests/ui/readonly_write_lock.stderr @@ -0,0 +1,16 @@ +error: this write lock is used only for reading + --> $DIR/readonly_write_lock.rs:16:22 + | +LL | let writer = lock.write().unwrap(); + | ^^^^^^^^^^^^ help: consider using a read lock instead: `lock.read()` + | + = note: `-D clippy::readonly-write-lock` implied by `-D warnings` + +error: this write lock is used only for reading + --> $DIR/readonly_write_lock.rs:21:22 + | +LL | let writer = lock.write().unwrap(); + | ^^^^^^^^^^^^ help: consider using a read lock instead: `lock.read()` + +error: aborting due to 2 previous errors + From 205a3741c1659b5157a5dfdaec2476c8a151ada2 Mon Sep 17 00:00:00 2001 From: Timo <30553356+y21@users.noreply.github.com> Date: Thu, 27 Jul 2023 20:37:15 +0200 Subject: [PATCH 81/90] make lint description more consistent (writer/reader -> write/read) Co-authored-by: Takayuki Nakata --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0e02c6d8e1b..bca2039860d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3536,7 +3536,7 @@ declare_clippy_lint! { #[clippy::version = "1.73.0"] pub READONLY_WRITE_LOCK, nursery, - "acquiring a writer lock when a reader lock would work" + "acquiring a write lock when a read lock would work" } pub struct Methods { From 5e88003ddac563a8ffc912d6e69332d479a92cc3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 28 Jul 2023 12:52:06 +0200 Subject: [PATCH 82/90] pattern match on local usage slice to avoid ICE --- clippy_lints/src/methods/readonly_write_lock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 404ded2b013..e3ec921da0c 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -29,12 +29,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver && let Some((local, _)) = mir.local_decls.iter_enumerated().find(|(_, decl)| { local.span.contains(decl.source_info.span) }) - && let Some(usage) = visit_local_usage(&[local], mir, Location { + && let Some(usages) = visit_local_usage(&[local], mir, Location { block: START_BLOCK, statement_index: 0, }) + && let [usage] = usages.as_slice() { - let writer_never_mutated = usage[0].local_consume_or_mutate_locs.is_empty(); + let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty(); if writer_never_mutated { span_lint_and_sugg( From 65c5afd196ea3f298824c3762ac040959490fa22 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 28 Jul 2023 23:45:09 +0200 Subject: [PATCH 83/90] Bump nightly version -> 2023-07-28 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index b658101a10d..833608faff0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-07-14" +channel = "nightly-2023-07-28" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 71cc39e1f2f4cd7e8cd6fa09c65ecab06ce9cc80 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 Jul 2023 00:00:19 -0400 Subject: [PATCH 84/90] Add debug assertions to `implements_trait` Improve debug assertions for `make_projection` --- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/incorrect_impls.rs | 8 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 10 +- clippy_utils/src/ty.rs | 184 +++++++++++-------- 6 files changed, 117 insertions(+), 93 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index c343f248d06..ad7b3b27383 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -471,12 +471,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); // If all of our fields implement `Eq`, we can implement `Eq` too if adt .all_fields() .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, [])); + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs index bce010bb168..f4edb17ae80 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/incorrect_impls.rs @@ -189,12 +189,8 @@ impl LateLintPass<'_> for IncorrectImpls { .diagnostic_items(trait_impl.def_id.krate) .name_to_id .get(&sym::Ord) - && implements_trait( - cx, - hir_ty_to_ty(cx.tcx, imp.self_ty), - *ord_def_id, - trait_impl.args, - ) + && trait_impl.self_ty() == trait_impl.args.type_at(1) + && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[]) { // If the `cmp` call likely needs to be fully qualified in the suggestion // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index a84a0a6eeb8..7b8c88235a9 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -109,7 +109,7 @@ fn is_ref_iterable<'tcx>( && let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder()) && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output && let param_env = cx.tcx.param_env(fn_id) - && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, []) + && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[]) && let Some(into_iter_ty) = make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty]) && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 23039f2f3f1..8b2f57160af 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -57,7 +57,7 @@ pub(super) fn check<'tcx>( cx.tcx .get_diagnostic_item(sym::Default) .map_or(false, |default_trait_id| { - implements_trait(cx, output_ty, default_trait_id, args) + implements_trait(cx, output_ty, default_trait_id, &[]) }) } else { false diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 5e26601537f..5ee26966fa7 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item, + implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; @@ -182,7 +182,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !ty.is_mutable_ptr(); if !is_copy(cx, ty); if ty.is_sized(cx.tcx, cx.param_env); - if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None])); + if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter( + cx.tcx, + cx.param_env, + ty, + t, + [Option::>::None], + )); if !implements_borrow_trait; if !all_borrowable_trait; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 44ec8813f19..dbb6b986952 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -3,6 +3,7 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; +use itertools::Itertools; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -13,17 +14,19 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, - List, ParamEnv, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - UintTy, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; -use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -207,15 +210,9 @@ pub fn implements_trait<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, trait_id: DefId, - ty_params: &[GenericArg<'tcx>], + args: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env( - cx.tcx, - cx.param_env, - ty, - trait_id, - ty_params.iter().map(|&arg| Some(arg)), - ) + implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, args.iter().map(|&x| Some(x))) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -224,7 +221,18 @@ pub fn implements_trait_with_env<'tcx>( param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, - ty_params: impl IntoIterator>>, + args: &[GenericArg<'tcx>], +) -> bool { + implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, args.iter().map(|&x| Some(x))) +} + +/// Same as `implements_trait_from_env` but takes the arguments as an iterator. +pub fn implements_trait_with_env_from_iter<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + args: impl IntoIterator>>>, ) -> bool { // Clippy shouldn't have infer types assert!(!ty.has_infer()); @@ -233,19 +241,37 @@ pub fn implements_trait_with_env<'tcx>( if ty.has_escaping_bound_vars() { return false; } + let infcx = tcx.infer_ctxt().build(); - let orig = TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }; - let ty_params = tcx.mk_args_from_iter( - ty_params + let trait_ref = TraitRef::new( + tcx, + trait_id, + Some(GenericArg::from(ty)) .into_iter() - .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())), + .chain(args.into_iter().map(|arg| { + arg.into().unwrap_or_else(|| { + let orig = TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }; + infcx.next_ty_var(orig).into() + }) + })), ); + + debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait); + #[cfg(debug_assertions)] + assert_generic_args_match(tcx, trait_id, trait_ref.args); + + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), + }; infcx - .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env) - .must_apply_modulo_regions() + .evaluate_obligation(&obligation) + .is_ok_and(EvaluationResult::must_apply_modulo_regions) } /// Checks whether this type implements `Drop`. @@ -1014,12 +1040,60 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { } } +/// Asserts that the given arguments match the generic parameters of the given item. +#[allow(dead_code)] +fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { + let g = tcx.generics_of(did); + let parent = g.parent.map(|did| tcx.generics_of(did)); + let count = g.parent_count + g.params.len(); + let params = parent + .map_or([].as_slice(), |p| p.params.as_slice()) + .iter() + .chain(&g.params) + .map(|x| &x.kind); + + assert!( + count == args.len(), + "wrong number of arguments for `{did:?}`: expected `{count}`, found {}\n\ + note: the expected arguments are: `[{}]`\n\ + the given arguments are: `{args:#?}`", + args.len(), + params.clone().map(GenericParamDefKind::descr).format(", "), + ); + + if let Some((idx, (param, arg))) = + params + .clone() + .zip(args.iter().map(|&x| x.unpack())) + .enumerate() + .find(|(_, (param, arg))| match (param, arg) { + (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false, + ( + GenericParamDefKind::Lifetime + | GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. }, + _, + ) => true, + }) + { + panic!( + "incorrect argument for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\ + note: the expected arguments are `[{}]`\n\ + the given arguments are `{args:#?}`", + param.descr(), + params.clone().map(GenericParamDefKind::descr).format(", "), + ); + } +} + /// Makes the projection type for the named associated type in the given impl or trait impl. /// /// This function is for associated types which are "known" to exist, and as such, will only return /// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions /// enabled this will check that the named associated type exists, the correct number of -/// substitutions are given, and that the correct kinds of substitutions are given (lifetime, +/// arguments are given, and that the correct kinds of arguments are given (lifetime, /// constant or type). This will not check if type normalization would succeed. pub fn make_projection<'tcx>( tcx: TyCtxt<'tcx>, @@ -1043,49 +1117,7 @@ pub fn make_projection<'tcx>( return None; }; #[cfg(debug_assertions)] - { - let generics = tcx.generics_of(assoc_item.def_id); - let generic_count = generics.parent_count + generics.params.len(); - let params = generics - .parent - .map_or([].as_slice(), |id| &*tcx.generics_of(id).params) - .iter() - .chain(&generics.params) - .map(|x| &x.kind); - - debug_assert!( - generic_count == args.len(), - "wrong number of args for `{:?}`: found `{}` expected `{generic_count}`.\n\ - note: the expected parameters are: {:#?}\n\ - the given arguments are: `{args:#?}`", - assoc_item.def_id, - args.len(), - params.map(ty::GenericParamDefKind::descr).collect::>(), - ); - - if let Some((idx, (param, arg))) = params - .clone() - .zip(args.iter().map(GenericArg::unpack)) - .enumerate() - .find(|(_, (param, arg))| { - !matches!( - (param, arg), - (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) - | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) - | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) - ) - }) - { - debug_assert!( - false, - "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\ - note: the expected parameters are {:#?}\n\ - the given arguments are {args:#?}", - param.descr(), - params.map(ty::GenericParamDefKind::descr).collect::>() - ); - } - } + assert_generic_args_match(tcx, assoc_item.def_id, args); Some(tcx.mk_alias_ty(assoc_item.def_id, args)) } @@ -1100,7 +1132,7 @@ pub fn make_projection<'tcx>( /// Normalizes the named associated type in the given impl or trait impl. /// /// This function is for associated types which are "known" to be valid with the given -/// substitutions, and as such, will only return `None` when debug assertions are disabled in order +/// arguments, and as such, will only return `None` when debug assertions are disabled in order /// to prevent ICE's. With debug assertions enabled this will check that type normalization /// succeeds as well as everything checked by `make_projection`. pub fn make_normalized_projection<'tcx>( @@ -1112,17 +1144,12 @@ pub fn make_normalized_projection<'tcx>( ) -> Option> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] - if let Some((i, subst)) = ty - .args - .iter() - .enumerate() - .find(|(_, subst)| subst.has_late_bound_regions()) - { + if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) { debug_assert!( false, "args contain late-bound region at index `{i}` which can't be normalized.\n\ use `TyCtxt::erase_late_bound_regions`\n\ - note: subst is `{subst:#?}`", + note: arg is `{arg:#?}`", ); return None; } @@ -1190,17 +1217,12 @@ pub fn make_normalized_projection_with_regions<'tcx>( ) -> Option> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] - if let Some((i, subst)) = ty - .args - .iter() - .enumerate() - .find(|(_, subst)| subst.has_late_bound_regions()) - { + if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) { debug_assert!( false, "args contain late-bound region at index `{i}` which can't be normalized.\n\ use `TyCtxt::erase_late_bound_regions`\n\ - note: subst is `{subst:#?}`", + note: arg is `{arg:#?}`", ); return None; } From 4d80a2ed2e1cd6df90f89d8f39bdfba06c8632fe Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 Jul 2023 01:11:08 -0400 Subject: [PATCH 85/90] Rework `redundant_closure` * Better track when a early-bound region appears when a late-bound region is required * Don't lint when the closure gives explicit types. --- clippy_lints/src/eta_reduction.rs | 348 +++++++++++++++++----------- clippy_lints/src/incorrect_impls.rs | 1 - clippy_utils/src/ty.rs | 5 + clippy_utils/src/usage.rs | 46 ++-- clippy_utils/src/visitors.rs | 10 + tests/ui/eta.fixed | 55 +++++ tests/ui/eta.rs | 55 +++++ tests/ui/eta.stderr | 8 +- 8 files changed, 371 insertions(+), 157 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 22e10accd35..255859da495 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,19 +1,22 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::usage::local_used_after_expr; +use clippy_utils::ty::type_diagnostic_name; +use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, TyKind, Unsafety}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{ + self, Binder, BoundConstness, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, + GenericArgsRef, ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults, +}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; declare_clippy_lint! { /// ### What it does @@ -72,14 +75,18 @@ declare_clippy_lint! { declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); impl<'tcx> LateLintPass<'tcx> for EtaReduction { + #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { + let body = if let ExprKind::Closure(c) = expr.kind + && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) + && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) + && !expr.span.from_expansion() + { + cx.tcx.hir().body(c.body) + } else { return; - } - let body = match expr.kind { - ExprKind::Closure(&Closure { body, .. }) => cx.tcx.hir().body(body), - _ => return, }; + if body.value.span.from_expansion() { if body.params.is_empty() { if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) { @@ -99,140 +106,217 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { return; } - let closure_ty = cx.typeck_results().expr_ty(expr); + let typeck = cx.typeck_results(); + let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { + closure_subs.as_closure() + } else { + return; + }; - if_chain!( - if !is_adjusted(cx, body.value); - if let ExprKind::Call(callee, args) = body.value.kind; - if let ExprKind::Path(_) = callee.kind; - if check_inputs(cx, body.params, None, args); - let callee_ty = cx.typeck_results().expr_ty_adjusted(callee); - let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id) - .map_or(callee_ty, |id| cx.tcx.type_of(id).instantiate_identity()); - if check_sig(cx, closure_ty, call_ty); - let args = cx.typeck_results().node_args(callee.hir_id); - // This fixes some false positives that I don't entirely understand - if args.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions(); - // A type param function ref like `T::f` is not 'static, however - // it is if cast like `T::f as fn()`. This seems like a rustc bug. - if !args.types().any(|t| matches!(t.kind(), ty::Param(_))); - let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); - if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); - if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); - if let ty::Closure(_, args) = *closure_ty.kind(); - // Don't lint if this is an inclusive range expression. - // They desugar to a call to `RangeInclusiveNew` which would have odd suggestions. (#10684) - if !matches!(higher::Range::hir(body.value), Some(higher::Range { - start: Some(_), - end: Some(_), - limits: rustc_ast::RangeLimits::Closed - })); - then { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() - && let args = cx.tcx.erase_late_bound_regions(args.as_closure().sig()).inputs() - && implements_trait( - cx, - callee_ty.peel_refs(), - fn_mut_id, - &args.iter().copied().map(Into::into).collect::>(), - ) - && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) + if is_adjusted(cx, body.value) { + return; + } + + match body.value.kind { + ExprKind::Call(callee, args) + if matches!(callee.kind, ExprKind::Path(..)) => + { + if matches!(higher::Range::hir(body.value), Some(higher::Range { + start: Some(_), + end: Some(_), + limits: rustc_ast::RangeLimits::Closed + })) { + return; + } + + let callee_ty = typeck.expr_ty(callee).peel_refs(); + if matches!( + type_diagnostic_name(cx, callee_ty), + Some(sym::Arc | sym::Rc) + ) || !check_inputs(typeck, body.params, None, args) { + return; + } + let callee_ty_adjusted = typeck.expr_adjustments(callee).last().map_or( + callee_ty, + |a| a.target.peel_refs(), + ); + + let sig = match callee_ty_adjusted.kind() { + ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(), + ty::FnPtr(sig) => sig.skip_binder(), + ty::Closure(_, subs) => cx + .tcx + .signature_unclosure(subs.as_closure().sig(), Unsafety::Normal) + .skip_binder(), + _ => { + if typeck.type_dependent_def_id(body.value.hir_id).is_some() + && let subs = typeck.node_args(body.value.hir_id) + && let output = typeck.expr_ty(body.value) + && let ty::Tuple(tys) = *subs.type_at(1).kind() { - // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {snippet}"); + cx.tcx.mk_fn_sig(tys, output, false, Unsafety::Normal, Abi::Rust) + } else { + return; } - - diag.span_suggestion( - expr.span, - "replace the closure with the function itself", - snippet, - Applicability::MachineApplicable, - ); - } - }); - } - ); - - if_chain!( - if !is_adjusted(cx, body.value); - if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind; - if check_inputs(cx, body.params, Some(receiver), args); - let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); - let args = cx.typeck_results().node_args(body.value.hir_id); - let call_ty = cx.tcx.type_of(method_def_id).instantiate(cx.tcx, args); - if check_sig(cx, closure_ty, call_ty); - then { - span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { - let name = get_ufcs_type_name(cx, method_def_id, args); - diag.span_suggestion( + }, + }; + if check_sig(cx, closure, sig) + && let generic_args = typeck.node_args(callee.hir_id) + // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not + // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result + // in a type which is `'static`. + // For now ignore all callee types which reference a type parameter. + && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE, expr.span, - "replace the closure with the method itself", - format!("{name}::{}", path.ident.name), - Applicability::MachineApplicable, + "redundant closure", + |diag| { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { + if let Ok((ClosureKind::FnMut, _)) + = cx.tcx.infer_ctxt().build().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + BoundConstness::NotConst, + ImplPolarity::Positive, + ) && path_to_local(callee) + .map_or( + false, + |l| local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr), + ) + { + // Mutable closure is used after current expr; we cannot consume it. + snippet = format!("&mut {snippet}"); + } + diag.span_suggestion( + expr.span, + "replace the closure with the function itself", + snippet, + Applicability::MachineApplicable, + ); + } + } ); - }) - } - ); + } + }, + ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { + if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) + && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) + { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.span, + "redundant closure", + |diag| { + let args = typeck.node_args(body.value.hir_id); + let name = get_ufcs_type_name(cx, method_def_id, args); + diag.span_suggestion( + expr.span, + "replace the closure with the method itself", + format!("{}::{}", name, path.ident.name), + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => (), + } } } fn check_inputs( - cx: &LateContext<'_>, + typeck: &TypeckResults<'_>, params: &[Param<'_>], - receiver: Option<&Expr<'_>>, - call_args: &[Expr<'_>], + self_arg: Option<&Expr<'_>>, + args: &[Expr<'_>], ) -> bool { - if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) { - return false; - } - let binding_modes = cx.typeck_results().pat_binding_modes(); - let check_inputs = |param: &Param<'_>, arg| { - match param.pat.kind { - PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {}, - _ => return false, - } - // checks that parameters are not bound as `ref` or `ref mut` - if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) { - return false; - } - - match *cx.typeck_results().expr_adjustments(arg) { - [] => true, - [ - Adjustment { - kind: Adjust::Deref(None), - .. - }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)), - .. - }, - ] => { - // re-borrow with the same mutability is allowed - let ty = cx.typeck_results().expr_ty(arg); - matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into()) - }, - _ => false, - } - }; - std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg)) + params.len() == self_arg.map_or(0, |_| 1) + args.len() + && params.iter().zip(self_arg.into_iter().chain(args)).all(|(p, arg)| { + matches!( + p.pat.kind,PatKind::Binding(BindingAnnotation::NONE, id, _, None) + if path_to_local_id(arg, id) + ) + // Only allow adjustments which change regions (i.e. re-borrowing). + && typeck + .expr_adjustments(arg) + .last() + .map_or(true, |a| a.target == typeck.expr_ty(arg)) + }) } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool { - let call_sig = call_ty.fn_sig(cx.tcx); - if call_sig.unsafety() == Unsafety::Unsafe { - return false; +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig: FnSig<'_>) -> bool { + call_sig.unsafety == Unsafety::Normal + && !has_late_bound_to_non_late_bound_regions( + cx.tcx + .signature_unclosure(closure.sig(), Unsafety::Normal) + .skip_binder(), + call_sig, + ) +} + +/// This walks through both signatures and checks for any time a late-bound region is expected by an +/// `impl Fn` type, but the target signature does not have a late-bound region in the same position. +/// +/// This is needed because rustc is unable to late bind early-bound regions in a function signature. +fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool { + fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool { + matches!(from_region.kind(), RegionKind::ReLateBound(..)) + && !matches!(to_region.kind(), RegionKind::ReLateBound(..)) } - if !closure_ty.has_late_bound_regions() { - return true; + + fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { + if from_subs.len() != to_subs.len() { + return true; + } + for (from_arg, to_arg) in to_subs.iter().zip(from_subs) { + match (from_arg.unpack(), to_arg.unpack()) { + (GenericArgKind::Lifetime(from_region), GenericArgKind::Lifetime(to_region)) => { + if check_region(from_region, to_region) { + return true; + } + }, + (GenericArgKind::Type(from_ty), GenericArgKind::Type(to_ty)) => { + if check_ty(from_ty, to_ty) { + return true; + } + }, + (GenericArgKind::Const(_), GenericArgKind::Const(_)) => (), + _ => return true, + } + } + false } - let ty::Closure(_, args) = closure_ty.kind() else { - return false; - }; - let closure_sig = cx.tcx.signature_unclosure(args.as_closure().sig(), Unsafety::Normal); - cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) + + fn check_ty(from_ty: Ty<'_>, to_ty: Ty<'_>) -> bool { + match (from_ty.kind(), to_ty.kind()) { + (&ty::Adt(_, from_subs), &ty::Adt(_, to_subs)) => check_subs(from_subs, to_subs), + (&ty::Array(from_ty, _), &ty::Array(to_ty, _)) | (&ty::Slice(from_ty), &ty::Slice(to_ty)) => { + check_ty(from_ty, to_ty) + }, + (&ty::Ref(from_region, from_ty, _), &ty::Ref(to_region, to_ty, _)) => { + check_region(from_region, to_region) || check_ty(from_ty, to_ty) + }, + (&ty::Tuple(from_tys), &ty::Tuple(to_tys)) => { + from_tys.len() != to_tys.len() + || from_tys + .iter() + .zip(to_tys) + .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) + }, + _ => from_ty.has_late_bound_regions(), + } + } + + assert!(from_sig.inputs_and_output.len() == to_sig.inputs_and_output.len()); + from_sig + .inputs_and_output + .iter() + .zip(to_sig.inputs_and_output) + .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) } fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: GenericArgsRef<'tcx>) -> String { @@ -241,7 +325,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: match assoc_item.container { ty::TraitContainer => cx.tcx.def_path_str(def_id), ty::ImplContainer => { - let ty = cx.tcx.type_of(def_id).skip_binder(); + let ty = cx.tcx.type_of(def_id).instantiate_identity(); match ty.kind() { ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()), ty::Array(..) diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs index f4edb17ae80..c19a46f4e97 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/incorrect_impls.rs @@ -189,7 +189,6 @@ impl LateLintPass<'_> for IncorrectImpls { .diagnostic_items(trait_impl.def_id.krate) .name_to_id .get(&sym::Ord) - && trait_impl.self_ty() == trait_impl.args.type_at(1) && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[]) { // If the `cmp` call likely needs to be fully qualified in the suggestion diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index dbb6b986952..d0e15aa8bb3 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -419,6 +419,11 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangI } } +/// Gets the diagnostic name of the type, if it has one +pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did())) +} + /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 8a7ea1e6cad..39ef76348d7 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,15 +1,15 @@ -use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; +use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend, Visitable}; +use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Node}; +use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; -use {crate as utils, rustc_hir as hir}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -154,6 +154,17 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { .is_some() } +pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { + for_each_expr_with_closures(cx, v, |e| { + if utils::path_to_local_id(e, local_id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false; @@ -166,32 +177,21 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr // let closure = || local; // closure(); // closure(); - let in_loop_or_closure = cx - .tcx - .hir() - .parent_iter(after.hir_id) - .take_while(|&(id, _)| id != block.hir_id) - .any(|(_, node)| { - matches!( - node, - Node::Expr(Expr { - kind: ExprKind::Loop(..) | ExprKind::Closure { .. }, - .. - }) - ) - }); - if in_loop_or_closure { - return true; - } + let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; for_each_expr_with_closures(cx, block, |e| { - if e.hir_id == after.hir_id { + if past_expr { + if utils::path_to_local_id(e, local_id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) + } + } else if e.hir_id == after.hir_id { past_expr = true; ControlFlow::Continue(Descend::No) - } else if past_expr && utils::path_to_local_id(e, local_id) { - ControlFlow::Break(()) } else { + past_expr = Some(e.hir_id) == loop_start; ControlFlow::Continue(Descend::Yes) } }) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 8dafa723afa..09f447b27eb 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -52,6 +52,16 @@ pub trait Visitable<'tcx> { /// Calls the corresponding `visit_*` function on the visitor. fn visit>(self, visitor: &mut V); } +impl<'tcx, T> Visitable<'tcx> for &'tcx [T] +where + &'tcx T: Visitable<'tcx>, +{ + fn visit>(self, visitor: &mut V) { + for x in self { + x.visit(visitor); + } + } +} macro_rules! visitable_ref { ($t:ident, $f:ident) => { impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index db7bd99e0ae..ddabe7616d0 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -345,3 +345,58 @@ fn angle_brackets_and_args() { let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); dyn_opt.map(::method_on_dyn); } + +fn _late_bound_to_early_bound_regions() { + struct Foo<'a>(&'a u32); + impl<'a> Foo<'a> { + fn f(x: &'a u32) -> Self { + Foo(x) + } + } + fn f(f: impl for<'a> Fn(&'a u32) -> Foo<'a>) -> Foo<'static> { + f(&0) + } + + let _ = f(|x| Foo::f(x)); + + struct Bar; + impl<'a> From<&'a u32> for Bar { + fn from(x: &'a u32) -> Bar { + Bar + } + } + fn f2(f: impl for<'a> Fn(&'a u32) -> Bar) -> Bar { + f(&0) + } + + let _ = f2(|x| ::from(x)); + + struct Baz<'a>(&'a u32); + fn f3(f: impl Fn(&u32) -> Baz<'_>) -> Baz<'static> { + f(&0) + } + + let _ = f3(|x| Baz(x)); +} + +fn _mixed_late_bound_and_early_bound_regions() { + fn f(t: T, f: impl Fn(T, &u32) -> u32) -> u32 { + f(t, &0) + } + fn f2<'a, T: 'a>(_: &'a T, y: &u32) -> u32 { + *y + } + let _ = f(&0, f2); +} + +fn _closure_with_types() { + fn f(x: T) -> T { + x + } + fn f2(f: impl Fn(T) -> T) -> T { + f(T::default()) + } + + let _ = f2(|x: u32| f(x)); + let _ = f2(|x| -> u32 { f(x) }); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 52fc17686fd..92ecff6eb1a 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -345,3 +345,58 @@ fn angle_brackets_and_args() { let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); dyn_opt.map(|d| d.method_on_dyn()); } + +fn _late_bound_to_early_bound_regions() { + struct Foo<'a>(&'a u32); + impl<'a> Foo<'a> { + fn f(x: &'a u32) -> Self { + Foo(x) + } + } + fn f(f: impl for<'a> Fn(&'a u32) -> Foo<'a>) -> Foo<'static> { + f(&0) + } + + let _ = f(|x| Foo::f(x)); + + struct Bar; + impl<'a> From<&'a u32> for Bar { + fn from(x: &'a u32) -> Bar { + Bar + } + } + fn f2(f: impl for<'a> Fn(&'a u32) -> Bar) -> Bar { + f(&0) + } + + let _ = f2(|x| ::from(x)); + + struct Baz<'a>(&'a u32); + fn f3(f: impl Fn(&u32) -> Baz<'_>) -> Baz<'static> { + f(&0) + } + + let _ = f3(|x| Baz(x)); +} + +fn _mixed_late_bound_and_early_bound_regions() { + fn f(t: T, f: impl Fn(T, &u32) -> u32) -> u32 { + f(t, &0) + } + fn f2<'a, T: 'a>(_: &'a T, y: &u32) -> u32 { + *y + } + let _ = f(&0, |x, y| f2(x, y)); +} + +fn _closure_with_types() { + fn f(x: T) -> T { + x + } + fn f2(f: impl Fn(T) -> T) -> T { + f(T::default()) + } + + let _ = f2(|x: u32| f(x)); + let _ = f2(|x| -> u32 { f(x) }); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 0ac0b901df4..ff40a2074e5 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -158,5 +158,11 @@ error: redundant closure LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` -error: aborting due to 26 previous errors +error: redundant closure + --> $DIR/eta.rs:389:19 + | +LL | let _ = f(&0, |x, y| f2(x, y)); + | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` + +error: aborting due to 27 previous errors From 7423c2760b0dbfd4379d2c3eac309d660b859f41 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 9 Jun 2023 18:59:24 -0400 Subject: [PATCH 86/90] Don't lint redundant closure for any function call inserted by the compiler. --- clippy_lints/src/eta_reduction.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 255859da495..8d6fb8438b6 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -6,7 +6,7 @@ use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, TyKind, Unsafety}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ @@ -119,16 +119,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { match body.value.kind { ExprKind::Call(callee, args) - if matches!(callee.kind, ExprKind::Path(..)) => + if matches!(callee.kind, ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))) => { - if matches!(higher::Range::hir(body.value), Some(higher::Range { - start: Some(_), - end: Some(_), - limits: rustc_ast::RangeLimits::Closed - })) { - return; - } - let callee_ty = typeck.expr_ty(callee).peel_refs(); if matches!( type_diagnostic_name(cx, callee_ty), From caf601434bda158829b26ba9af4bd8b03e64b8d0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 Jul 2023 03:26:32 -0400 Subject: [PATCH 87/90] Resolve type aliases in `type_certainty` --- clippy_utils/src/ty/type_certainty/mod.rs | 9 +++++++-- tests/ui/unwrap_or_else_default.fixed | 4 ++++ tests/ui/unwrap_or_else_default.rs | 4 ++++ tests/ui/unwrap_or_else_default.stderr | 10 ++++++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 67c4a293a86..45b5483e323 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -12,7 +12,7 @@ //! be considered a bug. use crate::def_path_res; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor}; use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; @@ -219,7 +219,12 @@ fn path_segment_certainty( // See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value. let certainty = lhs.join_clearing_def_ids(rhs); if resolves_to_type { - certainty.with_def_id(def_id) + if cx.tcx.def_kind(def_id) == DefKind::TyAlias { + adt_def_id(cx.tcx.type_of(def_id).instantiate_identity()) + .map_or(certainty, |def_id| certainty.with_def_id(def_id)) + } else { + certainty.with_def_id(def_id) + } } else { certainty } diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 89a6b1395e5..acdb96942ba 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -109,6 +109,10 @@ fn type_certainty(option: Option>) { // should not be changed: no type annotation, unconcretized initializer let option = None; option.unwrap_or_else(Vec::new).push(1); + + type Alias = Option>; + let option: Alias = Option::>::Some(Vec::new()); + option.unwrap_or_default().push(1); } fn method_call_with_deref() { diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index e5ebf35adda..55ccd00e1a2 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -109,6 +109,10 @@ fn type_certainty(option: Option>) { // should not be changed: no type annotation, unconcretized initializer let option = None; option.unwrap_or_else(Vec::new).push(1); + + type Alias = Option>; + let option: Alias = Option::>::Some(Vec::new()); + option.unwrap_or_else(Vec::new).push(1); } fn method_call_with_deref() { diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index 91e48375f18..af662c6def7 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -84,11 +84,17 @@ error: use of `unwrap_or_else` to construct default value LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +error: use of `unwrap_or_else` to construct default value + --> $DIR/unwrap_or_else_default.rs:115:12 + | +LL | option.unwrap_or_else(Vec::new).push(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + error: use of `or_insert_with` to construct default value - --> $DIR/unwrap_or_else_default.rs:128:32 + --> $DIR/unwrap_or_else_default.rs:132:32 | LL | let _ = inner_map.entry(0).or_insert_with(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors From 008746cae4976baab645a6ca2d49f8392ded68ae Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 30 Jul 2023 13:41:58 +0200 Subject: [PATCH 88/90] [`unnecessary_find_map`]: look for then_some --- clippy_lints/src/methods/unnecessary_filter_map.rs | 10 ++++++++++ tests/ui/unnecessary_find_map.rs | 4 ++++ tests/ui/unnecessary_find_map.stderr | 8 +++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index cc64a2e7948..fabf3fa0c0c 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -77,6 +77,16 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, + hir::ExprKind::MethodCall(segment, recv, [arg], _) => { + if segment.ident.name == sym!(then_some) + && cx.typeck_results().expr_ty(recv).is_bool() + && path_to_local_id(arg, arg_id) + { + (false, true) + } else { + (true, true) + } + }, hir::ExprKind::Block(block, _) => block .expr .as_ref() diff --git a/tests/ui/unnecessary_find_map.rs b/tests/ui/unnecessary_find_map.rs index a52390861b4..3be41b775aa 100644 --- a/tests/ui/unnecessary_find_map.rs +++ b/tests/ui/unnecessary_find_map.rs @@ -21,3 +21,7 @@ fn main() { fn find_map_none_changes_item_type() -> Option { "".chars().find_map(|_| None) } + +fn issue11260() { + let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); +} diff --git a/tests/ui/unnecessary_find_map.stderr b/tests/ui/unnecessary_find_map.stderr index fb33c122fe3..3d6ef4f44fd 100644 --- a/tests/ui/unnecessary_find_map.stderr +++ b/tests/ui/unnecessary_find_map.stderr @@ -34,5 +34,11 @@ error: this `.find_map` can be written more simply using `.map(..).next()` LL | let _ = (0..4).find_map(|x| Some(x + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: this `.find_map` can be written more simply using `.find` + --> $DIR/unnecessary_find_map.rs:26:14 + | +LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors From be6a103c8cd52e3f595102e219f349e8e7a06821 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:45:04 +0200 Subject: [PATCH 89/90] add more tests to unnecessary_find_map and unnecessary_filter_map --- tests/ui/unnecessary_filter_map.rs | 6 ++++++ tests/ui/unnecessary_filter_map.stderr | 8 +++++++- tests/ui/unnecessary_find_map.rs | 2 ++ tests/ui/unnecessary_find_map.stderr | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index 8e01c2674f1..3c8c6ec94c1 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -148,3 +148,9 @@ mod comment_1052978898 { }) } } + +fn issue11260() { + // #11260 is about unnecessary_find_map, but the fix also kind of applies to + // unnecessary_filter_map + let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); +} diff --git a/tests/ui/unnecessary_filter_map.stderr b/tests/ui/unnecessary_filter_map.stderr index 5585b10ab90..2d5403ce394 100644 --- a/tests/ui/unnecessary_filter_map.stderr +++ b/tests/ui/unnecessary_filter_map.stderr @@ -34,5 +34,11 @@ error: this `.filter_map` can be written more simply using `.map` LL | let _ = (0..4).filter_map(|x| Some(x + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:155:14 + | +LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/unnecessary_find_map.rs b/tests/ui/unnecessary_find_map.rs index 3be41b775aa..2c228fbbc95 100644 --- a/tests/ui/unnecessary_find_map.rs +++ b/tests/ui/unnecessary_find_map.rs @@ -23,5 +23,7 @@ fn find_map_none_changes_item_type() -> Option { } fn issue11260() { + let y = Some(1); let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); + let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(y)); // different option, so can't be just `.find()` } diff --git a/tests/ui/unnecessary_find_map.stderr b/tests/ui/unnecessary_find_map.stderr index 3d6ef4f44fd..3a995b41b17 100644 --- a/tests/ui/unnecessary_find_map.stderr +++ b/tests/ui/unnecessary_find_map.stderr @@ -35,7 +35,7 @@ LL | let _ = (0..4).find_map(|x| Some(x + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.find_map` can be written more simply using `.find` - --> $DIR/unnecessary_find_map.rs:26:14 + --> $DIR/unnecessary_find_map.rs:27:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 35d434d08e522bc613543a265a6191d0c185d0c7 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 30 Jul 2023 14:33:38 -0300 Subject: [PATCH 90/90] [arithmetic_side_effects] Fix #11262 --- clippy_lints/src/operators/arithmetic_side_effects.rs | 8 +++++--- tests/ui/arithmetic_side_effects.rs | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 35dd8fabe6e..f9108145cdb 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,7 +1,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; +use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; @@ -138,8 +138,10 @@ impl ArithmeticSideEffects { ) { return; }; - let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); - let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + let (mut actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (mut actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + actual_lhs = expr_or_init(cx, actual_lhs); + actual_rhs = expr_or_init(cx, actual_rhs); let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index ed75acee8a2..2ac2fa22086 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -486,4 +486,11 @@ pub fn issue_11145() { x += 1; } +pub fn issue_11262() { + let one = 1; + let zero = 0; + let _ = 2 / one; + let _ = 2 / zero; +} + fn main() {}