]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_lint/src/ptr_nulls.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / compiler / rustc_lint / src / ptr_nulls.rs
1 use crate::{lints::PtrNullChecksDiag, LateContext, LateLintPass, LintContext};
2 use rustc_ast::LitKind;
3 use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
4 use rustc_session::{declare_lint, declare_lint_pass};
5 use rustc_span::sym;
6
7 declare_lint! {
8 /// The `useless_ptr_null_checks` lint checks for useless null checks against pointers
9 /// obtained from non-null types.
10 ///
11 /// ### Example
12 ///
13 /// ```rust
14 /// # fn test() {}
15 /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */
16 /// # test;
17 ///
18 /// if (fn_ptr as *const ()).is_null() { /* ... */ }
19 /// ```
20 ///
21 /// {{produces}}
22 ///
23 /// ### Explanation
24 ///
25 /// Function pointers and references are assumed to be non-null, checking them for null
26 /// will always return false.
27 USELESS_PTR_NULL_CHECKS,
28 Warn,
29 "useless checking of non-null-typed pointer"
30 }
31
32 declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
33
34 /// This function detects and returns the original expression from a series of consecutive casts,
35 /// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`.
36 fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
37 let mut had_at_least_one_cast = false;
38 loop {
39 e = e.peel_blocks();
40 e = if let ExprKind::Cast(expr, t) = e.kind
41 && let TyKind::Ptr(_) = t.kind {
42 had_at_least_one_cast = true;
43 expr
44 } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
45 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
46 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
47 had_at_least_one_cast = true;
48 expr
49 } else if let ExprKind::Call(path, [arg]) = e.kind
50 && let ExprKind::Path(ref qpath) = path.kind
51 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
52 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) {
53 had_at_least_one_cast = true;
54 arg
55 } else if had_at_least_one_cast {
56 return Some(e);
57 } else {
58 return None;
59 };
60 }
61 }
62
63 fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> {
64 let expr = ptr_cast_chain(cx, expr)?;
65
66 let orig_ty = cx.typeck_results().expr_ty(expr);
67 if orig_ty.is_fn() {
68 Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span })
69 } else if orig_ty.is_ref() {
70 Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span })
71 } else {
72 None
73 }
74 }
75
76 impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
77 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
78 match expr.kind {
79 // Catching:
80 // <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>)
81 ExprKind::Call(path, [arg])
82 if let ExprKind::Path(ref qpath) = path.kind
83 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
84 && matches!(
85 cx.tcx.get_diagnostic_name(def_id),
86 Some(sym::ptr_const_is_null | sym::ptr_is_null)
87 )
88 && let Some(diag) = incorrect_check(cx, arg) =>
89 {
90 cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
91 }
92
93 // Catching:
94 // (fn_ptr as *<const/mut> <ty>).is_null()
95 ExprKind::MethodCall(_, receiver, _, _)
96 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
97 && matches!(
98 cx.tcx.get_diagnostic_name(def_id),
99 Some(sym::ptr_const_is_null | sym::ptr_is_null)
100 )
101 && let Some(diag) = incorrect_check(cx, receiver) =>
102 {
103 cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
104 }
105
106 ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
107 let to_check: &Expr<'_>;
108 let diag: PtrNullChecksDiag<'_>;
109 if let Some(ddiag) = incorrect_check(cx, left) {
110 to_check = right;
111 diag = ddiag;
112 } else if let Some(ddiag) = incorrect_check(cx, right) {
113 to_check = left;
114 diag = ddiag;
115 } else {
116 return;
117 }
118
119 match to_check.kind {
120 // Catching:
121 // (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
122 ExprKind::Cast(cast_expr, _)
123 if let ExprKind::Lit(spanned) = cast_expr.kind
124 && let LitKind::Int(v, _) = spanned.node && v == 0 =>
125 {
126 cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
127 },
128
129 // Catching:
130 // (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
131 ExprKind::Call(path, [])
132 if let ExprKind::Path(ref qpath) = path.kind
133 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
134 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
135 && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) =>
136 {
137 cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
138 },
139
140 _ => {},
141 }
142 }
143 _ => {}
144 }
145 }
146 }