Skip to content

Commit 7cdaabc

Browse files
committed
Suggest pointer::cast when possible in transmute_ptr_to_ref
Defensively add a cast to any type with lifetimes.
1 parent 93ebd0e commit 7cdaabc

File tree

7 files changed

+265
-65
lines changed

7 files changed

+265
-65
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
641641
store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
642642
store.register_late_pass(|| Box::new(no_effect::NoEffect));
643643
store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
644-
store.register_late_pass(|| Box::new(transmute::Transmute));
644+
store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
645645
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
646646
store.register_late_pass(move || {
647647
Box::new(cognitive_complexity::CognitiveComplexity::new(

clippy_lints/src/transmute/mod.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ mod wrong_transmute;
1616

1717
use clippy_utils::in_constant;
1818
use if_chain::if_chain;
19-
use rustc_hir::{Expr, ExprKind};
19+
use rustc_hir::{Expr, ExprKind, QPath};
2020
use rustc_lint::{LateContext, LateLintPass};
21-
use rustc_session::{declare_lint_pass, declare_tool_lint};
21+
use rustc_semver::RustcVersion;
22+
use rustc_session::{declare_tool_lint, impl_lint_pass};
2223
use rustc_span::symbol::sym;
2324

2425
declare_clippy_lint! {
@@ -385,7 +386,10 @@ declare_clippy_lint! {
385386
"transmute to or from a type with an undefined representation"
386387
}
387388

388-
declare_lint_pass!(Transmute => [
389+
pub struct Transmute {
390+
msrv: Option<RustcVersion>,
391+
}
392+
impl_lint_pass!(Transmute => [
389393
CROSSPOINTER_TRANSMUTE,
390394
TRANSMUTE_PTR_TO_REF,
391395
TRANSMUTE_PTR_TO_PTR,
@@ -401,13 +405,18 @@ declare_lint_pass!(Transmute => [
401405
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
402406
TRANSMUTE_UNDEFINED_REPR,
403407
]);
404-
408+
impl Transmute {
409+
#[must_use]
410+
pub fn new(msrv: Option<RustcVersion>) -> Self {
411+
Self { msrv }
412+
}
413+
}
405414
impl<'tcx> LateLintPass<'tcx> for Transmute {
406415
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
407416
if_chain! {
408417
if let ExprKind::Call(path_expr, [arg]) = e.kind;
409-
if let ExprKind::Path(ref qpath) = path_expr.kind;
410-
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
418+
if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
419+
if let Some(def_id) = path.res.opt_def_id();
411420
if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
412421
then {
413422
// Avoid suggesting non-const operations in const contexts:
@@ -427,7 +436,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
427436

428437
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
429438
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
430-
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
439+
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
431440
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
432441
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
433442
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
@@ -446,4 +455,6 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
446455
}
447456
}
448457
}
458+
459+
extract_msrv_attr!(LateContext);
449460
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
use super::utils::get_type_snippet;
21
use super::TRANSMUTE_PTR_TO_REF;
32
use clippy_utils::diagnostics::span_lint_and_then;
4-
use clippy_utils::sugg;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use clippy_utils::{meets_msrv, msrvs, sugg};
55
use rustc_errors::Applicability;
6-
use rustc_hir::{Expr, Mutability, QPath};
6+
use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
77
use rustc_lint::LateContext;
8-
use rustc_middle::ty::{self, Ty};
8+
use rustc_middle::ty::{self, Ty, TypeFoldable};
9+
use rustc_semver::RustcVersion;
910

1011
/// Checks for `transmute_ptr_to_ref` lint.
1112
/// Returns `true` if it's triggered, otherwise returns `false`.
@@ -15,7 +16,8 @@ pub(super) fn check<'tcx>(
1516
from_ty: Ty<'tcx>,
1617
to_ty: Ty<'tcx>,
1718
arg: &'tcx Expr<'_>,
18-
qpath: &'tcx QPath<'_>,
19+
path: &'tcx Path<'_>,
20+
msrv: Option<RustcVersion>,
1921
) -> bool {
2022
match (&from_ty.kind(), &to_ty.kind()) {
2123
(ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -34,23 +36,49 @@ pub(super) fn check<'tcx>(
3436
} else {
3537
("&*", "*const")
3638
};
39+
let mut app = Applicability::MachineApplicable;
3740

38-
let arg = if from_ptr_ty.ty == *to_ref_ty {
39-
arg
41+
let sugg = if let Some(ty) = get_explicit_type(path) {
42+
let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
43+
if meets_msrv(msrv, msrvs::POINTER_CAST) {
44+
format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip)
45+
} else if from_ptr_ty.has_erased_regions() {
46+
sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip)))
47+
.to_string()
48+
} else {
49+
sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string()
50+
}
51+
} else if from_ptr_ty.ty == *to_ref_ty {
52+
if from_ptr_ty.has_erased_regions() {
53+
if meets_msrv(msrv, msrvs::POINTER_CAST) {
54+
format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty)
55+
} else {
56+
sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty)))
57+
.to_string()
58+
}
59+
} else {
60+
sugg::make_unop(deref, arg).to_string()
61+
}
4062
} else {
41-
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty)))
63+
sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string()
4264
};
4365

