Skip to content

field does not exist error: note fields if Levenshtein suggestion fails #43442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(", ")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm; ideally we would put backticks around these field names. But, more importantly, we generally have a rule that we only list out things exhaustively up to a certain limit (typically 5), and then after that we will say "...and others" or something. See e.g. this code in method suggestions.

};
}
ty::TyRawPtr(..) => {
Expand All @@ -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<ast::Name>,
skip : Vec<InternedString>)
skip: Vec<InternedString>)
-> Option<Symbol> {
let name = field.node.as_str();
let names = variant.fields.iter().filter_map(|field| {
Expand All @@ -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<String> {
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
Expand Down Expand Up @@ -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: {}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, of course

available_field_names.join(", ")));
}
_ => bug!("non-ADT passed to report_unknown_field")
}

};
err.emit();
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/compile-fail/E0559.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions src/test/compile-fail/E0560.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 2 additions & 1 deletion src/test/compile-fail/issue-19922.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 2 additions & 1 deletion src/test/compile-fail/numeric-fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/struct-fields-too-many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
3 changes: 2 additions & 1 deletion src/test/compile-fail/suggest-private-fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/union/union-fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/did_you_mean/issue-36798_unknown_field.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

39 changes: 39 additions & 0 deletions src/test/ui/did_you_mean/issue-42599_available_fields_note.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
}
30 changes: 30 additions & 0 deletions src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr
Original file line number Diff line number Diff line change
@@ -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