]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/transmuting_null.rs
Add TransmutingNull Lint
[rust.git] / clippy_lints / src / transmuting_null.rs
1 use crate::consts::{constant_context, Constant};
2 use crate::utils::{match_qpath, span_lint};
3 use if_chain::if_chain;
4 use rustc::hir::{Expr, ExprKind};
5 use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
6 use rustc::{declare_tool_lint, lint_array};
7 use syntax::ast::LitKind;
8
9 declare_clippy_lint! {
10     /// **What it does:** Checks for transmute calls which would receive a null pointer.
11     ///
12     /// **Why is this bad?** Transmuting a null pointer is undefined behavior.
13     ///
14     /// **Known problems:** Not all cases can be detected at the moment of this writing.
15     /// For example, variables which hold a null pointer and are then fed to a `transmute`
16     /// call, aren't detectable yet.
17     ///
18     /// **Example:**
19     /// ```rust
20     /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
21     /// ```
22     pub TRANSMUTING_NULL,
23     correctness,
24     "transmutes from a null pointer to a reference, which is undefined behavior"
25 }
26
27 #[derive(Copy, Clone)]
28 pub struct Pass;
29
30 impl LintPass for Pass {
31     fn get_lints(&self) -> LintArray {
32         lint_array!(TRANSMUTING_NULL,)
33     }
34
35     fn name(&self) -> &'static str {
36         "TransmutingNull"
37     }
38 }
39
40 const LINT_MSG: &str = "transmuting a known null pointer into a reference.";
41
42 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
43     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
44         if in_external_macro(cx.sess(), expr.span) {
45             return;
46         }
47
48         if_chain! {
49             if let ExprKind::Call(ref func, ref args) = expr.node;
50             if let ExprKind::Path(ref path) = func.node;
51             if match_qpath(path, &["std", "mem", "transmute"]);
52             if args.len() == 1;
53
54             then {
55
56                 // Catching transmute over constants that resolve to `null`.
57                 let mut const_eval_context = constant_context(cx, cx.tables);
58                 if_chain! {
59                     if let ExprKind::Path(ref _qpath) = args[0].node;
60                     let x = const_eval_context.expr(&args[0]);
61                     if let Some(constant) = x;
62                     if let Constant::RawPtr(ptr_value) = constant;
63                     if ptr_value == 0;
64                     then {
65                         span_lint(
66                             cx,
67                             TRANSMUTING_NULL,
68                             expr.span,
69                             LINT_MSG)
70                     }
71                 }
72
73                 // Catching:
74                 // `std::mem::transmute(0 as *const i32)`
75                 if_chain! {
76                     if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].node;
77                     if let ExprKind::Lit(ref lit) = inner_expr.node;
78                     if let LitKind::Int(0, _) = lit.node;
79                     then {
80                         span_lint(
81                             cx,
82                             TRANSMUTING_NULL,
83                             expr.span,
84                             LINT_MSG)
85                     }
86                 }
87
88                 // Catching:
89                 // `std::mem::transmute(std::ptr::null::<i32>())`
90                 if_chain! {
91                     if let ExprKind::Call(ref func1, ref args1) = args[0].node;
92                     if let ExprKind::Path(ref path1) = func1.node;
93                     if match_qpath(path1, &["std", "ptr", "null"]);
94                     if args1.len() == 0;
95                     then {
96                         span_lint(
97                             cx,
98                             TRANSMUTING_NULL,
99                             expr.span,
100                             LINT_MSG)
101                     }
102                 }
103
104                 // FIXME:
105                 // Also catch transmutations of variables which are known nulls.
106                 // To do this, MIR const propagation seems to be the better tool.
107                 // Whenever MIR const prop routines are more developed, this will
108                 // become available. As of this writing (25/03/19) it is not yet.
109             }
110         }
111     }
112 }