1
- use std:: borrow:: Cow ;
2
-
3
1
use crate :: methods:: TYPE_ID_ON_BOX ;
4
2
use clippy_utils:: diagnostics:: span_lint_and_then;
5
3
use clippy_utils:: source:: snippet;
@@ -11,33 +9,33 @@ use rustc_middle::ty::print::with_forced_trimmed_paths;
11
9
use rustc_middle:: ty:: { self , ExistentialPredicate , Ty } ;
12
10
use rustc_span:: { sym, Span } ;
13
11
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 > > {
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 {
17
21
if let ty:: Dynamic ( preds, ..) = ty. kind ( ) {
18
- preds. iter ( ) . find_map ( |p| match p. skip_binder ( ) {
22
+ preds. iter ( ) . any ( |p| match p. skip_binder ( ) {
19
23
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)
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)
29
32
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
- }
33
+ } )
36
34
} ,
37
- _ => None ,
35
+ _ => false ,
38
36
} )
39
37
} else {
40
- None
38
+ false
41
39
}
42
40
}
43
41
@@ -48,36 +46,42 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
48
46
&& let ty:: Ref ( _, ty, _) = recv_ty. kind ( )
49
47
&& let ty:: Adt ( adt, args) = ty. kind ( )
50
48
&& adt. is_box ( )
51
- && let Some ( trait_path) = 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 ( )
52
51
{
52
+ let ty_name = with_forced_trimmed_paths ! ( ty. to_string( ) ) ;
53
+
53
54
span_lint_and_then (
54
55
cx,
55
56
TYPE_ID_ON_BOX ,
56
57
call_span,
57
- & format ! ( "calling `.type_id()` on `Box<dyn {trait_path}> `" ) ,
58
+ & format ! ( "calling `.type_id()` on `{ty_name} `" ) ,
58
59
|diag| {
59
60
let derefs = recv_adjusts
60
61
. iter ( )
61
62
. filter ( |adj| matches ! ( adj. kind, Adjust :: Deref ( None ) ) )
62
63
. count ( ) ;
63
64
64
- let mut sugg = "*" . repeat ( derefs + 1 ) ;
65
- sugg += & snippet ( cx, receiver. span , "<expr>" ) ;
66
-
67
65
diag. note (
68
66
"this returns the type id of the literal type `Box<_>` instead of the \
69
67
type id of the boxed value, which is most likely not what you want",
70
68
)
71
69
. note ( format ! (
72
- "if this is intentional, use `TypeId::of::<Box<dyn {trait_path}> >()` instead, \
70
+ "if this is intentional, use `TypeId::of::<{ty_name} >()` instead, \
73
71
which makes it more clear"
74
- ) )
75
- . span_suggestion (
76
- receiver. span ,
77
- "consider dereferencing first" ,
78
- format ! ( "({sugg})" ) ,
79
- Applicability :: MaybeIncorrect ,
80
- ) ;
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
+ }
81
85
} ,
82
86
) ;
83
87
}
0 commit comments