Fix coerce_container_to_any false positive on autoderef (#15057)
Fixes the false positive reported in rust-lang/rust-clippy#15045. ~I still need to work out how to fix the suggestion.~ changelog: none
This commit is contained in:
commit
bca8931bbb
4 changed files with 64 additions and 20 deletions
|
|
@ -1,9 +1,10 @@
|
|||
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};
|
||||
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;
|
||||
}
|
||||
|
|
@ -78,23 +74,37 @@ 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 ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else {
|
||||
return;
|
||||
};
|
||||
let sugg = sugg::make_unop(
|
||||
&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",
|
||||
format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")),
|
||||
sugg.to_string(),
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use std::any::Any;
|
||||
|
||||
fn main() {
|
||||
let x: Box<dyn Any> = Box::new(());
|
||||
let mut x: Box<dyn Any> = Box::new(());
|
||||
let ref_x = &x;
|
||||
|
||||
f(&*x);
|
||||
|
|
@ -15,12 +15,23 @@ 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;
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/15045
|
||||
#[allow(clippy::needless_borrow)]
|
||||
(&x).downcast_ref::<()>().unwrap();
|
||||
}
|
||||
|
||||
fn f(_: &dyn Any) {}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use std::any::Any;
|
||||
|
||||
fn main() {
|
||||
let x: Box<dyn Any> = Box::new(());
|
||||
let mut x: Box<dyn Any> = Box::new(());
|
||||
let ref_x = &x;
|
||||
|
||||
f(&x);
|
||||
|
|
@ -15,12 +15,23 @@ 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;
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/15045
|
||||
#[allow(clippy::needless_borrow)]
|
||||
(&x).downcast_ref::<()>().unwrap();
|
||||
}
|
||||
|
||||
fn f(_: &dyn Any) {}
|
||||
|
|
|
|||
|
|
@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box<dyn std::any::Any>` 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<dyn std::any::Any>` 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<dyn std::any::Any>` 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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue