Skip to content

Commit aabf808

Browse files
committed
Add lint for calling mem::discriminant on a non-enum type
1 parent 4c6201d commit aabf808

File tree

7 files changed

+220
-1
lines changed

7 files changed

+220
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ All notable changes to this project will be documented in this file.
744744
[`match_same_arms`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_same_arms
745745
[`match_wild_err_arm`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_wild_err_arm
746746
[`maybe_infinite_iter`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#maybe_infinite_iter
747+
[`mem_discriminant_non_enum`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
747748
[`mem_forget`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_forget
748749
[`mem_replace_option_with_none`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
749750
[`min_max`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#min_max

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in
99

1010
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
1111

12-
[There are 281 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
12+
[There are 282 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
1313

1414
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1515

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ pub mod loops;
144144
pub mod map_clone;
145145
pub mod map_unit_fn;
146146
pub mod matches;
147+
pub mod mem_discriminant;
147148
pub mod mem_forget;
148149
pub mod mem_replace;
149150
pub mod methods;
@@ -398,6 +399,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
398399
reg.register_early_lint_pass(box doc::Doc::new(conf.doc_valid_idents.clone()));
399400
reg.register_late_lint_pass(box neg_multiply::NegMultiply);
400401
reg.register_early_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval);
402+
reg.register_late_lint_pass(box mem_discriminant::MemDiscriminant);
401403
reg.register_late_lint_pass(box mem_forget::MemForget);
402404
reg.register_late_lint_pass(box mem_replace::MemReplace);
403405
reg.register_late_lint_pass(box arithmetic::Arithmetic::default());
@@ -612,6 +614,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
612614
matches::MATCH_REF_PATS,
613615
matches::MATCH_WILD_ERR_ARM,
614616
matches::SINGLE_MATCH,
617+
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
615618
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
616619
methods::CHARS_LAST_CMP,
617620
methods::CHARS_NEXT_CMP,
@@ -924,6 +927,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
924927
loops::NEVER_LOOP,
925928
loops::REVERSE_RANGE_LOOP,
926929
loops::WHILE_IMMUTABLE_CONDITION,
930+
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
927931
methods::CLONE_DOUBLE_REF,
928932
methods::TEMPORARY_CSTRING_AS_PTR,
929933
minmax::MIN_MAX,

clippy_lints/src/mem_discriminant.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
11+
use crate::rustc::hir::{Expr, ExprKind};
12+
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13+
use crate::rustc::{declare_tool_lint, lint_array};
14+
use crate::utils::{match_def_path, opt_def_id, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
15+
use if_chain::if_chain;
16+
17+
use std::iter;
18+
19+
/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
20+
///
21+
/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
22+
/// is unspecified.
23+
///
24+
/// **Known problems:** None.
25+
///
26+
/// **Example:**
27+
/// ```rust
28+
/// mem::discriminant(&"hello");
29+
/// mem::discriminant(&&Some(2));
30+
/// ```
31+
declare_clippy_lint! {
32+
pub MEM_DISCRIMINANT_NON_ENUM,
33+
correctness,
34+
"calling mem::descriminant on non-enum type"
35+
}
36+
37+
pub struct MemDiscriminant;
38+
39+
impl LintPass for MemDiscriminant {
40+
fn get_lints(&self) -> LintArray {
41+
lint_array![MEM_DISCRIMINANT_NON_ENUM]
42+
}
43+
}
44+
45+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
46+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
47+
if_chain! {
48+
if let ExprKind::Call(ref func, ref func_args) = expr.node;
49+
// is `mem::discriminant`
50+
if let ExprKind::Path(ref func_qpath) = func.node;
51+
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
52+
if match_def_path(cx.tcx, def_id, &paths::MEM_DISCRIMINANT);
53+
// type is non-enum
54+
let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
55+
if !ty_param.is_enum();
56+
57+
then {
58+
span_lint_and_then(
59+
cx,
60+
MEM_DISCRIMINANT_NON_ENUM,
61+
expr.span,
62+
&format!("calling `mem::discriminant` on non-enum type `{}`", ty_param),
63+
|db| {
64+
// if this is a reference to an enum, suggest dereferencing
65+
let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param);
66+
if ptr_depth >= 1 && base_ty.is_enum() {
67+
let param = &func_args[0];
68+
69+
// cancel out '&'s first
70+
let mut derefs_needed = ptr_depth;
71+
let mut cur_expr = param;
72+
while derefs_needed > 0 {
73+
if let ExprKind::AddrOf(_, ref inner_expr) = cur_expr.node {
74+
derefs_needed -= 1;
75+
cur_expr = inner_expr;
76+
} else {
77+
break;
78+
}
79+
}
80+
81+
let derefs: String = iter::repeat('*').take(derefs_needed).collect();
82+
db.span_suggestion(
83+
param.span,
84+
"try dereferencing",
85+
format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")),
86+
);
87+
}
88+
},
89+
)
90+
}
91+
}
92+
}
93+
}

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
5555
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
5656
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
5757
pub const LINT_ARRAY: [&str; 3] = ["rustc", "lint", "LintArray"];
58+
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
5859
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
5960
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
6061
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];

tests/ui/mem_discriminant.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
11+
#![deny(clippy::mem_discriminant_non_enum)]
12+
13+
use std::mem;
14+
15+
enum Foo {
16+
One(usize),
17+
Two(u8),
18+
}
19+
20+
struct A(Foo);
21+
22+
fn main() {
23+
// bad
24+
mem::discriminant(&"hello");
25+
mem::discriminant(&&Some(2));
26+
mem::discriminant(&&None::<u8>);
27+
mem::discriminant(&&Foo::One(5));
28+
mem::discriminant(&&Foo::Two(5));
29+
mem::discriminant(&A(Foo::One(0)));
30+
31+
let ro = &Some(3);
32+
let rro = &ro;
33+
mem::discriminant(&ro);
34+
mem::discriminant(rro);
35+
mem::discriminant(&rro);
36+
37+
38+
// ok
39+
mem::discriminant(&Some(2));
40+
mem::discriminant(&None::<u8>);
41+
mem::discriminant(&Foo::One(5));
42+
mem::discriminant(&Foo::Two(5));
43+
mem::discriminant(ro);
44+
}

tests/ui/mem_discriminant.stderr

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
error: calling `mem::discriminant` on non-enum type `&str`
2+
--> $DIR/mem_discriminant.rs:24:5
3+
|
4+
24 | mem::discriminant(&"hello");
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/mem_discriminant.rs:11:9
9+
|
10+
11 | #![deny(clippy::mem_discriminant_non_enum)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
14+
--> $DIR/mem_discriminant.rs:25:5
15+
|
16+
25 | mem::discriminant(&&Some(2));
17+
| ^^^^^^^^^^^^^^^^^^---------^
18+
| |
19+
| help: try dereferencing: `&Some(2)`
20+
21+
error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>`
22+
--> $DIR/mem_discriminant.rs:26:5
23+
|
24+
26 | mem::discriminant(&&None::<u8>);
25+
| ^^^^^^^^^^^^^^^^^^------------^
26+
| |
27+
| help: try dereferencing: `&None::<u8>`
28+
29+
error: calling `mem::discriminant` on non-enum type `&Foo`
30+
--> $DIR/mem_discriminant.rs:27:5
31+
|
32+
27 | mem::discriminant(&&Foo::One(5));
33+
| ^^^^^^^^^^^^^^^^^^-------------^
34+
| |
35+
| help: try dereferencing: `&Foo::One(5)`
36+
37+
error: calling `mem::discriminant` on non-enum type `&Foo`
38+
--> $DIR/mem_discriminant.rs:28:5
39+
|
40+
28 | mem::discriminant(&&Foo::Two(5));
41+
| ^^^^^^^^^^^^^^^^^^-------------^
42+
| |
43+
| help: try dereferencing: `&Foo::Two(5)`
44+
45+
error: calling `mem::discriminant` on non-enum type `A`
46+
--> $DIR/mem_discriminant.rs:29:5
47+
|
48+
29 | mem::discriminant(&A(Foo::One(0)));
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
52+
--> $DIR/mem_discriminant.rs:33:5
53+
|
54+
33 | mem::discriminant(&ro);
55+
| ^^^^^^^^^^^^^^^^^^---^
56+
| |
57+
| help: try dereferencing: `ro`
58+
59+
error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
60+
--> $DIR/mem_discriminant.rs:34:5
61+
|
62+
34 | mem::discriminant(rro);
63+
| ^^^^^^^^^^^^^^^^^^---^
64+
| |
65+
| help: try dereferencing: `*rro`
66+
67+
error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
68+
--> $DIR/mem_discriminant.rs:35:5
69+
|
70+
35 | mem::discriminant(&rro);
71+
| ^^^^^^^^^^^^^^^^^^----^
72+
| |
73+
| help: try dereferencing: `*rro`
74+
75+
error: aborting due to 9 previous errors
76+

0 commit comments

Comments
 (0)