@@ -36,6 +36,7 @@ use rustc_infer::infer;
3636use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
3737use rustc_middle:: ty;
3838use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AllowTwoPhase } ;
39+ use rustc_middle:: ty:: subst:: SubstsRef ;
3940use rustc_middle:: ty:: Ty ;
4041use rustc_middle:: ty:: TypeFoldable ;
4142use rustc_middle:: ty:: { AdtKind , Visibility } ;
@@ -46,8 +47,6 @@ use rustc_span::source_map::Span;
4647use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
4748use rustc_trait_selection:: traits:: { self , ObligationCauseCode } ;
4849
49- use std:: fmt:: Display ;
50-
5150impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
5251 fn check_expr_eq_type ( & self , expr : & ' tcx hir:: Expr < ' tcx > , expected : Ty < ' tcx > ) {
5352 let ty = self . check_expr_with_hint ( expr, expected) ;
@@ -1585,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15851584 base : & ' tcx hir:: Expr < ' tcx > ,
15861585 field : Ident ,
15871586 ) -> Ty < ' tcx > {
1587+ debug ! ( "check_field(expr: {:?}, base: {:?}, field: {:?})" , expr, base, field) ;
15881588 let expr_t = self . check_expr ( base) ;
15891589 let expr_t = self . structurally_resolved_type ( base. span , expr_t) ;
15901590 let mut private_candidate = None ;
15911591 let mut autoderef = self . autoderef ( expr. span , expr_t) ;
15921592 while let Some ( ( base_t, _) ) = autoderef. next ( ) {
1593+ debug ! ( "base_t: {:?}" , base_t) ;
15931594 match base_t. kind ( ) {
15941595 ty:: Adt ( base_def, substs) if !base_def. is_enum ( ) => {
15951596 debug ! ( "struct named {:?}" , base_t) ;
@@ -1706,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17061707 "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}" ,
17071708 field, base, expr, expr_t
17081709 ) ;
1709- let mut err = self . no_such_field_err ( field. span , field , expr_t) ;
1710+ let mut err = self . no_such_field_err ( field, expr_t) ;
17101711
17111712 match * expr_t. peel_refs ( ) . kind ( ) {
17121713 ty:: Array ( _, len) => {
@@ -1880,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18801881 }
18811882 }
18821883
1883- fn no_such_field_err < T : Display > (
1884+ fn no_such_field_err (
18841885 & self ,
1885- span : Span ,
1886- field : T ,
1887- expr_t : & ty:: TyS < ' _ > ,
1886+ field : Ident ,
1887+ expr_t : & ' tcx ty:: TyS < ' tcx > ,
18881888 ) -> DiagnosticBuilder < ' _ > {
1889- type_error_struct ! (
1889+ let span = field. span ;
1890+ debug ! ( "no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})" , span, field, expr_t) ;
1891+
1892+ let mut err = type_error_struct ! (
18901893 self . tcx( ) . sess,
1891- span,
1894+ field . span,
18921895 expr_t,
18931896 E0609 ,
18941897 "no field `{}` on type `{}`" ,
18951898 field,
18961899 expr_t
1897- )
1900+ ) ;
1901+
1902+ // try to add a suggestion in case the field is a nested field of a field of the Adt
1903+ if let Some ( ( fields, substs) ) = self . get_field_candidates ( span, & expr_t) {
1904+ for candidate_field in fields. iter ( ) {
1905+ if let Some ( field_path) =
1906+ self . check_for_nested_field ( span, field, candidate_field, substs, vec ! [ ] )
1907+ {
1908+ let field_path_str = field_path
1909+ . iter ( )
1910+ . map ( |id| id. name . to_ident_string ( ) )
1911+ . collect :: < Vec < String > > ( )
1912+ . join ( "." ) ;
1913+ debug ! ( "field_path_str: {:?}" , field_path_str) ;
1914+
1915+ err. span_suggestion_verbose (
1916+ field. span . shrink_to_lo ( ) ,
1917+ "one of the expressions' fields has a field of the same name" ,
1918+ format ! ( "{}." , field_path_str) ,
1919+ Applicability :: MaybeIncorrect ,
1920+ ) ;
1921+ }
1922+ }
1923+ }
1924+ err
1925+ }
1926+
1927+ fn get_field_candidates (
1928+ & self ,
1929+ span : Span ,
1930+ base_t : Ty < ' tcx > ,
1931+ ) -> Option < ( & Vec < ty:: FieldDef > , SubstsRef < ' tcx > ) > {
1932+ debug ! ( "get_field_candidates(span: {:?}, base_t: {:?}" , span, base_t) ;
1933+
1934+ let mut autoderef = self . autoderef ( span, base_t) ;
1935+ while let Some ( ( base_t, _) ) = autoderef. next ( ) {
1936+ match base_t. kind ( ) {
1937+ ty:: Adt ( base_def, substs) if !base_def. is_enum ( ) => {
1938+ let fields = & base_def. non_enum_variant ( ) . fields ;
1939+ // For compile-time reasons put a limit on number of fields we search
1940+ if fields. len ( ) > 100 {
1941+ return None ;
1942+ }
1943+ return Some ( ( fields, substs) ) ;
1944+ }
1945+ _ => { }
1946+ }
1947+ }
1948+ None
1949+ }
1950+
1951+ /// This method is called after we have encountered a missing field error to recursively
1952+ /// search for the field
1953+ fn check_for_nested_field (
1954+ & self ,
1955+ span : Span ,
1956+ target_field : Ident ,
1957+ candidate_field : & ty:: FieldDef ,
1958+ subst : SubstsRef < ' tcx > ,
1959+ mut field_path : Vec < Ident > ,
1960+ ) -> Option < Vec < Ident > > {
1961+ debug ! (
1962+ "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}" ,
1963+ span, candidate_field, field_path
1964+ ) ;
1965+
1966+ if candidate_field. ident == target_field {
1967+ Some ( field_path)
1968+ } else if field_path. len ( ) > 3 {
1969+ // For compile-time reasons and to avoid infinite recursion we only check for fields
1970+ // up to a depth of three
1971+ None
1972+ } else {
1973+ // recursively search fields of `candidate_field` if it's a ty::Adt
1974+
1975+ field_path. push ( candidate_field. ident . normalize_to_macros_2_0 ( ) ) ;
1976+ let field_ty = candidate_field. ty ( self . tcx , subst) ;
1977+ if let Some ( ( nested_fields, _) ) = self . get_field_candidates ( span, & field_ty) {
1978+ for field in nested_fields. iter ( ) {
1979+ let ident = field. ident . normalize_to_macros_2_0 ( ) ;
1980+ if ident == target_field {
1981+ return Some ( field_path) ;
1982+ } else {
1983+ let field_path = field_path. clone ( ) ;
1984+ if let Some ( path) = self . check_for_nested_field (
1985+ span,
1986+ target_field,
1987+ field,
1988+ subst,
1989+ field_path,
1990+ ) {
1991+ return Some ( path) ;
1992+ }
1993+ }
1994+ }
1995+ }
1996+ None
1997+ }
18981998 }
18991999
19002000 fn check_expr_index (
0 commit comments