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