1 use crate::{context::LintContext, LateContext, LateLintPass};
2 use rustc_errors::fluent;
4 use rustc_middle::ty::{visit::TypeVisitable, Ty};
5 use rustc_span::{symbol::sym, Span};
8 /// The `enum_intrinsics_non_enums` lint detects calls to
9 /// intrinsic functions that require an enum ([`core::mem::discriminant`],
10 /// [`core::mem::variant_count`]), but are called with a non-enum type.
12 /// [`core::mem::discriminant`]: https://doc.rust-lang.org/core/mem/fn.discriminant.html
13 /// [`core::mem::variant_count`]: https://doc.rust-lang.org/core/mem/fn.variant_count.html
17 /// ```rust,compile_fail
18 /// #![deny(enum_intrinsics_non_enums)]
19 /// core::mem::discriminant::<i32>(&123);
26 /// In order to accept any enum, the `mem::discriminant` and
27 /// `mem::variant_count` functions are generic over a type `T`.
28 /// This makes it technically possible for `T` to be a non-enum,
29 /// in which case the return value is unspecified.
31 /// This lint prevents such incorrect usage of these functions.
32 ENUM_INTRINSICS_NON_ENUMS,
34 "detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types"
37 declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
39 /// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
40 /// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
41 fn is_non_enum(t: Ty<'_>) -> bool {
42 !t.is_enum() && !t.needs_subst()
45 fn enforce_mem_discriminant(
47 func_expr: &hir::Expr<'_>,
51 let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
52 if is_non_enum(ty_param) {
54 ENUM_INTRINSICS_NON_ENUMS,
56 fluent::lint_enum_intrinsics_mem_discriminant,
57 |lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note),
62 fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
63 let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
64 if is_non_enum(ty_param) {
66 ENUM_INTRINSICS_NON_ENUMS,
68 fluent::lint_enum_intrinsics_mem_variant,
69 |lint| lint.set_arg("ty_param", ty_param).note(fluent::note),
74 impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
75 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
76 let hir::ExprKind::Call(func, args) = &expr.kind else { return };
77 let hir::ExprKind::Path(qpath) = &func.kind else { return };
78 let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() else { return };
79 let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return };
81 sym::mem_discriminant => enforce_mem_discriminant(cx, func, expr.span, args[0].span),
82 sym::mem_variant_count => enforce_mem_variant_count(cx, func, expr.span),