44-
diag.span_suggestion(
45-
e.span,
46-
"try",
47-
sugg::make_unop(deref, arg).to_string(),
48-
Applicability::Unspecified,
49-
);
66+
diag.span_suggestion(e.span, "try", sugg, app);
5067
},
5168
);
5269
true
5370
},
5471
_ => false,
5572
}
5673
}
74+
75+
/// Gets the type `Bar` in `…::transmute<Foo, &Bar>`.
76+
fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> {
77+
if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)?
78+
&& let TyKind::Rptr(_, ty) = &ty.kind
79+
{
80+
Some(ty.ty)
81+
} else {
82+
None
83+
}
84+
}

clippy_lints/src/transmute/utils.rs

+1-27
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,9 @@
1-
use clippy_utils::last_path_segment;
2-
use clippy_utils::source::snippet;
3-
use if_chain::if_chain;
4-
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
1+
use rustc_hir::Expr;
52
use rustc_lint::LateContext;
63
use rustc_middle::ty::{cast::CastKind, Ty};
74
use rustc_span::DUMMY_SP;
85
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
96

10-
/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
11-
/// not available , use
12-
/// the type's `ToString` implementation. In weird cases it could lead to types
13-
/// with invalid `'_`
14-
/// lifetime, but it should be rare.
15-
pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
16-
let seg = last_path_segment(path);
17-
if_chain! {
18-
if let Some(params) = seg.args;
19-
if !params.parenthesized;
20-
if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
21-
GenericArg::Type(ty) => Some(ty),
22-
_ => None,
23-
}).nth(1);
24-
if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
25-
then {
26-
return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
27-
}
28-
}
29-
30-
to_ref_ty.to_string()
31-
}
32-
337
// check if the component types of the transmuted collection and the result have different ABI,
348
// size or alignment
359
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {

tests/ui/transmute_ptr_to_ref.fixed

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// run-rustfix
2+
3+
#![feature(custom_inner_attributes)]
4+
#![warn(clippy::transmute_ptr_to_ref)]
5+
#![allow(clippy::match_single_binding)]
6+
7+
unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
8+
let _: &T = &*p;
9+
let _: &T = &*p;
10+
11+
let _: &mut T = &mut *m;
12+
let _: &mut T = &mut *m;
13+
14+
let _: &T = &*m;
15+
let _: &T = &*m;
16+
17+
let _: &mut T = &mut *(p as *mut T);
18+
let _ = &mut *(p as *mut T);
19+
20+
let _: &T = &*(o as *const T);
21+
let _: &T = &*(o as *const T);
22+
23+
let _: &mut T = &mut *(om as *mut T);
24+
let _: &mut T = &mut *(om as *mut T);
25+
26+
let _: &T = &*(om as *const T);
27+
let _: &T = &*(om as *const T);
28+
}
29+
30+
fn _issue1231() {
31+
struct Foo<'a, T> {
32+
bar: &'a T,
33+
}
34+
35+
let raw = 42 as *const i32;
36+
let _: &Foo<u8> = unsafe { &*raw.cast::<Foo<_>>() };
37+
38+
let _: &Foo<&u8> = unsafe { &*raw.cast::<Foo<&_>>() };
39+
40+
type Bar<'a> = &'a u8;
41+
let raw = 42 as *const i32;
42+
unsafe { &*(raw as *const u8) };
43+
}
44+
45+
unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
46+
match 0 {
47+
0 => &*x.cast::<&u32>(),
48+
1 => &*y.cast::<&u32>(),
49+
2 => &*x.cast::<&'b u32>(),
50+
_ => &*y.cast::<&'b u32>(),
51+
}
52+
}
53+
54+
unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
55+
#![clippy::msrv = "1.38"]
56+
let a = 0u32;
57+
let a = &a as *const u32;
58+
let _: &u32 = &*a;
59+
let _: &u32 = &*a.cast::<u32>();
60+
match 0 {
61+
0 => &*x.cast::<&u32>(),
62+
_ => &*x.cast::<&'b u32>(),
63+
}
64+
}
65+
66+
unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
67+
#![clippy::msrv = "1.37"]
68+
let a = 0u32;
69+
let a = &a as *const u32;
70+
let _: &u32 = &*a;
71+
let _: &u32 = &*(a as *const u32);
72+
match 0 {
73+
0 => &*(x as *const () as *const &u32),
74+
_ => &*(x as *const () as *const &'b u32),
75+
}
76+
}
77+
78+
fn main() {}

