rustc_lint/
ptr_nulls.rs
1use rustc_ast::LitKind;
2use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
3use rustc_session::{declare_lint, declare_lint_pass};
4use rustc_span::sym;
5
6use crate::lints::PtrNullChecksDiag;
7use crate::{LateContext, LateLintPass, LintContext};
8
9declare_lint! {
10 USELESS_PTR_NULL_CHECKS,
30 Warn,
31 "useless checking of non-null-typed pointer"
32}
33
34declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
35
36fn incorrect_check<'a, 'tcx: 'a>(
42 cx: &'a LateContext<'tcx>,
43 mut e: &'a Expr<'a>,
44) -> Option<PtrNullChecksDiag<'tcx>> {
45 let mut had_at_least_one_cast = false;
46 loop {
47 e = e.peel_blocks();
48 if let ExprKind::MethodCall(_, _expr, [], _) = e.kind
49 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
50 && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
51 && let Some(fn_name) = cx.tcx.opt_item_ident(def_id)
52 {
53 return Some(PtrNullChecksDiag::FnRet { fn_name });
54 } else if let ExprKind::Call(path, _args) = e.kind
55 && let ExprKind::Path(ref qpath) = path.kind
56 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
57 && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
58 && let Some(fn_name) = cx.tcx.opt_item_ident(def_id)
59 {
60 return Some(PtrNullChecksDiag::FnRet { fn_name });
61 }
62 e = if let ExprKind::Cast(expr, t) = e.kind
63 && let TyKind::Ptr(_) = t.kind
64 {
65 had_at_least_one_cast = true;
66 expr
67 } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
68 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
69 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut))
70 {
71 had_at_least_one_cast = true;
72 expr
73 } else if had_at_least_one_cast {
74 let orig_ty = cx.typeck_results().expr_ty(e);
75 return if orig_ty.is_fn() {
76 Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
77 } else if orig_ty.is_ref() {
78 Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
79 } else {
80 None
81 };
82 } else {
83 return None;
84 };
85 }
86}
87
88impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
89 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
90 match expr.kind {
91 ExprKind::Call(path, [arg])
94 if let ExprKind::Path(ref qpath) = path.kind
95 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
96 && matches!(
97 cx.tcx.get_diagnostic_name(def_id),
98 Some(sym::ptr_const_is_null | sym::ptr_is_null)
99 )
100 && let Some(diag) = incorrect_check(cx, arg) =>
101 {
102 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
103 }
104
105 ExprKind::MethodCall(_, receiver, _, _)
108 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
109 && matches!(
110 cx.tcx.get_diagnostic_name(def_id),
111 Some(sym::ptr_const_is_null | sym::ptr_is_null)
112 )
113 && let Some(diag) = incorrect_check(cx, receiver) =>
114 {
115 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
116 }
117
118 ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
119 let to_check: &Expr<'_>;
120 let diag: PtrNullChecksDiag<'_>;
121 if let Some(ddiag) = incorrect_check(cx, left) {
122 to_check = right;
123 diag = ddiag;
124 } else if let Some(ddiag) = incorrect_check(cx, right) {
125 to_check = left;
126 diag = ddiag;
127 } else {
128 return;
129 }
130
131 match to_check.kind {
132 ExprKind::Cast(cast_expr, _)
135 if let ExprKind::Lit(spanned) = cast_expr.kind
136 && let LitKind::Int(v, _) = spanned.node
137 && v == 0 =>
138 {
139 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
140 }
141
142 ExprKind::Call(path, [])
145 if let ExprKind::Path(ref qpath) = path.kind
146 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
147 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
148 && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) =>
149 {
150 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
151 }
152
153 _ => {}
154 }
155 }
156 _ => {}
157 }
158 }
159}