Skip to content

Commit 1ab7f36

Browse files
authored
Merge pull request #2103 from pizzaiter/fix-2099
Add some more lints for transmute
2 parents 6f36f21 + 47df717 commit 1ab7f36

File tree

4 files changed

+165
-1
lines changed

4 files changed

+165
-1
lines changed

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
544544
transmute::TRANSMUTE_PTR_TO_REF,
545545
transmute::USELESS_TRANSMUTE,
546546
transmute::WRONG_TRANSMUTE,
547+
transmute::TRANSMUTE_INT_TO_CHAR,
548+
transmute::TRANSMUTE_INT_TO_BOOL,
549+
transmute::TRANSMUTE_INT_TO_FLOAT,
547550
types::ABSURD_EXTREME_COMPARISONS,
548551
types::BORROWED_BOX,
549552
types::BOX_VEC,

clippy_lints/src/transmute.rs

+109-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use rustc::lint::*;
22
use rustc::ty::{self, Ty};
33
use rustc::hir::*;
4+
use std::borrow::Cow;
5+
use syntax::ast;
46
use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
57
use utils::{sugg, opt_def_id};
68

@@ -76,11 +78,73 @@ declare_lint! {
7678
"transmutes from a pointer to a reference type"
7779
}
7880

81+
/// **What it does:** Checks for transmutes from an integer to a `char`.
82+
///
83+
/// **Why is this bad?** Not every integer is a unicode scalar value.
84+
///
85+
/// **Known problems:** None.
86+
///
87+
/// **Example:**
88+
/// ```rust
89+
/// let _: char = std::mem::transmute(x); // where x: u32
90+
/// // should be:
91+
/// let _: Option<char> = std::char::from_u32(x);
92+
/// ```
93+
declare_lint! {
94+
pub TRANSMUTE_INT_TO_CHAR,
95+
Warn,
96+
"transmutes from an integer to a `char`"
97+
}
98+
99+
/// **What it does:** Checks for transmutes from an integer to a `bool`.
100+
///
101+
/// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
102+
///
103+
/// **Known problems:** None.
104+
///
105+
/// **Example:**
106+
/// ```rust
107+
/// let _: bool = std::mem::transmute(x); // where x: u8
108+
/// // should be:
109+
/// let _: bool = x != 0;
110+
/// ```
111+
declare_lint! {
112+
pub TRANSMUTE_INT_TO_BOOL,
113+
Warn,
114+
"transmutes from an integer to a `bool`"
115+
}
116+
117+
/// **What it does:** Checks for transmutes from an integer to a float.
118+
///
119+
/// **Why is this bad?** This might result in an invalid in-memory representation of a float.
120+
///
121+
/// **Known problems:** None.
122+
///
123+
/// **Example:**
124+
/// ```rust
125+
/// let _: f32 = std::mem::transmute(x); // where x: u32
126+
/// // should be:
127+
/// let _: f32 = f32::from_bits(x);
128+
/// ```
129+
declare_lint! {
130+
pub TRANSMUTE_INT_TO_FLOAT,
131+
Warn,
132+
"transmutes from an integer to a float"
133+
}
134+
79135
pub struct Transmute;
80136

81137
impl LintPass for Transmute {
82138
fn get_lints(&self) -> LintArray {
83-
lint_array!(CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE)
139+
lint_array!(
140+
CROSSPOINTER_TRANSMUTE,
141+
TRANSMUTE_PTR_TO_REF,
142+
USELESS_TRANSMUTE,
143+
WRONG_TRANSMUTE,
144+
TRANSMUTE_INT_TO_CHAR,
145+
TRANSMUTE_INT_TO_BOOL,
146+
TRANSMUTE_INT_TO_FLOAT
147+
)
84148
}
85149
}
86150

