Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parser: better error messages for @ in struct patterns #130725

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 20
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
.suggestion = try switching the order

parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns
.suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @`

parse_at_in_struct_pattern = Unexpected `@` in struct pattern
.note = struct patterns use `field: pattern` syntax to bind to fields
.help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended

parse_attr_after_generic = trailing attribute after generic parameter
.label = attributes must go before parameters

Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2583,6 +2583,25 @@ pub(crate) struct EnumPatternInsteadOfIdentifier {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_at_dot_dot_in_struct_pattern)]
pub(crate) struct AtDotDotInStructPattern {
#[primary_span]
pub span: Span,
#[suggestion(code = "", style = "verbose", applicability = "machine-applicable")]
pub remove: Span,
pub ident: Ident,
}

#[derive(Diagnostic)]
#[diag(parse_at_in_struct_pattern)]
#[note]
#[help]
pub(crate) struct AtInStructPattern {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_dot_dot_dot_for_remaining_fields)]
pub(crate) struct DotDotDotForRemainingFields {
Expand Down
54 changes: 41 additions & 13 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ use thin_vec::{thin_vec, ThinVec};

use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg,
UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens,
self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern,
DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern,
EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField,
GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt,
RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed,
TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern,
UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern, WrapInParens,
};
use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
Expand Down Expand Up @@ -1433,7 +1434,7 @@ impl<'a> Parser<'a> {

/// Parses the fields of a struct-like pattern.
fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)> {
let mut fields = ThinVec::new();
let mut fields: ThinVec<PatField> = ThinVec::new();
let mut etc = PatFieldsRest::None;
let mut ate_comma = true;
let mut delayed_err: Option<Diag<'a>> = None;
Expand All @@ -1454,12 +1455,22 @@ impl<'a> Parser<'a> {

// check that a comma comes after every field
if !ate_comma {
let mut err =
self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span });
let err = if self.token == token::At {
let prev_field = fields
.last()
.expect("Unreachable on first iteration, not empty otherwise")
.ident;
self.report_misplaced_at_in_struct_pat(prev_field)
} else {
let mut err = self
.dcx()
.create_err(ExpectedCommaAfterPatternField { span: self.token.span });
self.recover_misplaced_pattern_modifiers(&fields, &mut err);
err
};
if let Some(delayed) = delayed_err {
delayed.emit();
}
self.recover_misplaced_pattern_modifiers(&fields, &mut err);
return Err(err);
}
ate_comma = false;
Expand Down Expand Up @@ -1594,6 +1605,23 @@ impl<'a> Parser<'a> {
Ok((fields, etc))
}

#[deny(rustc::untranslatable_diagnostic)]
fn report_misplaced_at_in_struct_pat(&self, prev_field: Ident) -> Diag<'a> {
debug_assert_eq!(self.token, token::At);
let span = prev_field.span.to(self.token.span);
if let Some(dot_dot_span) =
self.look_ahead(1, |t| if t == &token::DotDot { Some(t.span) } else { None })
{
self.dcx().create_err(AtDotDotInStructPattern {
span: span.to(dot_dot_span),
remove: span.until(dot_dot_span),
ident: prev_field,
})
} else {
self.dcx().create_err(AtInStructPattern { span: span })
}
}

/// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest
/// the correct code.
fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec<PatField>, err: &mut Diag<'a>) {
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/pattern/at-in-struct-patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct Foo {
field1: u8,
field2: u8,
}

fn main() {
let foo = Foo { field1: 1, field2: 2 };
let Foo { var @ field1, .. } = foo; //~ ERROR Unexpected `@` in struct pattern
dbg!(var); //~ ERROR cannot find value `var` in this scope
let Foo { field1: _, bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns
let Foo { bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns
let Foo { @ } = foo; //~ ERROR expected identifier, found `@`
let Foo { @ .. } = foo; //~ ERROR expected identifier, found `@`
}
69 changes: 69 additions & 0 deletions tests/ui/pattern/at-in-struct-patterns.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
error: Unexpected `@` in struct pattern
--> $DIR/at-in-struct-patterns.rs:8:15
|
LL | let Foo { var @ field1, .. } = foo;
| --- ^^^^^
| |
| while parsing the fields for this pattern
|
= note: struct patterns use `field: pattern` syntax to bind to fields
= help: consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended

error: `@ ..` is not supported in struct patterns
--> $DIR/at-in-struct-patterns.rs:10:26
|
LL | let Foo { field1: _, bar @ .. } = foo;
| --- ^^^^^^^^
| |
| while parsing the fields for this pattern
|
help: bind to each field separately or, if you don't need them, just remove `bar @`
|
LL - let Foo { field1: _, bar @ .. } = foo;
LL + let Foo { field1: _, .. } = foo;
|

error: `@ ..` is not supported in struct patterns
--> $DIR/at-in-struct-patterns.rs:11:15
|
LL | let Foo { bar @ .. } = foo;
| --- ^^^^^^^^
| |
| while parsing the fields for this pattern
|
help: bind to each field separately or, if you don't need them, just remove `bar @`
|
LL - let Foo { bar @ .. } = foo;
LL + let Foo { .. } = foo;
|

error: expected identifier, found `@`
--> $DIR/at-in-struct-patterns.rs:12:15
|
LL | let Foo { @ } = foo;
| --- ^ expected identifier
| |
| while parsing the fields for this pattern

error: expected identifier, found `@`
--> $DIR/at-in-struct-patterns.rs:13:15
|
LL | let Foo { @ .. } = foo;
| --- ^ expected identifier
| |
| while parsing the fields for this pattern

error[E0425]: cannot find value `var` in this scope
--> $DIR/at-in-struct-patterns.rs:9:10
|
LL | dbg!(var);
| ^^^ not found in this scope
|
help: consider importing this function
|
LL + use std::env::var;
|

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0425`.
Loading