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
This commit is contained in:
commit
635c4a5e61
18 changed files with 94 additions and 46 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue