Skip to content

Commit 743ae5a

Browse files
committed
Expand incorrect_fn_null_check lint with reference null checking
1 parent c435af0 commit 743ae5a

File tree

5 files changed

+133
-23
lines changed

5 files changed

+133
-23
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,12 @@ lint_expectation = this lint expectation is unfulfilled
213213
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
214214
.rationale = {$rationale}
215215
216-
lint_fn_null_check = function pointers are not nullable, so checking them for null will always return false
216+
lint_fn_null_check_fn_ptr = function pointers are not nullable, so checking them for null will always return false
217217
.help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
218218
219+
lint_fn_null_check_ref = references are not nullable, so checking them for null will always return false
220+
.label = expression has type `{$orig_ty}`
221+
219222
lint_for_loops_over_fallibles =
220223
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
221224
.suggestion = consider using `if let` to clear intent

compiler/rustc_lint/src/fn_null_check.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,26 @@ declare_lint! {
3131

3232
declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]);
3333

34-
fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
34+
fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<FnNullCheckDiag<'a>> {
3535
let mut expr = expr.peel_blocks();
3636
let mut had_at_least_one_cast = false;
3737
while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind
3838
&& let TyKind::Ptr(_) = cast_ty.kind {
3939
expr = cast_expr.peel_blocks();
4040
had_at_least_one_cast = true;
4141
}
42-
had_at_least_one_cast && cx.typeck_results().expr_ty_adjusted(expr).is_fn()
42+
if !had_at_least_one_cast {
43+
None
44+
} else {
45+
let orig_ty = cx.typeck_results().expr_ty(expr);
46+
if orig_ty.is_fn() {
47+
Some(FnNullCheckDiag::FnPtr)
48+
} else if orig_ty.is_ref() {
49+
Some(FnNullCheckDiag::Ref { orig_ty, label: expr.span })
50+
} else {
51+
None
52+
}
53+
}
4354
}
4455

4556
impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks {
@@ -54,9 +65,9 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks {
5465
cx.tcx.get_diagnostic_name(def_id),
5566
Some(sym::ptr_const_is_null | sym::ptr_is_null)
5667
)
57-
&& is_fn_ptr_cast(cx, arg) =>
68+
&& let Some(diag) = incorrect_check(cx, arg) =>
5869
{
59-
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag)
70+
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag)
6071
}
6172

6273
// Catching:
@@ -67,17 +78,20 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks {
6778
cx.tcx.get_diagnostic_name(def_id),
6879
Some(sym::ptr_const_is_null | sym::ptr_is_null)
6980
)
70-
&& is_fn_ptr_cast(cx, receiver) =>
81+
&& let Some(diag) = incorrect_check(cx, receiver) =>
7182
{
72-
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag)
83+
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag)
7384
}
7485

7586
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
7687
let to_check: &Expr<'_>;
77-
if is_fn_ptr_cast(cx, left) {
88+
let diag: FnNullCheckDiag<'_>;
89+
if let Some(ddiag) = incorrect_check(cx, left) {
7890
to_check = right;
79-
} else if is_fn_ptr_cast(cx, right) {
91+
diag = ddiag;
92+
} else if let Some(ddiag) = incorrect_check(cx, right) {
8093
to_check = left;
94+
diag = ddiag;
8195
} else {
8296
return;
8397
}
@@ -89,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks {
89103
if let ExprKind::Lit(spanned) = cast_expr.kind
90104
&& let LitKind::Int(v, _) = spanned.node && v == 0 =>
91105
{
92-
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag)
106+
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag)
93107
},
94108

95109
// Catching:
@@ -100,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks {
100114
&& let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
101115
&& (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) =>
102116
{
103-
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag)
117+
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag)
104118
},
105119

106120
_ => {},

compiler/rustc_lint/src/lints.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,9 +615,17 @@ pub struct ExpectationNote {
615615

616616
// fn_null_check.rs
617617
#[derive(LintDiagnostic)]
618-
#[diag(lint_fn_null_check)]
619-
#[help]
620-
pub struct FnNullCheckDiag;
618+
pub enum FnNullCheckDiag<'a> {
619+
#[diag(lint_fn_null_check_fn_ptr)]
620+
#[help(lint_help)]
621+
FnPtr,
622+
#[diag(lint_fn_null_check_ref)]
623+
Ref {
624+
orig_ty: Ty<'a>,
625+
#[label]
626+
label: Span,
627+
},
628+
}
621629

622630
// for_loops_over_fallibles.rs
623631
#[derive(LintDiagnostic)]

