From a2b234554d7c175ec578c6003f7a966b05258752 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 12:26:45 -0700 Subject: [PATCH 1/3] Fix coerce_container_to_any false positive on autoderef --- clippy_lints/src/coerce_container_to_any.rs | 27 ++++++++++++--------- tests/ui/coerce_container_to_any.fixed | 4 +++ tests/ui/coerce_container_to_any.rs | 4 +++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 8c12a42ba4e4..467590219955 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -4,6 +4,7 @@ use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -49,23 +50,18 @@ declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - // If this expression has an effective type of `&dyn Any` ... - { - let coerced_ty = cx.typeck_results().expr_ty_adjusted(e); - - let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else { - return; - }; - if !is_dyn_any(cx.tcx, coerced_ref_ty) { - return; - } + // If this expression was coerced to `&dyn Any` ... + if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| { + matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target) + }) { + return; } let expr_ty = cx.typeck_results().expr_ty(e); let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else { return; }; - // ... but only due to coercion ... + // ... but it's not actually `&dyn Any` ... if is_dyn_any(cx.tcx, expr_ref_ty) { return; } @@ -89,12 +85,19 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { e.span, format!("coercing `{expr_ty}` to `&dyn Any`"), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")), + format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "..")), Applicability::MaybeIncorrect, ); } } +fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + let ty::Ref(_, ref_ty, _) = *ty.kind() else { + return false; + }; + is_dyn_any(tcx, ref_ty) +} + fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { let ty::Dynamic(traits, ..) = ty.kind() else { return false; diff --git a/tests/ui/coerce_container_to_any.fixed b/tests/ui/coerce_container_to_any.fixed index ae9d3ef9656f..eb8487bac176 100644 --- a/tests/ui/coerce_container_to_any.fixed +++ b/tests/ui/coerce_container_to_any.fixed @@ -21,6 +21,10 @@ fn main() { f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/tests/ui/coerce_container_to_any.rs b/tests/ui/coerce_container_to_any.rs index 9948bd48e0d8..7edb65fa0f70 100644 --- a/tests/ui/coerce_container_to_any.rs +++ b/tests/ui/coerce_container_to_any.rs @@ -21,6 +21,10 @@ fn main() { f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} From 8e285d6f1ae89a0edc4e93509b846661c012ca33 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 13:53:21 -0700 Subject: [PATCH 2/3] Use precedence-aware formatting for coerce_container_to_any suggestions --- clippy_lints/src/coerce_container_to_any.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 467590219955..fd3399decc16 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::sugg::{self, Sugg}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -74,18 +74,22 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { } // ... that's probably not intended. - let (span, deref_count) = match e.kind { + let (target_expr, deref_count) = match e.kind { // If `e` was already an `&` expression, skip `*&` in the suggestion - ExprKind::AddrOf(_, _, referent) => (referent.span, depth), - _ => (e.span, depth + 1), + ExprKind::AddrOf(_, _, referent) => (referent, depth), + _ => (e, depth + 1), }; + let sugg = sugg::make_unop( + &format!("&{}", str::repeat("*", deref_count)), + Sugg::hir(cx, target_expr, ".."), + ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, format!("coercing `{expr_ty}` to `&dyn Any`"), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "..")), + sugg.to_string(), Applicability::MaybeIncorrect, ); } From dc1b9ce754202493d05a8d7e1627f1d6bc272528 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 14:02:53 -0700 Subject: [PATCH 3/3] Fix reference mutability in coerce_container_to_any diagnostics --- clippy_lints/src/coerce_container_to_any.rs | 7 +++++-- tests/ui/coerce_container_to_any.fixed | 9 ++++++++- tests/ui/coerce_container_to_any.rs | 9 ++++++++- tests/ui/coerce_container_to_any.stderr | 14 +++++++++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index fd3399decc16..3311a35ef79e 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -79,15 +79,18 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { ExprKind::AddrOf(_, _, referent) => (referent, depth), _ => (e, depth + 1), }; + let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else { + return; + }; let sugg = sugg::make_unop( - &format!("&{}", str::repeat("*", deref_count)), + &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)), Sugg::hir(cx, target_expr, ".."), ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, - format!("coercing `{expr_ty}` to `&dyn Any`"), + format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()), "consider dereferencing", sugg.to_string(), Applicability::MaybeIncorrect, diff --git a/tests/ui/coerce_container_to_any.fixed b/tests/ui/coerce_container_to_any.fixed index eb8487bac176..b5b3f15b4de4 100644 --- a/tests/ui/coerce_container_to_any.fixed +++ b/tests/ui/coerce_container_to_any.fixed @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&*x); @@ -15,9 +15,16 @@ fn main() { let _: &dyn Any = &*x; //~^ coerce_container_to_any + let _: &dyn Any = &*x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut *x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; diff --git a/tests/ui/coerce_container_to_any.rs b/tests/ui/coerce_container_to_any.rs index 7edb65fa0f70..4d6527bb5525 100644 --- a/tests/ui/coerce_container_to_any.rs +++ b/tests/ui/coerce_container_to_any.rs @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&x); @@ -15,9 +15,16 @@ fn main() { let _: &dyn Any = &x; //~^ coerce_container_to_any + let _: &dyn Any = &mut x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; diff --git a/tests/ui/coerce_container_to_any.stderr b/tests/ui/coerce_container_to_any.stderr index 00ab77e0ce0f..26389c9186eb 100644 --- a/tests/ui/coerce_container_to_any.stderr +++ b/tests/ui/coerce_container_to_any.stderr @@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box` to `&dyn Any` LL | let _: &dyn Any = &x; | ^^ help: consider dereferencing: `&*x` -error: aborting due to 3 previous errors +error: coercing `&mut std::boxed::Box` to `&dyn Any` + --> tests/ui/coerce_container_to_any.rs:18:23 + | +LL | let _: &dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&*x` + +error: coercing `&mut std::boxed::Box` to `&mut dyn Any` + --> tests/ui/coerce_container_to_any.rs:21:27 + | +LL | let _: &mut dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&mut *x` + +error: aborting due to 5 previous errors