@@ -177,6 +241,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
177241
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
178242
},
179243
),
244+
(&ty::TyInt(ast::IntTy::I32), &ty::TyChar) |
245+
(&ty::TyUint(ast::UintTy::U32), &ty::TyChar) => span_lint_and_then(
246+
cx,
247+
TRANSMUTE_INT_TO_CHAR,
248+
e.span,
249+
&format!("transmute from a `{}` to a `char`", from_ty),
250+
|db| {
251+
let arg = sugg::Sugg::hir(cx, &args[0], "..");
252+
let arg = if let ty::TyInt(_) = from_ty.sty {
253+
arg.as_ty(ty::TyUint(ast::UintTy::U32))
254+
} else {
255+
arg
256+
};
257+
db.span_suggestion(e.span, "consider using", format!("std::char::from_u32({})", arg.to_string()));
258+
}
259+
),
260+
(&ty::TyInt(ast::IntTy::I8), &ty::TyBool) |
261+
(&ty::TyUint(ast::UintTy::U8), &ty::TyBool) => span_lint_and_then(
262+
cx,
263+
TRANSMUTE_INT_TO_BOOL,
264+
e.span,
265+
&format!("transmute from a `{}` to a `bool`", from_ty),
266+
|db| {
267+
let arg = sugg::Sugg::hir(cx, &args[0], "..");
268+
let zero = sugg::Sugg::NonParen(Cow::from("0"));
269+
db.span_suggestion(e.span, "consider using", sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string());
270+
}
271+
),
272+
(&ty::TyInt(_), &ty::TyFloat(_)) |
273+
(&ty::TyUint(_), &ty::TyFloat(_)) => span_lint_and_then(
274+
cx,
275+
TRANSMUTE_INT_TO_FLOAT,
276+
e.span,
277+
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
278+
|db| {
279+
let arg = sugg::Sugg::hir(cx, &args[0], "..");
280+
let arg = if let ty::TyInt(int_ty) = from_ty.sty {
281+
arg.as_ty(format!("u{}", int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())))
282+
} else {
283+
arg
284+
};
285+
db.span_suggestion(e.span, "consider using", format!("{}::from_bits({})", to_ty, arg.to_string()));
286+
}
287+
),
180288
_ => return,
181289
};
182290
}

tests/ui/transmute.rs

+17
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,21 @@ fn crosspointer() {
118118
}
119119
}
120120

121+
#[warn(transmute_int_to_char)]
122+
fn int_to_char() {
123+
let _: char = unsafe { std::mem::transmute(0_u32) };
124+
let _: char = unsafe { std::mem::transmute(0_i32) };
125+
}
126+
127+
#[warn(transmute_int_to_bool)]
128+
fn int_to_bool() {
129+
let _: bool = unsafe { std::mem::transmute(0_u8) };
130+
}
131+
132+
#[warn(transmute_int_to_float)]
133+
fn int_to_float() {
134+
let _: f32 = unsafe { std::mem::transmute(0_u32) };
135+
let _: f32 = unsafe { std::mem::transmute(0_i32) };
136+
}
137+
121138
fn main() { }

tests/ui/transmute.stderr

+36
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,39 @@ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
154154
117 | let _: *mut Usize = core::intrinsics::transmute(my_int());
155155
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
156156

157+
error: transmute from a `u32` to a `char`
158+
--> $DIR/transmute.rs:123:28
159+
|
160+
123 | let _: char = unsafe { std::mem::transmute(0_u32) };
161+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32)`
162+
|
163+
= note: `-D transmute-int-to-char` implied by `-D warnings`
164+
165+
error: transmute from a `i32` to a `char`
166+
--> $DIR/transmute.rs:124:28
167+
|
168+
124 | let _: char = unsafe { std::mem::transmute(0_i32) };
169+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32)`
170+
171+
error: transmute from a `u8` to a `bool`
172+
--> $DIR/transmute.rs:129:28
173+
|
174+
129 | let _: bool = unsafe { std::mem::transmute(0_u8) };
175+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
176+
|
177+
= note: `-D transmute-int-to-bool` implied by `-D warnings`
178+
179+
error: transmute from a `u32` to a `f32`
180+
--> $DIR/transmute.rs:134:27
181+
|
182+
134 | let _: f32 = unsafe { std::mem::transmute(0_u32) };
183+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
184+
|
185+
= note: `-D transmute-int-to-float` implied by `-D warnings`
186+
187+
error: transmute from a `i32` to a `f32`
188+
--> $DIR/transmute.rs:135:27
189+
|
190+
135 | let _: f32 = unsafe { std::mem::transmute(0_i32) };
191+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
192+

0 commit comments

Comments
 (0)