Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clippy_lints/src/casts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_ptr_alignment::check_cast_method(cx, expr);
cast_slice_different_sizes::check(cx, expr, self.msrv);
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
unnecessary_cast::check_ptr_cast(cx, expr);
}

fn check_body(&mut self, cx: &LateContext<'tcx>, body: &rustc_hir::Body<'tcx>) {
Expand Down
59 changes: 57 additions & 2 deletions clippy_lints/src/casts/unnecessary_cast.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::res::MaybeResPath as _;
use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym};
Expand All @@ -11,7 +11,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, Lit, Node, Path, QPath, TyKind, UnOp};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, FloatTy, GenericArg, InferTy, Ty};
use rustc_middle::ty::{self, FloatTy, GenericArg, InferTy, Ty, TypeVisitableExt};
use rustc_span::Symbol;
use std::ops::ControlFlow;

Expand Down Expand Up @@ -250,6 +250,61 @@ fn lint_unnecessary_cast(
);
}

pub(super) fn check_ptr_cast<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
if let ExprKind::MethodCall(method_path, receiver, [], _) = expr.kind
&& method_path.ident.name == sym::cast
&& let recv_ty = cx.typeck_results().expr_ty(receiver)
&& let result_ty = cx.typeck_results().expr_ty(expr)
&& let ty::RawPtr(from_pointee_ty, from_mutbl) = recv_ty.kind()
&& let ty::RawPtr(to_pointee_ty, to_mutbl) = result_ty.kind()
&& from_mutbl == to_mutbl
&& from_pointee_ty == to_pointee_ty
// Avoid linting intentional lifetime changes
&& !from_pointee_ty.has_erased_regions()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this based on lintcheck results from the first version. The first tokio-util case intentionally modified the lifetimes, which I think is a FP.

&& !expr.span.from_expansion()
{
// If explicit turbofish generic args are provided, check for type alias / cfg-dependent / infer
if let Some(generic_args) = method_path.args {
match generic_args.args {
// Skip `ptr.cast::<_>()`.
[rustc_hir::GenericArg::Infer(_)] => return false,
[rustc_hir::GenericArg::Type(cast_to_hir)] => {
let cast_to_hir_ty = cast_to_hir.as_unambig_ty();
match cast_to_hir_ty.kind {
// Skip `ptr.cast::<_>()` (in case it's represented as Type(Infer))
TyKind::Infer(()) => return false,
// Skip casts to type aliases or cfg-dependent types.
TyKind::Path(ref qpath)
if is_ty_alias(qpath) || is_hir_ty_cfg_dependant(cx, cast_to_hir_ty) =>
{
return false;
},
_ => {},
}
},
_ => {},
}
}

let recv_snip = snippet_opt(cx, receiver.span).unwrap_or_default();

span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
format!(
"casting raw pointers to the same type and constness is unnecessary (`{recv_ty}` -> `{result_ty}`)"
),
"try",
recv_snip,
Applicability::MaybeIncorrect,
);
return true;
}

false
}

fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<Lit> {
match expr.kind {
ExprKind::Lit(lit) => Some(lit),
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/manual_c_str_literals.edition2021.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//@[edition2021] edition:2021
//@[edition2018] check-pass
#![warn(clippy::manual_c_str_literals)]
#![allow(clippy::no_effect)]
#![allow(clippy::no_effect, clippy::unnecessary_cast)]

use std::ffi::CStr;

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/manual_c_str_literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//@[edition2021] edition:2021
//@[edition2018] check-pass
#![warn(clippy::manual_c_str_literals)]
#![allow(clippy::no_effect)]
#![allow(clippy::no_effect, clippy::unnecessary_cast)]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, I considered using #[expect(clippy::unnecessary_cast)] for let _: *const _ = b"foo\0".as_ptr().cast::<i8>();, but this appears to only occur in edition 2021 (where edition 2018 triggers an unfulfilled lint). Therefore, I'm using #[allow] instead.


use std::ffi::CStr;

Expand Down
59 changes: 59 additions & 0 deletions tests/ui/unnecessary_cast.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,62 @@ fn issue16475() -> *const u8 {
//~^ unnecessary_cast
}
}

mod issue_15624 {
type U32Alias = u32;

fn ptr_cast_same_type() {
let ptr_const: *const u32 = std::ptr::null();
let _: *const u32 = ptr_const;
//~^ unnecessary_cast

let _ = ptr_const;
//~^ unnecessary_cast

let ptr_mut: *mut u32 = std::ptr::null_mut();
let _: *mut u32 = ptr_mut;
//~^ unnecessary_cast

let _ = ptr_mut;
//~^ unnecessary_cast

let slice: &[u8] = &[1, 2, 3];
let _ = slice.as_ptr();
//~^ unnecessary_cast

// `.cast()` where the let binding's type annotation uses a type alias pointee
let _: *const U32Alias = ptr_const;
//~^ unnecessary_cast

// `.cast()` to a different type
let _: *const u8 = ptr_const.cast();

// `.cast::<T>()` with different type
let _ = ptr_const.cast::<u8>();

// `.cast::<_>()` with explicit wildcard inference
let _: *const u32 = ptr_const.cast::<_>();

// `.cast::<T>()` to a type alias
let _ = ptr_const.cast::<U32Alias>();

// `.cast_mut()` and `.cast_const()` are different operations
let _ = ptr_const.cast_mut();
let _ = ptr_mut.cast_const();
}

fn generic_ptr_cast_same_type<T>(ptr: *const T) -> *const T {
ptr
//~^ unnecessary_cast
}

fn generic_ptr_cast_different_type<T, U>(ptr: *const T) -> *const U {
ptr.cast()
}

// `.cast()` that changes a lifetime parameter.
struct Wrapper<'a>(&'a u32);
fn lifetime_cast<'a>(ptr: *const Wrapper<'static>) -> *const Wrapper<'a> {
ptr.cast()
}
}
59 changes: 59 additions & 0 deletions tests/ui/unnecessary_cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,62 @@ fn issue16475() -> *const u8 {
//~^ unnecessary_cast
}
}

