-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Uplift
clippy::fn_null_check
to rustc
- Loading branch information
Showing
6 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use crate::{lints::FnNullCheckDiag, LateContext, LateLintPass, LintContext}; | ||
use rustc_ast::LitKind; | ||
use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; | ||
use rustc_session::{declare_lint, declare_lint_pass}; | ||
use rustc_span::sym; | ||
|
||
declare_lint! { | ||
/// The `incorrect_fn_null_checks` lint checks for expression that checks if a | ||
/// function pointer is null. | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// # fn test() {} | ||
/// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ | ||
/// # test; | ||
/// | ||
/// if (fn_ptr as *const ()).is_null() { /* ... */ } | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// Function pointers are assumed to be non-null, checking them for null will always | ||
/// return false. | ||
INCORRECT_FN_NULL_CHECKS, | ||
Warn, | ||
"incorrect checking of null function pointer" | ||
} | ||
|
||
declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]); | ||
|
||
fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ||
let mut expr = expr.peel_blocks(); | ||
let mut had_at_least_one_cast = false; | ||
while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind | ||
&& let TyKind::Ptr(_) = cast_ty.kind { | ||
expr = cast_expr.peel_blocks(); | ||
had_at_least_one_cast = true; | ||
} | ||
had_at_least_one_cast && cx.typeck_results().expr_ty_adjusted(expr).is_fn() | ||
} | ||
|
||
impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
match expr.kind { | ||
// Catching: | ||
// <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>) | ||
ExprKind::Call(path, [arg]) | ||
if let ExprKind::Path(ref qpath) = path.kind | ||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() | ||
&& matches!( | ||
cx.tcx.get_diagnostic_name(def_id), | ||
Some(sym::ptr_const_is_null | sym::ptr_is_null) | ||
) | ||
&& is_fn_ptr_cast(cx, arg) => | ||
{ | ||
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) | ||
} | ||
|
||
// Catching: | ||
// (fn_ptr as *<const/mut> <ty>).is_null() | ||
ExprKind::MethodCall(_, receiver, _, _) | ||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) | ||
&& matches!( | ||
cx.tcx.get_diagnostic_name(def_id), | ||
Some(sym::ptr_const_is_null | sym::ptr_is_null) | ||
) | ||
&& is_fn_ptr_cast(cx, receiver) => | ||
{ | ||
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) | ||
} | ||
|
||
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { | ||
let to_check: &Expr<'_>; | ||
if is_fn_ptr_cast(cx, left) { | ||
to_check = right; | ||
} else if is_fn_ptr_cast(cx, right) { | ||
to_check = left; | ||
} else { | ||
return; | ||
} | ||
|
||
match to_check.kind { | ||
// Catching: | ||
// (fn_ptr as *<const/mut> <ty>) == (0 as <ty>) | ||
ExprKind::Cast(cast_expr, _) | ||
if let ExprKind::Lit(spanned) = cast_expr.kind | ||
&& let LitKind::Int(v, _) = spanned.node && v == 0 => | ||
{ | ||
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) | ||
}, | ||
|
||
// Catching: | ||
// (fn_ptr as *<const/mut> <ty>) == std::ptr::null() | ||
ExprKind::Call(path, []) | ||
if let ExprKind::Path(ref qpath) = path.kind | ||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() | ||
&& let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) | ||
&& (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) => | ||
{ | ||
cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) | ||
}, | ||
|
||
_ => {}, | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// check-pass | ||
|
||
fn main() { | ||
let fn_ptr = main; | ||
|
||
if (fn_ptr as *mut ()).is_null() {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as *const u8).is_null() {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as *const ()) == std::ptr::null() {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as *const ()) == (0 as *const ()) {} | ||
//~^ WARN function pointers are not nullable | ||
if <*const _>::is_null(fn_ptr as *const ()) {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ||
//~^ WARN function pointers are not nullable | ||
if (fn_ptr as fn() as *const ()).is_null() {} | ||
//~^ WARN function pointers are not nullable | ||
|
||
const ZPTR: *const () = 0 as *const _; | ||
const NOT_ZPTR: *const () = 1 as *const _; | ||
|
||
// unlike the uplifted clippy::fn_null_check lint we do | ||
// not lint on them | ||
if (fn_ptr as *const ()) == ZPTR {} | ||
if (fn_ptr as *const ()) == NOT_ZPTR {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:6:8 | ||
| | ||
LL | if (fn_ptr as *mut ()).is_null() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
= note: `#[warn(incorrect_fn_null_checks)]` on by default | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:8:8 | ||
| | ||
LL | if (fn_ptr as *const u8).is_null() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:10:8 | ||
| | ||
LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:12:8 | ||
| | ||
LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:14:8 | ||
| | ||
LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:16:8 | ||
| | ||
LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:18:8 | ||
| | ||
LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: function pointers are not nullable, so checking them for null will always return false | ||
--> $DIR/fn_null_check.rs:20:8 | ||
| | ||
LL | if (fn_ptr as fn() as *const ()).is_null() {} | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value | ||
|
||
warning: 8 warnings emitted | ||
|