diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index a7f7bf7854e6..8754602a00a1 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -2,11 +2,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{for_each_expr, Visitable}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local, SpanlessEq}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; +use rustc_hir::{self as hir, Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( if let ty::RawPtr(..) = cast_from.kind() // check both mutability and type are the same && cast_from.kind() == cast_to.kind() - && let ExprKind::Cast(_, cast_to_hir) = expr.kind + && let ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind // Ignore casts to e.g. type aliases and infer types // - p as pointer_alias // - p as _ @@ -41,6 +41,25 @@ pub(super) fn check<'tcx>( if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { return false; } + + if let Some(cast_from_hir_ty) = get_expr_hir_ty(cx, cast_from_expr) + && let TyKind::Ptr(from_pointee) = cast_from_hir_ty.kind + && let TyKind::Path(QPath::Resolved( + _, + Path { + segments: from_ty_seg, .. + }, + )) = from_pointee.ty.kind + && let QPath::Resolved( + _, + Path { + segments: to_ty_seg, .. + }, + ) = qpath + && !SpanlessEq::new(cx).eq_path_segments(from_ty_seg, to_ty_seg) + { + return false; + } }, // Ignore `p as *const _` TyKind::Infer => return false, @@ -300,3 +319,33 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) } + +/// Try to get the [`hir::Ty`] of an expr, return `None` if it can't be determained. +fn get_expr_hir_ty<'hir>(cx: &LateContext<'hir>, expr: &Expr<'hir>) -> Option<&'hir hir::Ty<'hir>> { + match expr.kind { + ExprKind::AddrOf(_, _, inner) => get_expr_hir_ty(cx, inner), + ExprKind::Block(hir::Block { expr: Some(inner), .. }, _) => get_expr_hir_ty(cx, inner), + ExprKind::Cast(_, ty) => Some(ty), + ExprKind::Path(_) if let Some(local_id) = path_to_local(expr) => match cx.tcx.parent_hir_node(local_id) { + Node::Param(param) => { + let parent_of_param = cx.tcx.parent_hir_node(param.hir_id); + let fn_decl = parent_of_param.fn_decl()?; + fn_decl.inputs.iter().find(|ty| ty.span == param.ty_span) + }, + Node::LetStmt(hir::LetStmt { ty, init, .. }) => { + ty.or_else(|| init.and_then(|init_expr| get_expr_hir_ty(cx, init_expr))) + }, + _ => None, + }, + ExprKind::MethodCall(hir::PathSegment { hir_id, .. }, ..) | ExprKind::Call(Expr { hir_id, .. }, ..) => { + cx.tcx.hir().fn_decl_by_hir_id(*hir_id).and_then(|fn_decl| { + if let hir::FnRetTy::Return(ty) = fn_decl.output { + Some(ty) + } else { + None + } + }) + }, + _ => None, + } +} diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 288541362cdd..b2650769549e 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -228,3 +228,15 @@ mod fixable { { *x }.pow(2) } } + +fn issue_12860() { + struct Foo<'a>(&'a [u8]); + + fn foo<'a, 'b>(x: &'a Foo<'a>) -> &'b Foo<'b> { + unsafe { &*(x as *const Foo<'a> as *const Foo<'b>) } + } + + fn bar<'a, 'b>(x: *const Foo<'a>) -> &'b Foo<'b> { + unsafe { &*(x as *const Foo<'b>) } + } +} diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index eef3a42e3514..ddaa0c50499b 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -228,3 +228,15 @@ mod fixable { (*x as usize).pow(2) } } + +fn issue_12860() { + struct Foo<'a>(&'a [u8]); + + fn foo<'a, 'b>(x: &'a Foo<'a>) -> &'b Foo<'b> { + unsafe { &*(x as *const Foo<'a> as *const Foo<'b>) } + } + + fn bar<'a, 'b>(x: *const Foo<'a>) -> &'b Foo<'b> { + unsafe { &*(x as *const Foo<'b>) } + } +}