@@ -63,10 +63,11 @@ use rustc_hir::{self as hir};
6363use rustc_macros:: extension;
6464use rustc_middle:: bug;
6565use rustc_middle:: dep_graph:: DepContext ;
66+ use rustc_middle:: traits:: PatternOriginExpr ;
6667use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeErrorToStringExt } ;
6768use rustc_middle:: ty:: print:: { PrintError , PrintTraitRefExt as _, with_forced_trimmed_paths} ;
6869use rustc_middle:: ty:: {
69- self , List , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
70+ self , List , ParamEnv , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
7071 TypeVisitableExt ,
7172} ;
7273use rustc_span:: def_id:: LOCAL_CRATE ;
@@ -77,7 +78,7 @@ use crate::error_reporting::TypeErrCtxt;
7778use crate :: errors:: { ObligationCauseFailureCode , TypeErrorAdditionalDiags } ;
7879use crate :: infer;
7980use crate :: infer:: relate:: { self , RelateResult , TypeRelation } ;
80- use crate :: infer:: { InferCtxt , TypeTrace , ValuePairs } ;
81+ use crate :: infer:: { InferCtxt , InferCtxtExt as _ , TypeTrace , ValuePairs } ;
8182use crate :: solve:: deeply_normalize_for_diagnostics;
8283use crate :: traits:: {
8384 IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
@@ -433,38 +434,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
433434 cause : & ObligationCause < ' tcx > ,
434435 exp_found : Option < ty:: error:: ExpectedFound < Ty < ' tcx > > > ,
435436 terr : TypeError < ' tcx > ,
437+ param_env : Option < ParamEnv < ' tcx > > ,
436438 ) {
437439 match * cause. code ( ) {
438- ObligationCauseCode :: Pattern { origin_expr : true , span : Some ( span) , root_ty } => {
439- let ty = self . resolve_vars_if_possible ( root_ty) ;
440- if !matches ! ( ty. kind( ) , ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) ) )
441- {
440+ ObligationCauseCode :: Pattern {
441+ origin_expr : Some ( origin_expr) ,
442+ span : Some ( span) ,
443+ root_ty,
444+ } => {
445+ let expected_ty = self . resolve_vars_if_possible ( root_ty) ;
446+ if !matches ! (
447+ expected_ty. kind( ) ,
448+ ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) )
449+ ) {
442450 // don't show type `_`
443451 if span. desugaring_kind ( ) == Some ( DesugaringKind :: ForLoop )
444- && let ty:: Adt ( def, args) = ty . kind ( )
452+ && let ty:: Adt ( def, args) = expected_ty . kind ( )
445453 && Some ( def. did ( ) ) == self . tcx . get_diagnostic_item ( sym:: Option )
446454 {
447455 err. span_label (
448456 span,
449457 format ! ( "this is an iterator with items of type `{}`" , args. type_at( 0 ) ) ,
450458 ) ;
451459 } else {
452- err. span_label ( span, format ! ( "this expression has type `{ty }`" ) ) ;
460+ err. span_label ( span, format ! ( "this expression has type `{expected_ty }`" ) ) ;
453461 }
454462 }
455463 if let Some ( ty:: error:: ExpectedFound { found, .. } ) = exp_found
456- && ty . boxed_ty ( ) == Some ( found )
457- && let Ok ( snippet ) = self . tcx . sess . source_map ( ) . span_to_snippet ( span )
464+ && let Ok ( mut peeled_snippet ) =
465+ self . tcx . sess . source_map ( ) . span_to_snippet ( origin_expr . peeled_span )
458466 {
459- err. span_suggestion (
460- span,
461- "consider dereferencing the boxed value" ,
462- format ! ( "*{snippet}" ) ,
463- Applicability :: MachineApplicable ,
464- ) ;
467+ // Parentheses are needed for cases like as casts.
468+ // We use the peeled_span for deref suggestions.
469+ // It's also safe to use for box, since box only triggers if there
470+ // wasn't a reference to begin with.
471+ if origin_expr. peeled_prefix_suggestion_parentheses {
472+ peeled_snippet = format ! ( "({peeled_snippet})" ) ;
473+ }
474+
475+ // Try giving a box suggestion first, as it is a special case of the
476+ // deref suggestion.
477+ if expected_ty. boxed_ty ( ) == Some ( found) {
478+ err. span_suggestion_verbose (
479+ span,
480+ "consider dereferencing the boxed value" ,
481+ format ! ( "*{peeled_snippet}" ) ,
482+ Applicability :: MachineApplicable ,
483+ ) ;
484+ } else if let Some ( param_env) = param_env
485+ && let Some ( prefix) = self . should_deref_suggestion_on_mismatch (
486+ param_env,
487+ found,
488+ expected_ty,
489+ origin_expr,
490+ )
491+ {
492+ err. span_suggestion_verbose (
493+ span,
494+ "consider dereferencing to access the inner value using the Deref trait" ,
495+ format ! ( "{prefix}{peeled_snippet}" ) ,
496+ Applicability :: MaybeIncorrect ,
497+ ) ;
498+ }
465499 }
466500 }
467- ObligationCauseCode :: Pattern { origin_expr : false , span : Some ( span) , .. } => {
501+ ObligationCauseCode :: Pattern { origin_expr : None , span : Some ( span) , .. } => {
468502 err. span_label ( span, "expected due to this" ) ;
469503 }
470504 ObligationCauseCode :: BlockTailExpression (
@@ -618,6 +652,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
618652 }
619653 }
620654
655+ /// Determines whether deref_to == <deref_from as Deref>::Target, and if so,
656+ /// returns a prefix that should be added to deref_from as a suggestion.
657+ fn should_deref_suggestion_on_mismatch (
658+ & self ,
659+ param_env : ParamEnv < ' tcx > ,
660+ deref_to : Ty < ' tcx > ,
661+ deref_from : Ty < ' tcx > ,
662+ origin_expr : PatternOriginExpr ,
663+ ) -> Option < String > {
664+ // origin_expr contains stripped away versions of our expression.
665+ // We'll want to use that to avoid suggesting things like *&x.
666+ // However, the type that we have access to hasn't been stripped away,
667+ // so we need to ignore the first n dereferences, where n is the number
668+ // that's been stripped away in origin_expr.
669+
670+ // Find a way to autoderef from deref_from to deref_to.
671+ let Some ( ( num_derefs, ( after_deref_ty, _) ) ) = ( self . autoderef_steps ) ( deref_from)
672+ . into_iter ( )
673+ . enumerate ( )
674+ . find ( |( _, ( ty, _) ) | self . infcx . can_eq ( param_env, * ty, deref_to) )
675+ else {
676+ return None ;
677+ } ;
678+
679+ if num_derefs <= origin_expr. peeled_count {
680+ return None ;
681+ }
682+
683+ let deref_part = "*" . repeat ( num_derefs - origin_expr. peeled_count ) ;
684+
685+ // If the user used a reference in the original expression, they probably
686+ // want the suggestion to still give a reference.
687+ if deref_from. is_ref ( ) && !after_deref_ty. is_ref ( ) {
688+ Some ( format ! ( "&{deref_part}" ) )
689+ } else {
690+ Some ( deref_part)
691+ }
692+ }
693+
621694 /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
622695 /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
623696 /// populate `other_value` with `other_ty`.
@@ -1406,8 +1479,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
14061479 Variable ( ty:: error:: ExpectedFound < Ty < ' a > > ) ,
14071480 Fixed ( & ' static str ) ,
14081481 }
1409- let ( expected_found, exp_found, is_simple_error, values) = match values {
1410- None => ( None , Mismatch :: Fixed ( "type" ) , false , None ) ,
1482+ let ( expected_found, exp_found, is_simple_error, values, param_env ) = match values {
1483+ None => ( None , Mismatch :: Fixed ( "type" ) , false , None , None ) ,
14111484 Some ( ty:: ParamEnvAnd { param_env, value : values } ) => {
14121485 let mut values = self . resolve_vars_if_possible ( values) ;
14131486 if self . next_trait_solver ( ) {
@@ -1459,7 +1532,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
14591532 diag. downgrade_to_delayed_bug ( ) ;
14601533 return ;
14611534 } ;
1462- ( Some ( vals) , exp_found, is_simple_error, Some ( values) )
1535+ ( Some ( vals) , exp_found, is_simple_error, Some ( values) , Some ( param_env ) )
14631536 }
14641537 } ;
14651538
@@ -1791,7 +1864,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17911864
17921865 // It reads better to have the error origin as the final
17931866 // thing.
1794- self . note_error_origin ( diag, cause, exp_found, terr) ;
1867+ self . note_error_origin ( diag, cause, exp_found, terr, param_env ) ;
17951868
17961869 debug ! ( ?diag) ;
17971870 }
0 commit comments