Skip to content

Commit

Permalink
On resolve error of [rest..], suggest [rest @ ..]
Browse files Browse the repository at this point in the history
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 #88404.
  • Loading branch information
estebank committed Nov 17, 2023
1 parent 1be1e84 commit 5c3e01a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 0 deletions.
8 changes: 8 additions & 0 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ struct DiagnosticMetadata<'ast> {
/// Only used for better errors on `let <pat>: <expr, not type>;`.
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,

current_pat: Option<&'ast Pat>,

/// Used to detect possible `if let` written without `let` and to provide structured suggestion.
in_if_condition: Option<&'ast Expr>,

Expand Down Expand Up @@ -703,6 +705,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
fn visit_expr(&mut self, expr: &'ast Expr) {
self.resolve_expr(expr, None);
}
fn visit_pat(&mut self, p: &'ast Pat) {
let prev = self.diagnostic_metadata.current_pat;
self.diagnostic_metadata.current_pat = Some(p);
visit::walk_pat(self, p);
self.diagnostic_metadata.current_pat = prev;
}
fn visit_local(&mut self, local: &'ast Local) {
let local_spans = match local.pat.kind {
// We check for this to avoid tuple struct fields.
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
code,
);

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

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

fn suggest_at_operator_in_slice_pat_with_range(
&mut self,
err: &mut Diagnostic,
path: &[Segment],
) {
if let Some(pat) = self.diagnostic_metadata.current_pat
&& let ast::PatKind::Range(Some(start), None, range) = &pat.kind
&& let ExprKind::Path(None, range_path) = &start.kind
&& let [segment] = &range_path.segments[..]
&& let [s] = path
&& segment.ident == s.ident
{
// We've encountered `[first, rest..]` where the user might have meant
// `[first, rest @ ..]` (#88404).
err.span_suggestion_verbose(
segment.ident.span.between(range.span),
format!(
"if you meant to collect the rest of the slice in `{}`, use the at operator",
segment.ident,
),
" @ ",
Applicability::MaybeIncorrect,
);
}
}

fn suggest_swapping_misplaced_self_ty_and_trait(
&mut self,
err: &mut Diagnostic,
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/match/issue-92100.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ error[E0425]: cannot find value `a` in this scope
|
LL | [a.., a] => {}
| ^ not found in this scope
|
help: if you meant to collect the rest of the slice in `a`, use the at operator
|
LL | [a @ .., a] => {}
| +

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn main() {
match &[1, 2, 3][..] {
[1, rest..] => println!("{rest:?}"),
//~^ ERROR cannot find value `rest` in this scope
//~| ERROR cannot find value `rest` in this scope
//~| ERROR `X..` patterns in slices are experimental
_ => {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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:?}"),
| +

error[E0425]: cannot find value `rest` in this scope
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35
|
LL | [1, rest..] => println!("{rest:?}"),
| ^^^^ not found in this scope

error[E0658]: `X..` patterns in slices are experimental
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
|
LL | [1, rest..] => println!("{rest:?}"),
| ^^^^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0425, E0658.
For more information about an error, try `rustc --explain E0425`.
5 changes: 5 additions & 0 deletions tests/ui/typeck/issue-105946.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ error[E0425]: cannot find value `_y` in this scope
|
LL | let [_y..] = [Box::new(1), Box::new(2)];
| ^^ not found in this scope
|
help: if you meant to collect the rest of the slice in `_y`, use the at operator
|
LL | let [_y @ ..] = [Box::new(1), Box::new(2)];
| +

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

0 comments on commit 5c3e01a

Please sign in to comment.