@@ -2417,9 +2417,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2417
2417
ty:: RawPtr ( ..) => {
2418
2418
self . suggest_first_deref_field ( & mut err, expr, base, ident) ;
2419
2419
}
2420
- ty:: Adt ( def, _) if !def. is_enum ( ) => {
2421
- self . suggest_fields_on_recordish ( & mut err, expr, def, ident) ;
2422
- }
2423
2420
ty:: Param ( param_ty) => {
2424
2421
self . point_at_param_definition ( & mut err, param_ty) ;
2425
2422
}
@@ -2579,34 +2576,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2579
2576
err. span_label ( param_span, format ! ( "type parameter '{param_name}' declared here" ) ) ;
2580
2577
}
2581
2578
2582
- fn suggest_fields_on_recordish (
2583
- & self ,
2584
- err : & mut Diagnostic ,
2585
- expr : & hir:: Expr < ' _ > ,
2586
- def : ty:: AdtDef < ' tcx > ,
2587
- field : Ident ,
2588
- ) {
2589
- let available_field_names = self . available_field_names ( def. non_enum_variant ( ) , expr, & [ ] ) ;
2590
- if let Some ( suggested_field_name) =
2591
- find_best_match_for_name ( & available_field_names, field. name , None )
2592
- {
2593
- err. span_suggestion (
2594
- field. span ,
2595
- "a field with a similar name exists" ,
2596
- suggested_field_name,
2597
- Applicability :: MaybeIncorrect ,
2598
- ) ;
2599
- } else {
2600
- err. span_label ( field. span , "unknown field" ) ;
2601
- if !available_field_names. is_empty ( ) {
2602
- err. note ( format ! (
2603
- "available fields are: {}" ,
2604
- self . name_series_display( available_field_names) ,
2605
- ) ) ;
2606
- }
2607
- }
2608
- }
2609
-
2610
2579
fn maybe_suggest_array_indexing (
2611
2580
& self ,
2612
2581
err : & mut Diagnostic ,
@@ -2655,18 +2624,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2655
2624
2656
2625
let mut err = type_error_struct ! (
2657
2626
self . tcx( ) . sess,
2658
- field . span,
2627
+ span,
2659
2628
expr_t,
2660
2629
E0609 ,
2661
2630
"no field `{field}` on type `{expr_t}`" ,
2662
2631
) ;
2663
2632
2664
2633
// try to add a suggestion in case the field is a nested field of a field of the Adt
2665
2634
let mod_id = self . tcx . parent_module ( id) . to_def_id ( ) ;
2666
- if let Some ( ( fields , args) ) =
2667
- self . get_field_candidates_considering_privacy ( span, expr_t, mod_id)
2635
+ for ( found_fields , args) in
2636
+ self . get_field_candidates_considering_privacy ( span, expr_t, mod_id, id )
2668
2637
{
2669
- let candidate_fields: Vec < _ > = fields
2638
+ let field_names = found_fields. iter ( ) . map ( |field| field. name ) . collect :: < Vec < _ > > ( ) ;
2639
+ let candidate_fields: Vec < _ > = found_fields
2640
+ . into_iter ( )
2670
2641
. filter_map ( |candidate_field| {
2671
2642
self . check_for_nested_field_satisfying (
2672
2643
span,
@@ -2675,6 +2646,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2675
2646
args,
2676
2647
vec ! [ ] ,
2677
2648
mod_id,
2649
+ id,
2678
2650
)
2679
2651
} )
2680
2652
. map ( |mut field_path| {
@@ -2699,6 +2671,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2699
2671
candidate_fields. iter ( ) . map ( |path| format ! ( "{path}." ) ) ,
2700
2672
Applicability :: MaybeIncorrect ,
2701
2673
) ;
2674
+ } else {
2675
+ if let Some ( field_name) = find_best_match_for_name ( & field_names, field. name , None ) {
2676
+ err. span_suggestion (
2677
+ field. span ,
2678
+ "a field with a similar name exists" ,
2679
+ field_name,
2680
+ Applicability :: MaybeIncorrect ,
2681
+ ) ;
2682
+ } else if !field_names. is_empty ( ) {
2683
+ let is = if field_names. len ( ) == 1 { " is" } else { "s are" } ;
2684
+ err. note ( format ! (
2685
+ "available field{is}: {}" ,
2686
+ self . name_series_display( field_names) ,
2687
+ ) ) ;
2688
+ }
2702
2689
}
2703
2690
}
2704
2691
err
@@ -2727,33 +2714,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2727
2714
span : Span ,
2728
2715
base_ty : Ty < ' tcx > ,
2729
2716
mod_id : DefId ,
2730
- ) -> Option < ( impl Iterator < Item = & ' tcx ty:: FieldDef > + ' tcx , GenericArgsRef < ' tcx > ) > {
2717
+ hir_id : hir:: HirId ,
2718
+ ) -> Vec < ( Vec < & ' tcx ty:: FieldDef > , GenericArgsRef < ' tcx > ) > {
2731
2719
debug ! ( "get_field_candidates(span: {:?}, base_t: {:?}" , span, base_ty) ;
2732
2720
2733
- for ( base_t, _) in self . autoderef ( span, base_ty) {
2734
- match base_t. kind ( ) {
2735
- ty:: Adt ( base_def, args) if !base_def. is_enum ( ) => {
2736
- let tcx = self . tcx ;
2737
- let fields = & base_def. non_enum_variant ( ) . fields ;
2738
- // Some struct, e.g. some that impl `Deref`, have all private fields
2739
- // because you're expected to deref them to access the _real_ fields.
2740
- // This, for example, will help us suggest accessing a field through a `Box<T>`.
2741
- if fields. iter ( ) . all ( |field| !field. vis . is_accessible_from ( mod_id, tcx) ) {
2742
- continue ;
2721
+ self . autoderef ( span, base_ty)
2722
+ . filter_map ( move |( base_t, _) | {
2723
+ match base_t. kind ( ) {
2724
+ ty:: Adt ( base_def, args) if !base_def. is_enum ( ) => {
2725
+ let tcx = self . tcx ;
2726
+ let fields = & base_def. non_enum_variant ( ) . fields ;
2727
+ // Some struct, e.g. some that impl `Deref`, have all private fields
2728
+ // because you're expected to deref them to access the _real_ fields.
2729
+ // This, for example, will help us suggest accessing a field through a `Box<T>`.
2730
+ if fields. iter ( ) . all ( |field| !field. vis . is_accessible_from ( mod_id, tcx) ) {
2731
+ return None ;
2732
+ }
2733
+ return Some ( (
2734
+ fields
2735
+ . iter ( )
2736
+ . filter ( move |field| {
2737
+ field. vis . is_accessible_from ( mod_id, tcx)
2738
+ && self . is_field_suggestable ( field, hir_id, span)
2739
+ } )
2740
+ // For compile-time reasons put a limit on number of fields we search
2741
+ . take ( 100 )
2742
+ . collect :: < Vec < _ > > ( ) ,
2743
+ * args,
2744
+ ) ) ;
2743
2745
}
2744
- return Some ( (
2745
- fields
2746
- . iter ( )
2747
- . filter ( move |field| field. vis . is_accessible_from ( mod_id, tcx) )
2748
- // For compile-time reasons put a limit on number of fields we search
2749
- . take ( 100 ) ,
2750
- args,
2751
- ) ) ;
2746
+ _ => None ,
2752
2747
}
2753
- _ => { }
2754
- }
2755
- }
2756
- None
2748
+ } )
2749
+ . collect ( )
2757
2750
}
2758
2751
2759
2752
/// This method is called after we have encountered a missing field error to recursively
@@ -2766,6 +2759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2766
2759
subst : GenericArgsRef < ' tcx > ,
2767
2760
mut field_path : Vec < Ident > ,
2768
2761
mod_id : DefId ,
2762
+ hir_id : HirId ,
2769
2763
) -> Option < Vec < Ident > > {
2770
2764
debug ! (
2771
2765
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}" ,
@@ -2781,20 +2775,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2781
2775
let field_ty = candidate_field. ty ( self . tcx , subst) ;
2782
2776
if matches ( candidate_field, field_ty) {
2783
2777
return Some ( field_path) ;
2784
- } else if let Some ( ( nested_fields, subst) ) =
2785
- self . get_field_candidates_considering_privacy ( span, field_ty, mod_id)
2786
- {
2787
- // recursively search fields of `candidate_field` if it's a ty::Adt
2788
- for field in nested_fields {
2789
- if let Some ( field_path) = self . check_for_nested_field_satisfying (
2790
- span,
2791
- matches,
2792
- field,
2793
- subst,
2794
- field_path. clone ( ) ,
2795
- mod_id,
2796
- ) {
2797
- return Some ( field_path) ;
2778
+ } else {
2779
+ for ( nested_fields, subst) in
2780
+ self . get_field_candidates_considering_privacy ( span, field_ty, mod_id, hir_id)
2781
+ {
2782
+ // recursively search fields of `candidate_field` if it's a ty::Adt
2783
+ for field in nested_fields {
2784
+ if let Some ( field_path) = self . check_for_nested_field_satisfying (
2785
+ span,
2786
+ matches,
2787
+ field,
2788
+ subst,
2789
+ field_path. clone ( ) ,
2790
+ mod_id,
2791
+ hir_id,
2792
+ ) {
2793
+ return Some ( field_path) ;
2794
+ }
2798
2795
}
2799
2796
}
2800
2797
}
0 commit comments