Add reachable_patterns lint to rfc-2008-non_exhaustive

Add linting on non_exhaustive structs and enum variants

Add ui tests for non_exhaustive reachable lint

Rename to non_exhaustive_omitted_patterns and avoid triggering on if let
This commit is contained in:
Devin Ragotzy 2021-09-10 16:45:04 -04:00
parent c3c0f80d60
commit 33a06b73d9
10 changed files with 626 additions and 68 deletions

View file

@ -4,8 +4,29 @@
pub enum NonExhaustiveEnum {
Unit,
Tuple(u32),
Struct { field: u32 }
Struct { field: u32 },
}
#[non_exhaustive]
pub enum NestedNonExhaustive {
A(NonExhaustiveEnum),
B,
C,
}
#[non_exhaustive]
pub enum EmptyNonExhaustiveEnum {}
pub enum VariantNonExhaustive {
#[non_exhaustive]
Bar {
x: u32,
y: u64,
},
Baz(u32, u16),
}
#[non_exhaustive]
pub enum NonExhaustiveSingleVariant {
A(bool),
}

View file

@ -1,3 +1,4 @@
#[derive(Default)]
#[non_exhaustive]
pub struct NormalStruct {
pub first_field: u16,
@ -15,7 +16,7 @@ pub struct TupleStruct(pub u16, pub u16);
pub struct FunctionalRecord {
pub first_field: u16,
pub second_field: u16,
pub third_field: bool
pub third_field: bool,
}
impl Default for FunctionalRecord {
@ -23,3 +24,10 @@ impl Default for FunctionalRecord {
FunctionalRecord { first_field: 640, second_field: 480, third_field: false }
}
}
#[derive(Default)]
#[non_exhaustive]
pub struct NestedStruct {
pub foo: u16,
pub bar: NormalStruct,
}

View file

@ -0,0 +1,160 @@
// Test that the `non_exhaustive_omitted_patterns` lint is triggered correctly.
// aux-build:enums.rs
extern crate enums;
// aux-build:structs.rs
extern crate structs;
use enums::{
EmptyNonExhaustiveEnum, NestedNonExhaustive, NonExhaustiveEnum, NonExhaustiveSingleVariant,
VariantNonExhaustive,
};
use structs::{FunctionalRecord, NestedStruct, NormalStruct};
#[non_exhaustive]
#[derive(Default)]
pub struct Foo {
a: u8,
b: usize,
c: String,
}
#[non_exhaustive]
pub enum Bar {
A,
B,
C,
}
fn main() {
let enumeration = Bar::A;
// Ok: this is a crate local non_exhaustive enum
match enumeration {
Bar::A => {}
Bar::B => {}
#[deny(non_exhaustive_omitted_patterns)]
_ => {}
}
let non_enum = NonExhaustiveEnum::Unit;
// Ok: without the attribute
match non_enum {
NonExhaustiveEnum::Unit => {}
NonExhaustiveEnum::Tuple(_) => {}
_ => {}
}
match non_enum {
NonExhaustiveEnum::Unit => {}
NonExhaustiveEnum::Tuple(_) => {}
#[deny(non_exhaustive_omitted_patterns)]
_ => {}
}
//~^^ some variants are not matched explicitly
match non_enum {
NonExhaustiveEnum::Unit | NonExhaustiveEnum::Struct { .. } => {}
#[deny(non_exhaustive_omitted_patterns)]
_ => {}
}
//~^^ some variants are not matched explicitly
let x = 5;
match non_enum {
NonExhaustiveEnum::Unit if x > 10 => {}
NonExhaustiveEnum::Tuple(_) => {}
NonExhaustiveEnum::Struct { .. } => {}
#[deny(non_exhaustive_omitted_patterns)]
_ => {}
}
//~^^ some variants are not matched explicitly
// Ok: all covered and not `unreachable-patterns`
#[deny(unreachable_patterns)]
match non_enum {
NonExhaustiveEnum::Unit => {}
NonExhaustiveEnum::Tuple(_) => {}
NonExhaustiveEnum::Struct { .. } => {}
#[deny(non_exhaustive_omitted_patterns)]
_ => {}
}
#[deny(non_exhaustive_omitted_patterns)]
match NestedNonExhaustive::B {
NestedNonExhaustive::A(NonExhaustiveEnum::Unit) => {}
NestedNonExhaustive::A(_) => {}
NestedNonExhaustive::B => {}
_ => {}
}
//~^^ some variants are not matched explicitly
//~^^^^^ some variants are not matched explicitly
// The io::ErrorKind has many `unstable` fields how do they interact with this
// lint
#[deny(non_exhaustive_omitted_patterns)]
match std::io::ErrorKind::Other {
std::io::ErrorKind::NotFound => {}
std::io::ErrorKind::PermissionDenied => {}
std::io::ErrorKind::ConnectionRefused => {}
std::io::ErrorKind::ConnectionReset => {}
std::io::ErrorKind::ConnectionAborted => {}
std::io::ErrorKind::NotConnected => {}
std::io::ErrorKind::AddrInUse => {}
std::io::ErrorKind::AddrNotAvailable => {}
std::io::ErrorKind::BrokenPipe => {}
std::io::ErrorKind::AlreadyExists => {}
std::io::ErrorKind::WouldBlock => {}
std::io::ErrorKind::InvalidInput => {}
std::io::ErrorKind::InvalidData => {}
std::io::ErrorKind::TimedOut => {}
std::io::ErrorKind::WriteZero => {}
std::io::ErrorKind::Interrupted => {}
std::io::ErrorKind::Other => {}
std::io::ErrorKind::UnexpectedEof => {}
std::io::ErrorKind::Unsupported => {}
std::io::ErrorKind::OutOfMemory => {}
// All stable variants are above and unstable in `_`
_ => {}
}
//~^^ some variants are not matched explicitly
#[warn(non_exhaustive_omitted_patterns)]
match VariantNonExhaustive::Baz(1, 2) {
VariantNonExhaustive::Baz(_, _) => {}
VariantNonExhaustive::Bar { x, .. } => {}
}
//~^^ some fields are not explicitly listed
#[warn(non_exhaustive_omitted_patterns)]
let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default();
//~^ some fields are not explicitly listed
// Ok: this is local
#[warn(non_exhaustive_omitted_patterns)]
let Foo { a, b, .. } = Foo::default();
#[warn(non_exhaustive_omitted_patterns)]
let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
//~^ some fields are not explicitly listed
//~^^ some fields are not explicitly listed
// Ok: because this only has 1 variant
#[deny(non_exhaustive_omitted_patterns)]
match NonExhaustiveSingleVariant::A(true) {
NonExhaustiveSingleVariant::A(true) => {}
_ => {}
}
#[deny(non_exhaustive_omitted_patterns)]
match NonExhaustiveSingleVariant::A(true) {
_ => {}
}
//~^^ some variants are not matched explicitly
// Ok: we don't lint on `if let` expressions
#[deny(non_exhaustive_omitted_patterns)]
if let NonExhaustiveEnum::Tuple(_) = non_enum {}
}

View file

@ -0,0 +1,146 @@
warning: some fields are not explicitly listed
--> $DIR/reachable-patterns.rs:127:9
|
LL | VariantNonExhaustive::Bar { x, .. } => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:124:12
|
LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
= note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
warning: some fields are not explicitly listed
--> $DIR/reachable-patterns.rs:132:9
|
LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:131:12
|
LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
= note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found
warning: some fields are not explicitly listed
--> $DIR/reachable-patterns.rs:140:29
|
LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:139:12
|
LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
= note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found
warning: some fields are not explicitly listed
--> $DIR/reachable-patterns.rs:140:9
|
LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed
|
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
= note: the pattern is of type `NestedStruct` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:54:9
|
LL | _ => {}
| ^ pattern `Struct { .. }` not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:53:16
|
LL | #[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 `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:61:9
|
LL | _ => {}
| ^ pattern `Tuple(_)` not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:60:16
|
LL | #[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 `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:71:9
|
LL | _ => {}
| ^ pattern `Unit` not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:70:16
|
LL | #[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 `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:88:32
|
LL | NestedNonExhaustive::A(_) => {}
| ^ patterns `Tuple(_)` and `Struct { .. }` not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:85:12
|
LL | #[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 `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:90:9
|
LL | _ => {}
| ^ pattern `C` not covered
|
= help: ensure that all variants are matched explicitly by adding the suggested match arms
= note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:120:9
|
LL | _ => {}
| ^ patterns `HostUnreachable`, `NetworkUnreachable`, `NetworkDown` and 18 more not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:97:12
|
LL | #[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
error: some variants are not matched explicitly
--> $DIR/reachable-patterns.rs:153:9
|
LL | _ => {}
| ^ pattern `A(_)` not covered
|
note: the lint level is defined here
--> $DIR/reachable-patterns.rs:151:12
|
LL | #[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 `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found
error: aborting due to 7 previous errors; 4 warnings emitted

View file

@ -16,13 +16,13 @@ error[E0603]: tuple struct constructor `TupleStruct` is private
LL | let ts_explicit = structs::TupleStruct(640, 480);
| ^^^^^^^^^^^ private tuple struct constructor
|
::: $DIR/auxiliary/structs.rs:11:24
::: $DIR/auxiliary/structs.rs:12:24
|
LL | pub struct TupleStruct(pub u16, pub u16);
| ---------------- a constructor is private if any of the fields is private
|
note: the tuple struct constructor `TupleStruct` is defined here
--> $DIR/auxiliary/structs.rs:11:1
--> $DIR/auxiliary/structs.rs:12:1
|
LL | pub struct TupleStruct(pub u16, pub u16);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -34,7 +34,7 @@ LL | let us_explicit = structs::UnitStruct;
| ^^^^^^^^^^ private unit struct
|
note: the unit struct `UnitStruct` is defined here
--> $DIR/auxiliary/structs.rs:8:1
--> $DIR/auxiliary/structs.rs:9:1
|
LL | pub struct UnitStruct;
| ^^^^^^^^^^^^^^^^^^^^^^