rustc_lint/
enum_intrinsics_non_enums.rs1use rustc_hir as hir;
2use rustc_middle::ty::Ty;
3use rustc_middle::ty::visit::TypeVisitableExt;
4use rustc_session::{declare_lint, declare_lint_pass};
5use rustc_span::{Span, sym};
6
7use crate::context::LintContext;
8use crate::lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant};
9use crate::{LateContext, LateLintPass};
10
11declare_lint! {
12 ENUM_INTRINSICS_NON_ENUMS,
37 Deny,
38 "detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types"
39}
40
41declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
42
43fn is_non_enum(t: Ty<'_>) -> bool {
46 !t.is_enum() && !t.has_param()
47}
48
49fn enforce_mem_discriminant(
50 cx: &LateContext<'_>,
51 func_expr: &hir::Expr<'_>,
52 expr_span: Span,
53 args_span: Span,
54) {
55 let ty_param = cx.typeck_results().node_args(func_expr.hir_id).type_at(0);
56 if is_non_enum(ty_param) {
57 cx.emit_span_lint(
58 ENUM_INTRINSICS_NON_ENUMS,
59 expr_span,
60 EnumIntrinsicsMemDiscriminate { ty_param, note: args_span },
61 );
62 }
63}
64
65fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
66 let ty_param = cx.typeck_results().node_args(func_expr.hir_id).type_at(0);
67 if is_non_enum(ty_param) {
68 cx.emit_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, EnumIntrinsicsMemVariant { ty_param });
69 }
70}
71
72impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
73 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
74 let hir::ExprKind::Call(func, args) = &expr.kind else { return };
75 let hir::ExprKind::Path(qpath) = &func.kind else { return };
76 let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() else { return };
77 let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return };
78 match name {
79 sym::mem_discriminant => enforce_mem_discriminant(cx, func, expr.span, args[0].span),
80 sym::mem_variant_count => enforce_mem_variant_count(cx, func, expr.span),
81 _ => {}
82 }
83 }
84}