]>
Commit | Line | Data |
---|---|---|
1 | use clippy_utils::consts::{constant_context, Constant}; | |
2 | use clippy_utils::diagnostics::span_lint; | |
3 | use clippy_utils::{is_expr_path_def_path, paths}; | |
4 | use if_chain::if_chain; | |
5 | use rustc_ast::LitKind; | |
6 | use rustc_hir::{Expr, ExprKind}; | |
7 | use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
8 | use rustc_middle::lint::in_external_macro; | |
9 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
10 | ||
11 | declare_clippy_lint! { | |
12 | /// **What it does:** Checks for transmute calls which would receive a null pointer. | |
13 | /// | |
14 | /// **Why is this bad?** Transmuting a null pointer is undefined behavior. | |
15 | /// | |
16 | /// **Known problems:** Not all cases can be detected at the moment of this writing. | |
17 | /// For example, variables which hold a null pointer and are then fed to a `transmute` | |
18 | /// call, aren't detectable yet. | |
19 | /// | |
20 | /// **Example:** | |
21 | /// ```rust | |
22 | /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; | |
23 | /// ``` | |
24 | pub TRANSMUTING_NULL, | |
25 | correctness, | |
26 | "transmutes from a null pointer to a reference, which is undefined behavior" | |
27 | } | |
28 | ||
29 | declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]); | |
30 | ||
31 | const LINT_MSG: &str = "transmuting a known null pointer into a reference"; | |
32 | ||
33 | impl<'tcx> LateLintPass<'tcx> for TransmutingNull { | |
34 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
35 | if in_external_macro(cx.sess(), expr.span) { | |
36 | return; | |
37 | } | |
38 | ||
39 | if_chain! { | |
40 | if let ExprKind::Call(func, [arg]) = expr.kind; | |
41 | if is_expr_path_def_path(cx, func, &paths::TRANSMUTE); | |
42 | ||
43 | then { | |
44 | // Catching transmute over constants that resolve to `null`. | |
45 | let mut const_eval_context = constant_context(cx, cx.typeck_results()); | |
46 | if_chain! { | |
47 | if let ExprKind::Path(ref _qpath) = arg.kind; | |
48 | let x = const_eval_context.expr(arg); | |
49 | if let Some(Constant::RawPtr(0)) = x; | |
50 | then { | |
51 | span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) | |
52 | } | |
53 | } | |
54 | ||
55 | // Catching: | |
56 | // `std::mem::transmute(0 as *const i32)` | |
57 | if_chain! { | |
58 | if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; | |
59 | if let ExprKind::Lit(ref lit) = inner_expr.kind; | |
60 | if let LitKind::Int(0, _) = lit.node; | |
61 | then { | |
62 | span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) | |
63 | } | |
64 | } | |
65 | ||
66 | // Catching: | |
67 | // `std::mem::transmute(std::ptr::null::<i32>())` | |
68 | if_chain! { | |
69 | if let ExprKind::Call(func1, []) = arg.kind; | |
70 | if is_expr_path_def_path(cx, func1, &paths::PTR_NULL); | |
71 | then { | |
72 | span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) | |
73 | } | |
74 | } | |
75 | ||
76 | // FIXME: | |
77 | // Also catch transmutations of variables which are known nulls. | |
78 | // To do this, MIR const propagation seems to be the better tool. | |
79 | // Whenever MIR const prop routines are more developed, this will | |
80 | // become available. As of this writing (25/03/19) it is not yet. | |
81 | } | |
82 | } | |
83 | } | |
84 | } |