diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b086c427ba5..4b40a46f74e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2956,6 +2956,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { format!("did you mean `{}`?", suggested_field_name)); } else { err.span_label(field.span, "unknown field"); + let struct_variant_def = def.struct_variant(); + let available_field_names = self.available_field_names( + struct_variant_def); + err.note(&format!("available fields are: {}", + available_field_names.join(", "))); }; } ty::TyRawPtr(..) => { @@ -2979,7 +2984,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Return an hint about the closest match in field names fn suggest_field_name(variant: &'tcx ty::VariantDef, field: &Spanned, - skip : Vec) + skip: Vec) -> Option { let name = field.node.as_str(); let names = variant.fields.iter().filter_map(|field| { @@ -2992,8 +2997,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }); - // only find fits with at least one matching letter - find_best_match_for_name(names, &name, Some(name.len())) + find_best_match_for_name(names, &name, None) + } + + fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + let mut available = Vec::new(); + for field in variant.fields.iter() { + let (_, def_scope) = self.tcx.adjust(field.name, variant.did, self.body_id); + if field.vis.is_accessible_from(def_scope, self.tcx) { + available.push(field.name.to_string()); + } + } + available } // Check tuple index expressions @@ -3107,14 +3122,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { format!("field does not exist - did you mean `{}`?", field_name)); } else { match ty.sty { - ty::TyAdt(adt, ..) if adt.is_enum() => { - err.span_label(field.name.span, format!("`{}::{}` does not have this field", - ty, variant.name)); - } - _ => { - err.span_label(field.name.span, format!("`{}` does not have this field", ty)); + ty::TyAdt(adt, ..) => { + if adt.is_enum() { + err.span_label(field.name.span, + format!("`{}::{}` does not have this field", + ty, variant.name)); + } else { + err.span_label(field.name.span, + format!("`{}` does not have this field", ty)); + } + let available_field_names = self.available_field_names(variant); + err.note(&format!("available fields are: {}", + available_field_names.join(", "))); } + _ => bug!("non-ADT passed to report_unknown_field") } + }; err.emit(); } diff --git a/src/test/compile-fail/E0559.rs b/src/test/compile-fail/E0559.rs index fa6c885843e..21bb2dc7002 100644 --- a/src/test/compile-fail/E0559.rs +++ b/src/test/compile-fail/E0559.rs @@ -15,5 +15,6 @@ enum Field { fn main() { let s = Field::Fool { joke: 0 }; //~^ ERROR E0559 - //~| NOTE field does not exist - did you mean `x`? + //~| NOTE `Field::Fool` does not have this field + //~| NOTE available fields are: x } diff --git a/src/test/compile-fail/E0560.rs b/src/test/compile-fail/E0560.rs index c6326a0f977..7aa6b2e86d6 100644 --- a/src/test/compile-fail/E0560.rs +++ b/src/test/compile-fail/E0560.rs @@ -16,4 +16,5 @@ fn main() { let s = Simba { mother: 1, father: 0 }; //~^ ERROR E0560 //~| NOTE `Simba` does not have this field + //~| NOTE available fields are: mother } diff --git a/src/test/compile-fail/issue-19922.rs b/src/test/compile-fail/issue-19922.rs index d7b2f2b3f99..429c4384117 100644 --- a/src/test/compile-fail/issue-19922.rs +++ b/src/test/compile-fail/issue-19922.rs @@ -15,5 +15,6 @@ enum Homura { fn main() { let homura = Homura::Akemi { kaname: () }; //~^ ERROR variant `Homura::Akemi` has no field named `kaname` - //~| NOTE field does not exist - did you mean `madoka`? + //~| NOTE `Homura::Akemi` does not have this field + //~| NOTE available fields are: madoka } diff --git a/src/test/compile-fail/numeric-fields.rs b/src/test/compile-fail/numeric-fields.rs index 00fde3025a6..242c3a3a33d 100644 --- a/src/test/compile-fail/numeric-fields.rs +++ b/src/test/compile-fail/numeric-fields.rs @@ -13,7 +13,8 @@ struct S(u8, u16); fn main() { let s = S{0b1: 10, 0: 11}; //~^ ERROR struct `S` has no field named `0b1` - //~| NOTE field does not exist - did you mean `1`? + //~| NOTE `S` does not have this field + //~| NOTE available fields are: 0, 1 match s { S{0: a, 0x1: b, ..} => {} //~^ ERROR does not have a field named `0x1` diff --git a/src/test/compile-fail/struct-fields-too-many.rs b/src/test/compile-fail/struct-fields-too-many.rs index 0848ada731a..78ab94d5fb4 100644 --- a/src/test/compile-fail/struct-fields-too-many.rs +++ b/src/test/compile-fail/struct-fields-too-many.rs @@ -18,5 +18,6 @@ fn main() { bar: 0 //~^ ERROR struct `BuildData` has no field named `bar` //~| NOTE `BuildData` does not have this field + //~| NOTE available fields are: foo }; } diff --git a/src/test/compile-fail/suggest-private-fields.rs b/src/test/compile-fail/suggest-private-fields.rs index 3672e0e90c2..959932af9b1 100644 --- a/src/test/compile-fail/suggest-private-fields.rs +++ b/src/test/compile-fail/suggest-private-fields.rs @@ -27,7 +27,8 @@ fn main () { //~| NOTE field does not exist - did you mean `a`? bb: 20, //~^ ERROR struct `xc::B` has no field named `bb` - //~| NOTE field does not exist - did you mean `a`? + //~| NOTE `xc::B` does not have this field + //~| NOTE available fields are: a }; // local crate struct let l = A { diff --git a/src/test/compile-fail/union/union-fields.rs b/src/test/compile-fail/union/union-fields.rs index b5d582a5746..2bcc2204e33 100644 --- a/src/test/compile-fail/union/union-fields.rs +++ b/src/test/compile-fail/union/union-fields.rs @@ -20,6 +20,7 @@ fn main() { let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field //~^ ERROR union `U` has no field named `c` //~| NOTE `U` does not have this field + //~| NOTE available fields are: a, b let u = U { ..u }; //~ ERROR union expressions should have exactly one field //~^ ERROR functional record update syntax requires a struct diff --git a/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr b/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr index 82e3eab0836..610466c894a 100644 --- a/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr +++ b/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr @@ -3,6 +3,8 @@ error[E0609]: no field `zz` on type `Foo` | 17 | f.zz; | ^^ unknown field + | + = note: available fields are: bar error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs b/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs new file mode 100644 index 00000000000..4b0cc7b96a7 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod submodule { + + #[derive(Default)] + pub struct Demo { + pub favorite_integer: isize, + secret_integer: isize, + pub innocently_misspellable: () + } + + impl Demo { + fn new_with_secret_two() -> Self { + Self { secret_integer: 2, inocently_mispellable: () } + } + + fn new_with_secret_three() -> Self { + Self { secret_integer: 3, egregiously_nonexistent_field: () } + } + } + +} + +fn main() { + use submodule::Demo; + + let demo = Demo::default(); + let innocent_field_misaccess = demo.inocently_mispellable; + // note shouldn't suggest private `secret_integer` field + let egregious_field_misaccess = demo.egregiously_nonexistent_field; +} diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr new file mode 100644 index 00000000000..17edac92fd9 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -0,0 +1,30 @@ +error[E0560]: struct `submodule::Demo` has no field named `inocently_mispellable` + --> $DIR/issue-42599_available_fields_note.rs:22:39 + | +22 | Self { secret_integer: 2, inocently_mispellable: () } + | ^^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`? + +error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field` + --> $DIR/issue-42599_available_fields_note.rs:26:39 + | +26 | Self { secret_integer: 3, egregiously_nonexistent_field: () } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field + | + = note: available fields are: favorite_integer, secret_integer, innocently_misspellable + +error[E0609]: no field `inocently_mispellable` on type `submodule::Demo` + --> $DIR/issue-42599_available_fields_note.rs:36:41 + | +36 | let innocent_field_misaccess = demo.inocently_mispellable; + | ^^^^^^^^^^^^^^^^^^^^^ did you mean `innocently_misspellable`? + +error[E0609]: no field `egregiously_nonexistent_field` on type `submodule::Demo` + --> $DIR/issue-42599_available_fields_note.rs:38:42 + | +38 | let egregious_field_misaccess = demo.egregiously_nonexistent_field; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown field + | + = note: available fields are: favorite_integer, innocently_misspellable + +error: aborting due to 4 previous errors +