@@ -2,25 +2,79 @@ use clippy_utils::diagnostics::span_lint_and_then;
2
2
use clippy_utils:: get_parent_expr;
3
3
use clippy_utils:: ty:: implements_trait;
4
4
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { Expr , ExprKind } ;
5
+ use rustc_hir:: { Expr , ExprKind , QPath } ;
6
6
use rustc_lint:: LateContext ;
7
7
use rustc_middle:: ty;
8
8
use rustc_middle:: ty:: print:: with_forced_trimmed_paths;
9
9
use rustc_span:: { sym, Span } ;
10
10
11
11
use super :: UNNECESSARY_FALLIBLE_CONVERSIONS ;
12
12
13
+ #[ derive( Copy , Clone ) ]
14
+ enum SpansKind {
15
+ TraitFn { trait_span : Span , fn_span : Span } ,
16
+ Fn { fn_span : Span } ,
17
+ }
18
+
13
19
/// What function is being called and whether that call is written as a method call or a function
14
20
/// call
15
21
#[ derive( Copy , Clone ) ]
16
22
#[ expect( clippy:: enum_variant_names) ]
17
23
enum FunctionKind {
18
24
/// `T::try_from(U)`
19
- TryFromFunction ,
25
+ TryFromFunction ( Option < SpansKind > ) ,
20
26
/// `t.try_into()`
21
27
TryIntoMethod ,
22
28
/// `U::try_into(t)`
23
- TryIntoFunction ,
29
+ TryIntoFunction ( Option < SpansKind > ) ,
30
+ }
31
+
32
+ impl FunctionKind {
33
+ fn appl_sugg ( & self , parent_unwrap_call : Option < Span > , primary_span : Span ) -> ( Applicability , Vec < ( Span , String ) > ) {
34
+ let Some ( unwrap_span) = parent_unwrap_call else {
35
+ return ( Applicability :: Unspecified , self . default_sugg ( primary_span) ) ;
36
+ } ;
37
+
38
+ match & self {
39
+ FunctionKind :: TryFromFunction ( None ) | FunctionKind :: TryIntoFunction ( None ) => {
40
+ ( Applicability :: Unspecified , self . default_sugg ( primary_span) )
41
+ } ,
42
+ _ => (
43
+ Applicability :: MachineApplicable ,
44
+ self . machine_applicable_sugg ( primary_span, unwrap_span) ,
45
+ ) ,
46
+ }
47
+ }
48
+
49
+ fn default_sugg ( & self , primary_span : Span ) -> Vec < ( Span , String ) > {
50
+ let replacement = match * self {
51
+ FunctionKind :: TryFromFunction ( _) => "From::from" ,
52
+ FunctionKind :: TryIntoFunction ( _) => "Into::into" ,
53
+ FunctionKind :: TryIntoMethod => "into" ,
54
+ } ;
55
+
56
+ vec ! [ ( primary_span, String :: from( replacement) ) ]
57
+ }
58
+
59
+ fn machine_applicable_sugg ( & self , primary_span : Span , unwrap_span : Span ) -> Vec < ( Span , String ) > {
60
+ let ( trait_name, fn_name) = match self {
61
+ FunctionKind :: TryFromFunction ( _) => ( "From" . to_owned ( ) , "from" . to_owned ( ) ) ,
62
+ FunctionKind :: TryIntoFunction ( _) | FunctionKind :: TryIntoMethod => ( "Into" . to_owned ( ) , "into" . to_owned ( ) ) ,
63
+ } ;
64
+
65
+ let mut sugg = match * self {
66
+ FunctionKind :: TryFromFunction ( Some ( spans) ) | FunctionKind :: TryIntoFunction ( Some ( spans) ) => match spans {
67
+ SpansKind :: TraitFn { trait_span, fn_span } => vec ! [ ( trait_span, trait_name) , ( fn_span, fn_name) ] ,
68
+ SpansKind :: Fn { fn_span } => vec ! [ ( fn_span, fn_name) ] ,
69
+ } ,
70
+ FunctionKind :: TryIntoMethod => vec ! [ ( primary_span, fn_name) ] ,
71
+ // Or the suggestion is not machine-applicable
72
+ _ => unreachable ! ( ) ,
73
+ } ;
74
+
75
+ sugg. push ( ( unwrap_span, String :: new ( ) ) ) ;
76
+ sugg
77
+ }
24
78
}
25
79
26
80
fn check < ' tcx > (
@@ -35,8 +89,8 @@ fn check<'tcx>(
35
89
&& self_ty != other_ty
36
90
&& let Some ( self_ty) = self_ty. as_type ( )
37
91
&& let Some ( from_into_trait) = cx. tcx . get_diagnostic_item ( match kind {
38
- FunctionKind :: TryFromFunction => sym:: From ,
39
- FunctionKind :: TryIntoMethod | FunctionKind :: TryIntoFunction => sym:: Into ,
92
+ FunctionKind :: TryFromFunction ( _ ) => sym:: From ,
93
+ FunctionKind :: TryIntoMethod | FunctionKind :: TryIntoFunction ( _ ) => sym:: Into ,
40
94
} )
41
95
// If `T: TryFrom<U>` and `T: From<U>` both exist, then that means that the `TryFrom`
42
96
// _must_ be from the blanket impl and cannot have been manually implemented
@@ -45,49 +99,37 @@ fn check<'tcx>(
45
99
&& implements_trait ( cx, self_ty, from_into_trait, & [ other_ty] )
46
100
&& let Some ( other_ty) = other_ty. as_type ( )
47
101
{
102
+ // Extend the span to include the unwrap/expect call:
103
+ // `foo.try_into().expect("..")`
104
+ // ^^^^^^^^^^^^^^^^^^^^^^^
105
+ //
106
+ // `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
107
+ // so that can be machine-applicable
48
108
let parent_unwrap_call = get_parent_expr ( cx, expr) . and_then ( |parent| {
49
109
if let ExprKind :: MethodCall ( path, .., span) = parent. kind
50
110
&& let sym:: unwrap | sym:: expect = path. ident . name
51
111
{
52
- Some ( span)
112
+ // include `.` before `unwrap`/`expect`
113
+ Some ( span. with_lo ( expr. span . hi ( ) ) )
53
114
} else {
54
115
None
55
116
}
56
117
} ) ;
57
- let ( source_ty, target_ty, sugg, span, applicability) = match kind {
58
- FunctionKind :: TryIntoMethod if let Some ( unwrap_span) = parent_unwrap_call => {
59
- // Extend the span to include the unwrap/expect call:
60
- // `foo.try_into().expect("..")`
61
- // ^^^^^^^^^^^^^^^^^^^^^^^
62
- //
63
- // `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
64
- // so that can be machine-applicable
65
-
66
- (
67
- self_ty,
68
- other_ty,
69
- "into()" ,
70
- primary_span. with_hi ( unwrap_span. hi ( ) ) ,
71
- Applicability :: MachineApplicable ,
72
- )
73
- } ,
74
- FunctionKind :: TryFromFunction => (
75
- other_ty,
76
- self_ty,
77
- "From::from" ,
78
- primary_span,
79
- Applicability :: Unspecified ,
80
- ) ,
81
- FunctionKind :: TryIntoFunction => (
82
- self_ty,
83
- other_ty,
84
- "Into::into" ,
85
- primary_span,
86
- Applicability :: Unspecified ,
87
- ) ,
88
- FunctionKind :: TryIntoMethod => ( self_ty, other_ty, "into" , primary_span, Applicability :: Unspecified ) ,
118
+
119
+ // If there is an unwrap/expect call, extend the span to include the call
120
+ let span = if let Some ( unwrap_call) = parent_unwrap_call {
121
+ primary_span. with_hi ( unwrap_call. hi ( ) )
122
+ } else {
123
+ primary_span
124
+ } ;
125
+
126
+ let ( source_ty, target_ty) = match kind {
127
+ FunctionKind :: TryIntoMethod | FunctionKind :: TryIntoFunction ( _) => ( self_ty, other_ty) ,
128
+ FunctionKind :: TryFromFunction ( _) => ( other_ty, self_ty) ,
89
129
} ;
90
130
131
+ let ( applicability, sugg) = kind. appl_sugg ( parent_unwrap_call, primary_span) ;
132
+
91
133
span_lint_and_then (
92
134
cx,
93
135
UNNECESSARY_FALLIBLE_CONVERSIONS ,
@@ -97,7 +139,7 @@ fn check<'tcx>(
97
139
with_forced_trimmed_paths ! ( {
98
140
diag. note( format!( "converting `{source_ty}` to `{target_ty}` cannot fail" ) ) ;
99
141
} ) ;
100
- diag. span_suggestion ( span , "use" , sugg, applicability) ;
142
+ diag. multipart_suggestion ( "use" , sugg, applicability) ;
101
143
} ,
102
144
) ;
103
145
}
@@ -125,13 +167,30 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
125
167
&& let Some ( item_def_id) = cx. qpath_res ( qpath, callee. hir_id ) . opt_def_id ( )
126
168
&& let Some ( trait_def_id) = cx. tcx . trait_of_item ( item_def_id)
127
169
{
170
+ let qpath_spans = match qpath {
171
+ QPath :: Resolved ( _, path) => {
172
+ if let [ trait_seg, fn_seg] = path. segments {
173
+ Some ( SpansKind :: TraitFn {
174
+ trait_span : trait_seg. ident . span ,
175
+ fn_span : fn_seg. ident . span ,
176
+ } )
177
+ } else {
178
+ None
179
+ }
180
+ } ,
181
+ QPath :: TypeRelative ( _, seg) => Some ( SpansKind :: Fn {
182
+ fn_span : seg. ident . span ,
183
+ } ) ,
184
+ QPath :: LangItem ( _, _) => unreachable ! ( "`TryFrom` and `TryInto` are not lang items" ) ,
185
+ } ;
186
+
128
187
check (
129
188
cx,
130
189
expr,
131
190
cx. typeck_results ( ) . node_args ( callee. hir_id ) ,
132
191
match cx. tcx . get_diagnostic_name ( trait_def_id) {
133
- Some ( sym:: TryFrom ) => FunctionKind :: TryFromFunction ,
134
- Some ( sym:: TryInto ) => FunctionKind :: TryIntoFunction ,
192
+ Some ( sym:: TryFrom ) => FunctionKind :: TryFromFunction ( qpath_spans ) ,
193
+ Some ( sym:: TryInto ) => FunctionKind :: TryIntoFunction ( qpath_spans ) ,
135
194
_ => return ,
136
195
} ,
137
196
callee. span ,
0 commit comments