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