about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPavel Grigorenko <GrigorenkoPV@ya.ru>2024-09-23 02:12:53 +0300
committerPavel Grigorenko <GrigorenkoPV@ya.ru>2024-09-23 02:12:53 +0300
commit9cd668beed38a8e98933733854a224c70998bf16 (patch)
tree0ac8ea5f3c8b8839224855bac9725e19c323137f
parent8ed95d1d9e149b5242316c91b3849c58f8320470 (diff)
downloadrust-9cd668beed38a8e98933733854a224c70998bf16.tar.gz
rust-9cd668beed38a8e98933733854a224c70998bf16.zip
Parser: better error messages for `@` in struct patterns
-rw-r--r--compiler/rustc_parse/messages.ftl7
-rw-r--r--compiler/rustc_parse/src/errors.rs19
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs54
-rw-r--r--tests/ui/pattern/at-in-struct-patterns.rs14
-rw-r--r--tests/ui/pattern/at-in-struct-patterns.stderr69
5 files changed, 150 insertions, 13 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 6cb851eb8df..5d1c300b453 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -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
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index e9fe2e6c1dd..e16ab5b4b06 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2584,6 +2584,25 @@ pub(crate) struct EnumPatternInsteadOfIdentifier {
 }
 
 #[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 {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 647df25c82e..b4cbe1accf4 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -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};
@@ -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;
@@ -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;
@@ -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>) {
diff --git a/tests/ui/pattern/at-in-struct-patterns.rs b/tests/ui/pattern/at-in-struct-patterns.rs
new file mode 100644
index 00000000000..e8fad61f317
--- /dev/null
+++ b/tests/ui/pattern/at-in-struct-patterns.rs
@@ -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 `@`
+}
diff --git a/tests/ui/pattern/at-in-struct-patterns.stderr b/tests/ui/pattern/at-in-struct-patterns.stderr
new file mode 100644
index 00000000000..ff75edfe681
--- /dev/null
+++ b/tests/ui/pattern/at-in-struct-patterns.stderr
@@ -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`.