Skip to content

Commit 069f851

Browse files
committed
initial compiling version of TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS
1 parent 5e84b8c commit 069f851

File tree

1 file changed

+72
-2
lines changed

1 file changed

+72
-2
lines changed

clippy_lints/src/transmute.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ use rustc_ast::ast;
77
use rustc_errors::Applicability;
88
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
99
use rustc_lint::{LateContext, LateLintPass};
10-
use rustc_middle::ty::{self, Ty};
10+
use rustc_middle::ty::{self, cast::CastKind, Ty};
11+
use rustc_span::DUMMY_SP;
1112
use rustc_session::{declare_lint_pass, declare_tool_lint};
13+
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
1214
use std::borrow::Cow;
1315

1416
declare_clippy_lint! {
@@ -624,7 +626,21 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
624626
);
625627
}
626628
},
627-
_ => return,
629+
(_, _) if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) => {
630+
span_lint(
631+
cx,
632+
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
633+
e.span,
634+
&format!(
635+
"transmute from `{}` to `{}` which could be expressed as a pointer cast instead",
636+
from_ty,
637+
to_ty
638+
)
639+
);
640+
},
641+
_ => {
642+
return
643+
},
628644
}
629645
}
630646
}
@@ -671,3 +687,57 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'
671687
false
672688
}
673689
}
690+
691+
/// Check if the the type conversion can be expressed as a pointer cast, instead of a transmute.
692+
fn can_be_expressed_as_pointer_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
693+
use CastKind::*;
694+
matches!(
695+
check_cast(cx, e, from_ty, to_ty),
696+
Some(
697+
PtrPtrCast
698+
| PtrAddrCast
699+
| AddrPtrCast
700+
| ArrayPtrCast
701+
| FnPtrPtrCast
702+
| FnPtrAddrCast
703+
)
704+
)
705+
}
706+
707+
/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of the cast.
708+
fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
709+
let hir_id = e.hir_id;
710+
let local_def_id = hir_id.owner;
711+
712+
Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
713+
let fn_ctxt = FnCtxt::new(
714+
&inherited,
715+
// TODO should we try to get the correct ParamEnv?
716+
ty::ParamEnv::empty(),
717+
hir_id
718+
);
719+
720+
// If we already have errors, we can't be sure we can pointer cast.
721+
if fn_ctxt.errors_reported_since_creation() {
722+
return None;
723+
}
724+
725+
if let Ok(check) = CastCheck::new(
726+
&fn_ctxt,
727+
e,
728+
from_ty,
729+
to_ty,
730+
// We won't show any error to the user, so we don't care what the span is here.
731+
DUMMY_SP,
732+
DUMMY_SP,
733+
) {
734+
check.do_check(&fn_ctxt)
735+
.ok()
736+
// do_check's documentation says that it might return Ok and create
737+
// errors in the fcx instead of returing Err in some cases.
738+
.filter(|_| !fn_ctxt.errors_reported_since_creation())
739+
} else {
740+
None
741+
}
742+
})
743+
}

0 commit comments

Comments
 (0)