Change:
I propose we add a lint that detects code like Some people seem to think that this is okay in contexts like or , but that is not the case -- on a NULL pointer is UB even as a place expression. To implement this, you can use the lint as a template: should check for  expressions with a operator, and then check the operand to be either a cast of to a pointer type, or a call to one of the methods. To detect the methods, make them "diagnostic items"; the lint does that e.g. for so it can again serve as a template here. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Ah, turns out clippy already has a lint for this: . So maybe we should just move that to rustc? Cc
claim
awesome. :) Let me know if you need any help.
Thanks :)
Did you mean something slightly different with your "okay in contexts like" examples, perhaps? Neither of those two actually compile as written, since they're just attempts to deference something that isn't even a pointer. You get: in both cases.
oops I screwed up the casts, should be fixed now.
Yeah, happy to have Clippy lints upstreamed to rust, let us know when it's done so we can deprecate it on our end
for detecting a call like you said to use . However I do not know what to use for the symbol argument. There is that defines symbol but nothing looks like creating a null ptr. Should I add one what should it look like ?
You first need to add the diagnostic item attribute to the relevant function inside , like so: This name must be globally unique, e.g. and . Then you add those names to the list in (please keep it alphabetically sorted), and then you can use them for .

Discussion:
<ins> // ignore-tidy-filelength </ins> //! Lints in the Rust compiler. //! //! This contains lints which can feasibly be implemented as their own

} } } <ins> declare_lint! { /// The `deref_nullptr` lint detects when an null pointer is dereferenced, /// which causes [undefined behavior]. /// /// ### Example /// /// ```rust,no_run /// # #![allow(unused)] /// use std::ptr; /// unsafe { ///     let x = &*ptr::null::<i32>(); ///     let x = ptr::addr_of!(*ptr::null::<i32>()); ///     let x = *(0 as *const i32); /// } /// ``` /// /// {{produces}} /// /// ### Explanation /// /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html pub DEREF_NULLPTR, Warn, "detects when an null pointer is dereferenced" } declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]); impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { /// test if expression is a null ptr fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { rustc_hir::ExprKind::Cast(ref expr, ref ty) => { if let rustc_hir::TyKind::Ptr(_) = ty.kind { return is_zero(expr) || is_null_ptr(cx, expr); } } // check for call to `core::ptr::null` or `core::ptr::null_mut` rustc_hir::ExprKind::Call(ref path, _) => { if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id) || cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id); } } } _ => {} } false } /// test if experssion is the literal `0` fn is_zero(expr: &hir::Expr<'_>) -> bool { match &expr.kind { rustc_hir::ExprKind::Lit(ref lit) => { if let LitKind::Int(a, _) = lit.node { return a == 0; } } _ => {} } false } if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind { if let rustc_hir::UnOp::Deref = un_op { if is_null_ptr(cx, expr_deref) { cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { let mut err = lint.build("dereferencing a null pointer"); err.span_label( expr.span, "this code causes undefined behavior when executed", ); err.emit(); }); } } } } } </ins>

UnreachablePub: UnreachablePub, ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, InvalidValue: InvalidValue, <ins> DerefNullPtr: DerefNullPtr, </ins> ] ); };

profiler_runtime, ptr_guaranteed_eq, ptr_guaranteed_ne, <ins> ptr_null, ptr_null_mut, </ins> ptr_offset_from, pub_macro_rules, pub_restricted,

#[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] #[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] <ins> #[rustc_diagnostic_item = "ptr_null"] </ins> pub const fn null<T>() -> *const T { 0 as *const T }

#[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] #[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] <ins> #[rustc_diagnostic_item = "ptr_null_mut"] </ins> pub const fn null_mut<T>() -> *mut T { 0 as *mut T }

// pretty-expanded FIXME #23616 <ins> #![allow(deref_nullptr)] </ins> use std::env; pub fn main() {

<ins> // test the deref_nullptr lint #![deny(deref_nullptr)] use std::ptr; struct Struct { field: u8, } fn f() { unsafe { let a = 1; let ub = *(a as *const i32); let ub = *(0 as *const i32); //~^ ERROR dereferencing a null pointer let ub = *ptr::null::<i32>(); //~^ ERROR dereferencing a null pointer let ub = *ptr::null_mut::<i32>(); //~^ ERROR dereferencing a null pointer let ub = *(ptr::null::<i16>() as *const i32); //~^ ERROR dereferencing a null pointer let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); //~^ ERROR dereferencing a null pointer let ub = &*ptr::null::<i32>(); //~^ ERROR dereferencing a null pointer let ub = &*ptr::null_mut::<i32>(); //~^ ERROR dereferencing a null pointer ptr::addr_of!(*ptr::null::<i32>()); //~^ ERROR dereferencing a null pointer ptr::addr_of_mut!(*ptr::null_mut::<i32>()); //~^ ERROR dereferencing a null pointer let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); //~^ ERROR dereferencing a null pointer } } fn main() {} </ins>

<ins> error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:15:18 | LL |         let ub = *(0 as *const i32); |                  ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed | note: the lint level is defined here --> $DIR/lint-deref-nullptr.rs:3:9 | LL | #![deny(deref_nullptr)] |         ^^^^^^^^^^^^^ error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:17:18 | LL |         let ub = *ptr::null::<i32>(); |                  ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:19:18 | LL |         let ub = *ptr::null_mut::<i32>(); |                  ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:21:18 | LL |         let ub = *(ptr::null::<i16>() as *const i32); |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:23:18 | LL |         let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:25:19 | LL |         let ub = &*ptr::null::<i32>(); |                   ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:27:19 | LL |         let ub = &*ptr::null_mut::<i32>(); |                   ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:29:23 | LL |         ptr::addr_of!(*ptr::null::<i32>()); |                       ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:31:27 | LL |         ptr::addr_of_mut!(*ptr::null_mut::<i32>()); |                           ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: dereferencing a null pointer --> $DIR/lint-deref-nullptr.rs:33:36 | LL |         let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); |                                    ^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed error: aborting due to 10 previous errors </ins>