Skip to content

Commit

Permalink
Rollup merge of rust-lang#67741 - estebank:point-at-pat-def, r=Centril
Browse files Browse the repository at this point in the history
When encountering an Item in a pat context, point at the item def

```
error[E0308]: mismatched types
  --> $DIR/const-in-struct-pat.rs:8:17
   |
LL | struct foo;
   | ----------- `foo` defined here
...
LL |     let Thing { foo } = t;
   |                 ^^^ expected struct `std::string::String`, found struct `foo`
   |
   = note: `foo` is interpreted as a unit struct, not a new binding
help: you can bind the struct field to a different name
   |
LL |     let Thing { foo: other_foo } = t;
   |                 ^^^^^^^^^^^^^^
```
```
error[E0308]: mismatched types
  --> $DIR/const.rs:14:9
   |
LL | const FOO: Foo = Foo{bar: 5};
   | ----------------------------- constant defined here
...
LL |         FOO => {},
   |         ^^^
   |         |
   |         expected `&Foo`, found struct `Foo`
   |         `FOO` is interpreted as a constant, not a new binding
   |         help: use different name to introduce a new binding: `other_foo`
```

Fix rust-lang#55631, fix rust-lang#48062, cc rust-lang#42876.
  • Loading branch information
Centril authored Mar 7, 2020
2 parents 2890b37 + 125159f commit e8bb6c0
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 28 deletions.
103 changes: 79 additions & 24 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ struct TopInfo<'tcx> {
/// found type `std::result::Result<_, _>`
/// ```
span: Option<Span>,
/// This refers to the parent pattern. Used to provide extra diagnostic information on errors.
/// ```text
/// error[E0308]: mismatched types
/// --> $DIR/const-in-struct-pat.rs:8:17
/// |
/// L | struct f;
/// | --------- unit struct defined here
/// ...
/// L | let Thing { f } = t;
/// | ^
/// | |
/// | expected struct `std::string::String`, found struct `f`
/// | `f` is interpreted as a unit struct, not a new binding
/// | help: bind the struct field to a different name instead: `f: other_f`
/// ```
parent_pat: Option<&'tcx Pat<'tcx>>,
}

impl<'tcx> FnCtxt<'_, 'tcx> {
Expand Down Expand Up @@ -120,7 +136,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Option<Span>,
origin_expr: bool,
) {
self.check_pat(pat, expected, INITIAL_BM, TopInfo { expected, origin_expr, span });
let info = TopInfo { expected, origin_expr, span, parent_pat: None };
self.check_pat(pat, expected, INITIAL_BM, info);
}

/// Type check the given `pat` against the `expected` type
Expand Down Expand Up @@ -161,8 +178,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
}
PatKind::Or(pats) => {
let parent_pat = Some(pat);
for pat in pats {
self.check_pat(pat, expected, def_bm, ti);
self.check_pat(pat, expected, def_bm, TopInfo { parent_pat, ..ti });
}
expected
}
Expand Down Expand Up @@ -501,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

fn check_pat_ident(
&self,
pat: &Pat<'_>,
pat: &'tcx Pat<'tcx>,
ba: hir::BindingAnnotation,
var_id: HirId,
sub: Option<&'tcx Pat<'tcx>>,
Expand Down Expand Up @@ -546,7 +564,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

if let Some(p) = sub {
self.check_pat(&p, expected, def_bm, ti);
self.check_pat(&p, expected, def_bm, TopInfo { parent_pat: Some(&pat), ..ti });
}

local_ty
Expand Down Expand Up @@ -647,6 +665,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
variant_ty
} else {
for field in fields {
let ti = TopInfo { parent_pat: Some(&pat), ..ti };
self.check_pat(&field.pat, self.tcx.types.err, def_bm, ti);
}
return self.tcx.types.err;
Expand All @@ -656,9 +675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.demand_eqtype_pat(pat.span, expected, pat_ty, ti);

// Type-check subpatterns.
if self
.check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm, ti)
{
if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) {
pat_ty
} else {
self.tcx.types.err
Expand Down Expand Up @@ -696,18 +713,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

// Type-check the path.
let pat_ty = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id).0;
if let Some(mut err) =
let (pat_ty, pat_res) =
self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id);
if let Some(err) =
self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty)
{
err.emit();
self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat);
}
pat_ty
}

