Skip to content

Commit c01402d

Browse files
committed
new lint for accidental double pointer into flat addr case
1 parent 86d348d commit c01402d

10 files changed

+148
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5193,6 +5193,7 @@ Released 2018-09-13
51935193
<!-- begin autogenerated links to lint list -->
51945194
[`absolute_paths`]: https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths
51955195
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
5196+
[`accidental_double_ref_into_flat_addr_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#accidental_double_ref_into_flat_addr_cast
51965197
[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
51975198
[`allow_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes
51985199
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason

clippy_lints/src/borrow_deref_ref.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ declare_clippy_lint! {
2121
/// False negative on such code:
2222
/// ```no_run
2323
/// let x = &12;
24-
/// let addr_x = &x as *const _ as usize;
25-
/// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
26-
/// // But if we fix it, assert will fail.
24+
/// let addr_x = x as *const _ as usize;
25+
/// let addr_y = &*x as *const _ as usize; // assert ok now, and lint triggered.
26+
/// // But if we fix it, assert will fail.
2727
/// assert_ne!(addr_x, addr_y);
2828
/// ```
2929
///
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use rustc_hir::{Expr, ExprKind};
2+
use rustc_lint::LateContext;
3+
use rustc_middle::ty::Ty;
4+
5+
pub fn check<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>, ty_into: Ty<'_>) {
6+
// Check this part:
7+
// ```
8+
// &some_ref as *const _ as usize
9+
// ^^^^^^^^
10+
// &some_ref as *const _ as *const u8
11+
// ^^^^^^^^^^^^
12+
// ```
13+
if !ty_into.is_numeric() && !ty_into.is_unsafe_ptr() {
14+
return;
15+
}
16+
17+
#[allow(clippy::items_after_statements)] // It is pretty reasonable to have it here?
18+
const EXPLICIT_DEREF: bool = true; // *mut and *const have to be explicity deref'd
19+
20+
// In case of casting into recursive pointer type (e.g. *mut *mut u8), skip this check.
21+
// Technically `builtin_deref` should always return us here a type, however,
22+
// lets not risk a potential crash with unwrap().
23+
if ty_into.is_unsafe_ptr() && ty_into.builtin_deref(EXPLICIT_DEREF).is_some_and(Ty::is_unsafe_ptr) {
24+
return;
25+
}
26+
27+
// The type from MIR is already inferred, so we cannot check if it is "as *const _"
28+
// if !ty_from.is_unsafe_ptr() ||
29+
// !ty_from.builtin_deref(true).is_some_and(Ty::is_ty_or_numeric_infer) {
30+
// return;
31+
// }
32+
33+
// Get these parts of the expression:
34+
// ```
35+
// &some_ref as *const _ as usize
36+
// ^^^^^^^^^ ^^^^^^^^^^^
37+
// &some_ref as *const _ as *const u8
38+
// ^^^^^^^^^ ^^^^^^^^^^^
39+
// ```
40+
let ExprKind::Cast(nested_cast_expr, nested_cast_ty) = cast_expr.kind else {
41+
return;
42+
};
43+
44+
// Check this part:
45+
// ```
46+
// &some_ref as *const _ as usize
47+
// ^^^^^^^^
48+
// &some_ref as *const _ as *const u8
49+
// ^^^^^^^^
50+
// ```
51+
// Here we cannot use MIR data (like `cast_from` from function above) as it has already
52+
// the type inferred. We have to use the data from HIR.
53+
let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = nested_cast_ty.kind else {
54+
return;
55+
};
56+
if !matches!(ty.kind, rustc_hir::TyKind::Infer) {
57+
return;
58+
}
59+
60+
// Get this part of the expression:
61+
// ```
62+
// &some_ref as *const _ as usize
63+
// ^^^^^^^^
64+
// &some_ref as *const _ as *const u8
65+
// ^^^^^^^^
66+
// ```
67+
let ExprKind::AddrOf(_, _, some_ref) = nested_cast_expr.kind else {
68+
return;
69+
};
70+
71+
// Check if `some_ref` is indeed a reference.
72+
if !cx.typeck_results().expr_ty(some_ref).is_ref() {
73+
return;
74+
}
75+
76+
clippy_utils::diagnostics::span_lint(
77+
cx,
78+
super::ACCIDENTAL_DOUBLE_REF_INTO_FLAT_ADDR_CAST,
79+
nested_cast_expr.span,
80+
"accidental cast",
81+
);
82+
}

clippy_lints/src/casts/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod accidental_double_ref_into_flat_addr_cast;
12
mod as_ptr_cast_mut;
23
mod as_underscore;
34
mod borrow_as_ptr;
@@ -717,6 +718,27 @@ declare_clippy_lint! {
717718
"using `as` to cast a reference to pointer"
718719
}
719720

721+
declare_clippy_lint! {
722+
/// ### What it does
723+
/// TODO
724+
///
725+
/// ### Why is this bad?
726+
/// TODO
727+
///
728+
/// ### Example
729+
/// ```no_run
730+
/// &s as *const _ as usize
731+
/// ```
732+
/// Use instead:
733+
/// ```no_run
734+
/// s as *const _ as usize
735+
/// ```
736+
#[clippy::version = "1.78.0"]
737+
pub ACCIDENTAL_DOUBLE_REF_INTO_FLAT_ADDR_CAST,
738+
suspicious,
739+
"TODO"
740+
}
741+
720742
pub struct Casts {
721743
msrv: Msrv,
722744
}
@@ -753,6 +775,7 @@ impl_lint_pass!(Casts => [
753775
CAST_NAN_TO_INT,
754776
ZERO_PTR,
755777
REF_AS_PTR,
778+
ACCIDENTAL_DOUBLE_REF_INTO_FLAT_ADDR_CAST,
756779
]);
757780

758781
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -795,6 +818,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
795818
}
796819

