@@ -190,6 +190,28 @@ declare_clippy_lint! {
190190 "transmutes from an integer to a float"
191191}
192192
193+ declare_clippy_lint ! {
194+ /// **What it does:** Checks for transmutes from a float to an integer.
195+ ///
196+ /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
197+ /// and safe.
198+ ///
199+ /// **Known problems:** None.
200+ ///
201+ /// **Example:**
202+ /// ```rust
203+ /// unsafe {
204+ /// let _: u32 = std::mem::transmute(1f32);
205+ /// }
206+ ///
207+ /// // should be:
208+ /// let _: u32 = 1f32.to_bits();
209+ /// ```
210+ pub TRANSMUTE_FLOAT_TO_INT ,
211+ nursery,
212+ "transmutes from a float to an integer"
213+ }
214+
193215declare_clippy_lint ! {
194216 /// **What it does:** Checks for transmutes from a pointer to a pointer, or
195217 /// from a reference to a reference.
@@ -254,6 +276,7 @@ declare_lint_pass!(Transmute => [
254276 TRANSMUTE_BYTES_TO_STR ,
255277 TRANSMUTE_INT_TO_BOOL ,
256278 TRANSMUTE_INT_TO_FLOAT ,
279+ TRANSMUTE_FLOAT_TO_INT ,
257280 UNSOUND_COLLECTION_TRANSMUTE ,
258281] ) ;
259282
@@ -520,6 +543,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
520543 ) ;
521544 } ,
522545 ) ,
546+ ( & ty:: Float ( float_ty) , & ty:: Int ( _) ) | ( & ty:: Float ( float_ty) , & ty:: Uint ( _) ) => span_lint_and_then(
547+ cx,
548+ TRANSMUTE_FLOAT_TO_INT ,
549+ e. span,
550+ & format!( "transmute from a `{}` to a `{}`" , from_ty, to_ty) ,
551+ |db| {
552+ let mut expr = & args[ 0 ] ;
553+ let mut arg = sugg:: Sugg :: hir( cx, expr, ".." ) ;
554+
555+ if let ExprKind :: Unary ( UnOp :: UnNeg , inner_expr) = & expr. kind {
556+ expr = & inner_expr;
557+ }
558+
559+ if_chain! {
560+ // if the expression is a float literal and it is unsuffixed then
561+ // add a suffix so the suggestion is valid and unambiguous
562+ let op = format!( "{}{}" , arg, float_ty. name_str( ) ) . into( ) ;
563+ if let ExprKind :: Lit ( lit) = & expr. kind;
564+ if let ast:: LitKind :: Float ( _, ast:: LitFloatType :: Unsuffixed ) = lit. node;
565+ then {
566+ match arg {
567+ sugg:: Sugg :: MaybeParen ( _) => arg = sugg:: Sugg :: MaybeParen ( op) ,
568+ _ => arg = sugg:: Sugg :: NonParen ( op)
569+ }
570+ }
571+ }
572+
573+ arg = sugg:: Sugg :: NonParen ( format!( "{}.to_bits()" , arg. maybe_par( ) ) . into( ) ) ;
574+
575+ // cast the result of `to_bits` if `to_ty` is signed
576+ arg = if let ty:: Int ( int_ty) = to_ty. kind {
577+ arg. as_ty( int_ty. name_str( ) . to_string( ) )
578+ } else {
579+ arg
580+ } ;
581+
582+ db. span_suggestion(
583+ e. span,
584+ "consider using" ,
585+ arg. to_string( ) ,
586+ Applicability :: Unspecified ,
587+ ) ;
588+ } ,
589+ ) ,
523590 ( & ty:: Adt ( ref from_adt, ref from_substs) , & ty:: Adt ( ref to_adt, ref to_substs) ) => {
524591 if from_adt. did != to_adt. did ||
525592 !COLLECTIONS . iter( ) . any( |path| match_def_path( cx, to_adt. did, path) ) {
0 commit comments