forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#83908 - Flying-Toast:master, r=davidtwco
Add enum_intrinsics_non_enums lint There is a clippy lint to prevent calling [`mem::discriminant`](https://doc.rust-lang.org/std/mem/fn.discriminant.html) with a non-enum type. I think the lint is worthy of being included in rustc, given that `discriminant::<T>()` where `T` is a non-enum has an unspecified return value, and there are no valid use cases where you'd actually want this. I've also made the lint check [variant_count](https://doc.rust-lang.org/core/mem/fn.variant_count.html) (rust-lang#73662). closes rust-lang#83899
- Loading branch information
Showing
22 changed files
with
292 additions
and
315 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
// run-pass | ||
|
||
#![allow(enum_intrinsics_non_enums)] | ||
|
||
use std::mem; | ||
|
||
enum ADT { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.