mod issue_15624 {
type U32Alias = u32;

fn ptr_cast_same_type() {
let ptr_const: *const u32 = std::ptr::null();
let _: *const u32 = ptr_const.cast();
//~^ unnecessary_cast

let _ = ptr_const.cast::<u32>();
//~^ unnecessary_cast

let ptr_mut: *mut u32 = std::ptr::null_mut();
let _: *mut u32 = ptr_mut.cast();
//~^ unnecessary_cast

let _ = ptr_mut.cast::<u32>();
//~^ unnecessary_cast

let slice: &[u8] = &[1, 2, 3];
let _ = slice.as_ptr().cast::<u8>();
//~^ unnecessary_cast

// `.cast()` where the let binding's type annotation uses a type alias pointee
let _: *const U32Alias = ptr_const.cast();
//~^ unnecessary_cast

// `.cast()` to a different type
let _: *const u8 = ptr_const.cast();

// `.cast::<T>()` with different type
let _ = ptr_const.cast::<u8>();

// `.cast::<_>()` with explicit wildcard inference
let _: *const u32 = ptr_const.cast::<_>();

// `.cast::<T>()` to a type alias
let _ = ptr_const.cast::<U32Alias>();

// `.cast_mut()` and `.cast_const()` are different operations
let _ = ptr_const.cast_mut();
let _ = ptr_mut.cast_const();
}

fn generic_ptr_cast_same_type<T>(ptr: *const T) -> *const T {
ptr.cast()
//~^ unnecessary_cast
}

fn generic_ptr_cast_different_type<T, U>(ptr: *const T) -> *const U {
ptr.cast()
}

// `.cast()` that changes a lifetime parameter.
struct Wrapper<'a>(&'a u32);
fn lifetime_cast<'a>(ptr: *const Wrapper<'static>) -> *const Wrapper<'a> {
ptr.cast()
}
}
44 changes: 43 additions & 1 deletion tests/ui/unnecessary_cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,47 @@ error: casting raw pointers to the same type and constness is unnecessary (`*con
LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)`

error: aborting due to 65 previous errors
error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`)
--> tests/ui/unnecessary_cast.rs:665:29
|
LL | let _: *const u32 = ptr_const.cast();
| ^^^^^^^^^^^^^^^^ help: try: `ptr_const`

error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`)
--> tests/ui/unnecessary_cast.rs:668:17
|
LL | let _ = ptr_const.cast::<u32>();
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_const`

error: casting raw pointers to the same type and constness is unnecessary (`*mut u32` -> `*mut u32`)
--> tests/ui/unnecessary_cast.rs:672:27
|
LL | let _: *mut u32 = ptr_mut.cast();
| ^^^^^^^^^^^^^^ help: try: `ptr_mut`

error: casting raw pointers to the same type and constness is unnecessary (`*mut u32` -> `*mut u32`)
--> tests/ui/unnecessary_cast.rs:675:17
|
LL | let _ = ptr_mut.cast::<u32>();
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_mut`

error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`)
--> tests/ui/unnecessary_cast.rs:679:17
|
LL | let _ = slice.as_ptr().cast::<u8>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.as_ptr()`

error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`)
--> tests/ui/unnecessary_cast.rs:683:34
|
LL | let _: *const U32Alias = ptr_const.cast();
| ^^^^^^^^^^^^^^^^ help: try: `ptr_const`

error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`)
--> tests/ui/unnecessary_cast.rs:704:9
|
LL | ptr.cast()
| ^^^^^^^^^^ help: try: `ptr`

error: aborting due to 72 previous errors

Loading