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