Skip to content

Commit fb02d99

Browse files
varkorfanzier
authored andcommitted
Fix handling of rest without base in LHS
1 parent ced8ce4 commit fb02d99

File tree

9 files changed

+58
-52
lines changed

9 files changed

+58
-52
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,16 @@ pub enum RangeLimits {
12191219
Closed,
12201220
}
12211221

1222+
#[derive(Clone, Encodable, Decodable, Debug)]
1223+
pub enum StructRest {
1224+
/// `..x`.
1225+
Base(P<Expr>),
1226+
/// `..`.
1227+
Rest(Span),
1228+
/// No trailing `..` or expression.
1229+
None,
1230+
}
1231+
12221232
#[derive(Clone, Encodable, Decodable, Debug)]
12231233
pub enum ExprKind {
12241234
/// A `box x` expression.
@@ -1343,9 +1353,8 @@ pub enum ExprKind {
13431353

13441354
/// A struct literal expression.
13451355
///
1346-
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
1347-
/// where `base` is the `Option<Expr>`.
1348-
Struct(Path, Vec<Field>, Option<P<Expr>>),
1356+
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`.
1357+
Struct(Path, Vec<Field>, StructRest),
13491358

13501359
/// An array literal constructed from one repeated element.
13511360
///

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
12751275
ExprKind::Struct(path, fields, expr) => {
12761276
vis.visit_path(path);
12771277
fields.flat_map_in_place(|field| vis.flat_map_field(field));
1278-
visit_opt(expr, |expr| vis.visit_expr(expr));
1278+
if let StructRest::Base(expr) = expr {
1279+
vis.visit_expr(expr);
1280+
}
12791281
}
12801282
ExprKind::Paren(expr) => {
12811283
vis.visit_expr(expr);

compiler/rustc_ast/src/visit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
724724
ExprKind::Struct(ref path, ref fields, ref optional_base) => {
725725
visitor.visit_path(path, expression.id);
726726
walk_list!(visitor, visit_field, fields);
727-
walk_list!(visitor, visit_expr, optional_base);
727+
if let StructRest::Base(expr) = optional_base {
728+
visitor.visit_expr(expr);
729+
}
728730
}
729731
ExprKind::Tup(ref subexpressions) => {
730732
walk_list!(visitor, visit_expr, subexpressions);

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
989989
}
990990
}
991991
// Structs.
992-
ExprKind::Struct(path, fields, base) => {
992+
ExprKind::Struct(path, fields, rest) => {
993993
let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| {
994994
let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments);
995995
hir::FieldPat {
@@ -1007,28 +1007,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
10071007
ParamMode::Optional,
10081008
ImplTraitContext::disallowed(),
10091009
);
1010-
let fields_omitted = match base {
1011-
None => false,
1012-
Some(e) => {
1013-
match e.kind {
1014-
ExprKind::Underscore => {}
1015-
_ => self
1016-
.sess
1017-
.struct_span_err(
1018-
e.span,
1019-
"functional record updates are not allowed in destructuring \
1020-
assignments",
1021-
)
1022-
.span_suggestion(
1023-
e.span,
1024-
"consider removing the trailing pattern",
1025-
String::new(),
1026-
rustc_errors::Applicability::MachineApplicable,
1027-
)
1028-
.emit(),
1029-
}
1010+
let fields_omitted = match rest {
1011+
StructRest::Base(e) => {
1012+
self.sess
1013+
.struct_span_err(
1014+
e.span,
1015+
"functional record updates are not allowed in destructuring \
1016+
assignments",
1017+
)
1018+
.span_suggestion(
1019+
e.span,
1020+
"consider removing the trailing pattern",
1021+
String::new(),
1022+
rustc_errors::Applicability::MachineApplicable,
1023+
)
1024+
.emit();
10301025
true
10311026
}
1027+
StructRest::Rest(_) => true,
1028+
StructRest::None => false,
10321029
};
10331030
let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
10341031
return self.pat(lhs.span, struct_pat);

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,7 +1729,7 @@ impl<'a> State<'a> {
17291729
&mut self,
17301730
path: &ast::Path,
17311731
fields: &[ast::Field],
1732-
wth: &Option<P<ast::Expr>>,
1732+
rest: &ast::StructRest,
17331733
attrs: &[ast::Attribute],
17341734
) {
17351735
self.print_path(path, true, 0);
@@ -1750,22 +1750,21 @@ impl<'a> State<'a> {
17501750
},
17511751
|f| f.span,
17521752
);
1753-
match *wth {
1754-
Some(ref expr) => {
1753+
match rest {
1754+
StructRest::Base(_) | StructRest::Rest(_) => {
17551755
self.ibox(INDENT_UNIT);
17561756
if !fields.is_empty() {
17571757
self.s.word(",");
17581758
self.s.space();
17591759
}
17601760
self.s.word("..");
1761-
self.print_expr(expr);
1762-
self.end();
1763-
}
1764-
_ => {
1765-
if !fields.is_empty() {
1766-
self.s.word(",")
1761+
if let StructRest::Base(ref expr) = *rest {
1762+
self.print_expr(expr);
17671763
}
1764+
self.end();
17681765
}
1766+
StructRest::None if !fields.is_empty() => self.s.word(","),
1767+
_ => {}
17691768
}
17701769
self.s.word("}");
17711770
}
@@ -1891,8 +1890,8 @@ impl<'a> State<'a> {
18911890
ast::ExprKind::Repeat(ref element, ref count) => {
18921891
self.print_expr_repeat(element, count, attrs);
18931892
}
1894-
ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
1895-
self.print_expr_struct(path, &fields[..], wth, attrs);
1893+
ast::ExprKind::Struct(ref path, ref fields, ref rest) => {
1894+
self.print_expr_struct(path, &fields[..], rest, attrs);
18961895
}
18971896
ast::ExprKind::Tup(ref exprs) => {
18981897
self.print_expr_tup(&exprs[..], attrs);

compiler/rustc_expand/src/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ impl<'a> ExtCtxt<'a> {
298298
path: ast::Path,
299299
fields: Vec<ast::Field>,
300300
) -> P<ast::Expr> {
301-
self.expr(span, ast::ExprKind::Struct(path, fields, None))
301+
self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None))
302302
}
303303
pub fn expr_struct_ident(
304304
&self,

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,7 +2089,7 @@ impl<'a> Parser<'a> {
20892089
recover: bool,
20902090
) -> PResult<'a, P<Expr>> {
20912091
let mut fields = Vec::new();
2092-
let mut base = None;
2092+
let mut base = ast::StructRest::None;
20932093
let mut recover_async = false;
20942094

20952095
attrs.extend(self.parse_inner_attributes()?);
@@ -2104,18 +2104,13 @@ impl<'a> Parser<'a> {
21042104
while self.token != token::CloseDelim(token::Brace) {
21052105
if self.eat(&token::DotDot) {
21062106
let exp_span = self.prev_token.span;
2107-
// If the struct ends in `.. }`, treat it like `.. _ }`.
2108-
// AST lowering will then report an error if it's not on the LHS of an assignment.
2107+
// We permit `.. }` on the left-hand side of a destructuring assignment.
21092108
if self.token == token::CloseDelim(token::Brace) {
2110-
base = Some(self.mk_expr(
2111-
self.prev_token.span.shrink_to_hi(),
2112-
ExprKind::Underscore,
2113-
AttrVec::new(),
2114-
));
2109+
base = StructRest::Rest(self.prev_token.span.shrink_to_hi());
21152110
break;
21162111
}
21172112
match self.parse_expr() {
2118-
Ok(e) => base = Some(e),
2113+
Ok(e) => base = StructRest::Base(e),
21192114
Err(mut e) if recover => {
21202115
e.emit();
21212116
self.recover_stmt();

compiler/rustc_save_analysis/src/dump_visitor.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> {
816816
path: &'tcx hir::QPath<'tcx>,
817817
fields: &'tcx [hir::Field<'tcx>],
818818
variant: &'tcx ty::VariantDef,
819-
base: Option<&'tcx hir::Expr<'tcx>>,
819+
rest: &'tcx StructRest,
820820
) {
821821
if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) {
822822
if let hir::QPath::Resolved(_, path) = path {
@@ -836,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> {
836836
}
837837
}
838838

839-
walk_list!(self, visit_expr, base);
839+
if let StructRest::Base(base) = rest {
840+
self.visit_expr(base);
841+
}
840842
}
841843

842844
fn process_method_call(
@@ -1399,7 +1401,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
13991401
debug!("visit_expr {:?}", ex.kind);
14001402
self.process_macro_use(ex.span);
14011403
match ex.kind {
1402-
hir::ExprKind::Struct(ref path, ref fields, ref base) => {
1404+
hir::ExprKind::Struct(ref path, ref fields, ref rest) => {
14031405
let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id);
14041406
let adt = match self.save_ctxt.typeck_results().expr_ty_opt(&hir_expr) {
14051407
Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(),
@@ -1409,7 +1411,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
14091411
}
14101412
};
14111413
let res = self.save_ctxt.get_path_res(hir_expr.hir_id);
1412-
self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *base)
1414+
self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), rest)
14131415
}
14141416
hir::ExprKind::MethodCall(ref seg, _, args, _) => {
14151417
self.process_method_call(ex, seg, args)

src/test/ui/destructuring-assignment/struct_destructure_fail.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ error: base expression required after `..`
1616
--> $DIR/struct_destructure_fail.rs:16:19
1717
|
1818
LL | Struct { a, .. };
19-
| ^ add the base expression here
19+
| ^ add a base expression here
2020

2121
error[E0026]: struct `Struct` does not have a field named `c`
2222
--> $DIR/struct_destructure_fail.rs:11:20

0 commit comments

Comments
 (0)