@@ -61,10 +61,11 @@ use rustc_hir::{self as hir};
6161use rustc_macros:: extension;
6262use rustc_middle:: bug;
6363use rustc_middle:: dep_graph:: DepContext ;
64+ use rustc_middle:: traits:: PatternOriginExpr ;
6465use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeErrorToStringExt } ;
6566use rustc_middle:: ty:: print:: { PrintError , PrintTraitRefExt as _, with_forced_trimmed_paths} ;
6667use rustc_middle:: ty:: {
67- self , List , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
68+ self , List , ParamEnv , Region , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
6869 TypeVisitableExt ,
6970} ;
7071use rustc_span:: { BytePos , DesugaringKind , Pos , Span , sym} ;
@@ -74,7 +75,7 @@ use crate::error_reporting::TypeErrCtxt;
7475use crate :: errors:: { ObligationCauseFailureCode , TypeErrorAdditionalDiags } ;
7576use crate :: infer;
7677use crate :: infer:: relate:: { self , RelateResult , TypeRelation } ;
77- use crate :: infer:: { InferCtxt , TypeTrace , ValuePairs } ;
78+ use crate :: infer:: { InferCtxt , InferCtxtExt as _ , TypeTrace , ValuePairs } ;
7879use crate :: solve:: deeply_normalize_for_diagnostics;
7980use crate :: traits:: {
8081 IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
@@ -339,38 +340,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
339340 cause : & ObligationCause < ' tcx > ,
340341 exp_found : Option < ty:: error:: ExpectedFound < Ty < ' tcx > > > ,
341342 terr : TypeError < ' tcx > ,
343+ param_env : Option < ParamEnv < ' tcx > > ,
342344 ) {
343345 match * cause. code ( ) {
344- ObligationCauseCode :: Pattern { origin_expr : true , span : Some ( span) , root_ty } => {
345- let ty = self . resolve_vars_if_possible ( root_ty) ;
346- if !matches ! ( ty. kind( ) , ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) ) )
347- {
346+ ObligationCauseCode :: Pattern {
347+ origin_expr : Some ( origin_expr) ,
348+ span : Some ( span) ,
349+ root_ty,
350+ } => {
351+ let expected_ty = self . resolve_vars_if_possible ( root_ty) ;
352+ if !matches ! (
353+ expected_ty. kind( ) ,
354+ ty:: Infer ( ty:: InferTy :: TyVar ( _) | ty:: InferTy :: FreshTy ( _) )
355+ ) {
348356 // don't show type `_`
349357 if span. desugaring_kind ( ) == Some ( DesugaringKind :: ForLoop )
350- && let ty:: Adt ( def, args) = ty . kind ( )
358+ && let ty:: Adt ( def, args) = expected_ty . kind ( )
351359 && Some ( def. did ( ) ) == self . tcx . get_diagnostic_item ( sym:: Option )
352360 {
353361 err. span_label (
354362 span,
355363 format ! ( "this is an iterator with items of type `{}`" , args. type_at( 0 ) ) ,
356364 ) ;
357365 } else {
358- err. span_label ( span, format ! ( "this expression has type `{ty }`" ) ) ;
366+ err. span_label ( span, format ! ( "this expression has type `{expected_ty }`" ) ) ;
359367 }
360368 }
361369 if let Some ( ty:: error:: ExpectedFound { found, .. } ) = exp_found
362- && ty . boxed_ty ( ) == Some ( found )
363- && let Ok ( snippet ) = self . tcx . sess . source_map ( ) . span_to_snippet ( span )
370+ && let Ok ( mut peeled_snippet ) =
371+ self . tcx . sess . source_map ( ) . span_to_snippet ( origin_expr . peeled_span )
364372 {
365- err. span_suggestion (
366- span,
367- "consider dereferencing the boxed value" ,
368- format ! ( "*{snippet}" ) ,
369- Applicability :: MachineApplicable ,
370- ) ;
373+ // Parentheses are needed for cases like as casts.
374+ // We use the peeled_span for deref suggestions.
375+ // It's also safe to use for box, since box only triggers if there
376+ // wasn't a reference to begin with.
377+ if origin_expr. peeled_prefix_suggestion_parentheses {
378+ peeled_snippet = format ! ( "({peeled_snippet})" ) ;
379+ }
380+
381+ // Try giving a box suggestion first, as it is a special case of the
382+ // deref suggestion.
383+ if expected_ty. boxed_ty ( ) == Some ( found) {
384+ err. span_suggestion_verbose (
385+ span,
386+ "consider dereferencing the boxed value" ,
387+ format ! ( "*{peeled_snippet}" ) ,
388+ Applicability :: MachineApplicable ,
389+ ) ;
390+ } else if let Some ( param_env) = param_env
391+ && let Some ( prefix) = self . should_deref_suggestion_on_mismatch (
392+ param_env,
393+ found,
394+ expected_ty,
395+ origin_expr,
396+ )
397+ {
398+ err. span_suggestion_verbose (
399+ span,
400+ "consider dereferencing to access the inner value using the Deref trait" ,
401+ format ! ( "{prefix}{peeled_snippet}" ) ,
402+ Applicability :: MaybeIncorrect ,
403+ ) ;
404+ }
371405 }
372406 }
373- ObligationCauseCode :: Pattern { origin_expr : false , span : Some ( span) , .. } => {
407+ ObligationCauseCode :: Pattern { origin_expr : None , span : Some ( span) , .. } => {
374408 err. span_label ( span, "expected due to this" ) ;
375409 }
376410 ObligationCauseCode :: BlockTailExpression (
@@ -524,6 +558,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
524558 }
525559 }
526560
561+ /// Determines whether deref_to == <deref_from as Deref>::Target, and if so,
562+ /// returns a prefix that should be added to deref_from as a suggestion.
563+ fn should_deref_suggestion_on_mismatch (
564+ & self ,
565+ param_env : ParamEnv < ' tcx > ,
566+ deref_to : Ty < ' tcx > ,
567+ deref_from : Ty < ' tcx > ,
568+ origin_expr : PatternOriginExpr ,
569+ ) -> Option < String > {
570+ // origin_expr contains stripped away versions of our expression.
571+ // We'll want to use that to avoid suggesting things like *&x.
572+ // However, the type that we have access to hasn't been stripped away,
573+ // so we need to ignore the first n dereferences, where n is the number
574+ // that's been stripped away in origin_expr.
575+
576+ // Find a way to autoderef from deraf_from to deref_to.
577+ let Some ( ( num_derefs, ( after_deref_ty, _) ) ) = ( self . autoderef_steps ) ( deref_from)
578+ . into_iter ( )
579+ . enumerate ( )
580+ . find ( |( _, ( ty, _) ) | self . infcx . can_eq ( param_env, * ty, deref_to) )
581+ else {
582+ return None ;
583+ } ;
584+
585+ if num_derefs <= origin_expr. peeled_count {
586+ return None ;
587+ }
588+
589+ let deref_part = "*" . repeat ( num_derefs - origin_expr. peeled_count ) ;
590+
591+ // If the user used a reference in the original expression, they probably
592+ // want the suggestion to still give a reference.
593+ if deref_from. is_ref ( ) && !after_deref_ty. is_ref ( ) {
594+ Some ( format ! ( "&{deref_part}" ) )
595+ } else {
596+ Some ( deref_part)
597+ }
598+ }
599+
527600 /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
528601 /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
529602 /// populate `other_value` with `other_ty`.
@@ -1312,8 +1385,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
13121385 Variable ( ty:: error:: ExpectedFound < Ty < ' a > > ) ,
13131386 Fixed ( & ' static str ) ,
13141387 }
1315- let ( expected_found, exp_found, is_simple_error, values) = match values {
1316- None => ( None , Mismatch :: Fixed ( "type" ) , false , None ) ,
1388+ let ( expected_found, exp_found, is_simple_error, values, param_env ) = match values {
1389+ None => ( None , Mismatch :: Fixed ( "type" ) , false , None , None ) ,
13171390 Some ( ty:: ParamEnvAnd { param_env, value : values } ) => {
13181391 let mut values = self . resolve_vars_if_possible ( values) ;
13191392 if self . next_trait_solver ( ) {
@@ -1365,7 +1438,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
13651438 diag. downgrade_to_delayed_bug ( ) ;
13661439 return ;
13671440 } ;
1368- ( Some ( vals) , exp_found, is_simple_error, Some ( values) )
1441+ ( Some ( vals) , exp_found, is_simple_error, Some ( values) , Some ( param_env ) )
13691442 }
13701443 } ;
13711444
@@ -1693,7 +1766,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16931766
16941767 // It reads better to have the error origin as the final
16951768 // thing.
1696- self . note_error_origin ( diag, cause, exp_found, terr) ;
1769+ self . note_error_origin ( diag, cause, exp_found, terr, param_env ) ;
16971770
16981771 debug ! ( ?diag) ;
16991772 }
0 commit comments