rustc_lint/
enum_intrinsics_non_enums.rs1use rustc_hir as hir;
2use rustc_middle::ty::{Ty, TypeVisitableExt};
3use rustc_session::{declare_lint, declare_lint_pass};
4use rustc_span::{Span, sym};
5
6use crate::context::LintContext;
7use crate::lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant};
8use crate::{LateContext, LateLintPass};
9
10#[doc = r" The `enum_intrinsics_non_enums` lint detects calls to"]
#[doc =
r" intrinsic functions that require an enum ([`core::mem::discriminant`],"]
#[doc =
r" [`core::mem::variant_count`]), but are called with a non-enum type."]
#[doc = r""]
#[doc =
r" [`core::mem::discriminant`]: https://doc.rust-lang.org/core/mem/fn.discriminant.html"]
#[doc =
r" [`core::mem::variant_count`]: https://doc.rust-lang.org/core/mem/fn.variant_count.html"]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(enum_intrinsics_non_enums)]"]
#[doc = r" core::mem::discriminant::<i32>(&123);"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" In order to accept any enum, the `mem::discriminant` and"]
#[doc = r" `mem::variant_count` functions are generic over a type `T`."]
#[doc = r" This makes it technically possible for `T` to be a non-enum,"]
#[doc = r" in which case the return value is unspecified."]
#[doc = r""]
#[doc = r" This lint prevents such incorrect usage of these functions."]
static ENUM_INTRINSICS_NON_ENUMS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "ENUM_INTRINSICS_NON_ENUMS",
default_level: ::rustc_lint_defs::Deny,
desc: "detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
11 ENUM_INTRINSICS_NON_ENUMS,
36 Deny,
37 "detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types"
38}
39
40pub struct EnumIntrinsicsNonEnums;
#[automatically_derived]
impl ::core::marker::Copy for EnumIntrinsicsNonEnums { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for EnumIntrinsicsNonEnums { }
#[automatically_derived]
impl ::core::clone::Clone for EnumIntrinsicsNonEnums {
#[inline]
fn clone(&self) -> EnumIntrinsicsNonEnums { *self }
}
impl ::rustc_lint_defs::LintPass for EnumIntrinsicsNonEnums {
fn name(&self) -> &'static str { "EnumIntrinsicsNonEnums" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([ENUM_INTRINSICS_NON_ENUMS]))
}
}
impl EnumIntrinsicsNonEnums {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([ENUM_INTRINSICS_NON_ENUMS]))
}
}declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
41
42fn is_non_enum(t: Ty<'_>) -> bool {
45 !t.is_enum() && !t.has_param()
46}
47
48fn enforce_mem_discriminant(
49 cx: &LateContext<'_>,
50 func_expr: &hir::Expr<'_>,
51 expr_span: Span,
52 args_span: Span,
53) {
54 let ty_param = cx.typeck_results().node_args(func_expr.hir_id).type_at(0);
55 if is_non_enum(ty_param) {
56 cx.emit_span_lint(
57 ENUM_INTRINSICS_NON_ENUMS,
58 expr_span,
59 EnumIntrinsicsMemDiscriminate { ty_param, note: args_span },
60 );
61 }
62}
63
64fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
65 let ty_param = cx.typeck_results().node_args(func_expr.hir_id).type_at(0);
66 if is_non_enum(ty_param) {
67 cx.emit_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, EnumIntrinsicsMemVariant { ty_param });
68 }
69}
70
71impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
72 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
73 let hir::ExprKind::Call(func, args) = &expr.kind else { return };
74 let hir::ExprKind::Path(qpath) = &func.kind else { return };
75 let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() else { return };
76 let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return };
77 match name {
78 sym::mem_discriminant => enforce_mem_discriminant(cx, func, expr.span, args[0].span),
79 sym::mem_variant_count => enforce_mem_variant_count(cx, func, expr.span),
80 _ => {}
81 }
82 }
83}