Change:
I just encountered this output: We currently mention available fields when the field used isn't found. It would be nice if we peeked at the types for the available fields to see if any of them has any fields (including through deref) of the name we originally wanted. In this case, I would love to see the following output: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
I'll work on this. claim
I was just about to open a PR when I saw that you claimed the issue. It's my fault for not claiming this issue myself obviously, so if you've already been investing time into this the issue its still yours, but if you haven't can I open a PR for this?
I've done nothing yet as I planned to work on this tomorrow morning, so this is yours :)
release-assignment
if you're still interested in contributing, it seems to me that might be similar in scope and section of the codebase you could work in.
Thank you.

Discussion:
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; <ins> use rustc_middle::ty::subst::SubstsRef; </ins> use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility};

use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_trait_selection::traits::{self, ObligationCauseCode}; <del> use std::fmt::Display; </del> impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { let ty = self.check_expr_with_hint(expr, expected);

base: &'tcx hir::Expr<'tcx>, field: Ident, ) -> Ty<'tcx> { <ins> debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); </ins> let expr_t = self.check_expr(base); let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, _)) = autoderef.next() { <ins> debug!("base_t: {:?}", base_t); </ins> match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", base_t);

"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", field, base, expr, expr_t ); <del> let mut err = self.no_such_field_err(field.span, field, expr_t); </del> <ins> let mut err = self.no_such_field_err(field, expr_t); </ins> match *expr_t.peel_refs().kind() { ty::Array(_, len) => {

} } <del> fn no_such_field_err<T: Display>( </del> <ins> fn no_such_field_err( </ins> &self, <del> span: Span, field: T, expr_t: &ty::TyS<'_>, </del> <ins> field: Ident, expr_t: &'tcx ty::TyS<'tcx>, </ins> ) -> DiagnosticBuilder<'_> { <del> type_error_struct!( </del> <ins> let span = field.span; debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); let mut err = type_error_struct!( </ins> self.tcx().sess, <del> span, </del> <ins> field.span, </ins> expr_t, E0609, "no field `{}` on type `{}`", field, expr_t <del> ) </del> <ins> ); // try to add a suggestion in case the field is a nested field of a field of the Adt if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) { for candidate_field in fields.iter() { if let Some(field_path) = self.check_for_nested_field(span, field, candidate_field, substs, vec![]) { let field_path_str = field_path .iter() .map(|id| id.name.to_ident_string()) .collect::<Vec<String>>() .join("."); debug!("field_path_str: {:?}", field_path_str); err.span_suggestion_verbose( field.span.shrink_to_lo(), "one of the expressions' fields has a field of the same name", format!("{}.", field_path_str), Applicability::MaybeIncorrect, ); } } } err } fn get_field_candidates( &self, span: Span, base_t: Ty<'tcx>, ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); let mut autoderef = self.autoderef(span, base_t); while let Some((base_t, _)) = autoderef.next() { match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { let fields = &base_def.non_enum_variant().fields; // For compile-time reasons put a limit on number of fields we search if fields.len() > 100 { return None; } return Some((fields, substs)); } _ => {} } } None } /// This method is called after we have encountered a missing field error to recursively /// search for the field fn check_for_nested_field( &self, span: Span, target_field: Ident, candidate_field: &ty::FieldDef, subst: SubstsRef<'tcx>, mut field_path: Vec<Ident>, ) -> Option<Vec<Ident>> { debug!( "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}", span, candidate_field, field_path ); if candidate_field.ident == target_field { Some(field_path) } else if field_path.len() > 3 { // For compile-time reasons and to avoid infinite recursion we only check for fields // up to a depth of three None } else { // recursively search fields of `candidate_field` if it's a ty::Adt field_path.push(candidate_field.ident.normalize_to_macros_2_0()); let field_ty = candidate_field.ty(self.tcx, subst); if let Some((nested_fields, _)) = self.get_field_candidates(span, &field_ty) { for field in nested_fields.iter() { let ident = field.ident.normalize_to_macros_2_0(); if ident == target_field { return Some(field_path); } else { let field_path = field_path.clone(); if let Some(path) = self.check_for_nested_field( span, target_field, field, subst, field_path, ) { return Some(path); } } } } None } </ins> } fn check_expr_index(

<ins> // In rustc_typeck::check::expr::no_such_field_err we recursively // look in subfields for the field. This recursive search is limited // in depth for compile-time reasons and to avoid infinite recursion // in case of cycles. This file tests that the limit in the recursion // depth is enforced. struct Foo { first: Bar, second: u32, third: u32, } struct Bar { bar: C, } struct C { c: D, } struct D { test: E, } struct E { e: F, } struct F { f: u32, } fn main() { let f = F { f: 6 }; let e = E { e: f }; let d = D { test: e }; let c = C { c: d }; let bar = Bar { bar: c }; let fooer = Foo { first: bar, second: 4, third: 5 }; let test = fooer.f; //~^ ERROR no field } </ins>

<ins> error[E0609]: no field `f` on type `Foo` --> $DIR/non-existent-field-present-in-subfield-recursion-limit.rs:41:22 | LL |     let test = fooer.f; |                      ^ unknown field | = note: available fields are: `first`, `second`, `third` error: aborting due to previous error For more information about this error, try `rustc --explain E0609`. </ins>

<ins> // run-rustfix struct Foo { first: Bar, _second: u32, _third: u32, } struct Bar { bar: C, } struct C { c: D, } struct D { test: E, } struct E { _e: F, } struct F { _f: u32, } fn main() { let f = F { _f: 6 }; let e = E { _e: f }; let d = D { test: e }; let c = C { c: d }; let bar = Bar { bar: c }; let fooer = Foo { first: bar, _second: 4, _third: 5 }; let _test = &fooer.first.bar.c; //~^ ERROR no field let _test2 = fooer.first.bar.c.test; //~^ ERROR no field } </ins>

<ins> // run-rustfix struct Foo { first: Bar, _second: u32, _third: u32, } struct Bar { bar: C, } struct C { c: D, } struct D { test: E, } struct E { _e: F, } struct F { _f: u32, } fn main() { let f = F { _f: 6 }; let e = E { _e: f }; let d = D { test: e }; let c = C { c: d }; let bar = Bar { bar: c }; let fooer = Foo { first: bar, _second: 4, _third: 5 }; let _test = &fooer.c; //~^ ERROR no field let _test2 = fooer.test; //~^ ERROR no field } </ins>

<ins> error[E0609]: no field `c` on type `Foo` --> $DIR/non-existent-field-present-in-subfield.rs:37:24 | LL |     let _test = &fooer.c; |                        ^ unknown field | = note: available fields are: `first`, `_second`, `_third` help: one of the expressions' fields has a field of the same name | LL |     let _test = &fooer.first.bar.c; |                        ^^^^^^^^^^ error[E0609]: no field `test` on type `Foo` --> $DIR/non-existent-field-present-in-subfield.rs:40:24 | LL |     let _test2 = fooer.test; |                        ^^^^ unknown field | = note: available fields are: `first`, `_second`, `_third` help: one of the expressions' fields has a field of the same name | LL |     let _test2 = fooer.first.bar.c.test; |                        ^^^^^^^^^^^^ error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0609`. </ins>