rust/clippy_lints/src/empty_enum.rs
Kevin Reid cfa150b0dd Rephrase and expand empty_enum documentation.
* Remove incorrect claim that “wrappers around it are the conventional
  way to define an uninhabited type”.
* Discuss why one would use `!`, a newtype struct, or keep the enum.
* Add links to relevant documentation.
2024-05-22 18:03:18 -07:00

89 lines
3.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! lint when there is an enum with no variants
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for `enum`s with no variants, which therefore are uninhabited types
/// (cannot be instantiated).
///
/// As of this writing, the `never_type` is still a nightly-only experimental API.
/// Therefore, this lint is only triggered if `#![feature(never_type)]` is enabled.
///
/// ### Why is this bad?
/// * If you only want a type which cant be instantiated, you should use [`!`]
/// (the primitive type "never"), because [`!`] has more extensive compiler support
/// (type inference, etc.) and implementations of common traits.
///
/// * If you need to introduce a distinct type, consider using a [newtype] `struct`
/// containing [`!`] instead (`struct MyType(pub !)`), because it is more idiomatic
/// to use a `struct` rather than an `enum` when an `enum` is unnecessary.
///
/// If you do this, note that the [visibility] of the [`!`] field determines whether
/// the uninhabitedness is visible in documentation, and whether it can be pattern
/// matched to mark code unreachable. If the field is not visible, then the struct
/// acts like any other struct with private fields.
///
/// * If the enum has no variants only because all variants happen to be
/// [disabled by conditional compilation][cfg], then it would be appropriate
/// to allow the lint, with `#[allow(empty_enum)]`.
///
/// For further information, visit
/// [the never types documentation][`!`].
///
/// ### Example
/// ```no_run
/// enum CannotExist {}
/// ```
///
/// Use instead:
/// ```no_run
/// #![feature(never_type)]
///
/// /// Use the `!` type directly...
/// type CannotExist = !;
///
/// /// ...or define a newtype which is distinct.
/// struct CannotExist2(pub !);
/// ```
///
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
/// [cfg]: https://doc.rust-lang.org/reference/conditional-compilation.html
/// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
/// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html
#[clippy::version = "pre 1.29.0"]
pub EMPTY_ENUM,
pedantic,
"enum with no variants"
}
declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
// Only suggest the `never_type` if the feature is enabled
if !cx.tcx.features().never_type {
return;
}
if let ItemKind::Enum(..) = item.kind {
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
if adt.variants().is_empty() {
span_lint_and_help(
cx,
EMPTY_ENUM,
item.span,
"enum with no variants",
None,
"consider using the uninhabited type `!` (never type) or a wrapper \
around it to introduce a type which can't be instantiated",
);
}
}
}
}