Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Point at enum definition when match patterns are not exhaustive #58380

Merged
merged 3 commits into from
Mar 4, 2019

Conversation

estebank
Copy link
Contributor

error[E0004]: non-exhaustive patterns: type `X` is non-empty
 --> file.rs:9:11
  |
1 | / enum X {
2 | |     A,
  | |     - variant not covered
3 | |     B,
  | |     - variant not covered
4 | |     C,
  | |     - variant not covered
5 | | }
  | |_- `X` defined here
...
9 |       match x {
  |             ^
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms

error[E0004]: non-exhaustive patterns: `B` and `C` not covered
  --> file.rs:11:11
   |
1  | / enum X {
2  | |     A,
3  | |     B,
4  | |     C,
   | |     - not covered
5  | | }
   | |_- `X` defined here
...
11 |       match x {
   |             ^ patterns `C` not covered

When a match expression doesn't have patterns covering every variant,
point at the enum's definition span. On a best effort basis, point at the
variant(s) that are missing. This does not handle the case when the missing
pattern is due to a field's enum variants:

enum E1 {
    A,
    B,
    C,
}
enum E2 {
    A(E1),
    B,
}
fn foo() {
    match E2::A(E1::A) {
        E2::A(E1::B) => {}
        E2::B => {}
    }
    //~^ ERROR `E2::A(E1::A)` and `E2::A(E1::C)` not handled
}

Unify look between match with no arms and match with some missing patterns.

Fix #37518.

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 11, 2019
@estebank
Copy link
Contributor Author

r? @zackmdavis

@rust-highfive

This comment has been minimized.

Copy link
Member

@zackmdavis zackmdavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming/commentary quibbles, otherwise r=me

@@ -204,25 +204,42 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
// is uninhabited.
let pat_ty = self.tables.node_id_to_type(scrut.hir_id);
let module = self.tcx.hir().get_module_parent(scrut.id);
let mut def_span = None;
let mut missing_variants = vec![];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually write empty vectors as Vec::new() (thinking, "no need to the macro sugar when there aren't any elements to initialize"), but rustfmt doesn't seem to have an opinion. (I guess you could argue that it's technically a semantic change.)

"ensure that all possible cases are being handled, \
possibly by adding wildcards or more match arms");
let mut err = create_e0004(self.tcx.sess, scrut.span, format!(
"non-exhaustive patterns: type `{}` is non-empty",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I realized that this was already here and not a part of the present changeset, but) do we even want to bother saying "type is non-empty" here? If we don't expect users to think or care about the empty enum very often, this might not be the most helpful time to educate them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to "non-exhaustive patterns: variant Foo of type Bar is not handled"

if def.variants.len() < 4 && !def.variants.is_empty() {
// keep around to point at the definition of non-covered variants
missing_variants = def.variants.iter()
.map(|variant| variant.ident.span)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call the variable missing_variant_spans or similar? (I balked at err.span_label(*variant on line 241.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collected the variant idents instead, keeping the vector name the same

src/librustc_mir/hair/pattern/check_match.rs Outdated Show resolved Hide resolved
LL | | }
| |_- `Terminator` defined here
...
LL | match x { //~ ERROR E0004
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(possibly a bad idea, probably out of scope for this PR) I find it counterintuitive for the primary span of a "non-exaustive patterns" error to point to the x in match x, rather than the body of match arms. (That could get very visually cluttered, though, especially with the secondary definition-spans introduced here. Also, pointing at the discriminant does seem intuitive when it's a pattern rather than an opaque variable, as in the examples in src/test/ui/check_match/issue-35609.stderr.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same thoughts you do, but I lay on the side of using the smallest possible primary span, for VSCode's sake.

@bors

This comment has been minimized.

```
error[E0004]: non-exhaustive patterns: type `X` is non-empty
 --> file.rs:9:11
  |
1 | / enum X {
2 | |     A,
  | |     - variant not covered
3 | |     B,
  | |     - variant not covered
4 | |     C,
  | |     - variant not covered
5 | | }
  | |_- `X` defined here
...
9 |       match x {
  |             ^
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms

error[E0004]: non-exhaustive patterns: `B` and `C` not covered
  --> file.rs:11:11
   |
1  | / enum X {
2  | |     A,
3  | |     B,
4  | |     C,
   | |     - not covered
5  | | }
   | |_- `X` defined here
...
11 |       match x {
   |             ^ patterns `C` not covered
```

When a match expression doesn't have patterns covering every variant,
point at the enum's definition span. On a best effort basis, point at the
variant(s) that are missing. This does not handle the case when the missing
pattern is due to a field's enum variants:

```
enum E1 {
    A,
    B,
    C,
}
enum E2 {
    A(E1),
    B,
}
fn foo() {
    match E2::A(E1::A) {
        E2::A(E1::B) => {}
        E2::B => {}
    }
    //~^ ERROR `E2::A(E1::A)` and `E2::A(E1::C)` not handled
}
```

Unify look between match with no arms and match with some missing patterns.

Fix rust-lang#37518.
@estebank
Copy link
Contributor Author

estebank commented Mar 4, 2019

@bors r=zackmdavis

@bors
Copy link
Contributor

bors commented Mar 4, 2019

📌 Commit d651281 has been approved by zackmdavis

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 4, 2019
@bors
Copy link
Contributor

bors commented Mar 4, 2019

⌛ Testing commit d651281 with merge a9da8fc...

bors added a commit that referenced this pull request Mar 4, 2019
Point at enum definition when match patterns are not exhaustive

```
error[E0004]: non-exhaustive patterns: type `X` is non-empty
 --> file.rs:9:11
  |
1 | / enum X {
2 | |     A,
  | |     - variant not covered
3 | |     B,
  | |     - variant not covered
4 | |     C,
  | |     - variant not covered
5 | | }
  | |_- `X` defined here
...
9 |       match x {
  |             ^
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms

error[E0004]: non-exhaustive patterns: `B` and `C` not covered
  --> file.rs:11:11
   |
1  | / enum X {
2  | |     A,
3  | |     B,
4  | |     C,
   | |     - not covered
5  | | }
   | |_- `X` defined here
...
11 |       match x {
   |             ^ patterns `C` not covered
```

When a match expression doesn't have patterns covering every variant,
point at the enum's definition span. On a best effort basis, point at the
variant(s) that are missing. This does not handle the case when the missing
pattern is due to a field's enum variants:

```
enum E1 {
    A,
    B,
    C,
}
enum E2 {
    A(E1),
    B,
}
fn foo() {
    match E2::A(E1::A) {
        E2::A(E1::B) => {}
        E2::B => {}
    }
    //~^ ERROR `E2::A(E1::A)` and `E2::A(E1::C)` not handled
}
```

Unify look between match with no arms and match with some missing patterns.

Fix #37518.
@bors
Copy link
Contributor

bors commented Mar 4, 2019

☀️ Test successful - checks-travis, status-appveyor
Approved by: zackmdavis
Pushing a9da8fc to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Mar 4, 2019
@bors bors merged commit d651281 into rust-lang:master Mar 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants