@@ -5,13 +5,33 @@ use rustc_errors::Applicability;
55use rustc_hir:: Expr ;
66use rustc_lint:: LateContext ;
77use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
8+ use rustc_middle:: ty:: print:: with_forced_trimmed_paths;
89use rustc_middle:: ty:: { self , ExistentialPredicate , Ty } ;
910use rustc_span:: { sym, Span } ;
1011
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 {
1221 if let ty:: Dynamic ( preds, ..) = ty. kind ( ) {
1322 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+ } ,
1535 _ => false ,
1636 } )
1737 } else {
@@ -26,36 +46,42 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
2646 && let ty:: Ref ( _, ty, _) = recv_ty. kind ( )
2747 && let ty:: Adt ( adt, args) = ty. kind ( )
2848 && 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 ( )
3051 {
52+ let ty_name = with_forced_trimmed_paths ! ( ty. to_string( ) ) ;
53+
3154 span_lint_and_then (
3255 cx,
3356 TYPE_ID_ON_BOX ,
3457 call_span,
35- "calling `.type_id()` on a `Box<dyn Any>`" ,
58+ & format ! ( "calling `.type_id()` on `{ty_name}`" ) ,
3659 |diag| {
3760 let derefs = recv_adjusts
3861 . iter ( )
3962 . filter ( |adj| matches ! ( adj. kind, Adjust :: Deref ( None ) ) )
4063 . count ( ) ;
4164
42- let mut sugg = "*" . repeat ( derefs + 1 ) ;
43- sugg += & snippet ( cx, receiver. span , "<expr>" ) ;
44-
4565 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 \
4767 type id of the boxed value, which is most likely not what you want",
4868 )
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+ }
5985 } ,
6086 ) ;
6187 }
0 commit comments