fn emit_bad_pat_path(
&self,
mut e: DiagnosticBuilder<'_>,
pat_span: Span,
res: Res,
pat_res: Res,
segments: &'b [hir::PathSegment<'b>],
parent_pat: Option<&Pat<'_>>,
) {
if let Some(span) = self.tcx.hir().res_span(pat_res) {
e.span_label(span, &format!("{} defined here", res.descr()));
if let [hir::PathSegment { ident, .. }] = &*segments {
e.span_label(
pat_span,
&format!(
"`{}` is interpreted as {} {}, not a new binding",
ident,
res.article(),
res.descr(),
),
);
let (msg, sugg) = match parent_pat {
Some(Pat { kind: hir::PatKind::Struct(..), .. }) => (
"bind the struct field to a different name instead",
format!("{}: other_{}", ident, ident.as_str().to_lowercase()),
),
_ => (
"introduce a new binding instead",
format!("other_{}", ident.as_str().to_lowercase()),
),
};
e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders);
}
}
e.emit();
}

fn check_pat_tuple_struct(
&self,
pat: &Pat<'_>,
pat: &'tcx Pat<'tcx>,
qpath: &hir::QPath<'_>,
subpats: &'tcx [&'tcx Pat<'tcx>],
ddpos: Option<usize>,
Expand All @@ -717,8 +772,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let tcx = self.tcx;
let on_error = || {
let parent_pat = Some(pat);
for pat in subpats {
self.check_pat(&pat, tcx.types.err, def_bm, ti);
self.check_pat(&pat, tcx.types.err, def_bm, TopInfo { parent_pat, ..ti });
}
};
let report_unexpected_res = |res: Res| {
Expand Down Expand Up @@ -793,7 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
self.check_pat(&subpat, field_ty, def_bm, ti);
self.check_pat(&subpat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti });

self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span);
}
Expand Down Expand Up @@ -938,8 +994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_struct_pat_fields(
&self,
adt_ty: Ty<'tcx>,
pat_id: HirId,
span: Span,
pat: &'tcx Pat<'tcx>,
variant: &'tcx ty::VariantDef,
fields: &'tcx [hir::FieldPat<'tcx>],
etc: bool,
Expand All @@ -950,7 +1005,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let (substs, adt) = match adt_ty.kind {
ty::Adt(adt, substs) => (substs, adt),
_ => span_bug!(span, "struct pattern is not an ADT"),
_ => span_bug!(pat.span, "struct pattern is not an ADT"),
};
let kind_name = adt.variant_descr();

Expand Down Expand Up @@ -983,7 +1038,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.get(&ident)
.map(|(i, f)| {
self.write_field_index(field.hir_id, *i);
self.tcx.check_stability(f.did, Some(pat_id), span);
self.tcx.check_stability(f.did, Some(pat.hir_id), span);
self.field_ty(span, f, substs)
})
.unwrap_or_else(|| {
Expand All @@ -994,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};

self.check_pat(&field.pat, field_ty, def_bm, ti);
self.check_pat(&field.pat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti });
}

let mut unmentioned_fields = variant
Expand All @@ -1017,7 +1072,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
struct_span_err!(
tcx.sess,
span,
pat.span,
E0638,
"`..` required with {} marked as non-exhaustive",
kind_name
Expand All @@ -1029,14 +1084,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if kind_name == "union" {
if fields.len() != 1 {
tcx.sess
.struct_span_err(span, "union patterns should have exactly one field")
.struct_span_err(pat.span, "union patterns should have exactly one field")
.emit();
}
if etc {
tcx.sess.struct_span_err(span, "`..` cannot be used in union patterns").emit();
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
}
} else if !etc && !unmentioned_fields.is_empty() {
self.error_unmentioned_fields(span, &unmentioned_fields, variant);
self.error_unmentioned_fields(pat.span, &unmentioned_fields, variant);
}
no_field_errors
}
Expand Down Expand Up @@ -1196,7 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

