diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 97fcfd7151a1c..f59f908465dd3 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1390,30 +1390,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 `.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, @@ -1429,6 +1406,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 `.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, @@ -1491,6 +1504,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(¶m, 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<'_>, diff --git a/src/test/ui/derived-errors/issue-30580.stderr b/src/test/ui/derived-errors/issue-30580.stderr index 14c575f2699a6..7bd0eaf77a95d 100644 --- a/src/test/ui/derived-errors/issue-30580.stderr +++ b/src/test/ui/derived-errors/issue-30580.stderr @@ -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 diff --git a/src/test/ui/issues/issue-31011.stderr b/src/test/ui/issues/issue-31011.stderr index ab0069510fcc0..c7618e0835b8d 100644 --- a/src/test/ui/issues/issue-31011.stderr +++ b/src/test/ui/issues/issue-31011.stderr @@ -4,6 +4,9 @@ error[E0609]: no field `trace` on type `&T` LL | if $ctx.trace { | ^^^^^ ... +LL | fn wrap(context: &T) -> () + | - type parameter 'T' declared here +LL | { LL | log!(context, "entered wrapper"); | --------------------------------- in this macro invocation diff --git a/src/test/ui/structs/struct-pat-derived-error.stderr b/src/test/ui/structs/struct-pat-derived-error.stderr index 673715cd3ef6b..6526ef58a4479 100644 --- a/src/test/ui/structs/struct-pat-derived-error.stderr +++ b/src/test/ui/structs/struct-pat-derived-error.stderr @@ -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 diff --git a/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs new file mode 100644 index 0000000000000..c57e8149574c9 --- /dev/null +++ b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs @@ -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(a: &T, b: &T) -> bool; + fn equals_val(a: T, b: T) -> bool; +} + +impl Eq for Point +{ + fn equals_ref(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(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)); +} diff --git a/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr new file mode 100644 index 0000000000000..4be4c91dfc2c3 --- /dev/null +++ b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr @@ -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(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(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(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(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(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(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(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(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`.