@@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet;
1313use rustc_data_structures:: stack:: ensure_sufficient_stack;
1414use rustc_errors:: {
1515 error_code, pluralize, struct_span_err, Applicability , Diagnostic , DiagnosticBuilder ,
16- ErrorGuaranteed , MultiSpan , Style ,
16+ ErrorGuaranteed , MultiSpan , Style , SuggestionStyle ,
1717} ;
1818use rustc_hir as hir;
1919use rustc_hir:: def:: DefKind ;
@@ -362,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> {
362362 err : & mut Diagnostic ,
363363 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
364364 ) ;
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+
365374 fn note_function_argument_obligation (
366375 & self ,
367376 body_id : LocalDefId ,
@@ -3521,15 +3530,92 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
35213530 err. replace_span_with ( path. ident . span , true ) ;
35223531 }
35233532 }
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
35303597 {
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+ ) ;
35333619 }
35343620 }
35353621 }
0 commit comments