Skip to content

Commit b42f325

Browse files
committed
New lint: Recommend using ptr::eq when possible
1 parent 0e66a78 commit b42f325

File tree

8 files changed

+203
-2
lines changed

8 files changed

+203
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,7 @@ Released 2018-09-13
11551155
[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
11561156
[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
11571157
[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
1158+
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
11581159
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
11591160
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
11601161
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

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

9-
[There are 338 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 339 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

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

clippy_lints/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ pub mod partialeq_ne_impl;
260260
pub mod path_buf_push_overwrite;
261261
pub mod precedence;
262262
pub mod ptr;
263+
pub mod ptr_eq;
263264
pub mod ptr_offset_with_cast;
264265
pub mod question_mark;
265266
pub mod ranges;
@@ -693,6 +694,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
693694
&ptr::CMP_NULL,
694695
&ptr::MUT_FROM_REF,
695696
&ptr::PTR_ARG,
697+
&ptr_eq::PTR_EQ,
696698
&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
697699
&question_mark::QUESTION_MARK,
698700
&ranges::ITERATOR_STEP_BY_ZERO,
@@ -803,6 +805,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
803805
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
804806
store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
805807
store.register_late_pass(|| box ptr::Ptr);
808+
store.register_late_pass(|| box ptr_eq::PtrEqLint);
806809
store.register_late_pass(|| box needless_bool::NeedlessBool);
807810
store.register_late_pass(|| box needless_bool::BoolComparison);
808811
store.register_late_pass(|| box approx_const::ApproxConstant);
@@ -1236,6 +1239,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
12361239
LintId::of(&ptr::CMP_NULL),
12371240
LintId::of(&ptr::MUT_FROM_REF),
12381241
LintId::of(&ptr::PTR_ARG),
1242+
LintId::of(&ptr_eq::PTR_EQ),
12391243
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
12401244
LintId::of(&question_mark::QUESTION_MARK),
12411245
LintId::of(&ranges::ITERATOR_STEP_BY_ZERO),
@@ -1379,6 +1383,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13791383
LintId::of(&panic_unimplemented::PANIC_PARAMS),
13801384
LintId::of(&ptr::CMP_NULL),
13811385
LintId::of(&ptr::PTR_ARG),
1386+
LintId::of(&ptr_eq::PTR_EQ),
13821387
LintId::of(&question_mark::QUESTION_MARK),
13831388
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
13841389
LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),

clippy_lints/src/ptr_eq.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use crate::utils;
2+
use if_chain::if_chain;
3+
use rustc::hir::{BinOpKind, Expr, ExprKind};
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc::{declare_lint_pass, declare_tool_lint};
6+
use rustc_errors::Applicability;
7+
8+
declare_clippy_lint! {
9+
/// **What it does:** Use `std::ptr::eq` when applicable
10+
///
11+
/// **Why is this bad?** This can be used to compare `&T` references
12+
/// (which coerce to `*const T` implicitly) by their address rather than
13+
/// comparing the values they point to.
14+
///
15+
/// **Known problems:** None
16+
///
17+
/// **Example:**
18+
/// ```rust
19+
/// let a = &[1, 2, 3];
20+
/// let b = &[1, 2, 3];
21+
///
22+
/// let _ = a as *const _ as usize == b as *const _ as usize;
23+
/// // Could be written
24+
/// let _ = std::ptr::eq(a, b);
25+
/// ```
26+
pub PTR_EQ,
27+
style,
28+
"use `std::ptr::eq` when applicable"
29+
}
30+
31+
declare_lint_pass!(PtrEqLint => [PTR_EQ]);
32+
33+
static LINT_MSG: &str = "use `std::ptr::eq` when applicable";
34+
35+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrEqLint {
36+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
37+
if utils::in_macro(expr.span) {
38+
return;
39+
}
40+
41+
if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind {
42+
if BinOpKind::Eq == op.node {
43+
let (left, right) = if_chain! {
44+
if let Some(lhs) = expr_as_cast_to_usize(cx, left);
45+
if let Some(rhs) = expr_as_cast_to_usize(cx, right);
46+
then {
47+
(lhs, rhs)
48+
}
49+
else {
50+
(&**left, &**right)
51+
}
52+
};
53+
54+
if_chain! {
55+
if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
56+
if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
57+
if let Some(left_snip) = utils::snippet_opt(cx, left_var.span);
58+
if let Some(right_snip) = utils::snippet_opt(cx, right_var.span);
59+
then {
60+
utils::span_lint_and_sugg(
61+
cx,
62+
PTR_EQ,
63+
expr.span,
64+
LINT_MSG,
65+
"try",
66+
format!("std::ptr::eq({}, {})", left_snip, right_snip),
67+
Applicability::MachineApplicable,
68+
);
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}
75+
76+
// If the given expression is a cast to an usize, return the lhs of the cast
77+
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
78+
fn expr_as_cast_to_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cast_expr: &'tcx Expr) -> Option<&'tcx Expr> {
79+
if cx.tables.expr_ty(cast_expr) == cx.tcx.types.usize {
80+
if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
81+
return Some(expr);
82+
}
83+
}
84+
None
85+
}
86+
87+
// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
88+
// E.g., `foo as *const _` returns `foo`.
89+
fn expr_as_cast_to_raw_pointer<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cast_expr: &'tcx Expr) -> Option<&'tcx Expr> {
90+
if cx.tables.expr_ty(cast_expr).is_unsafe_ptr() {
91+
if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
92+
return Some(expr);
93+
}
94+
}
95+
None
96+
}

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 338] = [
9+
pub const ALL_LINTS: [Lint; 339] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1568,6 +1568,13 @@ pub const ALL_LINTS: [Lint; 338] = [
15681568
deprecation: None,
15691569
module: "ptr",
15701570
},
1571+
Lint {
1572+
name: "ptr_eq",
1573+
group: "style",
1574+
desc: "use `std::ptr::eq` when applicable",
1575+
deprecation: None,
1576+
module: "ptr_eq",
1577+
},
15711578
Lint {
15721579
name: "ptr_offset_with_cast",
15731580
group: "complexity",

tests/ui/ptr_eq.fixed

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
#![warn(clippy::ptr_eq)]
3+
4+
macro_rules! mac {
5+
($a:expr, $b:expr) => {
6+
$a as *const _ as usize == $b as *const _ as usize
7+
};
8+
}
9+
10+
macro_rules! another_mac {
11+
($a:expr, $b:expr) => {
12+
$a as *const _ == $b as *const _
13+
};
14+
}
15+
16+
fn main() {
17+
let a = &[1, 2, 3];
18+
let b = &[1, 2, 3];
19+
20+
let _ = std::ptr::eq(a, b);
21+
let _ = std::ptr::eq(a, b);
22+
let _ = a.as_ptr() == b as *const _;
23+
let _ = a.as_ptr() == b.as_ptr();
24+
25+
// Do not lint
26+
27+
let _ = mac!(a, b);
28+
let _ = another_mac!(a, b);
29+
30+
let a = &mut [1, 2, 3];
31+
let b = &mut [1, 2, 3];
32+
33+
let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _;
34+
let _ = a.as_mut_ptr() == b.as_mut_ptr();
35+
36+
let _ = a == b;
37+
let _ = core::ptr::eq(a, b);
38+
}

tests/ui/ptr_eq.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
#![warn(clippy::ptr_eq)]
3+
4+
macro_rules! mac {
5+
($a:expr, $b:expr) => {
6+
$a as *const _ as usize == $b as *const _ as usize
7+
};
8+
}
9+
10+
macro_rules! another_mac {
11+
($a:expr, $b:expr) => {
12+
$a as *const _ == $b as *const _
13+
};
14+
}
15+
16+
fn main() {
17+
let a = &[1, 2, 3];
18+
let b = &[1, 2, 3];
19+
20+
let _ = a as *const _ as usize == b as *const _ as usize;
21+
let _ = a as *const _ == b as *const _;
22+
let _ = a.as_ptr() == b as *const _;
23+
let _ = a.as_ptr() == b.as_ptr();
24+
25+
// Do not lint
26+
27+
let _ = mac!(a, b);
28+
let _ = another_mac!(a, b);
29+
30+
let a = &mut [1, 2, 3];
31+
let b = &mut [1, 2, 3];
32+
33+
let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _;
34+
let _ = a.as_mut_ptr() == b.as_mut_ptr();
35+
36+
let _ = a == b;
37+
let _ = core::ptr::eq(a, b);
38+
}

tests/ui/ptr_eq.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: use `std::ptr::eq` when applicable
2+
--> $DIR/ptr_eq.rs:20:13
3+
|
4+
LL | let _ = a as *const _ as usize == b as *const _ as usize;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)`
6+
|
7+
= note: `-D clippy::ptr-eq` implied by `-D warnings`
8+
9+
error: use `std::ptr::eq` when applicable
10+
--> $DIR/ptr_eq.rs:21:13
11+
|
12+
LL | let _ = a as *const _ == b as *const _;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)