]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/transmute/mod.rs
5f3e98144f42ddfb5ccf9ce5021e652e0369a08d
[rust.git] / src / tools / clippy / clippy_lints / src / transmute / mod.rs
1 mod crosspointer_transmute;
2 mod transmute_float_to_int;
3 mod transmute_int_to_bool;
4 mod transmute_int_to_char;
5 mod transmute_int_to_float;
6 mod transmute_num_to_bytes;
7 mod transmute_ptr_to_ptr;
8 mod transmute_ptr_to_ref;
9 mod transmute_ref_to_ref;
10 mod transmute_undefined_repr;
11 mod transmutes_expressible_as_ptr_casts;
12 mod unsound_collection_transmute;
13 mod useless_transmute;
14 mod utils;
15 mod wrong_transmute;
16
17 use clippy_utils::in_constant;
18 use if_chain::if_chain;
19 use rustc_hir::{Expr, ExprKind, QPath};
20 use rustc_lint::{LateContext, LateLintPass};
21 use rustc_semver::RustcVersion;
22 use rustc_session::{declare_tool_lint, impl_lint_pass};
23 use rustc_span::symbol::sym;
24
25 declare_clippy_lint! {
26     /// ### What it does
27     /// Checks for transmutes that can't ever be correct on any
28     /// architecture.
29     ///
30     /// ### Why is this bad?
31     /// It's basically guaranteed to be undefined behavior.
32     ///
33     /// ### Known problems
34     /// When accessing C, users might want to store pointer
35     /// sized objects in `extradata` arguments to save an allocation.
36     ///
37     /// ### Example
38     /// ```ignore
39     /// let ptr: *const T = core::intrinsics::transmute('x')
40     /// ```
41     #[clippy::version = "pre 1.29.0"]
42     pub WRONG_TRANSMUTE,
43     correctness,
44     "transmutes that are confusing at best, undefined behavior at worst and always useless"
45 }
46
47 // FIXME: Move this to `complexity` again, after #5343 is fixed
48 declare_clippy_lint! {
49     /// ### What it does
50     /// Checks for transmutes to the original type of the object
51     /// and transmutes that could be a cast.
52     ///
53     /// ### Why is this bad?
54     /// Readability. The code tricks people into thinking that
55     /// something complex is going on.
56     ///
57     /// ### Example
58     /// ```rust,ignore
59     /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
60     /// ```
61     #[clippy::version = "pre 1.29.0"]
62     pub USELESS_TRANSMUTE,
63     complexity,
64     "transmutes that have the same to and from types or could be a cast/coercion"
65 }
66
67 // FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
68 declare_clippy_lint! {
69     /// ### What it does
70     ///Checks for transmutes that could be a pointer cast.
71     ///
72     /// ### Why is this bad?
73     /// Readability. The code tricks people into thinking that
74     /// something complex is going on.
75     ///
76     /// ### Example
77     ///
78     /// ```rust
79     /// # let p: *const [i32] = &[];
80     /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
81     /// ```
82     /// Use instead:
83     /// ```rust
84     /// # let p: *const [i32] = &[];
85     /// p as *const [u16];
86     /// ```
87     #[clippy::version = "1.47.0"]
88     pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
89     complexity,
90     "transmutes that could be a pointer cast"
91 }
92
93 declare_clippy_lint! {
94     /// ### What it does
95     /// Checks for transmutes between a type `T` and `*T`.
96     ///
97     /// ### Why is this bad?
98     /// It's easy to mistakenly transmute between a type and a
99     /// pointer to that type.
100     ///
101     /// ### Example
102     /// ```rust,ignore
103     /// core::intrinsics::transmute(t) // where the result type is the same as
104     ///                                // `*t` or `&t`'s
105     /// ```
106     #[clippy::version = "pre 1.29.0"]
107     pub CROSSPOINTER_TRANSMUTE,
108     complexity,
109     "transmutes that have to or from types that are a pointer to the other"
110 }
111
112 declare_clippy_lint! {
113     /// ### What it does
114     /// Checks for transmutes from a pointer to a reference.
115     ///
116     /// ### Why is this bad?
117     /// This can always be rewritten with `&` and `*`.
118     ///
119     /// ### Known problems
120     /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
121     /// while dereferencing raw pointer is not stable yet.
122     /// If you need to do this in those places,
123     /// you would have to use `transmute` instead.
124     ///
125     /// ### Example
126     /// ```rust,ignore
127     /// unsafe {
128     ///     let _: &T = std::mem::transmute(p); // where p: *const T
129     /// }
130     ///
131     /// // can be written:
132     /// let _: &T = &*p;
133     /// ```
134     #[clippy::version = "pre 1.29.0"]
135     pub TRANSMUTE_PTR_TO_REF,
136     complexity,
137     "transmutes from a pointer to a reference type"
138 }
139
140 declare_clippy_lint! {
141     /// ### What it does
142     /// Checks for transmutes from an integer to a `char`.
143     ///
144     /// ### Why is this bad?
145     /// Not every integer is a Unicode scalar value.
146     ///
147     /// ### Known problems
148     /// - [`from_u32`] which this lint suggests using is slower than `transmute`
149     /// as it needs to validate the input.
150     /// If you are certain that the input is always a valid Unicode scalar value,
151     /// use [`from_u32_unchecked`] which is as fast as `transmute`
152     /// but has a semantically meaningful name.
153     /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
154     ///
155     /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
156     /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
157     ///
158     /// ### Example
159     /// ```rust
160     /// let x = 1_u32;
161     /// unsafe {
162     ///     let _: char = std::mem::transmute(x); // where x: u32
163     /// }
164     ///
165     /// // should be:
166     /// let _ = std::char::from_u32(x).unwrap();
167     /// ```
168     #[clippy::version = "pre 1.29.0"]
169     pub TRANSMUTE_INT_TO_CHAR,
170     complexity,
171     "transmutes from an integer to a `char`"
172 }
173
174 declare_clippy_lint! {
175     /// ### What it does
176     /// Checks for transmutes from a `&[u8]` to a `&str`.
177     ///
178     /// ### Why is this bad?
179     /// Not every byte slice is a valid UTF-8 string.
180     ///
181     /// ### Known problems
182     /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
183     /// as it needs to validate the input.
184     /// If you are certain that the input is always a valid UTF-8,
185     /// use [`from_utf8_unchecked`] which is as fast as `transmute`
186     /// but has a semantically meaningful name.
187     /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
188     ///
189     /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
190     /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
191     ///
192     /// ### Example
193     /// ```rust
194     /// let b: &[u8] = &[1_u8, 2_u8];
195     /// unsafe {
196     ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
197     /// }
198     ///
199     /// // should be:
200     /// let _ = std::str::from_utf8(b).unwrap();
201     /// ```
202     #[clippy::version = "pre 1.29.0"]
203     pub TRANSMUTE_BYTES_TO_STR,
204     complexity,
205     "transmutes from a `&[u8]` to a `&str`"
206 }
207
208 declare_clippy_lint! {
209     /// ### What it does
210     /// Checks for transmutes from an integer to a `bool`.
211     ///
212     /// ### Why is this bad?
213     /// This might result in an invalid in-memory representation of a `bool`.
214     ///
215     /// ### Example
216     /// ```rust
217     /// let x = 1_u8;
218     /// unsafe {
219     ///     let _: bool = std::mem::transmute(x); // where x: u8
220     /// }
221     ///
222     /// // should be:
223     /// let _: bool = x != 0;
224     /// ```
225     #[clippy::version = "pre 1.29.0"]
226     pub TRANSMUTE_INT_TO_BOOL,
227     complexity,
228     "transmutes from an integer to a `bool`"
229 }
230
231 declare_clippy_lint! {
232     /// ### What it does
233     /// Checks for transmutes from an integer to a float.
234     ///
235     /// ### Why is this bad?
236     /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
237     /// and safe.
238     ///
239     /// ### Example
240     /// ```rust
241     /// unsafe {
242     ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
243     /// }
244     ///
245     /// // should be:
246     /// let _: f32 = f32::from_bits(1_u32);
247     /// ```
248     #[clippy::version = "pre 1.29.0"]
249     pub TRANSMUTE_INT_TO_FLOAT,
250     complexity,
251     "transmutes from an integer to a float"
252 }
253
254 declare_clippy_lint! {
255     /// ### What it does
256     /// Checks for transmutes from a float to an integer.
257     ///
258     /// ### Why is this bad?
259     /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
260     /// and safe.
261     ///
262     /// ### Example
263     /// ```rust
264     /// unsafe {
265     ///     let _: u32 = std::mem::transmute(1f32);
266     /// }
267     ///
268     /// // should be:
269     /// let _: u32 = 1f32.to_bits();
270     /// ```
271     #[clippy::version = "1.41.0"]
272     pub TRANSMUTE_FLOAT_TO_INT,
273     complexity,
274     "transmutes from a float to an integer"
275 }
276
277 declare_clippy_lint! {
278     /// ### What it does
279     /// Checks for transmutes from a number to an array of `u8`
280     ///
281     /// ### Why this is bad?
282     /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
283     /// is intuitive and safe.
284     ///
285     /// ### Example
286     /// ```rust
287     /// unsafe {
288     ///     let x: [u8; 8] = std::mem::transmute(1i64);
289     /// }
290     ///
291     /// // should be
292     /// let x: [u8; 8] = 0i64.to_ne_bytes();
293     /// ```
294     #[clippy::version = "1.58.0"]
295     pub TRANSMUTE_NUM_TO_BYTES,
296     complexity,
297     "transmutes from a number to an array of `u8`"
298 }
299
300 declare_clippy_lint! {
301     /// ### What it does
302     /// Checks for transmutes from a pointer to a pointer, or
303     /// from a reference to a reference.
304     ///
305     /// ### Why is this bad?
306     /// Transmutes are dangerous, and these can instead be
307     /// written as casts.
308     ///
309     /// ### Example
310     /// ```rust
311     /// let ptr = &1u32 as *const u32;
312     /// unsafe {
313     ///     // pointer-to-pointer transmute
314     ///     let _: *const f32 = std::mem::transmute(ptr);
315     ///     // ref-ref transmute
316     ///     let _: &f32 = std::mem::transmute(&1u32);
317     /// }
318     /// // These can be respectively written:
319     /// let _ = ptr as *const f32;
320     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
321     /// ```
322     #[clippy::version = "pre 1.29.0"]
323     pub TRANSMUTE_PTR_TO_PTR,
324     pedantic,
325     "transmutes from a pointer to a pointer / a reference to a reference"
326 }
327
328 declare_clippy_lint! {
329     /// ### What it does
330     /// Checks for transmutes between collections whose
331     /// types have different ABI, size or alignment.
332     ///
333     /// ### Why is this bad?
334     /// This is undefined behavior.
335     ///
336     /// ### Known problems
337     /// Currently, we cannot know whether a type is a
338     /// collection, so we just lint the ones that come with `std`.
339     ///
340     /// ### Example
341     /// ```rust
342     /// // different size, therefore likely out-of-bounds memory access
343     /// // You absolutely do not want this in your code!
344     /// unsafe {
345     ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
346     /// };
347     /// ```
348     ///
349     /// You must always iterate, map and collect the values:
350     ///
351     /// ```rust
352     /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
353     /// ```
354     #[clippy::version = "1.40.0"]
355     pub UNSOUND_COLLECTION_TRANSMUTE,
356     correctness,
357     "transmute between collections of layout-incompatible types"
358 }
359
360 declare_clippy_lint! {
361     /// ### What it does
362     /// Checks for transmutes between types which do not have a representation defined relative to
363     /// each other.
364     ///
365     /// ### Why is this bad?
366     /// The results of such a transmute are not defined.
367     ///
368     /// ### Known problems
369     /// This lint has had multiple problems in the past and was moved to `nursery`. See issue
370     /// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
371     ///
372     /// ### Example
373     /// ```rust
374     /// struct Foo<T>(u32, T);
375     /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
376     /// ```
377     /// Use instead:
378     /// ```rust
379     /// #[repr(C)]
380     /// struct Foo<T>(u32, T);
381     /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
382     /// ```
383     #[clippy::version = "1.60.0"]
384     pub TRANSMUTE_UNDEFINED_REPR,
385     nursery,
386     "transmute to or from a type with an undefined representation"
387 }
388
389 pub struct Transmute {
390     msrv: Option<RustcVersion>,
391 }
392 impl_lint_pass!(Transmute => [
393     CROSSPOINTER_TRANSMUTE,
394     TRANSMUTE_PTR_TO_REF,
395     TRANSMUTE_PTR_TO_PTR,
396     USELESS_TRANSMUTE,
397     WRONG_TRANSMUTE,
398     TRANSMUTE_INT_TO_CHAR,
399     TRANSMUTE_BYTES_TO_STR,
400     TRANSMUTE_INT_TO_BOOL,
401     TRANSMUTE_INT_TO_FLOAT,
402     TRANSMUTE_FLOAT_TO_INT,
403     TRANSMUTE_NUM_TO_BYTES,
404     UNSOUND_COLLECTION_TRANSMUTE,
405     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
406     TRANSMUTE_UNDEFINED_REPR,
407 ]);
408 impl Transmute {
409     #[must_use]
410     pub fn new(msrv: Option<RustcVersion>) -> Self {
411         Self { msrv }
412     }
413 }
414 impl<'tcx> LateLintPass<'tcx> for Transmute {
415     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
416         if_chain! {
417             if let ExprKind::Call(path_expr, [arg]) = e.kind;
418             if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
419             if let Some(def_id) = path.res.opt_def_id();
420             if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
421             then {
422                 // Avoid suggesting non-const operations in const contexts:
423                 // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
424                 // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
425                 // - char conversions (https://github.com/rust-lang/rust/issues/89259)
426                 let const_context = in_constant(cx, e.hir_id);
427
428                 let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
429                 // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
430                 let to_ty = cx.typeck_results().expr_ty(e);
431
432                 // If useless_transmute is triggered, the other lints can be skipped.
433                 if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
434                     return;
435                 }
436
437                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
438                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
439                     | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
440                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
441                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
442                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
443                     | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
444                     | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
445                     | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
446                     | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
447                     | (
448                         unsound_collection_transmute::check(cx, e, from_ty, to_ty)
449                         || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
450                     );
451
452                 if !linted {
453                     transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
454                 }
455             }
456         }
457     }
458
459     extract_msrv_attr!(LateContext);
460 }