diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 233a4c48862a..0348764009b2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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<'_>, diff --git a/tests/ui/resolve/suggest-range-struct-destructuring.rs b/tests/ui/resolve/suggest-range-struct-destructuring.rs new file mode 100644 index 000000000000..f690a7cad23f --- /dev/null +++ b/tests/ui/resolve/suggest-range-struct-destructuring.rs @@ -0,0 +1,15 @@ +use std::ops::Range; + +fn test_basic_range(r: Range) { + 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) { + let min..max = r; + //~^ ERROR cannot find value `min` in this scope + //~| ERROR cannot find value `max` in this scope +} + +fn main() {} diff --git a/tests/ui/resolve/suggest-range-struct-destructuring.stderr b/tests/ui/resolve/suggest-range-struct-destructuring.stderr new file mode 100644 index 000000000000..291abf0d01d3 --- /dev/null +++ b/tests/ui/resolve/suggest-range-struct-destructuring.stderr @@ -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`.