almost_complete_range: Delay suggestion creation.

This commit is contained in:
Jason Newcomb 2024-06-08 17:18:28 -04:00
parent d2400a49a4
commit 5b30f6a1a3

View File

@ -6,7 +6,6 @@ use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::Span;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -41,44 +40,68 @@ impl AlmostCompleteRange {
} }
impl EarlyLintPass for AlmostCompleteRange { impl EarlyLintPass for AlmostCompleteRange {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind
&& is_incomplete_range(start, end)
&& !in_external_macro(cx.sess(), e.span)
{
span_lint_and_then(
cx,
ALMOST_COMPLETE_RANGE,
e.span,
"almost complete ascii range",
|diag| {
let ctxt = e.span.ctxt(); let ctxt = e.span.ctxt();
let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) if let Some(start) = walk_span_to_context(start.span, ctxt)
&& let Some(end) = walk_span_to_context(end.span, ctxt) && let Some(end) = walk_span_to_context(end.span, ctxt)
&& self.msrv.meets(msrvs::RANGE_INCLUSIVE) && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
{ {
Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) diag.span_suggestion(
} else { trim_span(cx.sess().source_map(), start.between(end)),
None "use an inclusive range",
}; "..=".to_owned(),
check_range(cx, e.span, start, end, sugg); Applicability::MaybeIncorrect,
);
}
},
);
} }
} }
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) { fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
&& matches!(kind.node, RangeEnd::Excluded) && matches!(kind.node, RangeEnd::Excluded)
&& is_incomplete_range(start, end)
&& !in_external_macro(cx.sess(), p.span)
{ {
let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { span_lint_and_then(
"..=" cx,
ALMOST_COMPLETE_RANGE,
p.span,
"almost complete ascii range",
|diag| {
diag.span_suggestion(
kind.span,
"use an inclusive range",
if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
"..=".to_owned()
} else { } else {
"..." "...".to_owned()
}; },
check_range(cx, p.span, start, end, Some((kind.span, sugg))); Applicability::MaybeIncorrect,
);
},
);
} }
} }
extract_msrv_attr!(EarlyContext); extract_msrv_attr!(EarlyContext);
} }
fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { fn is_incomplete_range(start: &Expr, end: &Expr) -> bool {
if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind match (&start.peel_parens().kind, &end.peel_parens().kind) {
&& let ExprKind::Lit(end_token_lit) = end.peel_parens().kind (&ExprKind::Lit(start_lit), &ExprKind::Lit(end_lit)) => {
&& matches!( matches!(
( (LitKind::from_token_lit(start_lit), LitKind::from_token_lit(end_lit),),
LitKind::from_token_lit(start_token_lit),
LitKind::from_token_lit(end_token_lit),
),
( (
Ok(LitKind::Byte(b'a') | LitKind::Char('a')), Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
Ok(LitKind::Byte(b'z') | LitKind::Char('z')) Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
@ -90,12 +113,7 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg
Ok(LitKind::Byte(b'9') | LitKind::Char('9')), Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
) )
) )
&& !in_external_macro(cx.sess(), span) },
{ _ => false,
span_lint_and_then(cx, ALMOST_COMPLETE_RANGE, span, "almost complete ascii range", |diag| {
if let Some((span, sugg)) = sugg {
diag.span_suggestion(span, "use an inclusive range", sugg, Applicability::MaybeIncorrect);
}
});
} }
} }