@@ -36,6 +36,7 @@ use rustc_infer::infer;
36
36
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
37
37
use rustc_middle:: ty;
38
38
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AllowTwoPhase } ;
39
+ use rustc_middle:: ty:: subst:: SubstsRef ;
39
40
use rustc_middle:: ty:: Ty ;
40
41
use rustc_middle:: ty:: TypeFoldable ;
41
42
use rustc_middle:: ty:: { AdtKind , Visibility } ;
@@ -46,8 +47,6 @@ use rustc_span::source_map::Span;
46
47
use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
47
48
use rustc_trait_selection:: traits:: { self , ObligationCauseCode } ;
48
49
49
- use std:: fmt:: Display ;
50
-
51
50
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
52
51
fn check_expr_eq_type ( & self , expr : & ' tcx hir:: Expr < ' tcx > , expected : Ty < ' tcx > ) {
53
52
let ty = self . check_expr_with_hint ( expr, expected) ;
@@ -1585,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1585
1584
base : & ' tcx hir:: Expr < ' tcx > ,
1586
1585
field : Ident ,
1587
1586
) -> Ty < ' tcx > {
1587
+ debug ! ( "check_field(expr: {:?}, base: {:?}, field: {:?})" , expr, base, field) ;
1588
1588
let expr_t = self . check_expr ( base) ;
1589
1589
let expr_t = self . structurally_resolved_type ( base. span , expr_t) ;
1590
1590
let mut private_candidate = None ;
1591
1591
let mut autoderef = self . autoderef ( expr. span , expr_t) ;
1592
1592
while let Some ( ( base_t, _) ) = autoderef. next ( ) {
1593
+ debug ! ( "base_t: {:?}" , base_t) ;
1593
1594
match base_t. kind ( ) {
1594
1595
ty:: Adt ( base_def, substs) if !base_def. is_enum ( ) => {
1595
1596
debug ! ( "struct named {:?}" , base_t) ;
@@ -1706,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1706
1707
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}" ,
1707
1708
field, base, expr, expr_t
1708
1709
) ;
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) ;
1710
1711
1711
1712
match * expr_t. peel_refs ( ) . kind ( ) {
1712
1713
ty:: Array ( _, len) => {
@@ -1880,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1880
1881
}
1881
1882
}
1882
1883
1883
- fn no_such_field_err < T : Display > (
1884
+ fn no_such_field_err (
1884
1885
& self ,
1885
- span : Span ,
1886
- field : T ,
1887
- expr_t : & ty:: TyS < ' _ > ,
1886
+ field : Ident ,
1887
+ expr_t : & ' tcx ty:: TyS < ' tcx > ,
1888
1888
) -> 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 ! (
1890
1893
self . tcx( ) . sess,
1891
- span,
1894
+ field . span,
1892
1895
expr_t,
1893
1896
E0609 ,
1894
1897
"no field `{}` on type `{}`" ,
1895
1898
field,
1896
1899
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
+ }
1898
1998
}
1899
1999
1900
2000
fn check_expr_index (
0 commit comments