tests/ui/transmute_ptr_to_ref.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
// run-rustfix
2+
3+
#![feature(custom_inner_attributes)]
14
#![warn(clippy::transmute_ptr_to_ref)]
5+
#![allow(clippy::match_single_binding)]
26

37
unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
48
let _: &T = std::mem::transmute(p);
@@ -23,7 +27,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
2327
let _: &T = &*(om as *const T);
2428
}
2529

26-
fn issue1231() {
30+
fn _issue1231() {
2731
struct Foo<'a, T> {
2832
bar: &'a T,
2933
}
@@ -38,4 +42,37 @@ fn issue1231() {
3842
unsafe { std::mem::transmute::<_, Bar>(raw) };
3943
}
4044

45+
unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
46+
match 0 {
47+
0 => std::mem::transmute(x),
48+
1 => std::mem::transmute(y),
49+
2 => std::mem::transmute::<_, &&'b u32>(x),
50+
_ => std::mem::transmute::<_, &&'b u32>(y),
51+
}
52+
}
53+
54+
unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
55+
#![clippy::msrv = "1.38"]
56+
let a = 0u32;
57+
let a = &a as *const u32;
58+
let _: &u32 = std::mem::transmute(a);
59+
let _: &u32 = std::mem::transmute::<_, &u32>(a);
60+
match 0 {
61+
0 => std::mem::transmute(x),
62+
_ => std::mem::transmute::<_, &&'b u32>(x),
63+
}
64+
}
65+
66+
unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
67+
#![clippy::msrv = "1.37"]
68+
let a = 0u32;
69+
let a = &a as *const u32;
70+
let _: &u32 = std::mem::transmute(a);
71+
let _: &u32 = std::mem::transmute::<_, &u32>(a);
72+
match 0 {
73+
0 => std::mem::transmute(x),
74+
_ => std::mem::transmute::<_, &&'b u32>(x),
75+
}
76+
}
77+
4178
fn main() {}

0 commit comments

Comments
 (0)