@@ -2,7 +2,7 @@ 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;
@@ -29,6 +29,7 @@ fn check<'tcx>(
29
29
node_args : ty:: GenericArgsRef < ' tcx > ,
30
30
kind : FunctionKind ,
31
31
primary_span : Span ,
32
+ qpath : Option < & QPath < ' _ > > ,
32
33
) {
33
34
if let & [ self_ty, other_ty] = node_args. as_slice ( )
34
35
// useless_conversion already warns `T::try_from(T)`, so ignore it here
@@ -45,47 +46,79 @@ fn check<'tcx>(
45
46
&& implements_trait ( cx, self_ty, from_into_trait, & [ other_ty] )
46
47
&& let Some ( other_ty) = other_ty. as_type ( )
47
48
{
49
+ // Extend the span to include the unwrap/expect call:
50
+ // `foo.try_into().expect("..")`
51
+ // ^^^^^^^^^^^^^^^^^^^^^^^
52
+ //
53
+ // `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
54
+ // so that can be machine-applicable
48
55
let parent_unwrap_call = get_parent_expr ( cx, expr) . and_then ( |parent| {
49
56
if let ExprKind :: MethodCall ( path, .., span) = parent. kind
50
57
&& let sym:: unwrap | sym:: expect = path. ident . name
51
58
{
52
- Some ( span)
59
+ // include `.` before `unwrap`/`expect`
60
+ Some ( span. with_lo ( expr. span . hi ( ) ) )
53
61
} else {
54
62
None
55
63
}
56
64
} ) ;
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
65
66
- (
67
- self_ty,
68
- other_ty,
69
- "into()" ,
70
- primary_span. with_hi ( unwrap_span. hi ( ) ) ,
71
- Applicability :: MachineApplicable ,
72
- )
66
+ let span = if let Some ( unwrap_call) = parent_unwrap_call {
67
+ primary_span. with_hi ( unwrap_call. hi ( ) )
68
+ } else {
69
+ primary_span
70
+ } ;
71
+
72
+ let qpath_spans = qpath. and_then ( |qpath| match qpath {
73
+ QPath :: Resolved ( _, path) => {
74
+ let segments = path. segments . iter ( ) . map ( |seg| seg. ident ) . collect :: < Vec < _ > > ( ) ;
75
+ ( segments. len ( ) == 2 ) . then ( || vec ! [ segments[ 0 ] . span, segments[ 1 ] . span] )
76
+ } ,
77
+ QPath :: TypeRelative ( _, seg) => Some ( vec ! [ seg. ident. span] ) ,
78
+ QPath :: LangItem ( _, _) => unreachable ! ( "`TryFrom` and `TryInto` are not lang items" ) ,
79
+ } ) ;
80
+
81
+ let ( source_ty, target_ty, sugg, applicability) = match ( kind, & qpath_spans, parent_unwrap_call) {
82
+ ( FunctionKind :: TryIntoMethod , _, Some ( unwrap_span) ) => {
83
+ let sugg = vec ! [ ( primary_span, String :: from( "into" ) ) , ( unwrap_span, String :: new( ) ) ] ;
84
+ ( self_ty, other_ty, sugg, Applicability :: MachineApplicable )
85
+ } ,
86
+ ( FunctionKind :: TryFromFunction , Some ( spans) , Some ( unwrap_span) ) => {
87
+ let sugg = match spans. len ( ) {
88
+ 1 => vec ! [ ( spans[ 0 ] , String :: from( "from" ) ) , ( unwrap_span, String :: new( ) ) ] ,
89
+ 2 => vec ! [
90
+ ( spans[ 0 ] , String :: from( "From" ) ) ,
91
+ ( spans[ 1 ] , String :: from( "from" ) ) ,
92
+ ( unwrap_span, String :: new( ) ) ,
93
+ ] ,
94
+ _ => unreachable ! ( ) ,
95
+ } ;
96
+ ( other_ty, self_ty, sugg, Applicability :: MachineApplicable )
97
+ } ,
98
+ ( FunctionKind :: TryIntoFunction , Some ( spans) , Some ( unwrap_span) ) => {
99
+ let sugg = match spans. len ( ) {
100
+ 1 => vec ! [ ( spans[ 0 ] , String :: from( "into" ) ) , ( unwrap_span, String :: new( ) ) ] ,
101
+ 2 => vec ! [
102
+ ( spans[ 0 ] , String :: from( "Into" ) ) ,
103
+ ( spans[ 1 ] , String :: from( "into" ) ) ,
104
+ ( unwrap_span, String :: new( ) ) ,
105
+ ] ,
106
+ _ => unreachable ! ( ) ,
107
+ } ;
108
+ ( self_ty, other_ty, sugg, Applicability :: MachineApplicable )
109
+ } ,
110
+ ( FunctionKind :: TryFromFunction , _, _) => {
111
+ let sugg = vec ! [ ( primary_span, String :: from( "From::from" ) ) ] ;
112
+ ( other_ty, self_ty, sugg, Applicability :: Unspecified )
113
+ } ,
114
+ ( FunctionKind :: TryIntoFunction , _, _) => {
115
+ let sugg = vec ! [ ( primary_span, String :: from( "Into::into" ) ) ] ;
116
+ ( self_ty, other_ty, sugg, Applicability :: Unspecified )
117
+ } ,
118
+ ( FunctionKind :: TryIntoMethod , _, _) => {
119
+ let sugg = vec ! [ ( primary_span, String :: from( "into" ) ) ] ;
120
+ ( self_ty, other_ty, sugg, Applicability :: Unspecified )
73
121
} ,
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 ) ,
89
122
} ;
90
123
91
124
span_lint_and_then (
@@ -97,7 +130,7 @@ fn check<'tcx>(
97
130
with_forced_trimmed_paths ! ( {
98
131
diag. note( format!( "converting `{source_ty}` to `{target_ty}` cannot fail" ) ) ;
99
132
} ) ;
100
- diag. span_suggestion ( span , "use" , sugg, applicability) ;
133
+ diag. multipart_suggestion ( "use" , sugg, applicability) ;
101
134
} ,
102
135
) ;
103
136
}
@@ -113,6 +146,7 @@ pub(super) fn check_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
113
146
cx. typeck_results ( ) . node_args ( expr. hir_id ) ,
114
147
FunctionKind :: TryIntoMethod ,
115
148
path. ident . span ,
149
+ None ,
116
150
) ;
117
151
}
118
152
}
@@ -135,6 +169,7 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
135
169
_ => return ,
136
170
} ,
137
171
callee. span ,
172
+ Some ( qpath) ,
138
173
) ;
139
174
}
140
175
}
0 commit comments