diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 0cdf2fa2a5b9e..412a9fc8c1a63 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1397,9 +1397,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); } @@ -1409,6 +1406,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. @@ -1495,6 +1506,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`.