Skip to content

Commit 4b1bc27

Browse files
committed
Improve diagnostics and add tests for function calls
1 parent 8faac74 commit 4b1bc27

File tree

5 files changed

+64
-44
lines changed

5 files changed

+64
-44
lines changed

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking th
457457
.help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
458458
.label = expression has type `{$orig_ty}`
459459
460+
lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false
461+
460462
lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
461463
.label = expression has type `{$orig_ty}`
462464

compiler/rustc_lint/src/lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,8 @@ pub enum PtrNullChecksDiag<'a> {
634634
#[label]
635635
label: Span,
636636
},
637+
#[diag(lint_ptr_null_checks_fn_ret)]
638+
FnRet { fn_name: Ident },
637639
}
638640

639641
// for_loops_over_fallibles.rs

compiler/rustc_lint/src/ptr_nulls.rs

+29-23
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,30 @@ declare_lint! {
3131

3232
declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
3333

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>> {
34+
/// This function checks if the expression is from a series of consecutive casts,
35+
/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
36+
/// a fn ptr, a reference, or a function call whose definition is
37+
/// annotated with `#![rustc_never_returns_null_ptr]`.
38+
/// If this situation is present, the function returns the appropriate diagnostic.
39+
fn incorrect_check<'a, 'tcx: 'a>(
40+
cx: &'a LateContext<'tcx>,
41+
mut e: &'a Expr<'a>,
42+
) -> Option<PtrNullChecksDiag<'tcx>> {
3743
let mut had_at_least_one_cast = false;
3844
loop {
3945
e = e.peel_blocks();
46+
if let ExprKind::MethodCall(_, _expr, [], _) = e.kind
47+
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
48+
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
49+
&& let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
50+
return Some(PtrNullChecksDiag::FnRet { fn_name });
51+
} else if let ExprKind::Call(path, _args) = e.kind
52+
&& let ExprKind::Path(ref qpath) = path.kind
53+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
54+
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
55+
&& let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
56+
return Some(PtrNullChecksDiag::FnRet { fn_name });
57+
}
4058
e = if let ExprKind::Cast(expr, t) = e.kind
4159
&& let TyKind::Ptr(_) = t.kind {
4260
had_at_least_one_cast = true;
@@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'
4664
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
4765
had_at_least_one_cast = true;
4866
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-
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) {
53-
had_at_least_one_cast = true;
54-
arg
5567
} else if had_at_least_one_cast {
56-
return Some(e);
68+
let orig_ty = cx.typeck_results().expr_ty(e);
69+
return if orig_ty.is_fn() {
70+
Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
71+
} else if orig_ty.is_ref() {
72+
Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
73+
} else {
74+
None
75+
};
5776
} else {
5877
return None;
5978
};
6079
}
6180
}
6281

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-
7682
impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
7783
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
7884
match expr.kind {

tests/ui/lint/ptr_null_checks.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ fn main() {
3838
if (&mut 8 as *mut i32).is_null() {}
3939
//~^ WARN references are not nullable
4040
if ptr::from_mut(&mut 8).is_null() {}
41-
//~^ WARN references are not nullable
41+
//~^ WARN call is never null
4242
if (&8 as *const i32).is_null() {}
4343
//~^ WARN references are not nullable
4444
if ptr::from_ref(&8).is_null() {}
45-
//~^ WARN references are not nullable
45+
//~^ WARN call is never null
4646
if ptr::from_ref(&8).cast_mut().is_null() {}
47-
//~^ WARN references are not nullable
47+
//~^ WARN call is never null
4848
if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
49-
//~^ WARN references are not nullable
49+
//~^ WARN call is never null
5050
if (&8 as *const i32) == std::ptr::null() {}
5151
//~^ WARN references are not nullable
5252
let ref_num = &8;
@@ -65,6 +65,12 @@ fn main() {
6565
if (&*{ static_i32() } as *const i32).is_null() {}
6666
//~^ WARN references are not nullable
6767

68+
// ---------------- Functions -------------------
69+
if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
70+
//~^ WARN call is never null
71+
if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
72+
//~^ WARN call is never null
73+
6874
// ----------------------------------------------
6975
const ZPTR: *const () = 0 as *const _;
7076
const NOT_ZPTR: *const () = 1 as *const _;

tests/ui/lint/ptr_null_checks.stderr

+21-17
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,11 @@ LL | if (&mut 8 as *mut i32).is_null() {}
117117
| |
118118
| expression has type `&mut i32`
119119

120-
warning: references are not nullable, so checking them for null will always return false
120+
warning: returned pointer of `from_mut` call is never null, so checking it for null will always return false
121121
--> $DIR/ptr_null_checks.rs:40:8
122122
|
123123
LL | if ptr::from_mut(&mut 8).is_null() {}
124-
| ^^^^^^^^^^^^^^------^^^^^^^^^^^
125-
| |
126-
| expression has type `&mut i32`
124+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
127125

128126
warning: references are not nullable, so checking them for null will always return false
129127
--> $DIR/ptr_null_checks.rs:42:8
@@ -133,29 +131,23 @@ LL | if (&8 as *const i32).is_null() {}
133131
| |
134132
| expression has type `&i32`
135133

136-
warning: references are not nullable, so checking them for null will always return false
134+
warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
137135
--> $DIR/ptr_null_checks.rs:44:8
138136
|
139137
LL | if ptr::from_ref(&8).is_null() {}
140-
| ^^^^^^^^^^^^^^--^^^^^^^^^^^
141-
| |
142-
| expression has type `&i32`
138+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
143139

144-
warning: references are not nullable, so checking them for null will always return false
140+
warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
145141
--> $DIR/ptr_null_checks.rs:46:8
146142
|
147143
LL | if ptr::from_ref(&8).cast_mut().is_null() {}
148-
| ^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^
149-
| |
150-
| expression has type `&i32`
144+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
151145

152-
warning: references are not nullable, so checking them for null will always return false
146+
warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
153147
--> $DIR/ptr_null_checks.rs:48:8
154148
|
155149
LL | if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
156-
| ^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
157-
| |
158-
| expression has type `&i32`
150+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159151

160152
warning: references are not nullable, so checking them for null will always return false
161153
--> $DIR/ptr_null_checks.rs:50:8
@@ -221,5 +213,17 @@ LL | if (&*{ static_i32() } as *const i32).is_null() {}
221213
| |
222214
| expression has type `&i32`
223215

224-
warning: 25 warnings emitted
216+
warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
217+
--> $DIR/ptr_null_checks.rs:69:8
218+
|
219+
LL | if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
220+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
221+
222+
warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
223+
--> $DIR/ptr_null_checks.rs:71:8
224+
|
225+
LL | if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
226+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
227+
228+
warning: 27 warnings emitted
225229

0 commit comments

Comments
 (0)