|
| 1 | +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; |
| 2 | +use clippy_utils::is_inside_always_const_context; |
| 3 | +use clippy_utils::msrvs::{self, Msrv}; |
| 4 | +use clippy_utils::source::snippet_with_applicability; |
| 5 | +use clippy_utils::ty::is_type_diagnostic_item; |
| 6 | +use rustc_errors::Applicability; |
| 7 | +use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; |
| 8 | +use rustc_lint::LateContext; |
| 9 | +use rustc_span::sym; |
| 10 | + |
| 11 | +use super::USELESS_NONZERO_NEW_UNCHECKED; |
| 12 | + |
| 13 | +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) { |
| 14 | + if msrv.meets(msrvs::CONST_UNWRAP) |
| 15 | + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind |
| 16 | + && segment.ident.name == sym::new_unchecked |
| 17 | + && let [init_arg] = args |
| 18 | + && is_inside_always_const_context(cx.tcx, expr.hir_id) |
| 19 | + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) |
| 20 | + { |
| 21 | + let mut app = Applicability::MachineApplicable; |
| 22 | + let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app); |
| 23 | + let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context"); |
| 24 | + let sugg = format!( |
| 25 | + "{ty_str}::new({}).unwrap()", |
| 26 | + snippet_with_applicability(cx, init_arg.span, "_", &mut app) |
| 27 | + ); |
| 28 | + |
| 29 | + if let Node::Block(Block { |
| 30 | + stmts: [], |
| 31 | + span: block_span, |
| 32 | + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), |
| 33 | + .. |
| 34 | + }) = cx.tcx.parent_hir_node(expr.hir_id) |
| 35 | + { |
| 36 | + if !block_span.from_expansion() { |
| 37 | + // The expression is the only component of an `unsafe` block. Propose |
| 38 | + // to replace the block altogether. |
| 39 | + span_lint_and_sugg( |
| 40 | + cx, |
| 41 | + USELESS_NONZERO_NEW_UNCHECKED, |
| 42 | + *block_span, |
| 43 | + msg, |
| 44 | + "use instead", |
| 45 | + sugg, |
| 46 | + app, |
| 47 | + ); |
| 48 | + } |
| 49 | + } else { |
| 50 | + // The expression is enclosed in a larger `unsafe` context. Indicate that |
| 51 | + // this may no longer be needed for the fixed expression. |
| 52 | + span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| { |
| 53 | + diagnostic |
| 54 | + .span_suggestion(expr.span, "use instead", sugg, app) |
| 55 | + .note("the fixed expression does not require an `unsafe` context"); |
| 56 | + }); |
| 57 | + } |
| 58 | + } |
| 59 | +} |
0 commit comments