Skip to content

Commit f981739

Browse files
committed
refactor ptr_offset_with_cast
1 parent 5ac9657 commit f981739

File tree

9 files changed

+209
-164
lines changed

9 files changed

+209
-164
lines changed

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
448448
crate::methods::OR_THEN_UNWRAP_INFO,
449449
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
450450
crate::methods::PATH_ENDS_WITH_EXT_INFO,
451+
crate::methods::PTR_OFFSET_WITH_CAST_INFO,
451452
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
452453
crate::methods::READONLY_WRITE_LOCK_INFO,
453454
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
@@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
625626
crate::ptr::MUT_FROM_REF_INFO,
626627
crate::ptr::PTR_ARG_INFO,
627628
crate::ptr::PTR_EQ_INFO,
628-
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
629629
crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO,
630630
crate::pub_use::PUB_USE_INFO,
631631
crate::question_mark::QUESTION_MARK_INFO,

clippy_lints/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,6 @@ mod permissions_set_readonly_false;
302302
mod pointers_in_nomem_asm_block;
303303
mod precedence;
304304
mod ptr;
305-
mod ptr_offset_with_cast;
306305
mod pub_underscore_fields;
307306
mod pub_use;
308307
mod question_mark;
@@ -592,7 +591,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
592591
store.register_late_pass(|_| Box::new(unwrap::Unwrap));
593592
store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf)));
594593
store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf)));
595-
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
596594
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
597595
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
598596
store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf)));

clippy_lints/src/methods/mod.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ mod or_fun_call;
9191
mod or_then_unwrap;
9292
mod path_buf_push_overwrite;
9393
mod path_ends_with_ext;
94+
mod ptr_offset_with_cast;
9495
mod range_zip_with_len;
9596
mod read_line_without_trim;
9697
mod readonly_write_lock;
@@ -1725,6 +1726,43 @@ declare_clippy_lint! {
17251726
"Check for offset calculations on raw pointers to zero-sized types"
17261727
}
17271728

