Rollup merge of #36798 - gavinb:fix/36164, r=GuillaumeGomez
Improve error message and snippet for "did you mean `x`" - Fixes #36164 - Part of #35233 Based on the standalone example https://is.gd/8STXMd posted by @nikomatsakis and using the third formatting option mentioned in #36164 and agreed by @jonathandturner. Note however this does not address the question of [how to handle an empty or unknown suggestion](https://github.com/rust-lang/rust/issues/36164#issuecomment-244460024). @nikomatsakis any suggestions on how best to address that part?
This commit is contained in:
commit
5a4ecb4a0c
22 changed files with 89 additions and 36 deletions
|
@ -2959,18 +2959,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
.emit();
|
.emit();
|
||||||
self.tcx().types.err
|
self.tcx().types.err
|
||||||
} else {
|
} else {
|
||||||
let mut err = self.type_error_struct(expr.span, |actual| {
|
let mut err = self.type_error_struct(field.span, |actual| {
|
||||||
format!("attempted access of field `{}` on type `{}`, \
|
format!("no field `{}` on type `{}`",
|
||||||
but no field with that name was found",
|
|
||||||
field.node, actual)
|
field.node, actual)
|
||||||
}, expr_t);
|
}, expr_t);
|
||||||
match expr_t.sty {
|
match expr_t.sty {
|
||||||
ty::TyAdt(def, _) if !def.is_enum() => {
|
ty::TyAdt(def, _) if !def.is_enum() => {
|
||||||
if let Some(suggested_field_name) =
|
if let Some(suggested_field_name) =
|
||||||
Self::suggest_field_name(def.struct_variant(), field, vec![]) {
|
Self::suggest_field_name(def.struct_variant(), field, vec![]) {
|
||||||
err.span_help(field.span,
|
err.span_label(field.span,
|
||||||
&format!("did you mean `{}`?", suggested_field_name));
|
&format!("did you mean `{}`?", suggested_field_name));
|
||||||
};
|
} else {
|
||||||
|
err.span_label(field.span,
|
||||||
|
&format!("unknown field"));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
ty::TyRawPtr(..) => {
|
ty::TyRawPtr(..) => {
|
||||||
err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \
|
err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \
|
||||||
|
|
|
@ -11,6 +11,6 @@
|
||||||
// Check that bogus field access is non-fatal
|
// Check that bogus field access is non-fatal
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 0;
|
let x = 0;
|
||||||
let _ = x.foo; //~ ERROR attempted access of field
|
let _ = x.foo; //~ no field `foo` on type `{integer}`
|
||||||
let _ = x.bar; //~ ERROR attempted access of field
|
let _ = x.bar; //~ no field `bar` on type `{integer}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ fn main()
|
||||||
//~| NOTE required for the cast to the object type `Foo`
|
//~| NOTE required for the cast to the object type `Foo`
|
||||||
|
|
||||||
// check no error cascade
|
// check no error cascade
|
||||||
let _ = main.f as *const u32; //~ ERROR attempted access of field
|
let _ = main.f as *const u32; //~ no field `f` on type `fn() {main}`
|
||||||
|
|
||||||
let cf: *const Foo = &0;
|
let cf: *const Foo = &0;
|
||||||
let _ = cf as *const [u16];
|
let _ = cf as *const [u16];
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl<'a, 'tcx> Pass<'a, 'tcx>
|
||||||
pub fn tcx(&self) -> &'a &'tcx () { self.1 }
|
pub fn tcx(&self) -> &'a &'tcx () { self.1 }
|
||||||
fn lol(&mut self, b: &Foo)
|
fn lol(&mut self, b: &Foo)
|
||||||
{
|
{
|
||||||
b.c; //~ ERROR no field with that name was found
|
b.c; //~ ERROR no field `c` on type `&Foo`
|
||||||
self.tcx();
|
self.tcx();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,9 @@ struct A { x: i32, y: f64 }
|
||||||
|
|
||||||
#[cfg(not(works))]
|
#[cfg(not(works))]
|
||||||
unsafe fn access(n:*mut A) -> (i32, f64) {
|
unsafe fn access(n:*mut A) -> (i32, f64) {
|
||||||
let x : i32 = n.x; //~ ERROR attempted access of field `x`
|
let x : i32 = n.x; //~ no field `x` on type `*mut A`
|
||||||
//~| NOTE `n` is a native pointer; perhaps you need to deref with `(*n).x`
|
//~| NOTE `n` is a native pointer; perhaps you need to deref with `(*n).x`
|
||||||
let y : f64 = n.y; //~ ERROR attempted access of field `y`
|
let y : f64 = n.y; //~ no field `y` on type `*mut A`
|
||||||
//~| NOTE `n` is a native pointer; perhaps you need to deref with `(*n).y`
|
//~| NOTE `n` is a native pointer; perhaps you need to deref with `(*n).y`
|
||||||
(x, y)
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,5 @@
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let foo = "str";
|
let foo = "str";
|
||||||
println!("{}", foo.desc); //~ ERROR attempted access of field `desc` on type `&str`,
|
println!("{}", foo.desc); //~ no field `desc` on type `&str`
|
||||||
// but no field with that name was found
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,5 @@ const STRUCT: MyStruct = MyStruct { field: 42 };
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a: [isize; STRUCT.nonexistent_field];
|
let a: [isize; STRUCT.nonexistent_field];
|
||||||
//~^ ERROR attempted access of field `nonexistent_field`
|
//~^ no field `nonexistent_field` on type `MyStruct`
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,5 @@ enum Foo { Bar }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Foo::Bar.a;
|
Foo::Bar.a;
|
||||||
//~^ ERROR: attempted access of field `a` on type `Foo`, but no field with that name was found
|
//~^ no field `a` on type `Foo`
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
1.create_a_type_error[ //~ ERROR attempted access of field
|
1.create_a_type_error[ //~ no field `create_a_type_error` on type `{integer}`
|
||||||
()+() //~ ERROR binary operation `+` cannot be applied
|
()+() //~ ERROR binary operation `+` cannot be applied
|
||||||
// ^ ensure that we typeck the inner expression ^
|
// ^ ensure that we typeck the inner expression ^
|
||||||
];
|
];
|
||||||
|
|
|
@ -17,13 +17,13 @@ pub enum Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test(a: Foo) {
|
fn test(a: Foo) {
|
||||||
println!("{}", a.b); //~ ERROR attempted access of field
|
println!("{}", a.b); //~ no field `b` on type `Foo`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = Attribute::Code {
|
let x = Attribute::Code {
|
||||||
attr_name_idx: 42,
|
attr_name_idx: 42,
|
||||||
};
|
};
|
||||||
let z = (&x).attr_name_idx; //~ ERROR attempted access of field
|
let z = (&x).attr_name_idx; //~ no field `attr_name_idx` on type `&Attribute`
|
||||||
let y = x.attr_name_idx; //~ ERROR attempted access of field
|
let y = x.attr_name_idx; //~ no field `attr_name_idx` on type `Attribute`
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
macro_rules! log {
|
macro_rules! log {
|
||||||
( $ctx:expr, $( $args:expr),* ) => {
|
( $ctx:expr, $( $args:expr),* ) => {
|
||||||
if $ctx.trace {
|
if $ctx.trace {
|
||||||
//~^ ERROR attempted access of field `trace` on type `&T`, but no field with that name
|
//~^ no field `trace` on type `&T`
|
||||||
println!( $( $args, )* );
|
println!( $( $args, )* );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@
|
||||||
// Related issues: #20401, #20506, #20614, #20752, #20829, #20846, #20885, #20886
|
// Related issues: #20401, #20506, #20614, #20752, #20829, #20846, #20885, #20886
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
"".homura[""]; //~ ERROR no field with that name was found
|
"".homura[""]; //~ no field `homura` on type `&'static str`
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
foo: 0,
|
foo: 0,
|
||||||
bar: 0.5,
|
bar: 0.5,
|
||||||
};
|
};
|
||||||
let x = foo.baa;//~ ERROR attempted access of field `baa` on type `BuildData`
|
let x = foo.baa;//~ no field `baa` on type `BuildData`
|
||||||
//~^ HELP did you mean `bar`?
|
//~^ did you mean `bar`?
|
||||||
println!("{}", x);
|
println!("{}", x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct a {
|
||||||
|
|
||||||
impl a {
|
impl a {
|
||||||
fn foo(&self) {
|
fn foo(&self) {
|
||||||
let a { x, y } = self.d; //~ ERROR attempted access of field `d`
|
let a { x, y } = self.d; //~ ERROR no field `d` on type `&a`
|
||||||
//~^ ERROR struct `a` does not have a field named `x`
|
//~^ ERROR struct `a` does not have a field named `x`
|
||||||
//~^^ ERROR struct `a` does not have a field named `y`
|
//~^^ ERROR struct `a` does not have a field named `y`
|
||||||
//~^^^ ERROR pattern does not mention field `b`
|
//~^^^ ERROR pattern does not mention field `b`
|
||||||
|
|
|
@ -22,8 +22,8 @@ fn main() {
|
||||||
let u = U { principle: 0 };
|
let u = U { principle: 0 };
|
||||||
//~^ ERROR union `U` has no field named `principle`
|
//~^ ERROR union `U` has no field named `principle`
|
||||||
//~| NOTE field does not exist - did you mean `principal`?
|
//~| NOTE field does not exist - did you mean `principal`?
|
||||||
let w = u.principial; //~ ERROR attempted access of field `principial` on type `U`
|
let w = u.principial; //~ ERROR no field `principial` on type `U`
|
||||||
//~^ HELP did you mean `principal`?
|
//~^ did you mean `principal`?
|
||||||
|
|
||||||
let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
|
let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
|
||||||
//~^ HELP maybe a `()` to call it is missing?
|
//~^ HELP maybe a `()` to call it is missing?
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn f(p: *const Rec) -> isize {
|
||||||
// are prohibited by various checks, such as that the enum is
|
// are prohibited by various checks, such as that the enum is
|
||||||
// instantiable and so forth).
|
// instantiable and so forth).
|
||||||
|
|
||||||
return p.f; //~ ERROR attempted access of field `f` on type `*const Rec`
|
return p.f; //~ ERROR no field `f` on type `*const Rec`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -39,13 +39,13 @@ pub fn use_X() -> u32 {
|
||||||
let x: X = X { x: 22 };
|
let x: X = X { x: 22 };
|
||||||
//[cfail2]~^ ERROR struct `X` has no field named `x`
|
//[cfail2]~^ ERROR struct `X` has no field named `x`
|
||||||
x.x as u32
|
x.x as u32
|
||||||
//[cfail2]~^ ERROR attempted access of field `x`
|
//[cfail2]~^ ERROR no field `x` on type `X`
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
|
#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
|
||||||
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
||||||
embed.x.x as u32
|
embed.x.x as u32
|
||||||
//[cfail2]~^ ERROR attempted access of field `x`
|
//[cfail2]~^ ERROR no field `x` on type `X`
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustc_clean(label="TypeckItemBody", cfg="cfail2")]
|
#[rustc_clean(label="TypeckItemBody", cfg="cfail2")]
|
||||||
|
|
18
src/test/ui/did_you_mean/issue-36798.rs
Normal file
18
src/test/ui/did_you_mean/issue-36798.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
bar: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = Foo { bar: 22 };
|
||||||
|
f.baz;
|
||||||
|
}
|
8
src/test/ui/did_you_mean/issue-36798.stderr
Normal file
8
src/test/ui/did_you_mean/issue-36798.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: no field `baz` on type `Foo`
|
||||||
|
--> $DIR/issue-36798.rs:17:7
|
||||||
|
|
|
||||||
|
17 | f.baz;
|
||||||
|
| ^^^ did you mean `bar`?
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
18
src/test/ui/did_you_mean/issue-36798_unknown_field.rs
Normal file
18
src/test/ui/did_you_mean/issue-36798_unknown_field.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
bar: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = Foo { bar: 22 };
|
||||||
|
f.zz;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
error: no field `zz` on type `Foo`
|
||||||
|
--> $DIR/issue-36798_unknown_field.rs:17:7
|
||||||
|
|
|
||||||
|
17 | f.zz;
|
||||||
|
| ^^ unknown field
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -7,11 +7,11 @@ error: no method named `fake` found for type `{integer}` in the current scope
|
||||||
50 | fake_method_stmt!();
|
50 | fake_method_stmt!();
|
||||||
| -------------------- in this macro invocation
|
| -------------------- in this macro invocation
|
||||||
|
|
||||||
error: attempted access of field `fake` on type `{integer}`, but no field with that name was found
|
error: no field `fake` on type `{integer}`
|
||||||
--> $DIR/macro-backtrace-invalid-internals.rs:21:11
|
--> $DIR/macro-backtrace-invalid-internals.rs:21:13
|
||||||
|
|
|
|
||||||
21 | 1.fake
|
21 | 1.fake
|
||||||
| ^^^^^^
|
| ^^^^
|
||||||
...
|
...
|
||||||
51 | fake_field_stmt!();
|
51 | fake_field_stmt!();
|
||||||
| ------------------- in this macro invocation
|
| ------------------- in this macro invocation
|
||||||
|
@ -34,11 +34,11 @@ error: no method named `fake` found for type `{integer}` in the current scope
|
||||||
54 | let _ = fake_method_expr!();
|
54 | let _ = fake_method_expr!();
|
||||||
| ------------------- in this macro invocation
|
| ------------------- in this macro invocation
|
||||||
|
|
||||||
error: attempted access of field `fake` on type `{integer}`, but no field with that name was found
|
error: no field `fake` on type `{integer}`
|
||||||
--> $DIR/macro-backtrace-invalid-internals.rs:39:11
|
--> $DIR/macro-backtrace-invalid-internals.rs:39:13
|
||||||
|
|
|
|
||||||
39 | 1.fake
|
39 | 1.fake
|
||||||
| ^^^^^^
|
| ^^^^
|
||||||
...
|
...
|
||||||
55 | let _ = fake_field_expr!();
|
55 | let _ = fake_field_expr!();
|
||||||
| ------------------ in this macro invocation
|
| ------------------ in this macro invocation
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue