From f922483112e407d69c02eb653d7e4bc0ffe2cfa0 Mon Sep 17 00:00:00 2001 From: Wojciech Baranowski Date: Thu, 26 Sep 2019 18:14:27 +0300 Subject: [PATCH 1/3] Print ParamTy span when accessing a field (#52082) --- src/librustc_typeck/check/expr.rs | 34 ++++++++- src/test/ui/typeck/issue-52082.rs | 54 +++++++++++++++ src/test/ui/typeck/issue-52082.stderr | 99 +++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/typeck/issue-52082.rs create mode 100644 src/test/ui/typeck/issue-52082.stderr diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 97fcfd7151a1c..c2beaddecd5c1 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1393,9 +1393,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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); } @@ -1405,6 +1402,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } + let deref_t = match expr_t.kind { + ty::Ref(_, ref_t, _) => ref_t, + _ => &expr_t + }; + match deref_t.kind { + ty::Adt(def, _) if !def.is_enum() => { + self.suggest_fields_on_recordish(&mut err, def, field); + } + ty::Param(param_ty) => { + self.explain_param(&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. @@ -1491,6 +1502,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + fn explain_param( + &self, + err: &mut DiagnosticBuilder<'_>, + param: ty::ParamTy, + ) { + let generics = self.tcx.generics_of(self.body_id.owner_def_id()); + let param_def_id = generics.type_param(¶m, self.tcx).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_note(param_span, &format!("Type parameter '{}' was declared here", param_name)); + } + fn suggest_fields_on_recordish( &self, err: &mut DiagnosticBuilder<'_>, diff --git a/src/test/ui/typeck/issue-52082.rs b/src/test/ui/typeck/issue-52082.rs new file mode 100644 index 0000000000000..c57e8149574c9 --- /dev/null +++ b/src/test/ui/typeck/issue-52082.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.stderr b/src/test/ui/typeck/issue-52082.stderr new file mode 100644 index 0000000000000..b6b616630d51f --- /dev/null +++ b/src/test/ui/typeck/issue-52082.stderr @@ -0,0 +1,99 @@ +error[E0609]: no field `x` on type `&Point` + --> $DIR/issue-52082.rs:31:11 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:29:19 + | +LL | fn equals_ref(a: &Point, b: &Point) -> bool + | ^^^^^ + +error[E0609]: no field `x` on type `&Point` + --> $DIR/issue-52082.rs:31:18 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:29:19 + | +LL | fn equals_ref(a: &Point, b: &Point) -> bool + | ^^^^^ + +error[E0609]: no field `y` on type `&Point` + --> $DIR/issue-52082.rs:31:25 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:29:19 + | +LL | fn equals_ref(a: &Point, b: &Point) -> bool + | ^^^^^ + +error[E0609]: no field `y` on type `&Point` + --> $DIR/issue-52082.rs:31:32 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:29:19 + | +LL | fn equals_ref(a: &Point, b: &Point) -> bool + | ^^^^^ + +error[E0609]: no field `x` on type `Point` + --> $DIR/issue-52082.rs:39:11 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:37:19 + | +LL | fn equals_val(a: Point, b: Point) -> bool + | ^^^^^ + +error[E0609]: no field `x` on type `Point` + --> $DIR/issue-52082.rs:39:18 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:37:19 + | +LL | fn equals_val(a: Point, b: Point) -> bool + | ^^^^^ + +error[E0609]: no field `y` on type `Point` + --> $DIR/issue-52082.rs:39:25 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:37:19 + | +LL | fn equals_val(a: Point, b: Point) -> bool + | ^^^^^ + +error[E0609]: no field `y` on type `Point` + --> $DIR/issue-52082.rs:39:32 + | +LL | a.x == b.x && a.y == b.y + | ^ + | +note: Type parameter 'Point' was declared here + --> $DIR/issue-52082.rs:37:19 + | +LL | fn equals_val(a: Point, b: Point) -> bool + | ^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0609`. From f3744a1b3dd1c3d6a27a6061fc1866194a453ee2 Mon Sep 17 00:00:00 2001 From: Wojciech Baranowski Date: Sat, 28 Sep 2019 14:39:19 +0300 Subject: [PATCH 2/3] Implement CRs --- src/librustc_typeck/check/expr.rs | 26 ++---- src/test/ui/derived-errors/issue-30580.stderr | 2 +- src/test/ui/issues/issue-31011.stderr | 3 + .../structs/struct-pat-derived-error.stderr | 2 +- ...52082-type-param-shadows-existing-type.rs} | 0 ...2-type-param-shadows-existing-type.stderr} | 88 +++++++------------ 6 files changed, 46 insertions(+), 75 deletions(-) rename src/test/ui/typeck/{issue-52082.rs => issue-52082-type-param-shadows-existing-type.rs} (100%) rename src/test/ui/typeck/{issue-52082.stderr => issue-52082-type-param-shadows-existing-type.stderr} (55%) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index c2beaddecd5c1..dae4db4003aeb 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1392,26 +1392,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if !expr_t.is_primitive_ty() { let mut err = self.no_such_field_err(field.span, field, expr_t); - match expr_t.kind { + 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); } - _ => {} - } - - let deref_t = match expr_t.kind { - ty::Ref(_, ref_t, _) => ref_t, - _ => &expr_t - }; - match deref_t.kind { ty::Adt(def, _) if !def.is_enum() => { self.suggest_fields_on_recordish(&mut err, def, field); } ty::Param(param_ty) => { - self.explain_param(&mut err, param_ty); + self.point_at_param_definition(&mut err, param_ty); } _ => {} } @@ -1502,13 +1494,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn explain_param( - &self, - err: &mut DiagnosticBuilder<'_>, - param: ty::ParamTy, - ) { + 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 param_def_id = generics.type_param(¶m, self.tcx).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, @@ -1516,7 +1508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param_span = self.tcx.hir().span(param_hir_id); let param_name = self.tcx.hir().ty_param_name(param_hir_id); - err.span_note(param_span, &format!("Type parameter '{}' was declared here", param_name)); + err.span_label(param_span, &format!("type parameter '{}' declared here", param_name)); } fn suggest_fields_on_recordish( 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.rs b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs similarity index 100% rename from src/test/ui/typeck/issue-52082.rs rename to src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.rs diff --git a/src/test/ui/typeck/issue-52082.stderr b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr similarity index 55% rename from src/test/ui/typeck/issue-52082.stderr rename to src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr index b6b616630d51f..4be4c91dfc2c3 100644 --- a/src/test/ui/typeck/issue-52082.stderr +++ b/src/test/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr @@ -1,98 +1,74 @@ error[E0609]: no field `x` on type `&Point` - --> $DIR/issue-52082.rs:31:11 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:29:19 - | -LL | fn equals_ref(a: &Point, b: &Point) -> bool - | ^^^^^ error[E0609]: no field `x` on type `&Point` - --> $DIR/issue-52082.rs:31:18 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:29:19 - | -LL | fn equals_ref(a: &Point, b: &Point) -> bool - | ^^^^^ error[E0609]: no field `y` on type `&Point` - --> $DIR/issue-52082.rs:31:25 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:29:19 - | -LL | fn equals_ref(a: &Point, b: &Point) -> bool - | ^^^^^ error[E0609]: no field `y` on type `&Point` - --> $DIR/issue-52082.rs:31:32 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:29:19 - | -LL | fn equals_ref(a: &Point, b: &Point) -> bool - | ^^^^^ error[E0609]: no field `x` on type `Point` - --> $DIR/issue-52082.rs:39:11 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:37:19 - | -LL | fn equals_val(a: Point, b: Point) -> bool - | ^^^^^ error[E0609]: no field `x` on type `Point` - --> $DIR/issue-52082.rs:39:18 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:37:19 - | -LL | fn equals_val(a: Point, b: Point) -> bool - | ^^^^^ error[E0609]: no field `y` on type `Point` - --> $DIR/issue-52082.rs:39:25 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:37:19 - | -LL | fn equals_val(a: Point, b: Point) -> bool - | ^^^^^ error[E0609]: no field `y` on type `Point` - --> $DIR/issue-52082.rs:39:32 + --> $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 | ^ - | -note: Type parameter 'Point' was declared here - --> $DIR/issue-52082.rs:37:19 - | -LL | fn equals_val(a: Point, b: Point) -> bool - | ^^^^^ error: aborting due to 8 previous errors From 9ad99c30cbf344b763602698b67c04ec3ce3de56 Mon Sep 17 00:00:00 2001 From: Wojciech Baranowski Date: Sat, 28 Sep 2019 15:26:00 +0300 Subject: [PATCH 3/3] Refactor into ban_nonexisting_field method --- src/librustc_typeck/check/expr.rs | 64 ++++++++++++++++++------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index dae4db4003aeb..f59f908465dd3 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1390,33 +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.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(); + self.ban_nonexisting_field(field, base, expr, expr_t); } else { type_error_struct!( self.tcx().sess, @@ -1432,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,