blob: d19d3b8eb89d9172b177dd701ea073ab7aadb2b1 [file] [log] [blame] [edit]
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sym;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::Symbol;
use std::fmt;
use super::PTR_OFFSET_WITH_CAST;
pub(super) fn check(
cx: &LateContext<'_>,
method: Symbol,
expr: &Expr<'_>,
recv: &Expr<'_>,
arg: &Expr<'_>,
msrv: Msrv,
) {
// `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions
// became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable.
if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) {
return;
}
let method = match method {
sym::offset => Method::Offset,
sym::wrapping_offset => Method::WrappingOffset,
_ => return,
};
if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() {
return;
}
// Check if the argument to the method call is a cast from usize.
let cast_lhs_expr = match arg.kind {
ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs,
_ => return,
};
let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else {
return;
};
let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| {
diag.multipart_suggestion(
format!("use `{}` instead", method.suggestion()),
vec![
(method_name.ident.span, method.suggestion().to_string()),
(arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()),
],
Applicability::MachineApplicable,
);
});
}
#[derive(Copy, Clone)]
enum Method {
Offset,
WrappingOffset,
}
impl Method {
#[must_use]
fn suggestion(self) -> &'static str {
match self {
Self::Offset => "add",
Self::WrappingOffset => "wrapping_add",
}
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Offset => write!(f, "offset"),
Self::WrappingOffset => write!(f, "wrapping_offset"),
}
}
}