Skip to content

Commit 5c3e01a

Browse files
committed
On resolve error of [rest..], suggest [rest @ ..]
When writing a pattern to collect multiple entries of a slice in a single binding, it is easy to misremember or typo the appropriate syntax to do so, instead writing the experimental `X..` pattern syntax. When we encounter a resolve error because `X` isn't available, we suggest `X @ ..` as an alternative. ``` error[E0425]: cannot find value `rest` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 | LL | [1, rest..] => println!("{rest:?}"), | ^^^^ not found in this scope | help: if you meant to collect the rest of the slice in `rest`, use the at operator | LL | [1, rest @ ..] => println!("{rest:?}"), | + ``` Fix rust-lang#88404.
1 parent 1be1e84 commit 5c3e01a

File tree

6 files changed

+84
-0
lines changed

6 files changed

+84
-0
lines changed

compiler/rustc_resolve/src/late.rs

+8
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,8 @@ struct DiagnosticMetadata<'ast> {
603603
/// Only used for better errors on `let <pat>: <expr, not type>;`.
604604
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
605605

606+
current_pat: Option<&'ast Pat>,
607+
606608
/// Used to detect possible `if let` written without `let` and to provide structured suggestion.
607609
in_if_condition: Option<&'ast Expr>,
608610

@@ -703,6 +705,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
703705
fn visit_expr(&mut self, expr: &'ast Expr) {
704706
self.resolve_expr(expr, None);
705707
}
708+
fn visit_pat(&mut self, p: &'ast Pat) {
709+
let prev = self.diagnostic_metadata.current_pat;
710+
self.diagnostic_metadata.current_pat = Some(p);
711+
visit::walk_pat(self, p);
712+
self.diagnostic_metadata.current_pat = prev;
713+
}
706714
fn visit_local(&mut self, local: &'ast Local) {
707715
let local_spans = match local.pat.kind {
708716
// We check for this to avoid tuple struct fields.

compiler/rustc_resolve/src/late/diagnostics.rs

+27
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
431431
code,
432432
);
433433

434+
self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
434435
self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
435436

436437
if let Some((span, label)) = base_error.span_label {
@@ -1063,6 +1064,32 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
10631064
true
10641065
}
10651066

1067+
fn suggest_at_operator_in_slice_pat_with_range(
1068+
&mut self,
1069+
err: &mut Diagnostic,
1070+
path: &[Segment],
1071+
) {
1072+
if let Some(pat) = self.diagnostic_metadata.current_pat
1073+
&& let ast::PatKind::Range(Some(start), None, range) = &pat.kind
1074+
&& let ExprKind::Path(None, range_path) = &start.kind
1075+
&& let [segment] = &range_path.segments[..]
1076+
&& let [s] = path
1077+
&& segment.ident == s.ident
1078+
{
1079+
// We've encountered `[first, rest..]` where the user might have meant
1080+
// `[first, rest @ ..]` (#88404).
1081+
err.span_suggestion_verbose(
1082+
segment.ident.span.between(range.span),
1083+
format!(
1084+
"if you meant to collect the rest of the slice in `{}`, use the at operator",
1085+
segment.ident,
1086+
),
1087+
" @ ",
1088+
Applicability::MaybeIncorrect,
1089+
);
1090+
}
1091+
}
1092+
10661093
fn suggest_swapping_misplaced_self_ty_and_trait(
10671094
&mut self,
10681095
err: &mut Diagnostic,

tests/ui/match/issue-92100.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0425]: cannot find value `a` in this scope
33
|
44
LL | [a.., a] => {}
55
| ^ not found in this scope
6+
|
7+
help: if you meant to collect the rest of the slice in `a`, use the at operator
8+
|
9+
LL | [a @ .., a] => {}
10+
| +
611

712
error: aborting due to previous error
813

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
match &[1, 2, 3][..] {
3+
[1, rest..] => println!("{rest:?}"),
4+
//~^ ERROR cannot find value `rest` in this scope
5+
//~| ERROR cannot find value `rest` in this scope
6+
//~| ERROR `X..` patterns in slices are experimental
7+
_ => {}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0425]: cannot find value `rest` in this scope
2+
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
3+
|
4+
LL | [1, rest..] => println!("{rest:?}"),
5+
| ^^^^ not found in this scope
6+
|
7+
help: if you meant to collect the rest of the slice in `rest`, use the at operator
8+
|
9+
LL | [1, rest @ ..] => println!("{rest:?}"),
10+
| +
11+
12+
error[E0425]: cannot find value `rest` in this scope
13+
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35
14+
|
15+
LL | [1, rest..] => println!("{rest:?}"),
16+
| ^^^^ not found in this scope
17+
18+
error[E0658]: `X..` patterns in slices are experimental
19+
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
20+
|
21+
LL | [1, rest..] => println!("{rest:?}"),
22+
| ^^^^^^
23+
|
24+
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
25+
= help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable
26+
27+
error: aborting due to 3 previous errors
28+
29+
Some errors have detailed explanations: E0425, E0658.
30+
For more information about an error, try `rustc --explain E0425`.

tests/ui/typeck/issue-105946.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0425]: cannot find value `_y` in this scope
33
|
44
LL | let [_y..] = [Box::new(1), Box::new(2)];
55
| ^^ not found in this scope
6+
|
7+
help: if you meant to collect the rest of the slice in `_y`, use the at operator
8+
|
9+
LL | let [_y @ ..] = [Box::new(1), Box::new(2)];
10+
| +
611

712
error[E0658]: `X..` patterns in slices are experimental
813
--> $DIR/issue-105946.rs:6:10

0 commit comments

Comments
 (0)