[type_id_on_box]: lint of Any subtraits

This commit is contained in:
y21 2023-08-18 09:25:18 +02:00
parent 124e68bef8
commit 37be3e4dd5
4 changed files with 103 additions and 22 deletions

View File

@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::methods::TYPE_ID_ON_BOX;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
@ -5,17 +7,37 @@ use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::{self, ExistentialPredicate, Ty};
use rustc_span::{sym, Span};
fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
/// Checks if a [`Ty`] is a `dyn Any` or a `dyn Trait` where `Trait: Any`
/// and returns the name of the trait object.
fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Cow<'static, str>> {
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,
preds.iter().find_map(|p| match p.skip_binder() {
ExistentialPredicate::Trait(tr) => {
if cx.tcx.is_diagnostic_item(sym::Any, tr.def_id) {
Some(Cow::Borrowed("Any"))
} else if cx
.tcx
.super_predicates_of(tr.def_id)
.predicates
.iter()
.any(|(clause, _)| {
matches!(clause.kind().skip_binder(), ty::ClauseKind::Trait(super_tr)
if cx.tcx.is_diagnostic_item(sym::Any, super_tr.def_id()))
})
{
Some(Cow::Owned(with_forced_trimmed_paths!(cx.tcx.def_path_str(tr.def_id))))
} else {
None
}
},
_ => None,
})
} else {
false
None
}
}
@ -26,13 +48,13 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
&& let ty::Ref(_, ty, _) = recv_ty.kind()
&& let ty::Adt(adt, args) = ty.kind()
&& adt.is_box()
&& is_dyn_any(cx, args.type_at(0))
&& let Some(trait_path) = is_dyn_any(cx, args.type_at(0))
{
span_lint_and_then(
cx,
TYPE_ID_ON_BOX,
call_span,
"calling `.type_id()` on a `Box<dyn Any>`",
&format!("calling `.type_id()` on `Box<dyn {trait_path}>`"),
|diag| {
let derefs = recv_adjusts
.iter()
@ -43,13 +65,13 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
sugg += &snippet(cx, receiver.span, "<expr>");
diag.note(
"this returns the type id of the literal type `Box<dyn Any>` instead of the \
"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::<Box<dyn Any>>()` instead, \
which makes it more clear",
)
.note(format!(
"if this is intentional, use `TypeId::of::<Box<dyn {trait_path}>>()` instead, \
which makes it more clear"
))
.span_suggestion(
receiver.span,
"consider dereferencing first",

View File

@ -19,6 +19,21 @@ fn existential() -> impl Any {
Box::new(1) as Box<dyn Any>
}
trait AnySubTrait: Any {}
impl<T: Any> AnySubTrait for T {}
// `Any` is an indirect supertrait
trait AnySubSubTrait: AnySubTrait {}
impl<T: AnySubTrait> AnySubSubTrait for T {}
// This trait mentions `Any` in its predicates, but it is not a subtrait of `Any`.
trait NormalTrait
where
i32: Any,
{
}
impl<T> NormalTrait for T {}
fn main() {
let any_box: Box<dyn Any> = Box::new(0usize);
let _ = (*any_box).type_id();
@ -35,4 +50,13 @@ fn main() {
let b = BadBox(Box::new(0usize));
let _ = b.type_id(); // Don't lint. This is a call to `<BadBox as Any>::type_id`. Not `std::boxed::Box`!
let b: Box<dyn AnySubTrait> = Box::new(1);
let _ = (*b).type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
let b: Box<dyn AnySubSubTrait> = Box::new(1);
let _ = b.type_id(); // Known FN - Any is not an "immediate" supertrait
let b: Box<dyn NormalTrait> = Box::new(1);
let _ = b.type_id(); // `NormalTrait` does not have `Any` as its supertrait (even though it mentions it in `i32: Any`)
}

View File

@ -19,6 +19,21 @@ fn existential() -> impl Any {
Box::new(1) as Box<dyn Any>
}
trait AnySubTrait: Any {}
impl<T: Any> AnySubTrait for T {}
// `Any` is an indirect supertrait
trait AnySubSubTrait: AnySubTrait {}
impl<T: AnySubTrait> AnySubSubTrait for T {}
// This trait mentions `Any` in its predicates, but it is not a subtrait of `Any`.
trait NormalTrait
where
i32: Any,
{
}
impl<T> NormalTrait for T {}
fn main() {
let any_box: Box<dyn Any> = Box::new(0usize);
let _ = any_box.type_id();
@ -35,4 +50,13 @@ fn main() {
let b = BadBox(Box::new(0usize));
let _ = b.type_id(); // Don't lint. This is a call to `<BadBox as Any>::type_id`. Not `std::boxed::Box`!
let b: Box<dyn AnySubTrait> = Box::new(1);
let _ = b.type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
let b: Box<dyn AnySubSubTrait> = Box::new(1);
let _ = b.type_id(); // Known FN - Any is not an "immediate" supertrait
let b: Box<dyn NormalTrait> = Box::new(1);
let _ = b.type_id(); // `NormalTrait` does not have `Any` as its supertrait (even though it mentions it in `i32: Any`)
}

View File

@ -1,37 +1,48 @@
error: calling `.type_id()` on a `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:24:13
error: calling `.type_id()` on `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:39:13
|
LL | let _ = any_box.type_id();
| -------^^^^^^^^^^
| |
| help: consider dereferencing first: `(*any_box)`
|
= note: this returns the type id of the literal type `Box<dyn Any>` instead of the type id of the boxed value, which is most likely not what you want
= 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::<Box<dyn Any>>()` instead, which makes it more clear
= note: `-D clippy::type-id-on-box` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::type_id_on_box)]`
error: calling `.type_id()` on a `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:28:13
error: calling `.type_id()` on `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:43: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<dyn Any>` instead of the type id of the boxed value, which is most likely not what you want
= 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::<Box<dyn Any>>()` instead, which makes it more clear
error: calling `.type_id()` on a `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:34:13
error: calling `.type_id()` on `Box<dyn Any>`
--> tests/ui/type_id_on_box.rs:49:13
|
LL | let _ = b.type_id();
| -^^^^^^^^^^
| |
| help: consider dereferencing first: `(*b)`
|
= note: this returns the type id of the literal type `Box<dyn Any>` instead of the type id of the boxed value, which is most likely not what you want
= 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::<Box<dyn Any>>()` instead, which makes it more clear
error: aborting due to 3 previous errors
error: calling `.type_id()` on `Box<dyn AnySubTrait>`
--> tests/ui/type_id_on_box.rs:55:13
|
LL | let _ = b.type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
| -^^^^^^^^^^
| |
| 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::<Box<dyn AnySubTrait>>()` instead, which makes it more clear
error: aborting due to 4 previous errors