@@ -2191,7 +2191,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2191
2191
if let Some ( field_name) =
2192
2192
find_best_match_for_name ( & available_field_names, field. ident . name , None )
2193
2193
{
2194
- err. span_suggestion (
2194
+ err. span_label ( field. ident . span , "unknown field" ) ;
2195
+ err. span_suggestion_verbose (
2195
2196
field. ident . span ,
2196
2197
"a field with a similar name exists" ,
2197
2198
field_name,
@@ -2420,35 +2421,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2420
2421
ty : Ty < ' tcx > ,
2421
2422
) {
2422
2423
let Some ( output_ty) = self . get_impl_future_output_ty ( ty) else {
2424
+ err. span_label ( field_ident. span , "unknown field" ) ;
2423
2425
return ;
2424
2426
} ;
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 ;
2448
2435
}
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 ;
2451
2439
}
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
+ ) ;
2452
2450
}
2453
2451
2454
2452
fn ban_nonexisting_field (
@@ -2471,16 +2469,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2471
2469
ty:: RawPtr ( ..) => {
2472
2470
self . suggest_first_deref_field ( & mut err, expr, base, ident) ;
2473
2471
}
2474
- ty:: Adt ( def, _) if !def. is_enum ( ) => {
2475
- self . suggest_fields_on_recordish ( & mut err, expr, def, ident) ;
2476
- }
2477
2472
ty:: Param ( param_ty) => {
2473
+ err. span_label ( ident. span , "unknown field" ) ;
2478
2474
self . point_at_param_definition ( & mut err, param_ty) ;
2479
2475
}
2480
2476
ty:: Alias ( ty:: Opaque , _) => {
2481
2477
self . suggest_await_on_field_access ( & mut err, ident, base, base_ty. peel_refs ( ) ) ;
2482
2478
}
2483
- _ => { }
2479
+ _ => {
2480
+ err. span_label ( ident. span , "unknown field" ) ;
2481
+ }
2484
2482
}
2485
2483
2486
2484
self . suggest_fn_call ( & mut err, base, base_ty, |output_ty| {
@@ -2633,34 +2631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2633
2631
err. span_label ( param_span, format ! ( "type parameter '{param_name}' declared here" ) ) ;
2634
2632
}
2635
2633
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
-
2664
2634
fn maybe_suggest_array_indexing (
2665
2635
& self ,
2666
2636
err : & mut Diagnostic ,
@@ -2669,6 +2639,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2669
2639
field : Ident ,
2670
2640
len : ty:: Const < ' tcx > ,
2671
2641
) {
2642
+ err. span_label ( field. span , "unknown field" ) ;
2672
2643
if let ( Some ( len) , Ok ( user_index) ) =
2673
2644
( len. try_eval_target_usize ( self . tcx , self . param_env ) , field. as_str ( ) . parse :: < u64 > ( ) )
2674
2645
&& let Ok ( base) = self . tcx . sess . source_map ( ) . span_to_snippet ( base. span )
@@ -2691,6 +2662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2691
2662
base : & hir:: Expr < ' _ > ,
2692
2663
field : Ident ,
2693
2664
) {
2665
+ err. span_label ( field. span , "unknown field" ) ;
2694
2666
if let Ok ( base) = self . tcx . sess . source_map ( ) . span_to_snippet ( base. span ) {
2695
2667
let msg = format ! ( "`{base}` is a raw pointer; try dereferencing it" ) ;
2696
2668
let suggestion = format ! ( "(*{base}).{field}" ) ;
@@ -2709,18 +2681,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2709
2681
2710
2682
let mut err = type_error_struct ! (
2711
2683
self . tcx( ) . sess,
2712
- field . span,
2684
+ span,
2713
2685
expr_t,
2714
2686
E0609 ,
2715
2687
"no field `{field}` on type `{expr_t}`" ,
2716
2688
) ;
2717
2689
2718
2690
// try to add a suggestion in case the field is a nested field of a field of the Adt
2719
2691
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 ( )
2722
2697
{
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 ( )
2724
2708
. filter_map ( |candidate_field| {
2725
2709
self . check_for_nested_field_satisfying (
2726
2710
span,
@@ -2729,15 +2713,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2729
2713
args,
2730
2714
vec ! [ ] ,
2731
2715
mod_id,
2716
+ id,
2732
2717
)
2733
2718
} )
2734
2719
. map ( |mut field_path| {
2735
2720
field_path. pop ( ) ;
2736
2721
field_path
2737
2722
. 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 > ( )
2741
2725
} )
2742
2726
. collect :: < Vec < _ > > ( ) ;
2743
2727
@@ -2750,9 +2734,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2750
2734
if len > 1 { "some" } else { "one" } ,
2751
2735
if len > 1 { "have" } else { "has" } ,
2752
2736
) ,
2753
- candidate_fields. iter ( ) . map ( |path| format ! ( "{path}. " ) ) ,
2737
+ candidate_fields. iter ( ) . map ( |path| format ! ( "{unwrap}{ path}" ) ) ,
2754
2738
Applicability :: MaybeIncorrect ,
2755
2739
) ;
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
+ }
2756
2755
}
2757
2756
}
2758
2757
err
@@ -2781,33 +2780,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2781
2780
span : Span ,
2782
2781
base_ty : Ty < ' tcx > ,
2783
2782
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 > ) > {
2785
2785
debug ! ( "get_field_candidates(span: {:?}, base_t: {:?}" , span, base_ty) ;
2786
2786
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
+ ) ) ;
2797
2811
}
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 ,
2806
2813
}
2807
- _ => { }
2808
- }
2809
- }
2810
- None
2814
+ } )
2815
+ . collect ( )
2811
2816
}
2812
2817
2813
2818
/// This method is called after we have encountered a missing field error to recursively
@@ -2820,6 +2825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2820
2825
subst : GenericArgsRef < ' tcx > ,
2821
2826
mut field_path : Vec < Ident > ,
2822
2827
mod_id : DefId ,
2828
+ hir_id : HirId ,
2823
2829
) -> Option < Vec < Ident > > {
2824
2830
debug ! (
2825
2831
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}" ,
@@ -2835,20 +2841,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2835
2841
let field_ty = candidate_field. ty ( self . tcx , subst) ;
2836
2842
if matches ( candidate_field, field_ty) {
2837
2843
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
+ }
2852
2861
}
2853
2862
}
2854
2863
}
0 commit comments