diff options
| author | bors <bors@rust-lang.org> | 2023-09-16 03:40:20 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-09-16 03:40:20 +0000 |
| commit | 635c4a5e612b0ee8af6615635599702d3dce9906 (patch) | |
| tree | e6e5f854126129a3d5ea0b07379053aed15a83b4 /compiler/rustc_lint | |
| parent | e81f85fe9eebf8a5ba21438fb188a59d54095697 (diff) | |
| parent | 4b1bc2701067d7b340d102d40227091c00447f48 (diff) | |
| download | rust-635c4a5e612b0ee8af6615635599702d3dce9906.tar.gz rust-635c4a5e612b0ee8af6615635599702d3dce9906.zip | |
Auto merge of #114494 - est31:extend_useless_ptr_null_checks, r=jackh726
Make useless_ptr_null_checks smarter about some std functions This teaches the `useless_ptr_null_checks` lint that some std functions can't ever return null pointers, because they need to point to valid data, get references as input, etc. This is achieved by introducing an `#[rustc_never_returns_null_ptr]` attribute and adding it to these std functions (gated behind bootstrap `cfg_attr`). Later on, the attribute could maybe be used to tell LLVM that the returned pointer is never null. I don't expect much impact of that though, as the functions are pretty shallow and usually the input data is already never null. Follow-up of PR #113657 Fixes #114442
Diffstat (limited to 'compiler/rustc_lint')
| -rw-r--r-- | compiler/rustc_lint/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lints.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/ptr_nulls.rs | 52 |
3 files changed, 33 insertions, 23 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ba50c9e00e3..7377c6e2f35 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -453,6 +453,8 @@ lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking th .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value .label = expression has type `{$orig_ty}` +lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false + lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false .label = expression has type `{$orig_ty}` diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 3e26d6dc32d..c091c260a47 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -635,6 +635,8 @@ pub enum PtrNullChecksDiag<'a> { #[label] label: Span, }, + #[diag(lint_ptr_null_checks_fn_ret)] + FnRet { fn_name: Ident }, } // for_loops_over_fallibles.rs diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 02aff91032f..0de72d8d3db 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -31,12 +31,30 @@ declare_lint! { declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); -/// This function detects and returns the original expression from a series of consecutive casts, -/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`. -fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> { +/// This function checks if the expression is from a series of consecutive casts, +/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either +/// a fn ptr, a reference, or a function call whose definition is +/// annotated with `#![rustc_never_returns_null_ptr]`. +/// If this situation is present, the function returns the appropriate diagnostic. +fn incorrect_check<'a, 'tcx: 'a>( + cx: &'a LateContext<'tcx>, + mut e: &'a Expr<'a>, +) -> Option<PtrNullChecksDiag<'tcx>> { let mut had_at_least_one_cast = false; loop { e = e.peel_blocks(); + if let ExprKind::MethodCall(_, _expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { + return Some(PtrNullChecksDiag::FnRet { fn_name }); + } else if let ExprKind::Call(path, _args) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { + return Some(PtrNullChecksDiag::FnRet { fn_name }); + } e = if let ExprKind::Cast(expr, t) = e.kind && let TyKind::Ptr(_) = t.kind { had_at_least_one_cast = true; @@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&' && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) { had_at_least_one_cast = true; expr - } else if let ExprKind::Call(path, [arg]) = e.kind - && 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_from_ref | sym::ptr_from_mut)) { - had_at_least_one_cast = true; - arg } else if had_at_least_one_cast { - return Some(e); + let orig_ty = cx.typeck_results().expr_ty(e); + return if orig_ty.is_fn() { + Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span }) + } else if orig_ty.is_ref() { + Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span }) + } else { + None + }; } else { return None; }; } } -fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> { - let expr = ptr_cast_chain(cx, expr)?; - - let orig_ty = cx.typeck_results().expr_ty(expr); - if orig_ty.is_fn() { - Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span }) - } else if orig_ty.is_ref() { - Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) - } else { - None - } -} - impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { |
