Skip to content

New non_exhaustive_omitted_patterns lint exposes unstable and hidden enum variants. #89042

@m-ou-se

Description

@m-ou-se
Member

See #86809 (comment)

The non_exhaustive_omitted_patterns lint exposes #[unstable] and #[doc(hidden)] variants.

E.g. for io::Error:

error: some variants are not matched explicitly
  --> src/main.rs:47:9
   |
47 |         _ => {}
   |         ^ pattern `Uncategorized` not covered
   |
note: the lint level is defined here
  --> src/main.rs:4:12
   |
4  |     #[deny(non_exhaustive_omitted_patterns)]
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = help: ensure that all variants are matched explicitly by adding the suggested match arms
   = note: the matched value is of type `ErrorKind` and the `non_exhaustive_omitted_patterns` attribute was found

But Uncategorized is both hidden and unstable, and should not be considered part of the public api.

It'd be good to fix this before it hits stable in 1.57.

Activity

added
A-diagnosticsArea: Messages for errors, warnings, and lints
T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
C-bugCategory: This is a bug.
D-confusingDiagnostics: Confusing error or lint that should be reworked.
on Sep 17, 2021
added this to the 1.57.0 milestone on Sep 17, 2021
m-ou-se

m-ou-se commented on Sep 17, 2021

@m-ou-se
MemberAuthor
m-ou-se

m-ou-se commented on Sep 17, 2021

@m-ou-se
MemberAuthor

I'm not sure if the exact semantics for this lint in case of unstable and hidden variants have been discussed. E.g. whether it should only hide them when the enum comes from a different crate, or whether to warn about #[unstable] variants for which the #![feature] has been enabled.

Nadrieril

Nadrieril commented on Sep 17, 2021

@Nadrieril
Member

Is there an enum in std that has #[unstable] variants but isn't #[non_exhaustive] by any chance?
EDIT: I grepped through std and there's only c_void

DevinR528

DevinR528 commented on Sep 18, 2021

@DevinR528
Contributor

The problem is that std::io::ErrorKind, because it is non_exhaustive, triggers the lint with a NonExhaustive constructor in the list of missing patterns so it will only ever say that it's missing _ (wildcard). This behavior has hidden the need to filter for these variants or care about them at all, as far as the enum lints go.

I was able to get this to give me an odd warning but if you match on something that is the c_void type you either get _ wildcard because your match looks like match c_void {} or you have to turn on the feature so you can add a variant, which then the feature is on so it won't test either way.
error E0423, when constructing the c_void enum without a variant gives

error[E0423]: expected value, found enum `std::ffi::c_void`
  --> src/lib.rs:46:11
   |
46 |     match std::ffi::c_void {};
   |           ^^^^^^^^^^^^^^^^
   |
help: you might have meant to use one of the following enum variants
   |
46 |     match std::os::raw::c_void::__variant1 {};
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46 |     match std::os::raw::c_void::__variant2 {};
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Something like this could be done to filter the witness in is_useful before we lint for non_exhaustive_omitted_patterns?

witnesses.retain(|p| {
    if let rustc_middle::thir::PatKind::Variant { adt_def, variant_index, .. } = &*p.kind {
        let did = adt_def.variants[*variant_index].def_id;

        let x = cx.tcx.lookup_stability(did);
        if let Some(Stability { feature, level: StabilityLevel::Unstable { .. }, .. }) = x {
            return cx.tcx.stability().active_features.contains(&feature);
        }
    }
    true
});

As far as doc(hidden) since no other lints take this into account I don't see why this should. IMHO it seems like treating variants marked doc hidden any other way than simply ignoring their docs would be odd 🤷

Nadrieril

Nadrieril commented on Sep 18, 2021

@Nadrieril
Member

We'll want to completely ignore those variants even for exhaustiveness checking, so it's best to just not list unstable variants in SplitWildcard::new().
doc(hidden) is more subtle because as you say we don't want to change the logic in that case. But I think we can still omit them from the lints. For that we can do like you did in apply_constructor in your previous PR: filter out those variants from the witnesses list, and replace them with a _.

DevinR528

DevinR528 commented on Sep 18, 2021

@DevinR528
Contributor

But I think we can still omit them from the lints. For that, we can do like you did in apply_constructor in your previous PR: filter out those variants from the witnesses list, and replace them with a _.

So if an enum (should we do struct fields too?) has a doc(hidden) attribute on a variant and is not crate local we now treat it like it's non_exhaustive and just return a witnesses list of Wildcard?

enum Foo {
    A, B,
    #[doc(hidden)]
    C,
}

// Different crate
match Foo::A {
    Foo::A => {}
    Foo::B => {}
}

match Foo::A {
    Foo::A => {}
    Foo::C => {}
}

Will now give this for the first match (used to be C not _)

error[E0004]: non-exhaustive patterns: `_` not covered
  --> /home/devinr/aprog/rust/__forks__/rust/src/test/ui/rfc-2008-non-exhaustive/reachable-unstable.rs:38:11
   |
LL | / enum Foo {
LL | |     A,
LL | |     B,
LL | |     #[doc(hidden)]
LL | |     C,
LL | | }
   | |_- `Foo` defined here
...
LL |       match Foo::A {
   |             ^^^^^^ pattern `_` not covered
   |
   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
   = note: the matched value is of type `Foo`

but this for the second

error[E0004]: non-exhaustive patterns: `B` not covered
  --> /home/devinr/aprog/rust/__forks__/rust/src/test/ui/rfc-2008-non-exhaustive/reachable-unstable.rs:43:11
   |
LL | / enum Foo {
LL | |     A,
LL | |     B,
   | |     - not covered
LL | |     #[doc(hidden)]
LL | |     C,
LL | | }
   | |_- `Foo` defined here
...
LL |       match Foo::A {
   |             ^^^^^^ pattern `B` not covered
   |
   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
   = note: the matched value is of type `Foo`
Nadrieril

Nadrieril commented on Sep 19, 2021

@Nadrieril
Member

Yeah exactly! Thanks for the detailed example.
What do you think we should do for the following?

match Foo::A {
    Foo::A => {}
}

I think "patterns B and _ not covered" is better than just "pattern _ not covered" even though it looks a bit weird. Maybe "patterns B and others not covered"?

I didn't know we could doc(hidden) a struct field, but if so then we can do something similar there yeah.

I don't feel competent to make that decision though. @m-ou-se do you know who I could ask something like that?

26 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-bugCategory: This is a bug.D-confusingDiagnostics: Confusing error or lint that should be reworked.F-non_exhaustive_omitted_patterns_lint`#![feature(non_exhaustive_omitted_patterns_lint)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Participants

      @m-ou-se@Nadrieril@DevinR528@camelid

      Issue actions

        New non_exhaustive_omitted_patterns lint exposes unstable and hidden enum variants. · Issue #89042 · rust-lang/rust