797820
as_underscore::check(cx, expr, cast_to_hir);
821+
accidental_double_ref_into_flat_addr_cast::check(cx, expr, cast_to);
798822

799823
if self.msrv.meets(msrvs::PTR_FROM_REF) {
800824
ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir);

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
7878
crate::cargo::NEGATIVE_FEATURE_NAMES_INFO,
7979
crate::cargo::REDUNDANT_FEATURE_NAMES_INFO,
8080
crate::cargo::WILDCARD_DEPENDENCIES_INFO,
81+
crate::casts::ACCIDENTAL_DOUBLE_REF_INTO_FLAT_ADDR_CAST_INFO,
8182
crate::casts::AS_PTR_CAST_MUT_INFO,
8283
crate::casts::AS_UNDERSCORE_INFO,
8384
crate::casts::BORROW_AS_PTR_INFO,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![warn(clippy::accidental_double_ref_into_flat_addr_cast)]
2+
#![crate_type = "lib"]
3+
#![no_std]
4+
5+
struct S;
6+
7+
fn f(s: &S) -> usize {
8+
&s as *const _ as usize
9+
}
10+
11+
fn g(s: &S) -> *const u8 {
12+
&s as *const _ as *const u8
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: accidental cast
2+
--> tests/ui/accidental_double_ref_to_flat_addr.rs:8:5
3+
|
4+
LL | &s as *const _ as usize
5+
| ^^
6+
|
7+
= note: `-D clippy::accidental-double-ref-into-flat-addr-cast` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::accidental_double_ref_into_flat_addr_cast)]`
9+
10+
error: accidental cast
11+
--> tests/ui/accidental_double_ref_to_flat_addr.rs:12:5
12+
|
13+
LL | &s as *const _ as *const u8
14+
| ^^
15+
16+
error: aborting due to 2 previous errors
17+

tests/ui/borrow_deref_ref.fixed

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ with_span!(
6565
mod false_negative {
6666
fn foo() {
6767
let x = &12;
68-
let addr_x = &x as *const _ as usize;
69-
let addr_y = &x as *const _ as usize; // assert ok
68+
let addr_x = x as *const _ as usize;
69+
let addr_y = x as *const _ as usize; // assert ok
7070
// let addr_y = &x as *const _ as usize; // assert fail
7171
assert_ne!(addr_x, addr_y);
7272
}

tests/ui/borrow_deref_ref.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ with_span!(
6565
mod false_negative {
6666
fn foo() {
6767
let x = &12;
68-
let addr_x = &x as *const _ as usize;
69-
let addr_y = &&*x as *const _ as usize; // assert ok
68+
let addr_x = x as *const _ as usize;
69+
let addr_y = &*x as *const _ as usize; // assert ok
7070
// let addr_y = &x as *const _ as usize; // assert fail
7171
assert_ne!(addr_x, addr_y);
7272
}

tests/ui/borrow_deref_ref.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ LL | let b = &mut &*bar(&12);
1414
| ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)`
1515

1616
error: deref on an immutable reference
17-
--> tests/ui/borrow_deref_ref.rs:69:23
17+
--> tests/ui/borrow_deref_ref.rs:69:22
1818
|
19-
LL | let addr_y = &&*x as *const _ as usize; // assert ok
20-
| ^^^ help: if you would like to reborrow, try removing `&*`: `x`
19+
LL | let addr_y = &*x as *const _ as usize; // assert ok
20+
| ^^^ help: if you would like to reborrow, try removing `&*`: `x`
2121

2222
error: aborting due to 3 previous errors
2323

0 commit comments

Comments
 (0)