@@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet;
13
13
use rustc_data_structures:: stack:: ensure_sufficient_stack;
14
14
use rustc_errors:: {
15
15
error_code, pluralize, struct_span_err, Applicability , Diagnostic , DiagnosticBuilder ,
16
- ErrorGuaranteed , MultiSpan , Style ,
16
+ ErrorGuaranteed , MultiSpan , Style , SuggestionStyle ,
17
17
} ;
18
18
use rustc_hir as hir;
19
19
use rustc_hir:: def:: DefKind ;
@@ -362,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> {
362
362
err : & mut Diagnostic ,
363
363
trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
364
364
) ;
365
+
366
+ fn suggest_option_method_if_applicable (
367
+ & self ,
368
+ failed_pred : ty:: Predicate < ' tcx > ,
369
+ param_env : ty:: ParamEnv < ' tcx > ,
370
+ err : & mut Diagnostic ,
371
+ expr : & hir:: Expr < ' _ > ,
372
+ ) ;
373
+
365
374
fn note_function_argument_obligation (
366
375
& self ,
367
376
body_id : LocalDefId ,
@@ -3521,15 +3530,92 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
3521
3530
err. replace_span_with ( path. ident . span , true ) ;
3522
3531
}
3523
3532
}
3524
- if let Some ( Node :: Expr ( hir:: Expr {
3525
- kind :
3526
- hir:: ExprKind :: Call ( hir:: Expr { span, .. } , _)
3527
- | hir:: ExprKind :: MethodCall ( hir:: PathSegment { ident : Ident { span, .. } , .. } , ..) ,
3528
- ..
3529
- } ) ) = hir. find ( call_hir_id)
3533
+
3534
+ if let Some ( Node :: Expr ( expr) ) = hir. find ( call_hir_id) {
3535
+ if let hir:: ExprKind :: Call ( hir:: Expr { span, .. } , _)
3536
+ | hir:: ExprKind :: MethodCall (
3537
+ hir:: PathSegment { ident : Ident { span, .. } , .. } ,
3538
+ ..,
3539
+ ) = expr. kind
3540
+ {
3541
+ if Some ( * span) != err. span . primary_span ( ) {
3542
+ err. span_label ( * span, "required by a bound introduced by this call" ) ;
3543
+ }
3544
+ }
3545
+
3546
+ if let hir:: ExprKind :: MethodCall ( _, expr, ..) = expr. kind {
3547
+ self . suggest_option_method_if_applicable ( failed_pred, param_env, err, expr) ;
3548
+ }
3549
+ }
3550
+ }
3551
+
3552
+ fn suggest_option_method_if_applicable (
3553
+ & self ,
3554
+ failed_pred : ty:: Predicate < ' tcx > ,
3555
+ param_env : ty:: ParamEnv < ' tcx > ,
3556
+ err : & mut Diagnostic ,
3557
+ expr : & hir:: Expr < ' _ > ,
3558
+ ) {
3559
+ let tcx = self . tcx ;
3560
+ let infcx = self . infcx ;
3561
+ let Some ( typeck_results) = self . typeck_results . as_ref ( ) else { return } ;
3562
+
3563
+ // Make sure we're dealing with the `Option` type.
3564
+ let Some ( option_ty_adt) = typeck_results. expr_ty_adjusted ( expr) . ty_adt_def ( ) else { return } ;
3565
+ if !tcx. is_diagnostic_item ( sym:: Option , option_ty_adt. did ( ) ) {
3566
+ return ;
3567
+ }
3568
+
3569
+ // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`,
3570
+ // then suggest `Option::as_deref(_mut)` if `U` can deref to `T`
3571
+ if let ty:: PredicateKind :: Clause ( ty:: Clause :: Trait ( ty:: TraitPredicate { trait_ref, .. } ) )
3572
+ = failed_pred. kind ( ) . skip_binder ( )
3573
+ && tcx. is_fn_trait ( trait_ref. def_id )
3574
+ && let [ self_ty, found_ty] = trait_ref. substs . as_slice ( )
3575
+ && let Some ( fn_ty) = self_ty. as_type ( ) . filter ( |ty| ty. is_fn ( ) )
3576
+ && let fn_sig @ ty:: FnSig {
3577
+ abi : abi:: Abi :: Rust ,
3578
+ c_variadic : false ,
3579
+ unsafety : hir:: Unsafety :: Normal ,
3580
+ ..
3581
+ } = fn_ty. fn_sig ( tcx) . skip_binder ( )
3582
+
3583
+ // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
3584
+ && let Some ( & ty:: Ref ( _, target_ty, needs_mut) ) = fn_sig. inputs ( ) . first ( ) . map ( |t| t. kind ( ) )
3585
+ && !target_ty. has_escaping_bound_vars ( )
3586
+
3587
+ // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U`
3588
+ && let Some ( ty:: Tuple ( tys) ) = found_ty. as_type ( ) . map ( Ty :: kind)
3589
+ && let & [ found_ty] = tys. as_slice ( )
3590
+ && !found_ty. has_escaping_bound_vars ( )
3591
+
3592
+ // Extract `<U as Deref>::Target` assoc type and check that it is `T`
3593
+ && let Some ( deref_target_did) = tcx. lang_items ( ) . deref_target ( )
3594
+ && let projection = tcx. mk_projection ( deref_target_did, tcx. mk_substs ( & [ ty:: GenericArg :: from ( found_ty) ] ) )
3595
+ && let Ok ( deref_target) = tcx. try_normalize_erasing_regions ( param_env, projection)
3596
+ && deref_target == target_ty
3530
3597
{
3531
- if Some ( * span) != err. span . primary_span ( ) {
3532
- err. span_label ( * span, "required by a bound introduced by this call" ) ;
3598
+ let help = if let hir:: Mutability :: Mut = needs_mut
3599
+ && let Some ( deref_mut_did) = tcx. lang_items ( ) . deref_mut_trait ( )
3600
+ && infcx
3601
+ . type_implements_trait ( deref_mut_did, iter:: once ( found_ty) , param_env)
3602
+ . must_apply_modulo_regions ( )
3603
+ {
3604
+ Some ( ( "call `Option::as_deref_mut()` first" , ".as_deref_mut()" ) )
3605
+ } else if let hir:: Mutability :: Not = needs_mut {
3606
+ Some ( ( "call `Option::as_deref()` first" , ".as_deref()" ) )
3607
+ } else {
3608
+ None
3609
+ } ;
3610
+
3611
+ if let Some ( ( msg, sugg) ) = help {
3612
+ err. span_suggestion_with_style (
3613
+ expr. span . shrink_to_hi ( ) ,
3614
+ msg,
3615
+ sugg,
3616
+ Applicability :: MaybeIncorrect ,
3617
+ SuggestionStyle :: ShowAlways
3618
+ ) ;
3533
3619
}
3534
3620
}
3535
3621
}
0 commit comments