-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
The #[non_exhaustive]
attribute prevents exhaustively matching an enum outside of the defining crate. However, it's sometimes useful to have the opposite of this behavior - that is, prevent non-exhaustively matching an enum within the defining crate.
For example, non-exhaustively matching on DefPathData
or TyKind
is often the wrong choice. By writing an exhaustive match (explicitly mentioning all variants), any additions/removals from the enum will require explicit acknowledgment at the match site, helping to prevent bugs.
Examples from rustc
:
rust/src/librustc_mir/dataflow/impls/storage_liveness.rs
Lines 133 to 140 in c20d7ee
// Nothing to do for these. Match exhaustively so this fails to compile when new | |
// variants are added. | |
StatementKind::AscribeUserType(..) | |
| StatementKind::FakeRead(..) | |
| StatementKind::Nop | |
| StatementKind::Retag(..) | |
| StatementKind::StorageLive(..) => {} | |
} |
rust/src/librustc_hir/intravisit.rs
Lines 939 to 940 in c20d7ee
// N.B., deliberately force a compilation error if/when new fields are added. | |
let TraitItemRef { id, ident, ref kind, span: _, ref defaultness } = *trait_item_ref; |
rust/src/librustc_hir/intravisit.rs
Lines 948 to 958 in c20d7ee
// N.B., deliberately force a compilation error if/when new fields are added. | |
let ImplItem { | |
hir_id: _, | |
ident, | |
ref vis, | |
ref defaultness, | |
attrs, | |
ref generics, | |
ref kind, | |
span: _, | |
} = *impl_item; |
It would be useful to have a compiler lint to enforce this programmatically.
As an initial implementation, we could add an unstable attribute #[rustc_exhaustive]
and an internal lint EXHAUSTIVE_MATCH
. This lint fires on any non-exhaustive match
or destructuring let
expression (not if let
/while let
for single enum variants) that match on an ADT annotated with #[rustc_exhaustive]
. Since this would be a lint, it could be allowed on a case-by-case basis (e.g. performing an early exit on TyKind::Error
or TyKind::Infer
).
If this proves useful, it could be made available to all Rust programs after an RFC.