1
1
use super :: FnCtxt ;
2
2
3
- use crate :: errors:: { AddReturnTypeSuggestion , ExpectedReturnTypeLabel , SuggestBoxing } ;
3
+ use crate :: errors:: {
4
+ AddReturnTypeSuggestion , ExpectedReturnTypeLabel , SuggestBoxing , SuggestConvertViaMethod ,
5
+ } ;
4
6
use crate :: fluent_generated as fluent;
5
7
use crate :: method:: probe:: { IsSuggestion , Mode , ProbeScope } ;
6
8
use rustc_ast:: util:: parser:: { ExprPrecedence , PREC_POSTFIX } ;
@@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
275
277
expected_ty_expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
276
278
) -> bool {
277
279
let expr = expr. peel_blocks ( ) ;
280
+ let methods = self . get_conversion_methods ( expr. span , expected, found, expr. hir_id ) ;
281
+
278
282
if let Some ( ( suggestion, msg, applicability, verbose, annotation) ) =
279
283
self . suggest_deref_or_ref ( expr, found, expected)
280
284
{
@@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
325
329
}
326
330
}
327
331
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) {
329
335
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) )
331
339
&& let ty:: FnDef ( def_id, ..) = * found. kind ( )
332
340
&& let Some ( sp) = self . tcx . hir ( ) . span_if_local ( def_id)
333
341
{
@@ -343,97 +351,142 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
343
351
err. span_label ( sp, format ! ( "{descr} `{name}` defined here" ) ) ;
344
352
}
345
353
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) {
347
357
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
+ }
359
359
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
+ ]
420
391
} else {
421
- ".map(|x| x.as_str())"
392
+ vec ! [ ( expr . span . shrink_to_hi ( ) , format! ( ".{}()" , conversion_method . name ) ) ]
422
393
} ;
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 ;
431
456
}
432
457
}
433
458
434
459
false
435
460
}
436
461
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
+
437
490
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
438
491
/// in the heap by calling `Box::new()`.
439
492
pub ( in super :: super ) fn suggest_boxing_when_appropriate (
0 commit comments