@@ -5,13 +5,33 @@ use rustc_errors::Applicability;
5
5
use rustc_hir:: Expr ;
6
6
use rustc_lint:: LateContext ;
7
7
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
8
+ use rustc_middle:: ty:: print:: with_forced_trimmed_paths;
8
9
use rustc_middle:: ty:: { self , ExistentialPredicate , Ty } ;
9
10
use rustc_span:: { sym, Span } ;
10
11
11
- fn is_dyn_any ( cx : & LateContext < ' _ > , ty : Ty < ' _ > ) -> bool {
12
+ /// Checks if the given type is `dyn Any`, or a trait object that has `Any` as a supertrait.
13
+ /// Only in those cases will its vtable have a `type_id` method that returns the implementor's
14
+ /// `TypeId`, and only in those cases can we give a proper suggestion to dereference the box.
15
+ ///
16
+ /// If this returns false, then `.type_id()` likely (this may have FNs) will not be what the user
17
+ /// expects in any case and dereferencing it won't help either. It will likely require some
18
+ /// other changes, but it is still worth emitting a lint.
19
+ /// See <https://github.com/rust-lang/rust-clippy/pull/11350#discussion_r1544863005> for more details.
20
+ fn is_subtrait_of_any ( cx : & LateContext < ' _ > , ty : Ty < ' _ > ) -> bool {
12
21
if let ty:: Dynamic ( preds, ..) = ty. kind ( ) {
13
22
preds. iter ( ) . any ( |p| match p. skip_binder ( ) {
14
- ExistentialPredicate :: Trait ( tr) => cx. tcx . is_diagnostic_item ( sym:: Any , tr. def_id ) ,
23
+ ExistentialPredicate :: Trait ( tr) => {
24
+ cx. tcx . is_diagnostic_item ( sym:: Any , tr. def_id )
25
+ || cx
26
+ . tcx
27
+ . super_predicates_of ( tr. def_id )
28
+ . predicates
29
+ . iter ( )
30
+ . any ( |( clause, _) | {
31
+ matches ! ( clause. kind( ) . skip_binder( ) , ty:: ClauseKind :: Trait ( super_tr)
32
+ if cx. tcx. is_diagnostic_item( sym:: Any , super_tr. def_id( ) ) )
33
+ } )
34
+ } ,
15
35
_ => false ,
16
36
} )
17
37
} else {
@@ -26,36 +46,42 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
26
46
&& let ty:: Ref ( _, ty, _) = recv_ty. kind ( )
27
47
&& let ty:: Adt ( adt, args) = ty. kind ( )
28
48
&& adt. is_box ( )
29
- && is_dyn_any ( cx, args. type_at ( 0 ) )
49
+ && let inner_box_ty = args. type_at ( 0 )
50
+ && let ty:: Dynamic ( ..) = inner_box_ty. kind ( )
30
51
{
52
+ let ty_name = with_forced_trimmed_paths ! ( ty. to_string( ) ) ;
53
+
31
54
span_lint_and_then (
32
55
cx,
33
56
TYPE_ID_ON_BOX ,
34
57
call_span,
35
- "calling `.type_id()` on a `Box<dyn Any>`" ,
58
+ & format ! ( "calling `.type_id()` on `{ty_name}`" ) ,
36
59
|diag| {
37
60
let derefs = recv_adjusts
38
61
. iter ( )
39
62
. filter ( |adj| matches ! ( adj. kind, Adjust :: Deref ( None ) ) )
40
63
. count ( ) ;
41
64
42
- let mut sugg = "*" . repeat ( derefs + 1 ) ;
43
- sugg += & snippet ( cx, receiver. span , "<expr>" ) ;
44
-
45
65
diag. note (
46
- "this returns the type id of the literal type `Box<dyn Any >` instead of the \
66
+ "this returns the type id of the literal type `Box<_ >` instead of the \
47
67
type id of the boxed value, which is most likely not what you want",
48
68
)
49
- . note (
50
- "if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
51
- which makes it more clear",
52
- )
53
- . span_suggestion (
54
- receiver. span ,
55
- "consider dereferencing first" ,
56
- format ! ( "({sugg})" ) ,
57
- Applicability :: MaybeIncorrect ,
58
- ) ;
69
+ . note ( format ! (
70
+ "if this is intentional, use `TypeId::of::<{ty_name}>()` instead, \
71
+ which makes it more clear"
72
+ ) ) ;
73
+
74
+ if is_subtrait_of_any ( cx, inner_box_ty) {
75
+ let mut sugg = "*" . repeat ( derefs + 1 ) ;
76
+ sugg += & snippet ( cx, receiver. span , "<expr>" ) ;
77
+
78
+ diag. span_suggestion (
79
+ receiver. span ,
80
+ "consider dereferencing first" ,
81
+ format ! ( "({sugg})" ) ,
82
+ Applicability :: MaybeIncorrect ,
83
+ ) ;
84
+ }
59
85
} ,
60
86
) ;
61
87
}
0 commit comments