11use super :: FnCtxt ;
22
3- use crate :: errors:: { AddReturnTypeSuggestion , ExpectedReturnTypeLabel , SuggestBoxing } ;
3+ use crate :: errors:: {
4+ AddReturnTypeSuggestion , ExpectedReturnTypeLabel , SuggestBoxing , SuggestConvertViaMethod ,
5+ } ;
46use crate :: fluent_generated as fluent;
57use crate :: method:: probe:: { IsSuggestion , Mode , ProbeScope } ;
68use rustc_ast:: util:: parser:: { ExprPrecedence , PREC_POSTFIX } ;
@@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
275277 expected_ty_expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
276278 ) -> bool {
277279 let expr = expr. peel_blocks ( ) ;
280+ let methods = self . get_conversion_methods ( expr. span , expected, found, expr. hir_id ) ;
281+
278282 if let Some ( ( suggestion, msg, applicability, verbose, annotation) ) =
279283 self . suggest_deref_or_ref ( expr, found, expected)
280284 {
@@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
325329 }
326330 }
327331 return true ;
328- } else if self . suggest_else_fn_with_closure ( err, expr, found, expected) {
332+ }
333+
334+ if self . suggest_else_fn_with_closure ( err, expr, found, expected) {
329335 return true ;
330- } else if self . suggest_fn_call ( err, expr, found, |output| self . can_coerce ( output, expected) )
336+ }
337+
338+ if self . suggest_fn_call ( err, expr, found, |output| self . can_coerce ( output, expected) )
331339 && let ty:: FnDef ( def_id, ..) = * found. kind ( )
332340 && let Some ( sp) = self . tcx . hir ( ) . span_if_local ( def_id)
333341 {
@@ -343,97 +351,142 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
343351 err. span_label ( sp, format ! ( "{descr} `{name}` defined here" ) ) ;
344352 }
345353 return true ;
346- } else if self . suggest_cast ( err, expr, found, expected, expected_ty_expr) {
354+ }
355+
356+ if self . suggest_cast ( err, expr, found, expected, expected_ty_expr) {
347357 return true ;
348- } else {
349- let methods = self . get_conversion_methods ( expr. span , expected, found, expr. hir_id ) ;
350- if !methods. is_empty ( ) {
351- let mut suggestions = methods. iter ( )
352- . filter_map ( |conversion_method| {
353- let receiver_method_ident = expr. method_ident ( ) ;
354- if let Some ( method_ident) = receiver_method_ident
355- && method_ident. name == conversion_method. name
356- {
357- return None // do not suggest code that is already there (#53348)
358- }
358+ }
359359
360- let method_call_list = [ sym:: to_vec, sym:: to_string] ;
361- let mut sugg = if let ExprKind :: MethodCall ( receiver_method, ..) = expr. kind
362- && receiver_method. ident . name == sym:: clone
363- && method_call_list. contains ( & conversion_method. name )
364- // If receiver is `.clone()` and found type has one of those methods,
365- // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
366- // to an owned type (`Vec` or `String`). These conversions clone internally,
367- // so we remove the user's `clone` call.
368- {
369- vec ! [ (
370- receiver_method. ident. span,
371- conversion_method. name. to_string( )
372- ) ]
373- } else if expr. precedence ( ) . order ( )
374- < ExprPrecedence :: MethodCall . order ( )
375- {
376- vec ! [
377- ( expr. span. shrink_to_lo( ) , "(" . to_string( ) ) ,
378- ( expr. span. shrink_to_hi( ) , format!( ").{}()" , conversion_method. name) ) ,
379- ]
380- } else {
381- vec ! [ ( expr. span. shrink_to_hi( ) , format!( ".{}()" , conversion_method. name) ) ]
382- } ;
383- let struct_pat_shorthand_field = self . maybe_get_struct_pattern_shorthand_field ( expr) ;
384- if let Some ( name) = struct_pat_shorthand_field {
385- sugg. insert (
386- 0 ,
387- ( expr. span . shrink_to_lo ( ) , format ! ( "{}: " , name) ) ,
388- ) ;
389- }
390- Some ( sugg)
391- } )
392- . peekable ( ) ;
393- if suggestions. peek ( ) . is_some ( ) {
394- err. multipart_suggestions (
395- "try using a conversion method" ,
396- suggestions,
397- Applicability :: MaybeIncorrect ,
398- ) ;
399- return true ;
400- }
401- } else if let ty:: Adt ( found_adt, found_substs) = found. kind ( )
402- && self . tcx . is_diagnostic_item ( sym:: Option , found_adt. did ( ) )
403- && let ty:: Adt ( expected_adt, expected_substs) = expected. kind ( )
404- && self . tcx . is_diagnostic_item ( sym:: Option , expected_adt. did ( ) )
405- && let ty:: Ref ( _, inner_ty, _) = expected_substs. type_at ( 0 ) . kind ( )
406- && inner_ty. is_str ( )
407- {
408- let ty = found_substs. type_at ( 0 ) ;
409- let mut peeled = ty;
410- let mut ref_cnt = 0 ;
411- while let ty:: Ref ( _, inner, _) = peeled. kind ( ) {
412- peeled = * inner;
413- ref_cnt += 1 ;
414- }
415- if let ty:: Adt ( adt, _) = peeled. kind ( )
416- && Some ( adt. did ( ) ) == self . tcx . lang_items ( ) . string ( )
417- {
418- let sugg = if ref_cnt == 0 {
419- ".as_deref()"
360+ if !methods. is_empty ( ) {
361+ let mut suggestions = methods
362+ . iter ( )
363+ . filter_map ( |conversion_method| {
364+ let receiver_method_ident = expr. method_ident ( ) ;
365+ if let Some ( method_ident) = receiver_method_ident
366+ && method_ident. name == conversion_method. name
367+ {
368+ return None // do not suggest code that is already there (#53348)
369+ }
370+
371+ let method_call_list = [ sym:: to_vec, sym:: to_string] ;
372+ let mut sugg = if let ExprKind :: MethodCall ( receiver_method, ..) = expr. kind
373+ && receiver_method. ident . name == sym:: clone
374+ && method_call_list. contains ( & conversion_method. name )
375+ // If receiver is `.clone()` and found type has one of those methods,
376+ // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
377+ // to an owned type (`Vec` or `String`). These conversions clone internally,
378+ // so we remove the user's `clone` call.
379+ {
380+ vec ! [ (
381+ receiver_method. ident. span,
382+ conversion_method. name. to_string( )
383+ ) ]
384+ } else if expr. precedence ( ) . order ( )
385+ < ExprPrecedence :: MethodCall . order ( )
386+ {
387+ vec ! [
388+ ( expr. span. shrink_to_lo( ) , "(" . to_string( ) ) ,
389+ ( expr. span. shrink_to_hi( ) , format!( ").{}()" , conversion_method. name) ) ,
390+ ]
420391 } else {
421- ".map(|x| x.as_str())"
392+ vec ! [ ( expr . span . shrink_to_hi ( ) , format! ( ".{}()" , conversion_method . name ) ) ]
422393 } ;
423- err. span_suggestion_verbose (
424- expr. span . shrink_to_hi ( ) ,
425- fluent:: hir_typeck_convert_to_str,
426- sugg,
427- Applicability :: MachineApplicable ,
428- ) ;
429- return true ;
430- }
394+ let struct_pat_shorthand_field =
395+ self . maybe_get_struct_pattern_shorthand_field ( expr) ;
396+ if let Some ( name) = struct_pat_shorthand_field {
397+ sugg. insert ( 0 , ( expr. span . shrink_to_lo ( ) , format ! ( "{}: " , name) ) ) ;
398+ }
399+ Some ( sugg)
400+ } )
401+ . peekable ( ) ;
402+ if suggestions. peek ( ) . is_some ( ) {
403+ err. multipart_suggestions (
404+ "try using a conversion method" ,
405+ suggestions,
406+ Applicability :: MaybeIncorrect ,
407+ ) ;
408+ return true ;
409+ }
410+ }
411+
412+ if let Some ( ( found_ty_inner, expected_ty_inner, error_tys) ) =
413+ self . deconstruct_option_or_result ( found, expected)
414+ && let ty:: Ref ( _, peeled, hir:: Mutability :: Not ) = * expected_ty_inner. kind ( )
415+ {
416+ // Check that given `Result<_, E>`, our expected ty is `Result<_, &E>`
417+ let error_tys_equate_as_ref = error_tys. map_or ( true , |( found, expected) | {
418+ self . can_eq ( self . param_env , self . tcx . mk_imm_ref ( self . tcx . lifetimes . re_erased , found) , expected)
419+ } ) ;
420+ // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
421+ // but those checks need to be a bit more delicate and the benefit is diminishing.
422+ if self . can_eq ( self . param_env , found_ty_inner, peeled) && error_tys_equate_as_ref {
423+ err. subdiagnostic ( SuggestConvertViaMethod {
424+ span : expr. span . shrink_to_hi ( ) ,
425+ sugg : ".as_ref()" ,
426+ expected,
427+ found,
428+ } ) ;
429+ return true ;
430+ } else if let Some ( ( deref_ty, _) ) =
431+ self . autoderef ( expr. span , found_ty_inner) . silence_errors ( ) . nth ( 1 )
432+ && self . can_eq ( self . param_env , deref_ty, peeled)
433+ && error_tys_equate_as_ref
434+ {
435+ err. subdiagnostic ( SuggestConvertViaMethod {
436+ span : expr. span . shrink_to_hi ( ) ,
437+ sugg : ".as_deref()" ,
438+ expected,
439+ found,
440+ } ) ;
441+ return true ;
442+ } else if let ty:: Adt ( adt, _) = found_ty_inner. peel_refs ( ) . kind ( )
443+ && Some ( adt. did ( ) ) == self . tcx . lang_items ( ) . string ( )
444+ && peeled. is_str ( )
445+ && error_tys. map_or ( true , |( found, expected) | {
446+ self . can_eq ( self . param_env , found, expected)
447+ } )
448+ {
449+ err. span_suggestion_verbose (
450+ expr. span . shrink_to_hi ( ) ,
451+ fluent:: hir_typeck_convert_to_str,
452+ ".map(|x| x.as_str())" ,
453+ Applicability :: MachineApplicable ,
454+ ) ;
455+ return true ;
431456 }
432457 }
433458
434459 false
435460 }
436461
462+ fn deconstruct_option_or_result (
463+ & self ,
464+ found_ty : Ty < ' tcx > ,
465+ expected_ty : Ty < ' tcx > ,
466+ ) -> Option < ( Ty < ' tcx > , Ty < ' tcx > , Option < ( Ty < ' tcx > , Ty < ' tcx > ) > ) > {
467+ let ty:: Adt ( found_adt, found_substs) = found_ty. peel_refs ( ) . kind ( ) else {
468+ return None ;
469+ } ;
470+ let ty:: Adt ( expected_adt, expected_substs) = expected_ty. kind ( ) else {
471+ return None ;
472+ } ;
473+ if self . tcx . is_diagnostic_item ( sym:: Option , found_adt. did ( ) )
474+ && self . tcx . is_diagnostic_item ( sym:: Option , expected_adt. did ( ) )
475+ {
476+ Some ( ( found_substs. type_at ( 0 ) , expected_substs. type_at ( 0 ) , None ) )
477+ } else if self . tcx . is_diagnostic_item ( sym:: Result , found_adt. did ( ) )
478+ && self . tcx . is_diagnostic_item ( sym:: Result , expected_adt. did ( ) )
479+ {
480+ Some ( (
481+ found_substs. type_at ( 0 ) ,
482+ expected_substs. type_at ( 0 ) ,
483+ Some ( ( found_substs. type_at ( 1 ) , expected_substs. type_at ( 1 ) ) ) ,
484+ ) )
485+ } else {
486+ None
487+ }
488+ }
489+
437490 /// When encountering the expected boxed value allocated in the stack, suggest allocating it
438491 /// in the heap by calling `Box::new()`.
439492 pub ( in super :: super ) fn suggest_boxing_when_appropriate (
0 commit comments