Skip to content

Commit a362b2c

Browse files
committed
[type_id_on_box]: lint of Any subtraits
1 parent 124e68b commit a362b2c

File tree

4 files changed

+67
-22
lines changed

4 files changed

+67
-22
lines changed

clippy_lints/src/methods/type_id_on_box.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
1+
use std::borrow::Cow;
2+
13
use crate::methods::TYPE_ID_ON_BOX;
24
use clippy_utils::diagnostics::span_lint_and_then;
35
use clippy_utils::source::snippet;
46
use rustc_errors::Applicability;
57
use rustc_hir::Expr;
68
use rustc_lint::LateContext;
79
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
10+
use rustc_middle::ty::print::with_forced_trimmed_paths;
811
use rustc_middle::ty::{self, ExistentialPredicate, Ty};
912
use rustc_span::{sym, Span};
1013

11-
fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
14+
/// Checks if a [`Ty`] is a `dyn Any` or a `dyn Trait` where `Trait: Any`
15+
/// and returns the name of the trait object.
16+
fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Cow<'static, str>> {
1217
if let ty::Dynamic(preds, ..) = ty.kind() {
13-
preds.iter().any(|p| match p.skip_binder() {
14-
ExistentialPredicate::Trait(tr) => cx.tcx.is_diagnostic_item(sym::Any, tr.def_id),
15-
_ => false,
18+
preds.iter().find_map(|p| match p.skip_binder() {
19+
ExistentialPredicate::Trait(tr) => {
20+
if cx.tcx.is_diagnostic_item(sym::Any, tr.def_id) {
21+
Some(Cow::Borrowed("Any"))
22+
} else if cx
23+
.tcx
24+
.super_predicates_of(tr.def_id)
25+
.predicates
26+
.iter()
27+
.any(|(clause, _)| {
28+
matches!(clause.kind().skip_binder(), ty::ClauseKind::Trait(super_tr)
29+
if cx.tcx.is_diagnostic_item(sym::Any, super_tr.def_id()))
30+
})
31+
{
32+
Some(Cow::Owned(with_forced_trimmed_paths!(cx.tcx.def_path_str(tr.def_id))))
33+
} else {
34+
None
35+
}
36+
},
37+
_ => None,
1638
})
1739
} else {
18-
false
40+
None
1941
}
2042
}
2143

@@ -26,13 +48,13 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
2648
&& let ty::Ref(_, ty, _) = recv_ty.kind()
2749
&& let ty::Adt(adt, args) = ty.kind()
2850
&& adt.is_box()
29-
&& is_dyn_any(cx, args.type_at(0))
51+
&& let Some(trait_path) = is_dyn_any(cx, args.type_at(0))
3052
{
3153
span_lint_and_then(
3254
cx,
3355
TYPE_ID_ON_BOX,
3456
call_span,
35-
"calling `.type_id()` on a `Box<dyn Any>`",
57+
&format!("calling `.type_id()` on `Box<dyn {trait_path}>`"),
3658
|diag| {
3759
let derefs = recv_adjusts
3860
.iter()
@@ -43,13 +65,13 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
4365
sugg += &snippet(cx, receiver.span, "<expr>");
4466

4567
diag.note(
46-
"this returns the type id of the literal type `Box<dyn Any>` instead of the \
68+
"this returns the type id of the literal type `Box<_>` instead of the \
4769
type id of the boxed value, which is most likely not what you want",
4870
)
49-
.note(
50-
"if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
51-
which makes it more clear",
52-
)
71+
.note(format!(
72+
"if this is intentional, use `TypeId::of::<Box<dyn {trait_path}>>()` instead, \
73+
which makes it more clear"
74+
))
5375
.span_suggestion(
5476
receiver.span,
5577
"consider dereferencing first",

tests/ui/type_id_on_box.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ fn existential() -> impl Any {
1919
Box::new(1) as Box<dyn Any>
2020
}
2121

22+
trait AnySubTrait: Any {}
23+
impl<T: Any> AnySubTrait for T {}
24+
2225
fn main() {
2326
let any_box: Box<dyn Any> = Box::new(0usize);
2427
let _ = (*any_box).type_id();
@@ -35,4 +38,7 @@ fn main() {
3538

3639
let b = BadBox(Box::new(0usize));
3740
let _ = b.type_id(); // Don't lint. This is a call to `<BadBox as Any>::type_id`. Not `std::boxed::Box`!
41+
42+
let b: Box<dyn AnySubTrait> = Box::new(1);
43+
let _ = (*b).type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
3844
}

tests/ui/type_id_on_box.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ fn existential() -> impl Any {
1919
Box::new(1) as Box<dyn Any>
2020
}
2121

22+
trait AnySubTrait: Any {}
23+
impl<T: Any> AnySubTrait for T {}
24+
2225
fn main() {
2326
let any_box: Box<dyn Any> = Box::new(0usize);
2427
let _ = any_box.type_id();
@@ -35,4 +38,7 @@ fn main() {
3538

3639
let b = BadBox(Box::new(0usize));
3740
let _ = b.type_id(); // Don't lint. This is a call to `<BadBox as Any>::type_id`. Not `std::boxed::Box`!
41+
42+
let b: Box<dyn AnySubTrait> = Box::new(1);
43+
let _ = b.type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
3844
}

tests/ui/type_id_on_box.stderr

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,48 @@
1-
error: calling `.type_id()` on a `Box<dyn Any>`
2-
--> tests/ui/type_id_on_box.rs:24:13
1+
error: calling `.type_id()` on `Box<dyn Any>`
2+
--> tests/ui/type_id_on_box.rs:27:13
33
|
44
LL | let _ = any_box.type_id();
55
| -------^^^^^^^^^^
66
| |
77
| help: consider dereferencing first: `(*any_box)`
88
|
9-
= 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
9+
= 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
1010
= note: if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, which makes it more clear
1111
= note: `-D clippy::type-id-on-box` implied by `-D warnings`
1212
= help: to override `-D warnings` add `#[allow(clippy::type_id_on_box)]`
1313

14-
error: calling `.type_id()` on a `Box<dyn Any>`
15-
--> tests/ui/type_id_on_box.rs:28:13
14+
error: calling `.type_id()` on `Box<dyn Any>`
15+
--> tests/ui/type_id_on_box.rs:31:13
1616
|
1717
LL | let _ = any_box.type_id(); // 2 derefs are needed here to get to the `dyn Any`
1818
| -------^^^^^^^^^^
1919
| |
2020
| help: consider dereferencing first: `(**any_box)`
2121
|
22-
= 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
22+
= 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
2323
= note: if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, which makes it more clear
2424

25-
error: calling `.type_id()` on a `Box<dyn Any>`
26-
--> tests/ui/type_id_on_box.rs:34:13
25+
error: calling `.type_id()` on `Box<dyn Any>`
26+
--> tests/ui/type_id_on_box.rs:37:13
2727
|
2828
LL | let _ = b.type_id();
2929
| -^^^^^^^^^^
3030
| |
3131
| help: consider dereferencing first: `(*b)`
3232
|
33-
= 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
33+
= 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
3434
= note: if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, which makes it more clear
3535

36-
error: aborting due to 3 previous errors
36+
error: calling `.type_id()` on `Box<dyn AnySubTrait>`
37+
--> tests/ui/type_id_on_box.rs:43:13
38+
|
39+
LL | let _ = b.type_id(); // Lint if calling `type_id` on a `dyn Trait` where `Trait: Any`
40+
| -^^^^^^^^^^
41+
| |
42+
| help: consider dereferencing first: `(*b)`
43+
|
44+
= 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
45+
= note: if this is intentional, use `TypeId::of::<Box<dyn AnySubTrait>>()` instead, which makes it more clear
46+
47+
error: aborting due to 4 previous errors
3748

0 commit comments

Comments
 (0)