mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 19:12:50 +00:00
Add enum_intrinsics_non_enums lint
This commit is contained in:
parent
9a757817c3
commit
59b186d99a
106
compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
Normal file
106
compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use crate::{context::LintContext, LateContext, LateLintPass};
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_middle::ty::{fold::TypeFoldable, Ty};
|
||||||
|
use rustc_span::{symbol::sym, Span};
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `enum_intrinsics_non_enums` lint detects calls to
|
||||||
|
/// intrinsic functions that require an enum ([`core::mem::discriminant`],
|
||||||
|
/// [`core::mem::variant_count`]), but are called with a non-enum type.
|
||||||
|
///
|
||||||
|
/// [`core::mem::discriminant`]: https://doc.rust-lang.org/core/mem/fn.discriminant.html
|
||||||
|
/// [`core::mem::variant_count`]: https://doc.rust-lang.org/core/mem/fn.variant_count.html
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,compile_fail
|
||||||
|
/// #![deny(enum_intrinsics_non_enums)]
|
||||||
|
/// core::mem::discriminant::<i32>(&123);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// In order to accept any enum, the `mem::discriminant` and
|
||||||
|
/// `mem::variant_count` functions are generic over a type `T`.
|
||||||
|
/// This makes it technically possible for `T` to be a non-enum,
|
||||||
|
/// in which case the return value is unspecified.
|
||||||
|
///
|
||||||
|
/// This lint prevents such incorrect usage of these functions.
|
||||||
|
ENUM_INTRINSICS_NON_ENUMS,
|
||||||
|
Deny,
|
||||||
|
"detects calls to `core::mem::discriminant` and `core::mem::variant_count` with non-enum types"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
|
||||||
|
|
||||||
|
/// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
|
||||||
|
/// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
|
||||||
|
fn is_non_enum(t: Ty<'_>) -> bool {
|
||||||
|
!t.is_enum() && !t.potentially_needs_subst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce_mem_discriminant(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
func_expr: &hir::Expr<'_>,
|
||||||
|
expr_span: Span,
|
||||||
|
args_span: Span,
|
||||||
|
) {
|
||||||
|
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
|
||||||
|
if is_non_enum(ty_param) {
|
||||||
|
cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, expr_span, |builder| {
|
||||||
|
builder
|
||||||
|
.build(
|
||||||
|
"the return value of `mem::discriminant` is \
|
||||||
|
unspecified when called with a non-enum type",
|
||||||
|
)
|
||||||
|
.span_note(
|
||||||
|
args_span,
|
||||||
|
&format!(
|
||||||
|
"the argument to `discriminant` should be a \
|
||||||
|
reference to an enum, but it was passed \
|
||||||
|
a reference to a `{}`, which is not an enum.",
|
||||||
|
ty_param,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
|
||||||
|
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
|
||||||
|
if is_non_enum(ty_param) {
|
||||||
|
cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, |builder| {
|
||||||
|
builder
|
||||||
|
.build(
|
||||||
|
"the return value of `mem::variant_count` is \
|
||||||
|
unspecified when called with a non-enum type",
|
||||||
|
)
|
||||||
|
.note(&format!(
|
||||||
|
"the type parameter of `variant_count` should \
|
||||||
|
be an enum, but it was instantiated with \
|
||||||
|
the type `{}`, which is not an enum.",
|
||||||
|
ty_param,
|
||||||
|
))
|
||||||
|
.emit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||||
|
if let hir::ExprKind::Call(ref func, ref args) = expr.kind {
|
||||||
|
if let hir::ExprKind::Path(ref qpath) = func.kind {
|
||||||
|
if let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() {
|
||||||
|
if cx.tcx.is_diagnostic_item(sym::mem_discriminant, def_id) {
|
||||||
|
enforce_mem_discriminant(cx, func, expr.span, args[0].span);
|
||||||
|
} else if cx.tcx.is_diagnostic_item(sym::mem_variant_count, def_id) {
|
||||||
|
enforce_mem_variant_count(cx, func, expr.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ mod array_into_iter;
|
|||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
mod context;
|
mod context;
|
||||||
mod early;
|
mod early;
|
||||||
|
mod enum_intrinsics_non_enums;
|
||||||
mod internal;
|
mod internal;
|
||||||
mod late;
|
mod late;
|
||||||
mod levels;
|
mod levels;
|
||||||
@ -76,6 +77,7 @@ use rustc_span::Span;
|
|||||||
|
|
||||||
use array_into_iter::ArrayIntoIter;
|
use array_into_iter::ArrayIntoIter;
|
||||||
use builtin::*;
|
use builtin::*;
|
||||||
|
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
|
||||||
use internal::*;
|
use internal::*;
|
||||||
use methods::*;
|
use methods::*;
|
||||||
use non_ascii_idents::*;
|
use non_ascii_idents::*;
|
||||||
@ -168,6 +170,7 @@ macro_rules! late_lint_passes {
|
|||||||
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
|
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
|
||||||
NonPanicFmt: NonPanicFmt,
|
NonPanicFmt: NonPanicFmt,
|
||||||
NoopMethodCall: NoopMethodCall,
|
NoopMethodCall: NoopMethodCall,
|
||||||
|
EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
|
||||||
InvalidAtomicOrdering: InvalidAtomicOrdering,
|
InvalidAtomicOrdering: InvalidAtomicOrdering,
|
||||||
NamedAsmLabels: NamedAsmLabels,
|
NamedAsmLabels: NamedAsmLabels,
|
||||||
]
|
]
|
||||||
|
@ -816,6 +816,7 @@ symbols! {
|
|||||||
mem_size_of,
|
mem_size_of,
|
||||||
mem_size_of_val,
|
mem_size_of_val,
|
||||||
mem_uninitialized,
|
mem_uninitialized,
|
||||||
|
mem_variant_count,
|
||||||
mem_zeroed,
|
mem_zeroed,
|
||||||
member_constraints,
|
member_constraints,
|
||||||
memory,
|
memory,
|
||||||
|
@ -1053,6 +1053,7 @@ pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unstable(feature = "variant_count", issue = "73662")]
|
#[unstable(feature = "variant_count", issue = "73662")]
|
||||||
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
|
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
|
||||||
|
#[rustc_diagnostic_item = "mem_variant_count"]
|
||||||
pub const fn variant_count<T>() -> usize {
|
pub const fn variant_count<T>() -> usize {
|
||||||
intrinsics::variant_count::<T>()
|
intrinsics::variant_count::<T>()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// run-pass
|
// run-pass
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code, enum_intrinsics_non_enums)]
|
||||||
#![feature(variant_count)]
|
#![feature(variant_count)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
|
#![allow(enum_intrinsics_non_enums)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
enum ADT {
|
enum ADT {
|
||||||
|
67
src/test/ui/lint/lint-enum-intrinsics-non-enums.rs
Normal file
67
src/test/ui/lint/lint-enum-intrinsics-non-enums.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Test the enum_intrinsics_non_enums lint.
|
||||||
|
|
||||||
|
#![feature(variant_count)]
|
||||||
|
|
||||||
|
use std::mem::{discriminant, variant_count};
|
||||||
|
|
||||||
|
enum SomeEnum {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SomeStruct;
|
||||||
|
|
||||||
|
fn generic_discriminant<T>(v: &T) {
|
||||||
|
discriminant::<T>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_variant_count<T>() -> usize {
|
||||||
|
variant_count::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_discriminant() {
|
||||||
|
discriminant(&SomeEnum::A);
|
||||||
|
generic_discriminant(&SomeEnum::B);
|
||||||
|
|
||||||
|
discriminant(&());
|
||||||
|
//~^ error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
discriminant(&&SomeEnum::B);
|
||||||
|
//~^ error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
discriminant(&SomeStruct);
|
||||||
|
//~^ error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
discriminant(&123u32);
|
||||||
|
//~^ error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
discriminant(&&123i8);
|
||||||
|
//~^ error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_variant_count() {
|
||||||
|
variant_count::<SomeEnum>();
|
||||||
|
generic_variant_count::<SomeEnum>();
|
||||||
|
|
||||||
|
variant_count::<&str>();
|
||||||
|
//~^ error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
variant_count::<*const u8>();
|
||||||
|
//~^ error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
variant_count::<()>();
|
||||||
|
//~^ error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
|
||||||
|
variant_count::<&SomeEnum>();
|
||||||
|
//~^ error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_discriminant();
|
||||||
|
test_variant_count();
|
||||||
|
|
||||||
|
// The lint ignores cases where the type is generic, so these should be
|
||||||
|
// allowed even though their return values are unspecified
|
||||||
|
generic_variant_count::<SomeStruct>();
|
||||||
|
generic_discriminant::<SomeStruct>(&SomeStruct);
|
||||||
|
}
|
95
src/test/ui/lint/lint-enum-intrinsics-non-enums.stderr
Normal file
95
src/test/ui/lint/lint-enum-intrinsics-non-enums.stderr
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:26:5
|
||||||
|
|
|
||||||
|
LL | discriminant(&());
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[deny(enum_intrinsics_non_enums)]` on by default
|
||||||
|
note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `()`, which is not an enum.
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:26:18
|
||||||
|
|
|
||||||
|
LL | discriminant(&());
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:29:5
|
||||||
|
|
|
||||||
|
LL | discriminant(&&SomeEnum::B);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `&SomeEnum`, which is not an enum.
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:29:18
|
||||||
|
|
|
||||||
|
LL | discriminant(&&SomeEnum::B);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:32:5
|
||||||
|
|
|
||||||
|
LL | discriminant(&SomeStruct);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `SomeStruct`, which is not an enum.
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:32:18
|
||||||
|
|
|
||||||
|
LL | discriminant(&SomeStruct);
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:35:5
|
||||||
|
|
|
||||||
|
LL | discriminant(&123u32);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `u32`, which is not an enum.
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:35:18
|
||||||
|
|
|
||||||
|
LL | discriminant(&123u32);
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:38:5
|
||||||
|
|
|
||||||
|
LL | discriminant(&&123i8);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `&i8`, which is not an enum.
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:38:18
|
||||||
|
|
|
||||||
|
LL | discriminant(&&123i8);
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:46:5
|
||||||
|
|
|
||||||
|
LL | variant_count::<&str>();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the type parameter of `variant_count` should be an enum, but it was instantiated with the type `&str`, which is not an enum.
|
||||||
|
|
||||||
|
error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:49:5
|
||||||
|
|
|
||||||
|
LL | variant_count::<*const u8>();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the type parameter of `variant_count` should be an enum, but it was instantiated with the type `*const u8`, which is not an enum.
|
||||||
|
|
||||||
|
error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:52:5
|
||||||
|
|
|
||||||
|
LL | variant_count::<()>();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the type parameter of `variant_count` should be an enum, but it was instantiated with the type `()`, which is not an enum.
|
||||||
|
|
||||||
|
error: the return value of `mem::variant_count` is unspecified when called with a non-enum type
|
||||||
|
--> $DIR/lint-enum-intrinsics-non-enums.rs:55:5
|
||||||
|
|
|
||||||
|
LL | variant_count::<&SomeEnum>();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the type parameter of `variant_count` should be an enum, but it was instantiated with the type `&SomeEnum`, which is not an enum.
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user