diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index e4ace3bdabf0..6a747f4fadad 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -3,6 +3,8 @@ use hir::{Node, PatKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -39,7 +41,9 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { if matches!(pat.kind, PatKind::Wild) && !pat.span.from_expansion() - && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() + && let pat_ty = cx.typeck_results().pat_ty(pat) + && let (pat_ty, refs) = peel_refs_with_mutabilities(pat_ty) + && pat_ty.is_unit() { match cx.tcx.parent_hir_node(pat.hir_id) { Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { @@ -52,15 +56,29 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { }, _ => {}, } + + let sugg = refs.into_iter().map(Mutability::ref_prefix_str).chain(["()"]).collect(); span_lint_and_sugg( cx, IGNORED_UNIT_PATTERNS, pat.span, "matching over `()` is more explicit", "use `()` instead of `_`", - String::from("()"), + sugg, Applicability::MachineApplicable, ); } } } + +/// Like [`rustc_middle::ty::Ty::peel_refs`], but returns the mutability of each peeled ref +/// +/// `&&mut &mut &mut T` returns `(T, [Not, Mut, Mut, Mut])` +fn peel_refs_with_mutabilities(ty: Ty<'_>) -> (Ty<'_>, Vec) { + let (mut ty, mut refs) = (ty, vec![]); + while let ty::Ref(_, dest_ty, mutbl) = ty.kind() { + ty = *dest_ty; + refs.push(*mutbl); + } + (ty, refs) +} diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 118f0b488952..05a00bad3371 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -47,7 +47,7 @@ pub fn moo(_: ()) { fn test_unit_ref_1() { let x: (usize, &&&&&()) = (1, &&&&&&()); match x { - (1, ()) => unimplemented!(), + (1, &&&&&()) => unimplemented!(), //~^ ERROR: matching over `()` is more explicit _ => unimplemented!(), }; @@ -59,3 +59,20 @@ fn test_unit_ref_2(v: &[(usize, ())]) { let _ = x; } } + +fn issue15187() { + let func: fn(&()) = |&()| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&mut ()) = |&mut ()| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&&mut ()) = |&&mut ()| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&&mut &()) = |&&mut &()| {}; + //~^ ERROR: matching over `()` is more explicit + + #[allow(clippy::match_single_binding)] + match &() { + &() => todo!(), + //~^ ERROR: matching over `()` is more explicit + } +} diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index 92feb9e6c281..113c97980d46 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -59,3 +59,20 @@ fn test_unit_ref_2(v: &[(usize, ())]) { let _ = x; } } + +fn issue15187() { + let func: fn(&()) = |_| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&mut ()) = |_| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&&mut ()) = |_| {}; + //~^ ERROR: matching over `()` is more explicit + let func: fn(&&mut &()) = |_| {}; + //~^ ERROR: matching over `()` is more explicit + + #[allow(clippy::match_single_binding)] + match &() { + _ => todo!(), + //~^ ERROR: matching over `()` is more explicit + } +} diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index 00a254e39192..c1079bccf1fb 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -47,7 +47,7 @@ error: matching over `()` is more explicit --> tests/ui/ignored_unit_patterns.rs:50:13 | LL | (1, _) => unimplemented!(), - | ^ help: use `()` instead of `_`: `()` + | ^ help: use `()` instead of `_`: `&&&&&()` error: matching over `()` is more explicit --> tests/ui/ignored_unit_patterns.rs:57:13 @@ -55,5 +55,35 @@ error: matching over `()` is more explicit LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` -error: aborting due to 9 previous errors +error: matching over `()` is more explicit + --> tests/ui/ignored_unit_patterns.rs:64:26 + | +LL | let func: fn(&()) = |_| {}; + | ^ help: use `()` instead of `_`: `&()` + +error: matching over `()` is more explicit + --> tests/ui/ignored_unit_patterns.rs:66:30 + | +LL | let func: fn(&mut ()) = |_| {}; + | ^ help: use `()` instead of `_`: `&mut ()` + +error: matching over `()` is more explicit + --> tests/ui/ignored_unit_patterns.rs:68:31 + | +LL | let func: fn(&&mut ()) = |_| {}; + | ^ help: use `()` instead of `_`: `&&mut ()` + +error: matching over `()` is more explicit + --> tests/ui/ignored_unit_patterns.rs:70:32 + | +LL | let func: fn(&&mut &()) = |_| {}; + | ^ help: use `()` instead of `_`: `&&mut &()` + +error: matching over `()` is more explicit + --> tests/ui/ignored_unit_patterns.rs:75:9 + | +LL | _ => todo!(), + | ^ help: use `()` instead of `_`: `&()` + +error: aborting due to 14 previous errors