fn check_pat_ref(
&self,
pat: &Pat<'_>,
pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>,
mutbl: hir::Mutability,
expected: Ty<'tcx>,
Expand Down Expand Up @@ -1236,7 +1291,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
(tcx.types.err, tcx.types.err)
};
self.check_pat(&inner, inner_ty, def_bm, ti);
self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti });
rptr_ty
}

Expand Down
9 changes: 8 additions & 1 deletion src/test/ui/blind/blind-item-block-middle.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
error[E0308]: mismatched types
--> $DIR/blind-item-block-middle.rs:6:9
|
LL | mod foo { pub struct bar; }
| --------------- unit struct defined here
...
LL | let bar = 5;
| ^^^ expected integer, found struct `foo::bar`
| ^^^
| |
| expected integer, found struct `foo::bar`
| `bar` is interpreted as a unit struct, not a new binding
| help: introduce a new binding instead: `other_bar`

error: aborting due to previous error

Expand Down
9 changes: 8 additions & 1 deletion src/test/ui/issues/issue-33504.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
error[E0308]: mismatched types
--> $DIR/issue-33504.rs:7:13
|
LL | struct Test;
| ------------ unit struct defined here
...
LL | let Test = 1;
| ^^^^ expected integer, found struct `Test`
| ^^^^
| |
| expected integer, found struct `Test`
| `Test` is interpreted as a unit struct, not a new binding
| help: introduce a new binding instead: `other_test`

error: aborting due to previous error

Expand Down
9 changes: 8 additions & 1 deletion src/test/ui/issues/issue-4968.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
error[E0308]: mismatched types
--> $DIR/issue-4968.rs:5:16
|
LL | const A: (isize,isize) = (4,2);
| ------------------------------- constant defined here
LL | fn main() {
LL | match 42 { A => () }
| ^ expected integer, found tuple
| ^
| |
| expected integer, found tuple
| `A` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_a`
|
= note: expected type `{integer}`
found tuple `(isize, isize)`
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/issues/issue-5100.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:8:9
|
LL | enum A { B, C }
| - unit variant defined here
...
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/issues/issue-7867.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0308]: mismatched types
--> $DIR/issue-7867.rs:7:9
|
LL | enum A { B, C }
| - unit variant defined here
...
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/match/match-tag-nullary.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0308]: mismatched types
--> $DIR/match-tag-nullary.rs:4:40
|
LL | enum B { B }
| - unit variant defined here
LL |
LL | fn main() { let x: A = A::A; match x { B::B => { } } }
| - ^^^^ expected enum `A`, found enum `B`
| |
Expand Down
9 changes: 8 additions & 1 deletion src/test/ui/rfc-2005-default-binding-mode/const.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
error[E0308]: mismatched types
--> $DIR/const.rs:14:9
|
LL | const FOO: Foo = Foo{bar: 5};
| ----------------------------- constant defined here
...
LL | match &f {
| -- this expression has type `&Foo`
LL | FOO => {},
| ^^^ expected `&Foo`, found struct `Foo`
| ^^^
| |
| expected `&Foo`, found struct `Foo`
| `FOO` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_foo`

error: aborting due to previous error

Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/suggestions/const-in-struct-pat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[allow(non_camel_case_types)]
struct foo;
struct Thing {
foo: String,
}

fn example(t: Thing) {
let Thing { foo } = t; //~ ERROR mismatched types
}

fn main() {}
16 changes: 16 additions & 0 deletions src/test/ui/suggestions/const-in-struct-pat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/const-in-struct-pat.rs:8:17
|
LL | struct foo;
| ----------- unit struct defined here
...
LL | let Thing { foo } = t;
| ^^^ - this expression has type `Thing`
| |
| expected struct `std::string::String`, found struct `foo`
| `foo` is interpreted as a unit struct, not a new binding
| help: bind the struct field to a different name instead: `foo: other_foo`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

0 comments on commit e8bb6c0

Please sign in to comment.