tests/ui/lint/fn_null_check.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
fn main() {
44
let fn_ptr = main;
55

6+
// ------------- Function pointers ---------------
67
if (fn_ptr as *mut ()).is_null() {}
78
//~^ WARN function pointers are not nullable
89
if (fn_ptr as *const u8).is_null() {}
@@ -20,6 +21,26 @@ fn main() {
2021
if (fn_ptr as fn() as *const ()).is_null() {}
2122
//~^ WARN function pointers are not nullable
2223

24+
// ---------------- References ------------------
25+
if (&mut 8 as *mut i32).is_null() {}
26+
//~^ WARN references are not nullable
27+
if (&8 as *const i32).is_null() {}
28+
//~^ WARN references are not nullable
29+
if (&8 as *const i32) == std::ptr::null() {}
30+
//~^ WARN references are not nullable
31+
let ref_num = &8;
32+
if (ref_num as *const i32) == std::ptr::null() {}
33+
//~^ WARN references are not nullable
34+
if (b"\0" as *const u8).is_null() {}
35+
//~^ WARN references are not nullable
36+
if ("aa" as *const str).is_null() {}
37+
//~^ WARN references are not nullable
38+
if (&[1, 2] as *const i32).is_null() {}
39+
//~^ WARN references are not nullable
40+
if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {}
41+
//~^ WARN references are not nullable
42+
43+
// ----------------------------------------------
2344
const ZPTR: *const () = 0 as *const _;
2445
const NOT_ZPTR: *const () = 1 as *const _;
2546

tests/ui/lint/fn_null_check.stderr

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: function pointers are not nullable, so checking them for null will always return false
2-
--> $DIR/fn_null_check.rs:6:8
2+
--> $DIR/fn_null_check.rs:7:8
33
|
44
LL | if (fn_ptr as *mut ()).is_null() {}
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,60 +8,124 @@ LL | if (fn_ptr as *mut ()).is_null() {}
88
= note: `#[warn(incorrect_fn_null_checks)]` on by default
99

1010
warning: function pointers are not nullable, so checking them for null will always return false
11-
--> $DIR/fn_null_check.rs:8:8
11+
--> $DIR/fn_null_check.rs:9:8
1212
|
1313
LL | if (fn_ptr as *const u8).is_null() {}
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1515
|
1616
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
1717

1818
warning: function pointers are not nullable, so checking them for null will always return false
19-
--> $DIR/fn_null_check.rs:10:8
19+
--> $DIR/fn_null_check.rs:11:8
2020
|
2121
LL | if (fn_ptr as *const ()) == std::ptr::null() {}
2222
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2323
|
2424
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
2525

2626
warning: function pointers are not nullable, so checking them for null will always return false
27-
--> $DIR/fn_null_check.rs:12:8
27+
--> $DIR/fn_null_check.rs:13:8
2828
|
2929
LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {}
3030
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3131
|
3232
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
3333

3434
warning: function pointers are not nullable, so checking them for null will always return false
35-
--> $DIR/fn_null_check.rs:14:8
35+
--> $DIR/fn_null_check.rs:15:8
3636
|
3737
LL | if (fn_ptr as *const ()) == (0 as *const ()) {}
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3939
|
4040
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
4141

4242
warning: function pointers are not nullable, so checking them for null will always return false
43-
--> $DIR/fn_null_check.rs:16:8
43+
--> $DIR/fn_null_check.rs:17:8
4444
|
4545
LL | if <*const _>::is_null(fn_ptr as *const ()) {}
4646
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4747
|
4848
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
4949

5050
warning: function pointers are not nullable, so checking them for null will always return false
51-
--> $DIR/fn_null_check.rs:18:8
51+
--> $DIR/fn_null_check.rs:19:8
5252
|
5353
LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {}
5454
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5555
|
5656
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
5757

5858
warning: function pointers are not nullable, so checking them for null will always return false
59-
--> $DIR/fn_null_check.rs:20:8
59+
--> $DIR/fn_null_check.rs:21:8
6060
|
6161
LL | if (fn_ptr as fn() as *const ()).is_null() {}
6262
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6363
|
6464
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
6565

66-
warning: 8 warnings emitted
66+
warning: references are not nullable, so checking them for null will always return false
67+
--> $DIR/fn_null_check.rs:25:8
68+
|
69+
LL | if (&mut 8 as *mut i32).is_null() {}
70+
| ^------^^^^^^^^^^^^^^^^^^^^^^^
71+
| |
72+
| expression has type `&mut i32`
73+
74+
warning: references are not nullable, so checking them for null will always return false
75+
--> $DIR/fn_null_check.rs:27:8
76+
|
77+
LL | if (&8 as *const i32).is_null() {}
78+
| ^--^^^^^^^^^^^^^^^^^^^^^^^^^
79+
| |
80+
| expression has type `&i32`
81+
82+
warning: references are not nullable, so checking them for null will always return false
83+
--> $DIR/fn_null_check.rs:29:8
84+
|
85+
LL | if (&8 as *const i32) == std::ptr::null() {}
86+
| ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
87+
| |
88+
| expression has type `&i32`
89+
90+
warning: references are not nullable, so checking them for null will always return false
91+
--> $DIR/fn_null_check.rs:32:8
92+
|
93+
LL | if (ref_num as *const i32) == std::ptr::null() {}
94+
| ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
95+
| |
96+
| expression has type `&i32`
97+
98+
warning: references are not nullable, so checking them for null will always return false
99+
--> $DIR/fn_null_check.rs:34:8
100+
|
101+
LL | if (b"\0" as *const u8).is_null() {}
102+
| ^-----^^^^^^^^^^^^^^^^^^^^^^^^
103+
| |
104+
| expression has type `&[u8; 1]`
105+
106+
warning: references are not nullable, so checking them for null will always return false
107+
--> $DIR/fn_null_check.rs:36:8
108+
|
109+
LL | if ("aa" as *const str).is_null() {}
110+
| ^----^^^^^^^^^^^^^^^^^^^^^^^^^
111+
| |
112+
| expression has type `&str`
113+
114+
warning: references are not nullable, so checking them for null will always return false
115+
--> $DIR/fn_null_check.rs:38:8
116+
|
117+
LL | if (&[1, 2] as *const i32).is_null() {}
118+
| ^-------^^^^^^^^^^^^^^^^^^^^^^^^^
119+
| |
120+
| expression has type `&[i32; 2]`
121+
122+
warning: references are not nullable, so checking them for null will always return false
123+
--> $DIR/fn_null_check.rs:40:8
124+
|
125+
LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {}
126+
| ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
127+
| |
128+
| expression has type `&mut [i32; 2]`
129+
130+
warning: 16 warnings emitted
67131

0 commit comments

Comments
 (0)