Skip to content

Commit de5cff7

Browse files
authored
Rollup merge of rust-lang#113657 - Urgau:expand-incorrect_fn_null_check-lint, r=cjgillot
Expand, rename and improve `incorrect_fn_null_checks` lint This PR, - firstly, expand the lint by now linting on references - secondly, it renames the lint `incorrect_fn_null_checks` -> `useless_ptr_null_checks` - and thirdly it improves the lint by catching `ptr::from_mut`, `ptr::from_ref`, as well as `<*mut _>::cast` and `<*const _>::cast_mut` Fixes rust-lang#113601 cc `@est31`
2 parents fcf3006 + ee51953 commit de5cff7

File tree

17 files changed

+484
-225
lines changed

17 files changed

+484
-225
lines changed

Diff for: compiler/rustc_lint/messages.ftl

+7-3
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,6 @@ 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
217-
.help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
218-
219216
lint_for_loops_over_fallibles =
220217
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
221218
.suggestion = consider using `if let` to clear intent
@@ -454,6 +451,13 @@ lint_path_statement_drop = path statement drops value
454451
455452
lint_path_statement_no_effect = path statement with no effect
456453
454+
lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false
455+
.help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
456+
.label = expression has type `{$orig_ty}`
457+
458+
lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
459+
.label = expression has type `{$orig_ty}`
460+
457461
lint_query_instability = using `{$query}` can result in unstable query results
458462
.note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale
459463

Diff for: compiler/rustc_lint/src/fn_null_check.rs

-112
This file was deleted.

Diff for: compiler/rustc_lint/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ mod early;
5757
mod enum_intrinsics_non_enums;
5858
mod errors;
5959
mod expect;
60-
mod fn_null_check;
6160
mod for_loops_over_fallibles;
6261
pub mod hidden_unicode_codepoints;
6362
mod internal;
@@ -76,6 +75,7 @@ mod noop_method_call;
7675
mod opaque_hidden_inferred_bound;
7776
mod pass_by_value;
7877
mod passes;
78+
mod ptr_nulls;
7979
mod redundant_semicolon;
8080
mod reference_casting;
8181
mod traits;
@@ -102,7 +102,6 @@ use builtin::*;
102102
use deref_into_dyn_supertrait::*;
103103
use drop_forget_useless::*;
104104
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
105-
use fn_null_check::*;
106105
use for_loops_over_fallibles::*;
107106
use hidden_unicode_codepoints::*;
108107
use internal::*;
@@ -117,6 +116,7 @@ use nonstandard_style::*;
117116
use noop_method_call::*;
118117
use opaque_hidden_inferred_bound::*;
119118
use pass_by_value::*;
119+
use ptr_nulls::*;
120120
use redundant_semicolon::*;
121121
use reference_casting::*;
122122
use traits::*;
@@ -227,7 +227,7 @@ late_lint_methods!(
227227
// Depends on types used in type definitions
228228
MissingCopyImplementations: MissingCopyImplementations,
229229
// Depends on referenced function signatures in expressions
230-
IncorrectFnNullChecks: IncorrectFnNullChecks,
230+
PtrNullChecks: PtrNullChecks,
231231
MutableTransmutes: MutableTransmutes,
232232
TypeAliasBounds: TypeAliasBounds,
233233
TrivialConstraints: TrivialConstraints,

Diff for: compiler/rustc_lint/src/lints.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,23 @@ pub struct ExpectationNote {
613613
pub rationale: Symbol,
614614
}
615615

616-
// fn_null_check.rs
616+
// ptr_nulls.rs
617617
#[derive(LintDiagnostic)]
618-
#[diag(lint_fn_null_check)]
619-
#[help]
620-
pub struct FnNullCheckDiag;
618+
pub enum PtrNullChecksDiag<'a> {
619+
#[diag(lint_ptr_null_checks_fn_ptr)]
620+
#[help(lint_help)]
621+
FnPtr {
622+
orig_ty: Ty<'a>,
623+
#[label]
624+
label: Span,
625+
},
626+
#[diag(lint_ptr_null_checks_ref)]
627+
Ref {
628+
orig_ty: Ty<'a>,
629+
#[label]
630+
label: Span,
631+
},
632+
}
621633

622634
// for_loops_over_fallibles.rs
623635
#[derive(LintDiagnostic)]

