From b14eba0fa3a4f89ae567d6805b3da4e5517dc366 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Oct 2025 11:34:22 +0200 Subject: [PATCH 1/2] misc: move the infallible stuff out of the way --- clippy_lints/src/matches/match_as_ref.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 2c4d08639f61..7b23cdabf2e8 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -21,24 +21,18 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: } else { None } + && let output_ty = cx.typeck_results().expr_ty(expr) + && let input_ty = cx.typeck_results().expr_ty(ex) + && let Some(input_ty) = option_arg_ty(cx, input_ty) + && let Some(output_ty) = option_arg_ty(cx, output_ty) + && let ty::Ref(_, output_ty, _) = *output_ty.kind() { let method = match arm_ref_mutbl { Mutability::Not => "as_ref", Mutability::Mut => "as_mut", }; - let output_ty = cx.typeck_results().expr_ty(expr); - let input_ty = cx.typeck_results().expr_ty(ex); - - let cast = if let Some(input_ty) = option_arg_ty(cx, input_ty) - && let Some(output_ty) = option_arg_ty(cx, output_ty) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() - && input_ty != output_ty - { - ".map(|x| x as _)" - } else { - "" - }; + let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; let mut applicability = Applicability::MachineApplicable; span_lint_and_then( From 789d36f95663954137674043173e7a7db725d73b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Oct 2025 11:45:27 +0200 Subject: [PATCH 2/2] fix(match_as_ref): also suggest downcasting references --- clippy_lints/src/matches/match_as_ref.rs | 25 ++++++++++++-- tests/ui/match_as_ref.fixed | 6 ++++ tests/ui/match_as_ref.rs | 14 ++++++++ tests/ui/match_as_ref.stderr | 44 +++++++++++++++++++++++- 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 7b23cdabf2e8..b375073567a0 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -25,14 +25,35 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: && let input_ty = cx.typeck_results().expr_ty(ex) && let Some(input_ty) = option_arg_ty(cx, input_ty) && let Some(output_ty) = option_arg_ty(cx, output_ty) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() + && let ty::Ref(_, output_ty, output_mutbl) = *output_ty.kind() { let method = match arm_ref_mutbl { Mutability::Not => "as_ref", Mutability::Mut => "as_mut", }; - let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; + let cast = if arm_ref_mutbl != output_mutbl || input_ty != output_ty { + // We'll need to downcast either the type (`&i32 as &dyn std::fmt::Debug`): + // ``` + // let _: Option<&dyn std::fmt::Debug> = match 0i32 { + // Some(ref t) => Some(t), + // None => None, + // }; + // ``` + // or the reference (`&mut i32 as &i32`) + // ``` + // let _: Option<&i32> = match 0i32 { + // Some(ref mut t) => Some(t), + // None => None, + // }; + // ``` + // or both. + // + // `.map` will take care of all of it. + ".map(|x| x as _)" + } else { + "" + }; let mut applicability = Applicability::MachineApplicable; span_lint_and_then( diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 3653b81eda22..b3e97e1889bf 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -84,3 +84,9 @@ fn recv_requiring_parens() { let _ = (!S).as_ref(); } + +fn issue15932() { + let _: Option<&i32> = Some(0).as_mut().map(|x| x as _); + + let _: Option<&dyn std::fmt::Debug> = Some(0).as_mut().map(|x| x as _); +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index d58cfd81aeab..7cabdc6a93cb 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -100,3 +100,17 @@ fn recv_requiring_parens() { Some(ref v) => Some(v), }; } + +fn issue15932() { + let _: Option<&i32> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; + + let _: Option<&dyn std::fmt::Debug> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; +} diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index 226655970ab8..47eeb449a9cd 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -83,5 +83,47 @@ LL - }; LL + let _ = (!S).as_ref(); | -error: aborting due to 4 previous errors +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:105:27 + | +LL | let _: Option<&i32> = match Some(0) { + | ___________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | +help: use `Option::as_mut()` directly + | +LL - let _: Option<&i32> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&i32> = Some(0).as_mut().map(|x| x as _); + | + +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:111:43 + | +LL | let _: Option<&dyn std::fmt::Debug> = match Some(0) { + | ___________________________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | +help: use `Option::as_mut()` directly + | +LL - let _: Option<&dyn std::fmt::Debug> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&dyn std::fmt::Debug> = Some(0).as_mut().map(|x| x as _); + | + +error: aborting due to 6 previous errors