|
| 1 | +// ignore-tidy-filelength |
| 2 | + |
1 | 3 | //! Lints in the Rust compiler.
|
2 | 4 | //!
|
3 | 5 | //! This contains lints which can feasibly be implemented as their own
|
@@ -2964,3 +2966,88 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
|
2964 | 2966 | }
|
2965 | 2967 | }
|
2966 | 2968 | }
|
| 2969 | + |
| 2970 | +declare_lint! { |
| 2971 | + /// The `deref_nullptr` lint detects when an null pointer is dereferenced, |
| 2972 | + /// which causes [undefined behavior]. |
| 2973 | + /// |
| 2974 | + /// ### Example |
| 2975 | + /// |
| 2976 | + /// ```rust,no_run |
| 2977 | + /// # #![allow(unused)] |
| 2978 | + /// use std::ptr; |
| 2979 | + /// unsafe { |
| 2980 | + /// let x = &*ptr::null::<i32>(); |
| 2981 | + /// let x = ptr::addr_of!(*ptr::null::<i32>()); |
| 2982 | + /// let x = *(0 as *const i32); |
| 2983 | + /// } |
| 2984 | + /// ``` |
| 2985 | + /// |
| 2986 | + /// {{produces}} |
| 2987 | + /// |
| 2988 | + /// ### Explanation |
| 2989 | + /// |
| 2990 | + /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, |
| 2991 | + /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. |
| 2992 | + /// |
| 2993 | + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html |
| 2994 | + pub DEREF_NULLPTR, |
| 2995 | + Warn, |
| 2996 | + "detects when an null pointer is dereferenced" |
| 2997 | +} |
| 2998 | + |
| 2999 | +declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]); |
| 3000 | + |
| 3001 | +impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { |
| 3002 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { |
| 3003 | + /// test if expression is a null ptr |
| 3004 | + fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { |
| 3005 | + match &expr.kind { |
| 3006 | + rustc_hir::ExprKind::Cast(ref expr, ref ty) => { |
| 3007 | + if let rustc_hir::TyKind::Ptr(_) = ty.kind { |
| 3008 | + return is_zero(expr) || is_null_ptr(cx, expr); |
| 3009 | + } |
| 3010 | + } |
| 3011 | + // check for call to `core::ptr::null` or `core::ptr::null_mut` |
| 3012 | + rustc_hir::ExprKind::Call(ref path, _) => { |
| 3013 | + if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { |
| 3014 | + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { |
| 3015 | + return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id) |
| 3016 | + || cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id); |
| 3017 | + } |
| 3018 | + } |
| 3019 | + } |
| 3020 | + _ => {} |
| 3021 | + } |
| 3022 | + false |
| 3023 | + } |
| 3024 | + |
| 3025 | + /// test if experssion is the literal `0` |
| 3026 | + fn is_zero(expr: &hir::Expr<'_>) -> bool { |
| 3027 | + match &expr.kind { |
| 3028 | + rustc_hir::ExprKind::Lit(ref lit) => { |
| 3029 | + if let LitKind::Int(a, _) = lit.node { |
| 3030 | + return a == 0; |
| 3031 | + } |
| 3032 | + } |
| 3033 | + _ => {} |
| 3034 | + } |
| 3035 | + false |
| 3036 | + } |
| 3037 | + |
| 3038 | + if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind { |
| 3039 | + if let rustc_hir::UnOp::Deref = un_op { |
| 3040 | + if is_null_ptr(cx, expr_deref) { |
| 3041 | + cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { |
| 3042 | + let mut err = lint.build("dereferencing a null pointer"); |
| 3043 | + err.span_label( |
| 3044 | + expr.span, |
| 3045 | + "this code causes undefined behavior when executed", |
| 3046 | + ); |
| 3047 | + err.emit(); |
| 3048 | + }); |
| 3049 | + } |
| 3050 | + } |
| 3051 | + } |
| 3052 | + } |
| 3053 | +} |
0 commit comments