Auto merge of #13042 - Jarcho:almost_range, r=Manishearth

`almost_complete_range`: Delay suggestion creation.

Does less work when the lint doesn't trigger.

changelog: none
This commit is contained in:
bors 2024-07-08 22:39:01 +00:00
commit f6020fbd44
1 changed files with 61 additions and 43 deletions

View File

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