1- use std:: borrow:: Cow ;
2-
31use crate :: methods:: TYPE_ID_ON_BOX ;
42use clippy_utils:: diagnostics:: span_lint_and_then;
53use clippy_utils:: source:: snippet;
@@ -11,33 +9,33 @@ use rustc_middle::ty::print::with_forced_trimmed_paths;
119use rustc_middle:: ty:: { self , ExistentialPredicate , Ty } ;
1210use rustc_span:: { sym, Span } ;
1311
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 {
1721 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 ( ) {
1923 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)
2932 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+ } )
3634 } ,
37- _ => None ,
35+ _ => false ,
3836 } )
3937 } else {
40- None
38+ false
4139 }
4240}
4341
@@ -48,36 +46,42 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
4846 && let ty:: Ref ( _, ty, _) = recv_ty. kind ( )
4947 && let ty:: Adt ( adt, args) = ty. kind ( )
5048 && 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 ( )
5251 {
52+ let ty_name = with_forced_trimmed_paths ! ( ty. to_string( ) ) ;
53+
5354 span_lint_and_then (
5455 cx,
5556 TYPE_ID_ON_BOX ,
5657 call_span,
57- & format ! ( "calling `.type_id()` on `Box<dyn {trait_path}> `" ) ,
58+ & format ! ( "calling `.type_id()` on `{ty_name} `" ) ,
5859 |diag| {
5960 let derefs = recv_adjusts
6061 . iter ( )
6162 . filter ( |adj| matches ! ( adj. kind, Adjust :: Deref ( None ) ) )
6263 . count ( ) ;
6364
64- let mut sugg = "*" . repeat ( derefs + 1 ) ;
65- sugg += & snippet ( cx, receiver. span , "<expr>" ) ;
66-
6765 diag. note (
6866 "this returns the type id of the literal type `Box<_>` instead of the \
6967 type id of the boxed value, which is most likely not what you want",
7068 )
7169 . 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, \
7371 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+ }
8185 } ,
8286 ) ;
8387 }
0 commit comments