-
Notifications
You must be signed in to change notification settings - Fork 13k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9a75781
commit 59b186d
Showing
8 changed files
with
277 additions
and
1 deletion.
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 | ||
|