Skip to content

Commit 757750d

Browse files
bors[bot]HMPerson1flip1995
committed
Merge #3346
3346: Add lint for calling `mem::discriminant` on a non-enum type r=flip1995 a=HMPerson1 Also, if the type is a reference to an enum, we suggest removing `&`s and/or dereferencing. Fixes #3342 Co-authored-by: HMPerson1 <[email protected]> Co-authored-by: Philipp Krones <[email protected]>
2 parents 4c6201d + d53e6f8 commit 757750d

File tree

7 files changed

+261
-1
lines changed

7 files changed

+261
-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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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::rustc_errors::Applicability;
15+
use crate::utils::{match_def_path, opt_def_id, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
16+
use if_chain::if_chain;
17+
18+
use std::iter;
19+
20+
/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
21+
///
22+
/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
23+
/// is unspecified.
24+
///
25+
/// **Known problems:** None.
26+
///
27+
/// **Example:**
28+
/// ```rust
29+
/// mem::discriminant(&"hello");
30+
/// mem::discriminant(&&Some(2));
31+
/// ```
32+
declare_clippy_lint! {
33+
pub MEM_DISCRIMINANT_NON_ENUM,
34+
correctness,
35+
"calling mem::descriminant on non-enum type"
36+
}
37+
38+
pub struct MemDiscriminant;
39+
40+
impl LintPass for MemDiscriminant {
41+
fn get_lints(&self) -> LintArray {
42+
lint_array![MEM_DISCRIMINANT_NON_ENUM]
43+
}
44+
}
45+
46+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
47+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
48+
if_chain! {
49+
if let ExprKind::Call(ref func, ref func_args) = expr.node;
50+
// is `mem::discriminant`
51+
if let ExprKind::Path(ref func_qpath) = func.node;
52+
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
53+
if match_def_path(cx.tcx, def_id, &paths::MEM_DISCRIMINANT);
54+
// type is non-enum
55+
let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
56+
if !ty_param.is_enum();
57+
58+
then {
59+
span_lint_and_then(
60+
cx,
61+
MEM_DISCRIMINANT_NON_ENUM,
62+
expr.span,
63+
&format!("calling `mem::discriminant` on non-enum type `{}`", ty_param),
64+
|db| {
65+
// if this is a reference to an enum, suggest dereferencing
66+
let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param);
67+
if ptr_depth >= 1 && base_ty.is_enum() {
68+
let param = &func_args[0];
69+
70+
// cancel out '&'s first
71+
let mut derefs_needed = ptr_depth;
72+
let mut cur_expr = param;
73+
while derefs_needed > 0 {
74+
if let ExprKind::AddrOf(_, ref inner_expr) = cur_expr.node {
75+
derefs_needed -= 1;
76+
cur_expr = inner_expr;
77+
} else {
78+
break;
79+
}
80+
}
81+
82+
let derefs: String = iter::repeat('*').take(derefs_needed).collect();
83+
db.span_suggestion_with_applicability(
84+
param.span,
85+
"try dereferencing",
86+
format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")),
87+
Applicability::MachineApplicable,
88+
);
89+
}
90+
},
91+
)
92+
}
93+
}
94+
}
95+
}

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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
macro_rules! mem_discriminant_but_in_a_macro {
38+
($param:expr) => (mem::discriminant($param))
39+
}
40+
41+
mem_discriminant_but_in_a_macro!(&rro);
42+
43+
let rrrrro = &&&rro;
44+
mem::discriminant(&rrrrro);
45+
mem::discriminant(*rrrrro);
46+
47+
// ok
48+
mem::discriminant(&Some(2));
49+
mem::discriminant(&None::<u8>);
50+
mem::discriminant(&Foo::One(5));
51+
mem::discriminant(&Foo::Two(5));
52+
mem::discriminant(ro);
53+
mem::discriminant(*rro);
54+
mem::discriminant(****rrrrro);
55+
}

tests/ui/mem_discriminant.stderr

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
76+
--> $DIR/mem_discriminant.rs:38:27
77+
|
78+
38 | ($param:expr) => (mem::discriminant($param))
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
80+
...
81+
41 | mem_discriminant_but_in_a_macro!(&rro);
82+
| ---------------------------------------
83+
| | |
84+
| | help: try dereferencing: `*rro`
85+
| in this macro invocation
86+
87+
error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option<i32>`
88+
--> $DIR/mem_discriminant.rs:44:5
89+
|
90+
44 | mem::discriminant(&rrrrro);
91+
| ^^^^^^^^^^^^^^^^^^-------^
92+
| |
93+
| help: try dereferencing: `****rrrrro`
94+
95+
error: calling `mem::discriminant` on non-enum type `&&&std::option::Option<i32>`
96+
--> $DIR/mem_discriminant.rs:45:5
97+
|
98+
45 | mem::discriminant(*rrrrro);
99+
| ^^^^^^^^^^^^^^^^^^-------^
100+
| |
101+
| help: try dereferencing: `****rrrrro`
102+
103+
error: aborting due to 12 previous errors
104+

0 commit comments

Comments
 (0)