Skip to content

Commit daea9a2

Browse files
committed
refactor ptr_offset_with_cast
1 parent 5ac9657 commit daea9a2

File tree

7 files changed

+145
-158
lines changed

7 files changed

+145
-158
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,12 +4999,14 @@ 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
},
5005+
(sym::offset | sym::wrapping_offset, [arg]) => {
5006+
zst_offset::check(cx, expr, recv);
5007+
5008+
ptr_offset_with_cast::check(cx, name, expr, recv, arg);
5009+
},
49695010
(sym::all, [arg]) => {
49705011
unused_enumerate_index::check(cx, expr, recv, arg);
49715012
needless_character_iteration::check(cx, expr, recv, arg, true);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::sym;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::LateContext;
7+
use rustc_middle::ty;
8+
use rustc_span::Symbol;
9+
use std::fmt;
10+
11+
use super::PTR_OFFSET_WITH_CAST;
12+
13+
pub(super) fn check(cx: &LateContext<'_>, method: Symbol, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
14+
let ty::RawPtr(_, _) = cx.typeck_results().expr_ty(recv).kind() else {
15+
return;
16+
};
17+
18+
let method = match method {
19+
sym::offset => Method::Offset,
20+
sym::wrapping_offset => Method::WrappingOffset,
21+
_ => return,
22+
};
23+
24+
// Check if the argument to the method call is a cast from usize
25+
let cast_lhs_expr = match arg.kind {
26+
ExprKind::Cast(lhs, _) if is_expr_ty_usize(cx, lhs) => lhs,
27+
_ => return,
28+
};
29+
30+
let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
31+
span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| {
32+
if let Some(sugg) = build_suggestion(cx, method, recv, cast_lhs_expr) {
33+
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
34+
}
35+
});
36+
}
37+
38+
// Is the type of the expression a usize?
39+
fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
40+
cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize
41+
}
42+
43+
fn build_suggestion(
44+
cx: &LateContext<'_>,
45+
method: Method,
46+
receiver_expr: &Expr<'_>,
47+
cast_lhs_expr: &Expr<'_>,
48+
) -> Option<String> {
49+
let receiver = receiver_expr.span.get_source_text(cx)?;
50+
let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?;
51+
Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
52+
}
53+
54+
#[derive(Copy, Clone)]
55+
enum Method {
56+
Offset,
57+
WrappingOffset,
58+
}
59+
60+
impl Method {
61+
#[must_use]
62+
fn suggestion(self) -> &'static str {
63+
match self {
64+
Self::Offset => "add",
65+
Self::WrappingOffset => "wrapping_add",
66+
}
67+
}
68+
}
69+
70+
impl fmt::Display for Method {
71+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+
match self {
73+
Self::Offset => write!(f, "offset"),
74+
Self::WrappingOffset => write!(f, "wrapping_offset"),
75+
}
76+
}
77+
}

clippy_lints/src/ptr_offset_with_cast.rs

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

tests/ui/ptr_offset_with_cast.fixed

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,16 @@ 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);
2124
}
2225
}
26+
27+
#[derive(Clone, Copy)]
28+
struct S;
29+
30+
impl S {
31+
fn offset(self, _: isize) -> Self { self }
32+
fn wrapping_offset(self, _: isize) -> Self { self }
33+
}

tests/ui/ptr_offset_with_cast.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,16 @@ 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);
2124
}
2225
}
26+
27+
#[derive(Clone, Copy)]
28+
struct S;
29+
30+
impl S {
31+
fn offset(self, _: isize) -> Self { self }
32+
fn wrapping_offset(self, _: isize) -> Self { self }
33+
}

0 commit comments

Comments
 (0)