Parser: better error messages for @ in struct patterns

This commit is contained in:
Pavel Grigorenko 2024-09-23 02:12:53 +03:00
parent 8ed95d1d9e
commit 9cd668beed
5 changed files with 150 additions and 13 deletions

View File

@ -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

View File

@ -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 {

View File

@ -17,15 +17,16 @@
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};
@ -1433,7 +1434,7 @@ fn parse_pat_box(&mut self) -> PResult<'a, PatKind> {
/// 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;
@ -1454,12 +1455,22 @@ fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)
// 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;
@ -1594,6 +1605,23 @@ fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)
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>) {

View File

@ -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 `@`
}

View File

@ -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`.