Rollup merge of #64852 - Baranowski:param_note_52082, r=estebank

Print ParamTy span when accessing a field (#52082)
This commit is contained in:
Mazdak Farrokhzad 2019-09-28 22:12:07 +02:00 committed by GitHub
commit 6145757a26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 188 additions and 26 deletions

View file

@ -1394,30 +1394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if self.method_exists(field, expr_t, expr.hir_id, true) {
self.ban_take_value_of_method(expr, expr_t, field);
} else if !expr_t.is_primitive_ty() {
let mut err = self.no_such_field_err(field.span, field, expr_t);
match expr_t.kind {
ty::Adt(def, _) if !def.is_enum() => {
self.suggest_fields_on_recordish(&mut err, def, field);
}
ty::Array(_, len) => {
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
}
ty::RawPtr(..) => {
self.suggest_first_deref_field(&mut err, expr, base, field);
}
_ => {}
}
if field.name == kw::Await {
// We know by construction that `<expr>.await` is either on Rust 2015
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
err.note("to `.await` a `Future`, switch to Rust 2018");
err.help("set `edition = \"2018\"` in `Cargo.toml`");
err.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
}
err.emit();
self.ban_nonexisting_field(field, base, expr, expr_t);
} else {
type_error_struct!(
self.tcx().sess,
@ -1433,6 +1410,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx().types.err
}
fn ban_nonexisting_field(
&self,
field: ast::Ident,
base: &'tcx hir::Expr,
expr: &'tcx hir::Expr,
expr_t: Ty<'tcx>,
) {
let mut err = self.no_such_field_err(field.span, field, expr_t);
match expr_t.peel_refs().kind {
ty::Array(_, len) => {
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
}
ty::RawPtr(..) => {
self.suggest_first_deref_field(&mut err, expr, base, field);
}
ty::Adt(def, _) if !def.is_enum() => {
self.suggest_fields_on_recordish(&mut err, def, field);
}
ty::Param(param_ty) => {
self.point_at_param_definition(&mut err, param_ty);
}
_ => {}
}
if field.name == kw::Await {
// We know by construction that `<expr>.await` is either on Rust 2015
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
err.note("to `.await` a `Future`, switch to Rust 2018");
err.help("set `edition = \"2018\"` in `Cargo.toml`");
err.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
}
err.emit();
}
fn ban_private_field_access(
&self,
expr: &hir::Expr,
@ -1495,6 +1508,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) {
let generics = self.tcx.generics_of(self.body_id.owner_def_id());
let generic_param = generics.type_param(&param, self.tcx);
if let ty::GenericParamDefKind::Type{synthetic: Some(..), ..} = generic_param.kind {
return;
}
let param_def_id = generic_param.def_id;
let param_hir_id = match self.tcx.hir().as_local_hir_id(param_def_id) {
Some(x) => x,
None => return,
};
let param_span = self.tcx.hir().span(param_hir_id);
let param_name = self.tcx.hir().ty_param_name(param_hir_id);
err.span_label(param_span, &format!("type parameter '{}' declared here", param_name));
}
fn suggest_fields_on_recordish(
&self,
err: &mut DiagnosticBuilder<'_>,

View file

@ -2,7 +2,7 @@ error[E0609]: no field `c` on type `&Foo`
--> $DIR/issue-30580.rs:12:11
|
LL | b.c;
| ^
| ^ help: a field with a similar name exists: `a`
error: aborting due to previous error

View file

@ -4,6 +4,9 @@ error[E0609]: no field `trace` on type `&T`
LL | if $ctx.trace {
| ^^^^^
...
LL | fn wrap<T>(context: &T) -> ()
| - type parameter 'T' declared here
LL | {
LL | log!(context, "entered wrapper");
| --------------------------------- in this macro invocation

View file

@ -2,7 +2,7 @@ error[E0609]: no field `d` on type `&A`
--> $DIR/struct-pat-derived-error.rs:8:31
|
LL | let A { x, y } = self.d;
| ^
| ^ help: a field with a similar name exists: `b`
error[E0026]: struct `A` does not have fields named `x`, `y`
--> $DIR/struct-pat-derived-error.rs:8:17

View file

@ -0,0 +1,54 @@
// Fix issue 52082: Confusing error if accidentially defining a type paramter with the same name as
// an existing type
//
// To this end, make sure that when trying to retrieve a field of a (reference to) type parameter,
// rustc points to the point where the parameter was defined.
#[derive(Debug)]
struct Point
{
x: i32,
y: i32
}
impl Point
{
fn add(a: &Point, b: &Point) -> Point
{
Point {x: a.x + b.x, y: a.y + b.y}
}
}
trait Eq
{
fn equals_ref<T>(a: &T, b: &T) -> bool;
fn equals_val<T>(a: T, b: T) -> bool;
}
impl Eq for Point
{
fn equals_ref<Point>(a: &Point, b: &Point) -> bool
{
a.x == b.x && a.y == b.y //~ ERROR no field `x` on type `&Point` [E0609]
//~|ERROR no field `x` on type `&Point` [E0609]
//~|ERROR no field `y` on type `&Point` [E0609]
//~|ERROR no field `y` on type `&Point` [E0609]
}
fn equals_val<Point>(a: Point, b: Point) -> bool
{
a.x == b.x && a.y == b.y //~ ERROR no field `x` on type `Point` [E0609]
//~|ERROR no field `x` on type `Point` [E0609]
//~|ERROR no field `y` on type `Point` [E0609]
//~|ERROR no field `y` on type `Point` [E0609]
}
}
fn main()
{
let p1 = Point {x: 0, y: 10};
let p2 = Point {x: 20, y: 42};
println!("{:?}", Point::add(&p1, &p2));
println!("p1: {:?}, p2: {:?}", p1, p2);
println!("&p1 == &p2: {:?}", Point::equals_ref(&p1, &p2));
println!("p1 == p2: {:?}", Point::equals_val(p1, p2));
}

View file

@ -0,0 +1,75 @@
error[E0609]: no field `x` on type `&Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:11
|
LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `x` on type `&Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:18
|
LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `y` on type `&Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:25
|
LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `y` on type `&Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:32
|
LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `x` on type `Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:11
|
LL | fn equals_val<Point>(a: Point, b: Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `x` on type `Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:18
|
LL | fn equals_val<Point>(a: Point, b: Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `y` on type `Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:25
|
LL | fn equals_val<Point>(a: Point, b: Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error[E0609]: no field `y` on type `Point`
--> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:32
|
LL | fn equals_val<Point>(a: Point, b: Point) -> bool
| ----- type parameter 'Point' declared here
LL | {
LL | a.x == b.x && a.y == b.y
| ^
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0609`.