@@ -7,8 +7,10 @@ use rustc_ast::ast;
77use rustc_errors:: Applicability ;
88use rustc_hir:: { Expr , ExprKind , GenericArg , Mutability , QPath , TyKind , UnOp } ;
99use rustc_lint:: { LateContext , LateLintPass } ;
10- use rustc_middle:: ty:: { self , Ty } ;
10+ use rustc_middle:: ty:: { self , cast :: CastKind , Ty } ;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12+ use rustc_span:: DUMMY_SP ;
13+ use rustc_typeck:: check:: { cast:: CastCheck , FnCtxt , Inherited } ;
1214use std:: borrow:: Cow ;
1315
1416declare_clippy_lint ! {
@@ -48,6 +50,29 @@ declare_clippy_lint! {
4850 "transmutes that have the same to and from types or could be a cast/coercion"
4951}
5052
53+ // FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
54+ declare_clippy_lint ! {
55+ /// **What it does:**Checks for transmutes that could be a pointer cast.
56+ ///
57+ /// **Why is this bad?** Readability. The code tricks people into thinking that
58+ /// something complex is going on.
59+ ///
60+ /// **Known problems:** None.
61+ ///
62+ /// **Example:**
63+ ///
64+ /// ```rust,ignore
65+ /// core::intrinsics::transmute::<*const [i32], *const [u16]>(p)
66+ /// ```
67+ /// Use instead:
68+ /// ```rust
69+ /// p as *const [u16]
70+ /// ```
71+ pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
72+ complexity,
73+ "transmutes that could be a pointer cast"
74+ }
75+
5176declare_clippy_lint ! {
5277 /// **What it does:** Checks for transmutes between a type `T` and `*T`.
5378 ///
@@ -269,6 +294,7 @@ declare_clippy_lint! {
269294 correctness,
270295 "transmute between collections of layout-incompatible types"
271296}
297+
272298declare_lint_pass ! ( Transmute => [
273299 CROSSPOINTER_TRANSMUTE ,
274300 TRANSMUTE_PTR_TO_REF ,
@@ -281,6 +307,7 @@ declare_lint_pass!(Transmute => [
281307 TRANSMUTE_INT_TO_FLOAT ,
282308 TRANSMUTE_FLOAT_TO_INT ,
283309 UNSOUND_COLLECTION_TRANSMUTE ,
310+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
284311] ) ;
285312
286313// used to check for UNSOUND_COLLECTION_TRANSMUTE
@@ -601,7 +628,25 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
601628 ) ;
602629 }
603630 } ,
604- _ => return ,
631+ ( _, _) if can_be_expressed_as_pointer_cast( cx, e, from_ty, to_ty) => span_lint_and_then(
632+ cx,
633+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
634+ e. span,
635+ & format!(
636+ "transmute from `{}` to `{}` which could be expressed as a pointer cast instead" ,
637+ from_ty,
638+ to_ty
639+ ) ,
640+ |diag| {
641+ if let Some ( arg) = sugg:: Sugg :: hir_opt( cx, & args[ 0 ] ) {
642+ let sugg = arg. as_ty( & to_ty. to_string( ) ) . to_string( ) ;
643+ diag. span_suggestion( e. span, "try" , sugg, Applicability :: MachineApplicable ) ;
644+ }
645+ }
646+ ) ,
647+ _ => {
648+ return ;
649+ } ,
605650 }
606651 }
607652 }
@@ -648,3 +693,59 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'
648693 false
649694 }
650695}
696+
697+ /// Check if the type conversion can be expressed as a pointer cast, instead of
698+ /// a transmute. In certain cases, including some invalid casts from array
699+ /// references to pointers, this may cause additional errors to be emitted and/or
700+ /// ICE error messages. This function will panic if that occurs.
701+ fn can_be_expressed_as_pointer_cast < ' tcx > (
702+ cx : & LateContext < ' tcx > ,
703+ e : & ' tcx Expr < ' _ > ,
704+ from_ty : Ty < ' tcx > ,
705+ to_ty : Ty < ' tcx > ,
706+ ) -> bool {
707+ use CastKind :: * ;
708+ matches ! (
709+ check_cast( cx, e, from_ty, to_ty) ,
710+ Some ( PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast )
711+ )
712+ }
713+
714+ /// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of
715+ /// the cast. In certain cases, including some invalid casts from array references
716+ /// to pointers, this may cause additional errors to be emitted and/or ICE error
717+ /// messages. This function will panic if that occurs.
718+ fn check_cast < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > , from_ty : Ty < ' tcx > , to_ty : Ty < ' tcx > ) -> Option < CastKind > {
719+ let hir_id = e. hir_id ;
720+ let local_def_id = hir_id. owner ;
721+
722+ Inherited :: build ( cx. tcx , local_def_id) . enter ( |inherited| {
723+ let fn_ctxt = FnCtxt :: new ( & inherited, cx. param_env , hir_id) ;
724+
725+ // If we already have errors, we can't be sure we can pointer cast.
726+ assert ! (
727+ !fn_ctxt. errors_reported_since_creation( ) ,
728+ "Newly created FnCtxt contained errors"
729+ ) ;
730+
731+ if let Ok ( check) = CastCheck :: new (
732+ & fn_ctxt, e, from_ty, to_ty,
733+ // We won't show any error to the user, so we don't care what the span is here.
734+ DUMMY_SP , DUMMY_SP ,
735+ ) {
736+ let res = check. do_check ( & fn_ctxt) ;
737+
738+ // do_check's documentation says that it might return Ok and create
739+ // errors in the fcx instead of returing Err in some cases. Those cases
740+ // should be filtered out before getting here.
741+ assert ! (
742+ !fn_ctxt. errors_reported_since_creation( ) ,
743+ "`fn_ctxt` contained errors after cast check!"
744+ ) ;
745+
746+ res. ok ( )
747+ } else {
748+ None
749+ }
750+ } )
751+ }
0 commit comments