Suggest struct pattern when destructuring Range with .. syntax

This commit is contained in:
delta17920 2025-12-13 15:18:34 +00:00
parent f2c70877a7
commit cb301751b3
3 changed files with 117 additions and 0 deletions

View file

@ -440,6 +440,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
self.suggest_range_struct_destructuring(&mut err, path, source);
self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
if let Some((span, label)) = base_error.span_label {
@ -1383,6 +1384,41 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}
fn suggest_range_struct_destructuring(
&self,
err: &mut Diag<'_>,
path: &[Segment],
source: PathSource<'_, '_, '_>,
) {
// We accept Expr here because range bounds (start..end) are parsed as expressions
if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {
return;
}
if let Some(pat) = self.diag_metadata.current_pat
&& let ast::PatKind::Range(Some(start_expr), Some(end_expr), _) = &pat.kind
&& let (ast::ExprKind::Path(None, start_path), ast::ExprKind::Path(None, end_path)) =
(&start_expr.kind, &end_expr.kind)
&& path.len() == 1
{
let ident = path[0].ident;
if (start_path.segments.len() == 1 && start_path.segments[0].ident == ident)
|| (end_path.segments.len() == 1 && end_path.segments[0].ident == ident)
{
let start_name = start_path.segments[0].ident;
let end_name = end_path.segments[0].ident;
err.span_suggestion_verbose(
pat.span,
"if you meant to destructure a `Range`, use the struct pattern",
format!("std::ops::Range {{ start: {}, end: {} }}", start_name, end_name),
Applicability::MaybeIncorrect,
);
}
}
}
fn suggest_swapping_misplaced_self_ty_and_trait(
&mut self,
err: &mut Diag<'_>,

View file

@ -0,0 +1,15 @@
use std::ops::Range;
fn test_basic_range(r: Range<u32>) {
let start..end = r;
//~^ ERROR cannot find value `start` in this scope
//~| ERROR cannot find value `end` in this scope
}
fn test_different_names(r: Range<u32>) {
let min..max = r;
//~^ ERROR cannot find value `min` in this scope
//~| ERROR cannot find value `max` in this scope
}
fn main() {}

View file

@ -0,0 +1,66 @@
error[E0425]: cannot find value `start` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:4:9
|
LL | let start..end = r;
| ^^^^^ not found in this scope
|
help: if you meant to destructure a `Range`, use the struct pattern
|
LL - let start..end = r;
LL + let std::ops::Range { start: start, end: end } = r;
|
error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:4:16
|
LL | let start..end = r;
| ^^^ not found in this scope
|
help: if you meant to destructure a `Range`, use the struct pattern
|
LL - let start..end = r;
LL + let std::ops::Range { start: start, end: end } = r;
|
error[E0425]: cannot find value `min` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:10:9
|
LL | let min..max = r;
| ^^^
...
LL | fn main() {}
| --------- similarly named function `main` defined here
|
help: if you meant to destructure a `Range`, use the struct pattern
|
LL - let min..max = r;
LL + let std::ops::Range { start: min, end: max } = r;
|
help: a function with a similar name exists
|
LL | let main..max = r;
| +
help: consider importing this function
|
LL + use std::cmp::min;
|
error[E0425]: cannot find value `max` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:10:14
|
LL | let min..max = r;
| ^^^ not found in this scope
|
help: if you meant to destructure a `Range`, use the struct pattern
|
LL - let min..max = r;
LL + let std::ops::Range { start: min, end: max } = r;
|
help: consider importing this function
|
LL + use std::cmp::max;
|
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0425`.