@@ -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
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
+ use rustc_span:: DUMMY_SP ;
13
+ use rustc_typeck:: check:: { cast:: CastCheck , FnCtxt , Inherited } ;
12
14
use std:: borrow:: Cow ;
13
15
14
16
declare_clippy_lint ! {
@@ -48,6 +50,31 @@ declare_clippy_lint! {
48
50
"transmutes that have the same to and from types or could be a cast/coercion"
49
51
}
50
52
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
65
+ /// # let p: *const [i32] = &[];
66
+ /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
67
+ /// ```
68
+ /// Use instead:
69
+ /// ```rust
70
+ /// # let p: *const [i32] = &[];
71
+ /// p as *const [u16];
72
+ /// ```
73
+ pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
74
+ complexity,
75
+ "transmutes that could be a pointer cast"
76
+ }
77
+
51
78
declare_clippy_lint ! {
52
79
/// **What it does:** Checks for transmutes between a type `T` and `*T`.
53
80
///
@@ -269,6 +296,7 @@ declare_clippy_lint! {
269
296
correctness,
270
297
"transmute between collections of layout-incompatible types"
271
298
}
299
+
272
300
declare_lint_pass ! ( Transmute => [
273
301
CROSSPOINTER_TRANSMUTE ,
274
302
TRANSMUTE_PTR_TO_REF ,
@@ -281,6 +309,7 @@ declare_lint_pass!(Transmute => [
281
309
TRANSMUTE_INT_TO_FLOAT ,
282
310
TRANSMUTE_FLOAT_TO_INT ,
283
311
UNSOUND_COLLECTION_TRANSMUTE ,
312
+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
284
313
] ) ;
285
314
286
315
// used to check for UNSOUND_COLLECTION_TRANSMUTE
@@ -601,7 +630,25 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
601
630
) ;
602
631
}
603
632
} ,
604
- _ => return ,
633
+ ( _, _) if can_be_expressed_as_pointer_cast( cx, e, from_ty, to_ty) => span_lint_and_then(
634
+ cx,
635
+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS ,
636
+ e. span,
637
+ & format!(
638
+ "transmute from `{}` to `{}` which could be expressed as a pointer cast instead" ,
639
+ from_ty,
640
+ to_ty
641
+ ) ,
642
+ |diag| {
643
+ if let Some ( arg) = sugg:: Sugg :: hir_opt( cx, & args[ 0 ] ) {
644
+ let sugg = arg. as_ty( & to_ty. to_string( ) ) . to_string( ) ;
645
+ diag. span_suggestion( e. span, "try" , sugg, Applicability :: MachineApplicable ) ;
646
+ }
647
+ }
648
+ ) ,
649
+ _ => {
650
+ return ;
651
+ } ,
605
652
}
606
653
}
607
654
}
@@ -648,3 +695,59 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'
648
695
false
649
696
}
650
697
}
698
+
699
+ /// Check if the type conversion can be expressed as a pointer cast, instead of
700
+ /// a transmute. In certain cases, including some invalid casts from array
701
+ /// references to pointers, this may cause additional errors to be emitted and/or
702
+ /// ICE error messages. This function will panic if that occurs.
703
+ fn can_be_expressed_as_pointer_cast < ' tcx > (
704
+ cx : & LateContext < ' tcx > ,
705
+ e : & ' tcx Expr < ' _ > ,
706
+ from_ty : Ty < ' tcx > ,
707
+ to_ty : Ty < ' tcx > ,
708
+ ) -> bool {
709
+ use CastKind :: { AddrPtrCast , ArrayPtrCast , FnPtrAddrCast , FnPtrPtrCast , PtrAddrCast , PtrPtrCast } ;
710
+ matches ! (
711
+ check_cast( cx, e, from_ty, to_ty) ,
712
+ Some ( PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast )
713
+ )
714
+ }
715
+
716
+ /// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
717
+ /// the cast. In certain cases, including some invalid casts from array references
718
+ /// to pointers, this may cause additional errors to be emitted and/or ICE error
719
+ /// messages. This function will panic if that occurs.
720
+ fn check_cast < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > , from_ty : Ty < ' tcx > , to_ty : Ty < ' tcx > ) -> Option < CastKind > {
721
+ let hir_id = e. hir_id ;
722
+ let local_def_id = hir_id. owner ;
723
+
724
+ Inherited :: build ( cx. tcx , local_def_id) . enter ( |inherited| {
725
+ let fn_ctxt = FnCtxt :: new ( & inherited, cx. param_env , hir_id) ;
726
+
727
+ // If we already have errors, we can't be sure we can pointer cast.
728
+ assert ! (
729
+ !fn_ctxt. errors_reported_since_creation( ) ,
730
+ "Newly created FnCtxt contained errors"
731
+ ) ;
732
+
733
+ if let Ok ( check) = CastCheck :: new (
734
+ & fn_ctxt, e, from_ty, to_ty,
735
+ // We won't show any error to the user, so we don't care what the span is here.
736
+ DUMMY_SP , DUMMY_SP ,
737
+ ) {
738
+ let res = check. do_check ( & fn_ctxt) ;
739
+
740
+ // do_check's documentation says that it might return Ok and create
741
+ // errors in the fcx instead of returing Err in some cases. Those cases
742
+ // should be filtered out before getting here.
743
+ assert ! (
744
+ !fn_ctxt. errors_reported_since_creation( ) ,
745
+ "`fn_ctxt` contained errors after cast check!"
746
+ ) ;
747
+
748
+ res. ok ( )
749
+ } else {
750
+ None
751
+ }
752
+ } )
753
+ }
0 commit comments