Diff for: compiler/rustc_lint/src/ptr_nulls.rs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use crate::{lints::PtrNullChecksDiag, LateContext, LateLintPass, LintContext};
2+
use rustc_ast::LitKind;
3+
use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
4+
use rustc_session::{declare_lint, declare_lint_pass};
5+
use rustc_span::sym;
6+
7+
declare_lint! {
8+
/// The `useless_ptr_null_checks` lint checks for useless null checks against pointers
9+
/// obtained from non-null types.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust
14+
/// # fn test() {}
15+
/// let fn_ptr: fn() = /* somehow obtained nullable function pointer */
16+
/// # test;
17+
///
18+
/// if (fn_ptr as *const ()).is_null() { /* ... */ }
19+
/// ```
20+
///
21+
/// {{produces}}
22+
///
23+
/// ### Explanation
24+
///
25+
/// Function pointers and references are assumed to be non-null, checking them for null
26+
/// will always return false.
27+
USELESS_PTR_NULL_CHECKS,
28+
Warn,
29+
"useless checking of non-null-typed pointer"
30+
}
31+
32+
declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
33+
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>> {
37+
let mut had_at_least_one_cast = false;
38+
loop {
39+
e = e.peel_blocks();
40+
e = if let ExprKind::Cast(expr, t) = e.kind
41+
&& let TyKind::Ptr(_) = t.kind {
42+
had_at_least_one_cast = true;
43+
expr
44+
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
45+
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
46+
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
47+
had_at_least_one_cast = true;
48+
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+
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) {
53+
had_at_least_one_cast = true;
54+
arg
55+
} else if had_at_least_one_cast {
56+
return Some(e);
57+
} else {
58+
return None;
59+
};
60+
}
61+
}
62+
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+
76+
impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
77+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
78+
match expr.kind {
79+
// Catching:
80+
// <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>)
81+
ExprKind::Call(path, [arg])
82+
if let ExprKind::Path(ref qpath) = path.kind
83+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
84+
&& matches!(
85+
cx.tcx.get_diagnostic_name(def_id),
86+
Some(sym::ptr_const_is_null | sym::ptr_is_null)
87+
)
88+
&& let Some(diag) = incorrect_check(cx, arg) =>
89+
{
90+
cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
91+
}
92+
93+
// Catching:
94+
// (fn_ptr as *<const/mut> <ty>).is_null()
95+
ExprKind::MethodCall(_, receiver, _, _)
96+
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
97+
&& matches!(
98+
cx.tcx.get_diagnostic_name(def_id),
99+
Some(sym::ptr_const_is_null | sym::ptr_is_null)
100+
)
101+
&& let Some(diag) = incorrect_check(cx, receiver) =>
102+
{
103+
cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
104+
}
105+
106+
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
107+
let to_check: &Expr<'_>;
108+
let diag: PtrNullChecksDiag<'_>;
109+
if let Some(ddiag) = incorrect_check(cx, left) {
110+
to_check = right;
111+
diag = ddiag;
112+
} else if let Some(ddiag) = incorrect_check(cx, right) {
113+
to_check = left;
114+
diag = ddiag;
115+
} else {
116+
return;
117+
}
118+
119+
match to_check.kind {
120+
// Catching:
121+
// (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
122+
ExprKind::Cast(cast_expr, _)
123+
if let ExprKind::Lit(spanned) = cast_expr.kind
124+
&& let LitKind::Int(v, _) = spanned.node && v == 0 =>
125+
{
126+
cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
127+
},
128+
129+
// Catching:
130+
// (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
131+
ExprKind::Call(path, [])
132+
if let ExprKind::Path(ref qpath) = path.kind
133+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
134+
&& let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
135+
&& (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) =>
136+
{
137+
cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
138+
},
139+
140+
_ => {},
141+
}
142+
}
143+
_ => {}
144+
}
145+
}
146+
}

Diff for: compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1155,8 +1155,10 @@ symbols! {
11551155
profiler_builtins,
11561156
profiler_runtime,
11571157
ptr,
1158+
ptr_cast,
11581159
ptr_cast_mut,
11591160
ptr_const_is_null,
1161+
ptr_from_mut,
11601162
ptr_from_ref,
11611163
ptr_guaranteed_cmp,
11621164
ptr_is_null,

Diff for: library/core/src/ptr/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
710710
#[inline(always)]
711711
#[must_use]
712712
#[unstable(feature = "ptr_from_ref", issue = "106116")]
713+
#[rustc_diagnostic_item = "ptr_from_mut"]
713714
pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
714715
r
715716
}

Diff for: library/core/src/ptr/mut_ptr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl<T: ?Sized> *mut T {
5454
/// Casts to a pointer of another type.
5555
#[stable(feature = "ptr_cast", since = "1.38.0")]
5656
#[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
57+
#[rustc_diagnostic_item = "ptr_cast"]
5758
#[inline(always)]
5859
pub const fn cast<U>(self) -> *mut U {
5960
self as _

Diff for: src/tools/clippy/clippy_lints/src/renamed_lints.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
4343
("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
4444
("clippy::forget_copy", "forgetting_copy_types"),
4545
("clippy::forget_ref", "forgetting_references"),
46-
("clippy::fn_null_check", "incorrect_fn_null_checks"),
46+
("clippy::fn_null_check", "useless_ptr_null_checks"),
4747
("clippy::into_iter_on_array", "array_into_iter"),
4848
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
4949
("clippy::invalid_ref", "invalid_value"),

0 commit comments

Comments
 (0)