Rollup merge of #130725 - GrigorenkoPV:@-in-struct-patterns, r=Nadrieril

Parser: better error messages for `@` in struct patterns
This commit is contained in:
Jubilee 2024-10-02 21:26:58 -07:00 committed by GitHub
commit b7c33e2f20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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 parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
.suggestion = try switching the order .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 parse_attr_after_generic = trailing attribute after generic parameter
.label = attributes must go before parameters .label = attributes must go before parameters

View File

@ -2571,6 +2571,25 @@ pub(crate) struct EnumPatternInsteadOfIdentifier {
pub span: Span, 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)] #[derive(Diagnostic)]
#[diag(parse_dot_dot_dot_for_remaining_fields)] #[diag(parse_dot_dot_dot_for_remaining_fields)]
pub(crate) struct DotDotDotForRemainingFields { pub(crate) struct DotDotDotForRemainingFields {

View File

@ -17,15 +17,16 @@
use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos}; use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
use crate::errors::{ use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, self, AmbiguousRangePattern, AtDotDotInStructPattern, AtInStructPattern,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed,
TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern,
UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern, WrapInParens,
}; };
use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; 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. /// Parses the fields of a struct-like pattern.
fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)> { 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 etc = PatFieldsRest::None;
let mut ate_comma = true; let mut ate_comma = true;
let mut delayed_err: Option<Diag<'a>> = None; 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 // check that a comma comes after every field
if !ate_comma { if !ate_comma {
let mut err = let err = if self.token == token::At {
self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span }); 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 { if let Some(delayed) = delayed_err {
delayed.emit(); delayed.emit();
} }
self.recover_misplaced_pattern_modifiers(&fields, &mut err);
return Err(err); return Err(err);
} }
ate_comma = false; ate_comma = false;
@ -1594,6 +1605,23 @@ fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)
Ok((fields, etc)) 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 /// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest
/// the correct code. /// the correct code.
fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec<PatField>, err: &mut Diag<'a>) { 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`.