@@ -7,8 +7,10 @@ use rustc_ast::ast;
7
7
use rustc_errors:: Applicability ;
8
8
use rustc_hir:: { Expr , ExprKind , GenericArg , Mutability , QPath , TyKind , UnOp } ;
9
9
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 ;
11
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
+ use rustc_typeck:: check:: { cast:: CastCheck , FnCtxt , Inherited } ;
12
14
use std:: borrow:: Cow ;
13
15
14
16
declare_clippy_lint ! {
@@ -624,7 +626,21 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
624
626
) ;
625
627
}
626
628
} ,
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
+ } ,
628
644
}
629
645
}
630
646
}
@@ -671,3 +687,57 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'
671
687
false
672
688
}
673
689
}
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