1729+
declare_clippy_lint! {
1730+
/// ### What it does
1731+
/// Checks for usage of the `offset` pointer method with a `usize` casted to an
1732+
/// `isize`.
1733+
///
1734+
/// ### Why is this bad?
1735+
/// If we’re always increasing the pointer address, we can avoid the numeric
1736+
/// cast by using the `add` method instead.
1737+
///
1738+
/// ### Example
1739+
/// ```no_run
1740+
/// let vec = vec![b'a', b'b', b'c'];
1741+
/// let ptr = vec.as_ptr();
1742+
/// let offset = 1_usize;
1743+
///
1744+
/// unsafe {
1745+
/// ptr.offset(offset as isize);
1746+
/// }
1747+
/// ```
1748+
///
1749+
/// Could be written:
1750+
///
1751+
/// ```no_run
1752+
/// let vec = vec![b'a', b'b', b'c'];
1753+
/// let ptr = vec.as_ptr();
1754+
/// let offset = 1_usize;
1755+
///
1756+
/// unsafe {
1757+
/// ptr.add(offset);
1758+
/// }
1759+
/// ```
1760+
#[clippy::version = "1.30.0"]
1761+
pub PTR_OFFSET_WITH_CAST,
1762+
complexity,
1763+
"unneeded pointer offset cast"
1764+
}
1765+
17281766
declare_clippy_lint! {
17291767
/// ### What it does
17301768
/// Checks for `FileType::is_file()`.
@@ -4665,6 +4703,7 @@ impl_lint_pass!(Methods => [
46654703
UNINIT_ASSUMED_INIT,
46664704
MANUAL_SATURATING_ARITHMETIC,
46674705
ZST_OFFSET,
4706+
PTR_OFFSET_WITH_CAST,
46684707
FILETYPE_IS_FILE,
46694708
OPTION_AS_REF_DEREF,
46704709
UNNECESSARY_LAZY_EVALUATIONS,
@@ -4960,10 +4999,7 @@ impl Methods {
49604999
// Handle method calls whose receiver and arguments may not come from expansion
49615000
if let Some((name, recv, args, span, call_span)) = method_call(expr) {
49625001
match (name, args) {
4963-
(
4964-
sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub,
4965-
[_arg],
4966-
) => {
5002+
(sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => {
49675003
zst_offset::check(cx, expr, recv);
49685004
},
49695005
(sym::all, [arg]) => {
@@ -5334,6 +5370,11 @@ impl Methods {
53345370
},
53355371
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
53365372
},
5373+
(sym::offset | sym::wrapping_offset, [arg]) => {
5374+
zst_offset::check(cx, expr, recv);
5375+
5376+
ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv);
5377+
},
53375378
(sym::ok_or_else, [arg]) => {
53385379
unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
53395380
},
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::sym;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::LateContext;
7+
use rustc_span::Symbol;
8+
use std::fmt;
9+
10+
use super::PTR_OFFSET_WITH_CAST;
11+
12+
pub(super) fn check(
13+
cx: &LateContext<'_>,
14+
method: Symbol,
15+
expr: &Expr<'_>,
16+
recv: &Expr<'_>,
17+
arg: &Expr<'_>,
18+
msrv: Msrv,
19+
) {
20+
// `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions
21+
// became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable.
22+
if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) {
23+
return;
24+
}
25+
26+
let method = match method {
27+
sym::offset => Method::Offset,
28+
sym::wrapping_offset => Method::WrappingOffset,
29+
_ => return,
30+
};
31+
32+
if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() {
33+
return;
34+
}
35+
36+
// Check if the argument to the method call is a cast from usize.
37+
let cast_lhs_expr = match arg.kind {
38+
ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs,
39+
_ => return,
40+
};
41+
42+
let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else {
43+
return;
44+
};
45+
46+
let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
47+
span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| {
48+
diag.multipart_suggestion(
49+
format!("use `{}` instead", method.suggestion()),
50+
vec![
51+
(method_name.ident.span, method.suggestion().to_string()),
52+
(arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()),
53+
],
54+
Applicability::MachineApplicable,
55+
);
56+
});
57+
}
58+
59+
#[derive(Copy, Clone)]
60+
enum Method {
61+
Offset,
62+
WrappingOffset,
63+
}
64+
65+
impl Method {
66+
#[must_use]
67+
fn suggestion(self) -> &'static str {
68+
match self {
69+
Self::Offset => "add",
70+
Self::WrappingOffset => "wrapping_add",
71+
}
72+
}
73+
}
74+
75+
impl fmt::Display for Method {
76+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77+
match self {
78+
Self::Offset => write!(f, "offset"),
79+
Self::WrappingOffset => write!(f, "wrapping_offset"),
80+
}
81+
}
82+
}

clippy_lints/src/ptr_offset_with_cast.rs

Lines changed: 0 additions & 151 deletions
This file was deleted.

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ msrv_aliases! {
7373
1,29,0 { ITER_FLATTEN }
7474
1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
7575
1,27,0 { ITERATOR_TRY_FOLD }
76-
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
76+
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS }
7777
1,24,0 { IS_ASCII_DIGIT, PTR_NULL }
7878
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
7979
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }

tests/ui/ptr_offset_with_cast.fixed

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(clippy::unnecessary_cast, clippy::useless_vec)]
1+
#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)]
22

33
fn main() {
44
let vec = vec![b'a', b'b', b'c'];
@@ -18,5 +18,25 @@ fn main() {
1818
//~^ ptr_offset_with_cast
1919
let _ = ptr.wrapping_offset(offset_isize as isize);
2020
let _ = ptr.wrapping_offset(offset_u8 as isize);
21+
22+
let _ = S.offset(offset_usize as isize);
23+
let _ = S.wrapping_offset(offset_usize as isize);
24+
25+
let _ = (&ptr).add(offset_usize);
26+
//~^ ptr_offset_with_cast
27+
let _ = (&ptr).wrapping_add(offset_usize);
28+
//~^ ptr_offset_with_cast
29+
}
30+
}
31+
32+
#[derive(Clone, Copy)]
33+
struct S;
34+
35+
impl S {
36+
fn offset(self, _: isize) -> Self {
37+
self
38+
}
39+
fn wrapping_offset(self, _: isize) -> Self {
40+
self
2141
}
2242
}

0 commit comments

Comments
 (0)