]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/transmute.rs
Merge pull request #1053 from oli-obk/char_float_transmute
[rust.git] / clippy_lints / src / transmute.rs
1 use rustc::lint::*;
2 use rustc::ty::TypeVariants::{TyRawPtr, TyRef};
3 use rustc::ty;
4 use rustc::hir::*;
5 use utils::{match_def_path, paths, snippet_opt, span_lint, span_lint_and_then};
6
7 /// **What it does:** This lint checks for transmutes that can't ever be correct on any architecture
8 ///
9 /// **Why is this bad?** It's basically guaranteed to be undefined behaviour
10 ///
11 /// **Known problems:** When accessing C, users might want to store pointer sized objects in `extradata` arguments to save an allocation.
12 ///
13 /// **Example:** `let ptr: *const T = core::intrinsics::transmute('x')`.
14 declare_lint! {
15     pub WRONG_TRANSMUTE,
16     Warn,
17     "transmutes that are confusing at best, undefined behaviour at worst and always useless"
18 }
19
20 /// **What it does:** This lint checks for transmutes to the original type of the object and transmutes that could be a cast.
21 ///
22 /// **Why is this bad?** Readability. The code tricks people into thinking that something complex is going on
23 ///
24 /// **Known problems:** None.
25 ///
26 /// **Example:** `core::intrinsics::transmute(t)` where the result type is the same as `t`'s.
27 declare_lint! {
28     pub USELESS_TRANSMUTE,
29     Warn,
30     "transmutes that have the same to and from types or could be a cast/coercion"
31 }
32
33 /// **What it does:*** This lint checks for transmutes between a type `T` and `*T`.
34 ///
35 /// **Why is this bad?** It's easy to mistakenly transmute between a type and a pointer to that type.
36 ///
37 /// **Known problems:** None.
38 ///
39 /// **Example:** `core::intrinsics::transmute(t)` where the result type is the same as `*t` or `&t`'s.
40 declare_lint! {
41     pub CROSSPOINTER_TRANSMUTE,
42     Warn,
43     "transmutes that have to or from types that are a pointer to the other"
44 }
45
46 /// **What it does:*** This lint checks for transmutes from a pointer to a reference.
47 ///
48 /// **Why is this bad?** This can always be rewritten with `&` and `*`.
49 ///
50 /// **Known problems:** None.
51 ///
52 /// **Example:**
53 /// ```rust
54 /// let _: &T = std::mem::transmute(p); // where p: *const T
55 /// // can be written:
56 /// let _: &T = &*p;
57 /// ```
58 declare_lint! {
59     pub TRANSMUTE_PTR_TO_REF,
60     Warn,
61     "transmutes from a pointer to a reference type"
62 }
63
64 pub struct Transmute;
65
66 impl LintPass for Transmute {
67     fn get_lints(&self) -> LintArray {
68         lint_array![CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE]
69     }
70 }
71
72 impl LateLintPass for Transmute {
73     fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
74         if let ExprCall(ref path_expr, ref args) = e.node {
75             if let ExprPath(None, _) = path_expr.node {
76                 let def_id = cx.tcx.expect_def(path_expr.id).def_id();
77
78                 if match_def_path(cx, def_id, &paths::TRANSMUTE) {
79                     let from_ty = cx.tcx.expr_ty(&args[0]);
80                     let to_ty = cx.tcx.expr_ty(e);
81
82                     match (&from_ty.sty, &to_ty.sty) {
83                         _ if from_ty == to_ty => span_lint(
84                             cx,
85                             USELESS_TRANSMUTE,
86                             e.span,
87                             &format!("transmute from a type (`{}`) to itself", from_ty),
88                         ),
89                         (&TyRef(_, rty), &TyRawPtr(ptr_ty)) => span_lint_and_then(
90                             cx,
91                             USELESS_TRANSMUTE,
92                             e.span,
93                             "transmute from a reference to a pointer",
94                             |db| {
95                                 if let Some(arg) = snippet_opt(cx, args[0].span) {
96                                     let sugg = if ptr_ty == rty {
97                                         format!("{} as {}", arg, to_ty)
98                                     } else {
99                                         format!("{} as {} as {}", arg, cx.tcx.mk_ptr(rty), to_ty)
100                                     };
101
102                                     db.span_suggestion(e.span, "try", sugg);
103                                 }
104                             },
105                         ),
106                         (&ty::TyInt(_), &TyRawPtr(_)) |
107                         (&ty::TyUint(_), &TyRawPtr(_)) => span_lint_and_then(
108                             cx,
109                             USELESS_TRANSMUTE,
110                             e.span,
111                             "transmute from an integer to a pointer",
112                             |db| {
113                                 if let Some(arg) = snippet_opt(cx, args[0].span) {
114                                     db.span_suggestion(e.span, "try", format!("{} as {}", arg, to_ty));
115                                 }
116                             },
117                         ),
118                         (&ty::TyFloat(_), &TyRef(..)) |
119                         (&ty::TyFloat(_), &TyRawPtr(_)) |
120                         (&ty::TyChar, &TyRef(..)) |
121                         (&ty::TyChar, &TyRawPtr(_)) => span_lint(
122                             cx,
123                             WRONG_TRANSMUTE,
124                             e.span,
125                             &format!("transmute from a `{}` to a pointer", from_ty),
126                         ),
127                         (&TyRawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
128                             cx,
129                             CROSSPOINTER_TRANSMUTE,
130                             e.span,
131                             &format!("transmute from a type (`{}`) to the type that it points to (`{}`)",
132                                      from_ty,
133                                      to_ty),
134                         ),
135                         (_, &TyRawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
136                             cx,
137                             CROSSPOINTER_TRANSMUTE,
138                             e.span,
139                             &format!("transmute from a type (`{}`) to a pointer to that type (`{}`)",
140                                      from_ty,
141                                      to_ty),
142                         ),
143                         (&TyRawPtr(from_pty), &TyRef(_, to_rty)) => span_lint_and_then(
144                             cx,
145                             TRANSMUTE_PTR_TO_REF,
146                             e.span,
147                             &format!("transmute from a pointer type (`{}`) to a reference type (`{}`)",
148                                     from_ty,
149                                     to_ty),
150                             |db| {
151                                 if let Some(arg) = snippet_opt(cx, args[0].span) {
152                                     let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
153                                         ("&mut *", "*mut")
154                                     } else {
155                                         ("&*", "*const")
156                                     };
157
158
159                                     let sugg = if from_pty.ty == to_rty.ty {
160                                         // Put things in parentheses if they are more complex
161                                         match args[0].node {
162                                             ExprPath(..) | ExprCall(..) | ExprMethodCall(..) | ExprBlock(..) => {
163                                                 format!("{}{}", deref, arg)
164                                             }
165                                             _ => format!("{}({})", deref, arg)
166                                         }
167                                     } else {
168                                         format!("{}({} as {} {})", deref, arg, cast, to_rty.ty)
169                                     };
170
171                                     db.span_suggestion(e.span, "try", sugg);
172                                 }
173                             },
174                         ),
175                         _ => return,
176                     };
177                 }
178             }
179         }
180     }
181 }