diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index a121b5a9bed3d..027b09b7f30b8 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -779,8 +779,7 @@ pub enum PatKind {
Ident(BindingAnnotation, Ident, Option
>),
/// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
- /// The `bool` is `true` in the presence of a `..`.
- Struct(Option
>, Path, ThinVec, /* recovered */ bool),
+ Struct(Option>, Path, ThinVec, PatFieldsRest),
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Option>, Path, ThinVec
>),
@@ -837,6 +836,15 @@ pub enum PatKind {
MacCall(P),
}
+/// Whether the `..` is present in a struct fields pattern.
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+pub enum PatFieldsRest {
+ /// `module::StructName { field, ..}`
+ Rest,
+ /// `module::StructName { field }`
+ None,
+}
+
/// The kind of borrow in an `AddrOf` expression,
/// e.g., `&place` or `&raw const place`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 017314ee4d14a..3ffa4f1f2e6eb 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -82,7 +82,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: self.lower_span(f.span),
}
}));
- break hir::PatKind::Struct(qpath, fs, *etc);
+ break hir::PatKind::Struct(qpath, fs, *etc == ast::PatFieldsRest::Rest);
}
PatKind::Tuple(pats) => {
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index d6c15ec35b603..619fd7a49ebe3 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1427,7 +1427,7 @@ impl<'a> State<'a> {
}
self.nbsp();
self.word("{");
- let empty = fields.is_empty() && !etc;
+ let empty = fields.is_empty() && *etc == ast::PatFieldsRest::None;
if !empty {
self.space();
}
@@ -1445,7 +1445,7 @@ impl<'a> State<'a> {
},
|f| f.pat.span,
);
- if *etc {
+ if *etc == ast::PatFieldsRest::Rest {
if !fields.is_empty() {
self.word_space(",");
}
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 86f555fa08bcf..c17d8472dcca3 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -488,7 +488,7 @@ impl<'a> ExtCtxt<'a> {
path: ast::Path,
field_pats: ThinVec,
) -> P {
- self.pat(span, PatKind::Struct(None, path, field_pats, false))
+ self.pat(span, PatKind::Struct(None, path, field_pats, ast::PatFieldsRest::None))
}
pub fn pat_tuple(&self, span: Span, pats: ThinVec>) -> P {
self.pat(span, PatKind::Tuple(pats))
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 80233eddb9b7f..3fc4179af65ad 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -15,7 +15,7 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::{
self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat,
- PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
+ PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult};
@@ -890,7 +890,8 @@ impl<'a> Parser<'a> {
e.span_label(path.span, "while parsing the fields for this pattern");
e.emit();
self.recover_stmt();
- (ThinVec::new(), true)
+ // When recovering, pretend we had `Foo { .. }`, to avoid cascading errors.
+ (ThinVec::new(), PatFieldsRest::Rest)
});
self.bump();
Ok(PatKind::Struct(qself, path, fields, etc))
@@ -964,9 +965,9 @@ impl<'a> Parser<'a> {
}
/// Parses the fields of a struct-like pattern.
- fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec, bool)> {
+ fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec, PatFieldsRest)> {
let mut fields = ThinVec::new();
- let mut etc = false;
+ let mut etc = PatFieldsRest::None;
let mut ate_comma = true;
let mut delayed_err: Option> = None;
let mut first_etc_and_maybe_comma_span = None;
@@ -1000,7 +1001,7 @@ impl<'a> Parser<'a> {
|| self.check_noexpect(&token::DotDotDot)
|| self.check_keyword(kw::Underscore)
{
- etc = true;
+ etc = PatFieldsRest::Rest;
let mut etc_sp = self.token.span;
if first_etc_and_maybe_comma_span.is_none() {
if let Some(comma_tok) = self
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 65600009c1d79..77adcdd0e6bfb 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -293,7 +293,7 @@ fn extend_with_struct_pat(
qself1: &Option>,
path1: &ast::Path,
fps1: &mut [ast::PatField],
- rest1: bool,
+ rest1: ast::PatFieldsRest,
start: usize,
alternatives: &mut ThinVec
>,
) -> bool {
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 8504999b8ff51..fbde0cd76c6ae 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -276,7 +276,7 @@ fn rewrite_struct_pat(
qself: &Option>,
path: &ast::Path,
fields: &[ast::PatField],
- ellipsis: bool,
+ ellipsis: ast::PatFieldsRest,
span: Span,
context: &RewriteContext<'_>,
shape: Shape,
@@ -285,11 +285,14 @@ fn rewrite_struct_pat(
let path_shape = shape.sub_width(2)?;
let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
- if fields.is_empty() && !ellipsis {
+ if fields.is_empty() && ellipsis == ast::PatFieldsRest::None {
return Some(format!("{path_str} {{}}"));
}
- let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
+ let (ellipsis_str, terminator) = match ellipsis {
+ ast::PatFieldsRest::Rest => (", ..", ".."),
+ ast::PatFieldsRest::None => ("", "}"),
+ };
// 3 = ` { `, 2 = ` }`.
let (h_shape, v_shape) =
@@ -324,7 +327,7 @@ fn rewrite_struct_pat(
let has_trailing_comma = fmt.needs_trailing_separator();
- if ellipsis {
+ if ellipsis == ast::PatFieldsRest::Rest {
if fields_str.contains('\n') || fields_str.len() > one_line_width {
// Add a missing trailing comma.
if !has_trailing_comma {