@@ -2191,7 +2191,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21912191 if let Some ( field_name) =
21922192 find_best_match_for_name ( & available_field_names, field. ident . name , None )
21932193 {
2194- err. span_suggestion (
2194+ err. span_label ( field. ident . span , "unknown field" ) ;
2195+ err. span_suggestion_verbose (
21952196 field. ident . span ,
21962197 "a field with a similar name exists" ,
21972198 field_name,
@@ -2420,35 +2421,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24202421 ty : Ty < ' tcx > ,
24212422 ) {
24222423 let Some ( output_ty) = self . get_impl_future_output_ty ( ty) else {
2424+ err. span_label ( field_ident. span , "unknown field" ) ;
24232425 return ;
24242426 } ;
2425- let mut add_label = true ;
2426- if let ty:: Adt ( def, _) = output_ty. kind ( ) {
2427- // no field access on enum type
2428- if !def. is_enum ( ) {
2429- if def
2430- . non_enum_variant ( )
2431- . fields
2432- . iter ( )
2433- . any ( |field| field. ident ( self . tcx ) == field_ident)
2434- {
2435- add_label = false ;
2436- err. span_label (
2437- field_ident. span ,
2438- "field not available in `impl Future`, but it is available in its `Output`" ,
2439- ) ;
2440- err. span_suggestion_verbose (
2441- base. span . shrink_to_hi ( ) ,
2442- "consider `await`ing on the `Future` and access the field of its `Output`" ,
2443- ".await" ,
2444- Applicability :: MaybeIncorrect ,
2445- ) ;
2446- }
2447- }
2427+ let ty:: Adt ( def, _) = output_ty. kind ( ) else {
2428+ err. span_label ( field_ident. span , "unknown field" ) ;
2429+ return ;
2430+ } ;
2431+ // no field access on enum type
2432+ if def. is_enum ( ) {
2433+ err. span_label ( field_ident. span , "unknown field" ) ;
2434+ return ;
24482435 }
2449- if add_label {
2450- err. span_label ( field_ident. span , format ! ( "field not found in `{ty}`" ) ) ;
2436+ if !def. non_enum_variant ( ) . fields . iter ( ) . any ( |field| field. ident ( self . tcx ) == field_ident) {
2437+ err. span_label ( field_ident. span , "unknown field" ) ;
2438+ return ;
24512439 }
2440+ err. span_label (
2441+ field_ident. span ,
2442+ "field not available in `impl Future`, but it is available in its `Output`" ,
2443+ ) ;
2444+ err. span_suggestion_verbose (
2445+ base. span . shrink_to_hi ( ) ,
2446+ "consider `await`ing on the `Future` and access the field of its `Output`" ,
2447+ ".await" ,
2448+ Applicability :: MaybeIncorrect ,
2449+ ) ;
24522450 }
24532451
24542452 fn ban_nonexisting_field (
@@ -2471,16 +2469,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24712469 ty:: RawPtr ( ..) => {
24722470 self . suggest_first_deref_field ( & mut err, expr, base, ident) ;
24732471 }
2474- ty:: Adt ( def, _) if !def. is_enum ( ) => {
2475- self . suggest_fields_on_recordish ( & mut err, expr, def, ident) ;
2476- }
24772472 ty:: Param ( param_ty) => {
2473+ err. span_label ( ident. span , "unknown field" ) ;
24782474 self . point_at_param_definition ( & mut err, param_ty) ;
24792475 }
24802476 ty:: Alias ( ty:: Opaque , _) => {
24812477 self . suggest_await_on_field_access ( & mut err, ident, base, base_ty. peel_refs ( ) ) ;
24822478 }
2483- _ => { }
2479+ _ => {
2480+ err. span_label ( ident. span , "unknown field" ) ;
2481+ }
24842482 }
24852483
24862484 self . suggest_fn_call ( & mut err, base, base_ty, |output_ty| {
@@ -2633,34 +2631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26332631 err. span_label ( param_span, format ! ( "type parameter '{param_name}' declared here" ) ) ;
26342632 }
26352633
2636- fn suggest_fields_on_recordish (
2637- & self ,
2638- err : & mut Diagnostic ,
2639- expr : & hir:: Expr < ' _ > ,
2640- def : ty:: AdtDef < ' tcx > ,
2641- field : Ident ,
2642- ) {
2643- let available_field_names = self . available_field_names ( def. non_enum_variant ( ) , expr, & [ ] ) ;
2644- if let Some ( suggested_field_name) =
2645- find_best_match_for_name ( & available_field_names, field. name , None )
2646- {
2647- err. span_suggestion (
2648- field. span ,
2649- "a field with a similar name exists" ,
2650- suggested_field_name,
2651- Applicability :: MaybeIncorrect ,
2652- ) ;
2653- } else {
2654- err. span_label ( field. span , "unknown field" ) ;
2655- if !available_field_names. is_empty ( ) {
2656- err. note ( format ! (
2657- "available fields are: {}" ,
2658- self . name_series_display( available_field_names) ,
2659- ) ) ;
2660- }
2661- }
2662- }
2663-
26642634 fn maybe_suggest_array_indexing (
26652635 & self ,
26662636 err : & mut Diagnostic ,
@@ -2669,6 +2639,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26692639 field : Ident ,
26702640 len : ty:: Const < ' tcx > ,
26712641 ) {
2642+ err. span_label ( field. span , "unknown field" ) ;
26722643 if let ( Some ( len) , Ok ( user_index) ) =
26732644 ( len. try_eval_target_usize ( self . tcx , self . param_env ) , field. as_str ( ) . parse :: < u64 > ( ) )
26742645 && let Ok ( base) = self . tcx . sess . source_map ( ) . span_to_snippet ( base. span )
@@ -2691,6 +2662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26912662 base : & hir:: Expr < ' _ > ,
26922663 field : Ident ,
26932664 ) {
2665+ err. span_label ( field. span , "unknown field" ) ;
26942666 if let Ok ( base) = self . tcx . sess . source_map ( ) . span_to_snippet ( base. span ) {
26952667 let msg = format ! ( "`{base}` is a raw pointer; try dereferencing it" ) ;
26962668 let suggestion = format ! ( "(*{base}).{field}" ) ;
@@ -2709,18 +2681,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27092681
27102682 let mut err = type_error_struct ! (
27112683 self . tcx( ) . sess,
2712- field . span,
2684+ span,
27132685 expr_t,
27142686 E0609 ,
27152687 "no field `{field}` on type `{expr_t}`" ,
27162688 ) ;
27172689
27182690 // try to add a suggestion in case the field is a nested field of a field of the Adt
27192691 let mod_id = self . tcx . parent_module ( id) . to_def_id ( ) ;
2720- if let Some ( ( fields, args) ) =
2721- self . get_field_candidates_considering_privacy ( span, expr_t, mod_id)
2692+ let ( ty, unwrap) = if let ty:: Adt ( def, args) = expr_t. kind ( )
2693+ && ( self . tcx . is_diagnostic_item ( sym:: Result , def. did ( ) )
2694+ || self . tcx . is_diagnostic_item ( sym:: Option , def. did ( ) ) )
2695+ && let Some ( arg) = args. get ( 0 )
2696+ && let Some ( ty) = arg. as_type ( )
27222697 {
2723- let candidate_fields: Vec < _ > = fields
2698+ ( ty, "unwrap()." )
2699+ } else {
2700+ ( expr_t, "" )
2701+ } ;
2702+ for ( found_fields, args) in
2703+ self . get_field_candidates_considering_privacy ( span, ty, mod_id, id)
2704+ {
2705+ let field_names = found_fields. iter ( ) . map ( |field| field. name ) . collect :: < Vec < _ > > ( ) ;
2706+ let candidate_fields: Vec < _ > = found_fields
2707+ . into_iter ( )
27242708 . filter_map ( |candidate_field| {
27252709 self . check_for_nested_field_satisfying (
27262710 span,
@@ -2729,15 +2713,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27292713 args,
27302714 vec ! [ ] ,
27312715 mod_id,
2716+ id,
27322717 )
27332718 } )
27342719 . map ( |mut field_path| {
27352720 field_path. pop ( ) ;
27362721 field_path
27372722 . iter ( )
2738- . map ( |id| id. name . to_ident_string ( ) )
2739- . collect :: < Vec < String > > ( )
2740- . join ( "." )
2723+ . map ( |id| format ! ( "{}." , id. name. to_ident_string( ) ) )
2724+ . collect :: < String > ( )
27412725 } )
27422726 . collect :: < Vec < _ > > ( ) ;
27432727
@@ -2750,9 +2734,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27502734 if len > 1 { "some" } else { "one" } ,
27512735 if len > 1 { "have" } else { "has" } ,
27522736 ) ,
2753- candidate_fields. iter ( ) . map ( |path| format ! ( "{path}. " ) ) ,
2737+ candidate_fields. iter ( ) . map ( |path| format ! ( "{unwrap}{ path}" ) ) ,
27542738 Applicability :: MaybeIncorrect ,
27552739 ) ;
2740+ } else {
2741+ if let Some ( field_name) = find_best_match_for_name ( & field_names, field. name , None ) {
2742+ err. span_suggestion_verbose (
2743+ field. span ,
2744+ "a field with a similar name exists" ,
2745+ format ! ( "{unwrap}{}" , field_name) ,
2746+ Applicability :: MaybeIncorrect ,
2747+ ) ;
2748+ } else if !field_names. is_empty ( ) {
2749+ let is = if field_names. len ( ) == 1 { " is" } else { "s are" } ;
2750+ err. note ( format ! (
2751+ "available field{is}: {}" ,
2752+ self . name_series_display( field_names) ,
2753+ ) ) ;
2754+ }
27562755 }
27572756 }
27582757 err
@@ -2781,33 +2780,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27812780 span : Span ,
27822781 base_ty : Ty < ' tcx > ,
27832782 mod_id : DefId ,
2784- ) -> Option < ( impl Iterator < Item = & ' tcx ty:: FieldDef > + ' tcx , GenericArgsRef < ' tcx > ) > {
2783+ hir_id : hir:: HirId ,
2784+ ) -> Vec < ( Vec < & ' tcx ty:: FieldDef > , GenericArgsRef < ' tcx > ) > {
27852785 debug ! ( "get_field_candidates(span: {:?}, base_t: {:?}" , span, base_ty) ;
27862786
2787- for ( base_t, _) in self . autoderef ( span, base_ty) {
2788- match base_t. kind ( ) {
2789- ty:: Adt ( base_def, args) if !base_def. is_enum ( ) => {
2790- let tcx = self . tcx ;
2791- let fields = & base_def. non_enum_variant ( ) . fields ;
2792- // Some struct, e.g. some that impl `Deref`, have all private fields
2793- // because you're expected to deref them to access the _real_ fields.
2794- // This, for example, will help us suggest accessing a field through a `Box<T>`.
2795- if fields. iter ( ) . all ( |field| !field. vis . is_accessible_from ( mod_id, tcx) ) {
2796- continue ;
2787+ self . autoderef ( span, base_ty)
2788+ . filter_map ( move |( base_t, _) | {
2789+ match base_t. kind ( ) {
2790+ ty:: Adt ( base_def, args) if !base_def. is_enum ( ) => {
2791+ let tcx = self . tcx ;
2792+ let fields = & base_def. non_enum_variant ( ) . fields ;
2793+ // Some struct, e.g. some that impl `Deref`, have all private fields
2794+ // because you're expected to deref them to access the _real_ fields.
2795+ // This, for example, will help us suggest accessing a field through a `Box<T>`.
2796+ if fields. iter ( ) . all ( |field| !field. vis . is_accessible_from ( mod_id, tcx) ) {
2797+ return None ;
2798+ }
2799+ return Some ( (
2800+ fields
2801+ . iter ( )
2802+ . filter ( move |field| {
2803+ field. vis . is_accessible_from ( mod_id, tcx)
2804+ && self . is_field_suggestable ( field, hir_id, span)
2805+ } )
2806+ // For compile-time reasons put a limit on number of fields we search
2807+ . take ( 100 )
2808+ . collect :: < Vec < _ > > ( ) ,
2809+ * args,
2810+ ) ) ;
27972811 }
2798- return Some ( (
2799- fields
2800- . iter ( )
2801- . filter ( move |field| field. vis . is_accessible_from ( mod_id, tcx) )
2802- // For compile-time reasons put a limit on number of fields we search
2803- . take ( 100 ) ,
2804- args,
2805- ) ) ;
2812+ _ => None ,
28062813 }
2807- _ => { }
2808- }
2809- }
2810- None
2814+ } )
2815+ . collect ( )
28112816 }
28122817
28132818 /// This method is called after we have encountered a missing field error to recursively
@@ -2820,6 +2825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28202825 subst : GenericArgsRef < ' tcx > ,
28212826 mut field_path : Vec < Ident > ,
28222827 mod_id : DefId ,
2828+ hir_id : HirId ,
28232829 ) -> Option < Vec < Ident > > {
28242830 debug ! (
28252831 "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}" ,
@@ -2835,20 +2841,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28352841 let field_ty = candidate_field. ty ( self . tcx , subst) ;
28362842 if matches ( candidate_field, field_ty) {
28372843 return Some ( field_path) ;
2838- } else if let Some ( ( nested_fields, subst) ) =
2839- self . get_field_candidates_considering_privacy ( span, field_ty, mod_id)
2840- {
2841- // recursively search fields of `candidate_field` if it's a ty::Adt
2842- for field in nested_fields {
2843- if let Some ( field_path) = self . check_for_nested_field_satisfying (
2844- span,
2845- matches,
2846- field,
2847- subst,
2848- field_path. clone ( ) ,
2849- mod_id,
2850- ) {
2851- return Some ( field_path) ;
2844+ } else {
2845+ for ( nested_fields, subst) in
2846+ self . get_field_candidates_considering_privacy ( span, field_ty, mod_id, hir_id)
2847+ {
2848+ // recursively search fields of `candidate_field` if it's a ty::Adt
2849+ for field in nested_fields {
2850+ if let Some ( field_path) = self . check_for_nested_field_satisfying (
2851+ span,
2852+ matches,
2853+ field,
2854+ subst,
2855+ field_path. clone ( ) ,
2856+ mod_id,
2857+ hir_id,
2858+ ) {
2859+ return Some ( field_path) ;
2860+ }
28522861 }
28532862 }
28542863 }
0 commit comments