@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
22use clippy_utils:: get_parent_expr;
33use clippy_utils:: ty:: implements_trait;
44use rustc_errors:: Applicability ;
5- use rustc_hir:: { Expr , ExprKind } ;
5+ use rustc_hir:: { Expr , ExprKind , QPath } ;
66use rustc_lint:: LateContext ;
77use rustc_middle:: ty;
88use rustc_middle:: ty:: print:: with_forced_trimmed_paths;
@@ -29,6 +29,7 @@ fn check<'tcx>(
2929 node_args : ty:: GenericArgsRef < ' tcx > ,
3030 kind : FunctionKind ,
3131 primary_span : Span ,
32+ qpath : Option < & QPath < ' _ > > ,
3233) {
3334 if let & [ self_ty, other_ty] = node_args. as_slice ( )
3435 // useless_conversion already warns `T::try_from(T)`, so ignore it here
@@ -45,47 +46,79 @@ fn check<'tcx>(
4546 && implements_trait ( cx, self_ty, from_into_trait, & [ other_ty] )
4647 && let Some ( other_ty) = other_ty. as_type ( )
4748 {
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
4855 let parent_unwrap_call = get_parent_expr ( cx, expr) . and_then ( |parent| {
4956 if let ExprKind :: MethodCall ( path, .., span) = parent. kind
5057 && let sym:: unwrap | sym:: expect = path. ident . name
5158 {
52- Some ( span)
59+ // include `.` before `unwrap`/`expect`
60+ Some ( span. with_lo ( expr. span . hi ( ) ) )
5361 } else {
5462 None
5563 }
5664 } ) ;
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
6565
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 )
73121 } ,
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 ) ,
89122 } ;
90123
91124 span_lint_and_then (
@@ -97,7 +130,7 @@ fn check<'tcx>(
97130 with_forced_trimmed_paths ! ( {
98131 diag. note( format!( "converting `{source_ty}` to `{target_ty}` cannot fail" ) ) ;
99132 } ) ;
100- diag. span_suggestion ( span , "use" , sugg, applicability) ;
133+ diag. multipart_suggestion ( "use" , sugg, applicability) ;
101134 } ,
102135 ) ;
103136 }
@@ -113,6 +146,7 @@ pub(super) fn check_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
113146 cx. typeck_results ( ) . node_args ( expr. hir_id ) ,
114147 FunctionKind :: TryIntoMethod ,
115148 path. ident . span ,
149+ None ,
116150 ) ;
117151 }
118152}
@@ -135,6 +169,7 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
135169 _ => return ,
136170 } ,
137171 callee. span ,
172+ Some ( qpath) ,
138173 ) ;
139174 }
140175}
0 commit comments