@@ -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 ;
@@ -27,6 +27,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
27
27
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
28
28
use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk , LateBoundRegionConversionTime } ;
29
29
use rustc_middle:: hir:: map;
30
+ use rustc_middle:: query:: Key ;
30
31
use rustc_middle:: ty:: error:: TypeError :: { self , Sorts } ;
31
32
use rustc_middle:: ty:: {
32
33
self , suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind ,
@@ -357,6 +358,15 @@ pub trait TypeErrCtxtExt<'tcx> {
357
358
err : & mut Diagnostic ,
358
359
trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
359
360
) ;
361
+
362
+ fn suggest_option_method_if_applicable (
363
+ & self ,
364
+ failed_pred : ty:: Predicate < ' tcx > ,
365
+ param_env : ty:: ParamEnv < ' tcx > ,
366
+ err : & mut Diagnostic ,
367
+ expr : & hir:: Expr < ' _ > ,
368
+ ) ;
369
+
360
370
fn note_function_argument_obligation (
361
371
& self ,
362
372
body_id : LocalDefId ,
@@ -3660,15 +3670,92 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
3660
3670
err. replace_span_with ( path. ident . span , true ) ;
3661
3671
}
3662
3672
}
3663
- if let Some ( Node :: Expr ( hir:: Expr {
3664
- kind :
3665
- hir:: ExprKind :: Call ( hir:: Expr { span, .. } , _)
3666
- | hir:: ExprKind :: MethodCall ( hir:: PathSegment { ident : Ident { span, .. } , .. } , ..) ,
3667
- ..
3668
- } ) ) = hir. find ( call_hir_id)
3673
+
3674
+ if let Some ( Node :: Expr ( expr) ) = hir. find ( call_hir_id) {
3675
+ if let hir:: ExprKind :: Call ( hir:: Expr { span, .. } , _)
3676
+ | hir:: ExprKind :: MethodCall (
3677
+ hir:: PathSegment { ident : Ident { span, .. } , .. } ,
3678
+ ..,
3679
+ ) = expr. kind
3680
+ {
3681
+ if Some ( * span) != err. span . primary_span ( ) {
3682
+ err. span_label ( * span, "required by a bound introduced by this call" ) ;
3683
+ }
3684
+ }
3685
+
3686
+ if let hir:: ExprKind :: MethodCall ( _, expr, ..) = expr. kind {
3687
+ self . suggest_option_method_if_applicable ( failed_pred, param_env, err, expr) ;
3688
+ }
3689
+ }
3690
+ }
3691
+
3692
+ fn suggest_option_method_if_applicable (
3693
+ & self ,
3694
+ failed_pred : ty:: Predicate < ' tcx > ,
3695
+ param_env : ty:: ParamEnv < ' tcx > ,
3696
+ err : & mut Diagnostic ,
3697
+ expr : & hir:: Expr < ' _ > ,
3698
+ ) {
3699
+ let tcx = self . tcx ;
3700
+ let infcx = self . infcx ;
3701
+ let Some ( typeck_results) = self . typeck_results . as_ref ( ) else { return } ;
3702
+
3703
+ // Make sure we're dealing with the `Option` type.
3704
+ let Some ( ty_adt_did) = typeck_results. expr_ty_adjusted ( expr) . ty_adt_id ( ) else { return } ;
3705
+ if !tcx. is_diagnostic_item ( sym:: Option , ty_adt_did) {
3706
+ return ;
3707
+ }
3708
+
3709
+ // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`,
3710
+ // then suggest `Option::as_deref(_mut)` if `U` can deref to `T`
3711
+ if let ty:: PredicateKind :: Clause ( ty:: Clause :: Trait ( ty:: TraitPredicate { trait_ref, .. } ) )
3712
+ = failed_pred. kind ( ) . skip_binder ( )
3713
+ && tcx. is_fn_trait ( trait_ref. def_id )
3714
+ && let [ self_ty, found_ty] = trait_ref. substs . as_slice ( )
3715
+ && let Some ( fn_ty) = self_ty. as_type ( ) . filter ( |ty| ty. is_fn ( ) )
3716
+ && let fn_sig @ ty:: FnSig {
3717
+ abi : abi:: Abi :: Rust ,
3718
+ c_variadic : false ,
3719
+ unsafety : hir:: Unsafety :: Normal ,
3720
+ ..
3721
+ } = fn_ty. fn_sig ( tcx) . skip_binder ( )
3722
+
3723
+ // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
3724
+ && let Some ( & ty:: Ref ( _, target_ty, needs_mut) ) = fn_sig. inputs ( ) . first ( ) . map ( |t| t. kind ( ) )
3725
+ && !target_ty. has_escaping_bound_vars ( )
3726
+
3727
+ // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U`
3728
+ && let Some ( ty:: Tuple ( tys) ) = found_ty. as_type ( ) . map ( Ty :: kind)
3729
+ && let & [ found_ty] = tys. as_slice ( )
3730
+ && !found_ty. has_escaping_bound_vars ( )
3731
+
3732
+ // Extract `<U as Deref>::Target` assoc type and check that it is `T`
3733
+ && let Some ( deref_target_did) = tcx. lang_items ( ) . deref_target ( )
3734
+ && let projection = tcx. mk_projection ( deref_target_did, tcx. mk_substs ( & [ ty:: GenericArg :: from ( found_ty) ] ) )
3735
+ && let Ok ( deref_target) = tcx. try_normalize_erasing_regions ( param_env, projection)
3736
+ && deref_target == target_ty
3669
3737
{
3670
- if Some ( * span) != err. span . primary_span ( ) {
3671
- err. span_label ( * span, "required by a bound introduced by this call" ) ;
3738
+ let help = if let hir:: Mutability :: Mut = needs_mut
3739
+ && let Some ( deref_mut_did) = tcx. lang_items ( ) . deref_mut_trait ( )
3740
+ && infcx
3741
+ . type_implements_trait ( deref_mut_did, iter:: once ( found_ty) , param_env)
3742
+ . must_apply_modulo_regions ( )
3743
+ {
3744
+ Some ( ( "call `Option::as_deref_mut()` first" , ".as_deref_mut()" ) )
3745
+ } else if let hir:: Mutability :: Not = needs_mut {
3746
+ Some ( ( "call `Option::as_deref()` first" , ".as_deref()" ) )
3747
+ } else {
3748
+ None
3749
+ } ;
3750
+
3751
+ if let Some ( ( msg, sugg) ) = help {
3752
+ err. span_suggestion_with_style (
3753
+ expr. span . shrink_to_hi ( ) ,
3754
+ msg,
3755
+ sugg,
3756
+ Applicability :: MaybeIncorrect ,
3757
+ SuggestionStyle :: ShowAlways
3758
+ ) ;
3672
3759
}
3673
3760
}
3674
3761
}
0 commit comments