diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f13d67b9c1584..6a2b8ca6d0126 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1061,7 +1061,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 112); +rustc_data_structures::static_assert_size!(Expr, 120); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1192,6 +1192,7 @@ impl Expr { ExprKind::Field(..) => ExprPrecedence::Field, ExprKind::Index(..) => ExprPrecedence::Index, ExprKind::Range(..) => ExprPrecedence::Range, + ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, ExprKind::Break(..) => ExprPrecedence::Break, @@ -1218,6 +1219,16 @@ pub enum RangeLimits { Closed, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum StructRest { + /// `..x`. + Base(P), + /// `..`. + Rest(Span), + /// No trailing `..` or expression. + None, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -1312,8 +1323,10 @@ pub enum ExprKind { Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). Index(P, P), - /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`). + /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). Range(Option>, Option>, RangeLimits), + /// An underscore, used in destructuring assignment to ignore a value. + Underscore, /// Variable reference, possibly containing `::` and/or type /// parameters (e.g., `foo::bar::`). @@ -1340,9 +1353,8 @@ pub enum ExprKind { /// A struct literal expression. /// - /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, - /// where `base` is the `Option`. - Struct(Path, Vec, Option>), + /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`. + Struct(Path, Vec, StructRest), /// An array literal constructed from one repeated element. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index fe9ad58c9ac84..4e9284e6cf815 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1218,6 +1218,7 @@ pub fn noop_visit_expr( visit_opt(e1, |e1| vis.visit_expr(e1)); visit_opt(e2, |e2| vis.visit_expr(e2)); } + ExprKind::Underscore => {} ExprKind::Path(qself, path) => { vis.visit_qself(qself); vis.visit_path(path); @@ -1274,7 +1275,9 @@ pub fn noop_visit_expr( ExprKind::Struct(path, fields, expr) => { vis.visit_path(path); fields.flat_map_in_place(|field| vis.flat_map_field(field)); - visit_opt(expr, |expr| vis.visit_expr(expr)); + if let StructRest::Base(expr) = expr { + vis.visit_expr(expr); + } } ExprKind::Paren(expr) => { vis.visit_expr(expr); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2ab6667ac3cf1..b85dba4ccb104 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -724,7 +724,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Struct(ref path, ref fields, ref optional_base) => { visitor.visit_path(path, expression.id); walk_list!(visitor, visit_field, fields); - walk_list!(visitor, visit_expr, optional_base); + if let StructRest::Base(expr) = optional_base { + visitor.visit_expr(expr); + } } ExprKind::Tup(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); @@ -807,6 +809,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_expr, start); walk_list!(visitor, visit_expr, end); } + ExprKind::Underscore => {} ExprKind::Path(ref maybe_qself, ref path) => { if let Some(ref qself) = *maybe_qself { visitor.visit_ty(&qself.ty); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a6ac056b93b5e..f8318b12448a2 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -8,7 +8,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_span::hygiene::ForLoopLoc; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -146,7 +146,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) } ExprKind::Assign(ref el, ref er, span) => { - hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) + self.lower_expr_assign(el, er, span, e.span) } ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( self.lower_binop(op), @@ -163,6 +163,16 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Range(ref e1, ref e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } + ExprKind::Underscore => { + self.sess + .struct_span_err( + e.span, + "expected expression, found reserved identifier `_`", + ) + .span_label(e.span, "expected expression") + .emit(); + hir::ExprKind::Err + } ExprKind::Path(ref qself, ref path) => { let qpath = self.lower_qpath( e.id, @@ -186,8 +196,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + ExprKind::Struct(ref path, ref fields, ref rest) => { + let rest = match rest { + StructRest::Base(e) => Some(self.lower_expr(e)), + StructRest::Rest(sp) => { + self.sess + .struct_span_err(*sp, "base expression required after `..`") + .span_label(*sp, "add a base expression here") + .emit(); + Some(&*self.arena.alloc(self.expr_err(*sp))) + } + StructRest::None => None, + }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, @@ -197,7 +217,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::disallowed(), )), self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, + rest, ) } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), @@ -840,6 +860,236 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// Destructure the LHS of complex assignments. + /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`. + fn lower_expr_assign( + &mut self, + lhs: &Expr, + rhs: &Expr, + eq_sign_span: Span, + whole_span: Span, + ) -> hir::ExprKind<'hir> { + // Return early in case of an ordinary assignment. + let is_ordinary = match &lhs.kind { + ExprKind::Array(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Underscore => false, + ExprKind::Call(callee, ..) => { + // Check for tuple struct constructor. + if let ExprKind::Path(qself, path) = &callee.kind { + let qpath = self.lower_qpath( + callee.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + match qpath { + hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(..), _), .. }, + ) => false, + _ => true, + } + } else { + true + } + } + // `(..)`. + ExprKind::Paren(e) => { + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + false + } else { + true + } + } + _ => true, + }; + if is_ordinary { + return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span); + } + + let mut assignments = vec![]; + + // The LHS becomes a pattern: `(lhs1, lhs2)`. + let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments); + let rhs = self.lower_expr(rhs); + + // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`. + let destructure_let = self.stmt_let_pat( + ThinVec::new(), + whole_span, + Some(rhs), + pat, + hir::LocalSource::AssignDesugar(eq_sign_span), + ); + + // `a = lhs1; b = lhs2;`. + let stmts = self + .arena + .alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter())); + + // Wrap everything in a block. + hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None) + } + + /// Convert the LHS of a destructuring assignment to a pattern. + /// Each sub-assignment is recorded in `assignments`. + fn destructure_assign( + &mut self, + lhs: &Expr, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> &'hir hir::Pat<'hir> { + match &lhs.kind { + // Underscore pattern. + ExprKind::Underscore => { + return self.pat_without_dbm(lhs.span, hir::PatKind::Wild); + } + // Slice patterns. + ExprKind::Array(elements) => { + let (pats, rest) = + self.destructure_sequence(elements, "slice", eq_sign_span, assignments); + let slice_pat = if let Some((i, span)) = rest { + let (before, after) = pats.split_at(i); + hir::PatKind::Slice( + before, + Some(self.pat_without_dbm(span, hir::PatKind::Wild)), + after, + ) + } else { + hir::PatKind::Slice(pats, None, &[]) + }; + return self.pat_without_dbm(lhs.span, slice_pat); + } + // Tuple structs. + ExprKind::Call(callee, args) => { + if let ExprKind::Path(qself, path) = &callee.kind { + let (pats, rest) = + self.destructure_sequence(args, "tuple struct", eq_sign_span, assignments); + let qpath = self.lower_qpath( + callee.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + match qpath { + hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(..), _), .. }, + ) => { + // Destructure like a tuple struct since the path is in fact a constructor. + let tuple_struct_pat = + hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); + return self.pat_without_dbm(lhs.span, tuple_struct_pat); + } + _ => { + // If the path is not a constructor, lower as an ordinary LHS. + // Typecheck will report an error later. + } + } + } + } + // Structs. + ExprKind::Struct(path, fields, rest) => { + let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { + let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); + hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat, + is_shorthand: f.is_shorthand, + span: f.span, + } + })); + let qpath = self.lower_qpath( + lhs.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let fields_omitted = match rest { + StructRest::Base(e) => { + self.sess + .struct_span_err( + e.span, + "functional record updates are not allowed in destructuring \ + assignments", + ) + .span_suggestion( + e.span, + "consider removing the trailing pattern", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); + true + } + StructRest::Rest(_) => true, + StructRest::None => false, + }; + let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); + return self.pat_without_dbm(lhs.span, struct_pat); + } + // Tuples. + ExprKind::Tup(elements) => { + let (pats, rest) = + self.destructure_sequence(elements, "tuple", eq_sign_span, assignments); + let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0)); + return self.pat_without_dbm(lhs.span, tuple_pat); + } + // `(..)`. We special-case this for consistency with declarations. + ExprKind::Paren(e) => { + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + let tuple_pat = hir::PatKind::Tuple(&[], Some(0)); + return self.pat_without_dbm(lhs.span, tuple_pat); + } + } + _ => {} + } + // Treat all other cases as normal lvalue. + let ident = Ident::new(sym::lhs, lhs.span); + let (pat, binding) = self.pat_ident(lhs.span, ident); + let ident = self.expr_ident(lhs.span, ident, binding); + let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span); + let expr = self.expr(lhs.span, assign, ThinVec::new()); + assignments.push(self.stmt_expr(lhs.span, expr)); + pat + } + + /// Destructure a sequence of expressions occurring on the LHS of an assignment. + /// Such a sequence occurs in a tuple (struct)/slice. + /// Return a sequence of corresponding patterns, and the index and the span of `..` if it + /// exists. + /// Each sub-assignment is recorded in `assignments`. + fn destructure_sequence( + &mut self, + elements: &[AstP], + ctx: &str, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) { + let mut rest = None; + let elements = + self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| { + // Check for `..` pattern. + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + if let Some((_, prev_span)) = rest { + self.ban_extra_rest_pat(e.span, prev_span, ctx); + } else { + rest = Some((i, e.span)); + } + None + } else { + Some(self.destructure_assign(e, eq_sign_span, assignments)) + } + })); + (elements, rest) + } + /// Desugar `..=` into `std::ops::RangeInclusive::new(, )`. fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { let e1 = self.lower_expr_mut(e1); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 599599f415f1c..af2f96d5e6253 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2531,6 +2531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id, kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None), span, + default_binding_modes: true, }), hir_id, ) @@ -2541,7 +2542,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { - self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span }) + self.arena.alloc(hir::Pat { + hir_id: self.next_id(), + kind, + span, + default_binding_modes: true, + }) + } + + fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { + self.arena.alloc(hir::Pat { + hir_id: self.next_id(), + kind, + span, + default_binding_modes: false, + }) } fn ty_path( diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a1cbcde1f4291..e4e7b24d29e52 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -273,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Construct a `Pat` with the `HirId` of `p.id` lowered. fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { - self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span }) + self.arena.alloc(hir::Pat { + hir_id: self.lower_node_id(p.id), + kind, + span: p.span, + default_binding_modes: true, + }) } /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern. - fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { + crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { self.diagnostic() .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx)) .span_label(sp, &format!("can only be used once per {} pattern", ctx)) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index f20084497671f..181783441f3ff 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,6 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); + if sess.parse_sess.span_diagnostic.err_count() == 0 { + // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is + // involved, so we only emit errors where there are no other parsing errors. + gate_all!(destructuring_assignment, "destructuring assignments are unstable"); + } // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 9fcba90244330..51317c34e1bba 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1729,7 +1729,7 @@ impl<'a> State<'a> { &mut self, path: &ast::Path, fields: &[ast::Field], - wth: &Option>, + rest: &ast::StructRest, attrs: &[ast::Attribute], ) { self.print_path(path, true, 0); @@ -1750,22 +1750,21 @@ impl<'a> State<'a> { }, |f| f.span, ); - match *wth { - Some(ref expr) => { + match rest { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { self.ibox(INDENT_UNIT); if !fields.is_empty() { self.s.word(","); self.s.space(); } self.s.word(".."); - self.print_expr(expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.s.word(",") + if let ast::StructRest::Base(ref expr) = *rest { + self.print_expr(expr); } + self.end(); } + ast::StructRest::None if !fields.is_empty() => self.s.word(","), + _ => {} } self.s.word("}"); } @@ -1891,8 +1890,8 @@ impl<'a> State<'a> { ast::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(element, count, attrs); } - ast::ExprKind::Struct(ref path, ref fields, ref wth) => { - self.print_expr_struct(path, &fields[..], wth, attrs); + ast::ExprKind::Struct(ref path, ref fields, ref rest) => { + self.print_expr_struct(path, &fields[..], rest, attrs); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(&exprs[..], attrs); @@ -2069,6 +2068,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren(e, fake_prec); } } + ast::ExprKind::Underscore => self.s.word("_"), ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), ast::ExprKind::Break(opt_label, ref opt_expr) => { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1c9bfb902d61a..30f0fc6cddfa2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -298,7 +298,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, fields: Vec, ) -> P { - self.expr(span, ast::ExprKind::Struct(path, fields, None)) + self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None)) } pub fn expr_struct_ident( &self, diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ad926a810e6bf..84114fc773533 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -610,6 +610,9 @@ declare_features! ( /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), + /// Allows the use of destructuring assignments. + (active, destructuring_assignment, "1.49.0", Some(71126), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b9ec18688c5f2..6767041ecee83 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -732,6 +732,9 @@ pub struct Pat<'hir> { pub hir_id: HirId, pub kind: PatKind<'hir>, pub span: Span, + // Whether to use default binding modes. + // At present, this is false only for destructuring assignment. + pub default_binding_modes: bool, } impl Pat<'_> { @@ -1680,6 +1683,9 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, + /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// The span is that of the `=` sign. + AssignDesugar(Span), } /// Hints at the original code for a `match _ { .. }`. diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 30b700a1d4f63..04d456936eba6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None), hir::LocalSource::AsyncFn => ("async fn binding", None), hir::LocalSource::AwaitDesugar => ("`await` future binding", None), + hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; self.check_irrefutable(&loc.pat, msg, sp); self.check_patterns(&loc.pat); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c2a13d4b0dec1..6e199b78b6789 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1116,6 +1116,9 @@ impl<'a> Parser<'a> { } else { self.parse_lit_expr(attrs) } + } else if self.eat_keyword(kw::Underscore) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); + Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) } else { self.parse_lit_expr(attrs) } @@ -2087,7 +2090,7 @@ impl<'a> Parser<'a> { recover: bool, ) -> PResult<'a, P> { let mut fields = Vec::new(); - let mut base = None; + let mut base = ast::StructRest::None; let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); @@ -2102,8 +2105,14 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; + // We permit `.. }` on the left-hand side of a destructuring assignment. + if self.token == token::CloseDelim(token::Brace) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); + base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); + break; + } match self.parse_expr() { - Ok(e) => base = Some(e), + Ok(e) => base = ast::StructRest::Base(e), Err(mut e) if recover => { e.emit(); self.recover_stmt(); diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index dbb5e3cc9f066..40d60a8394be3 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -816,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> { path: &'tcx hir::QPath<'tcx>, fields: &'tcx [hir::Field<'tcx>], variant: &'tcx ty::VariantDef, - base: Option<&'tcx hir::Expr<'tcx>>, + rest: Option<&'tcx hir::Expr<'tcx>>, ) { if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { @@ -836,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> { } } - walk_list!(self, visit_expr, base); + if let Some(base) = rest { + self.visit_expr(&base); + } } fn process_method_call( @@ -1399,7 +1401,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { debug!("visit_expr {:?}", ex.kind); self.process_macro_use(ex.span); match ex.kind { - hir::ExprKind::Struct(ref path, ref fields, ref base) => { + hir::ExprKind::Struct(ref path, ref fields, ref rest) => { let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id); let adt = match self.save_ctxt.typeck_results().expr_ty_opt(&hir_expr) { 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> { } }; let res = self.save_ctxt.get_path_res(hir_expr.hir_id); - self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *base) + self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *rest) } hir::ExprKind::MethodCall(ref seg, _, args, _) => { self.process_method_call(ex, seg, args) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1a6c45b6c80d2..2324dba80f543 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -434,6 +434,7 @@ symbols! { deref_mut, deref_target, derive, + destructuring_assignment, diagnostic, direct, discriminant_kind, diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 324aa1a66a6d5..af19ad08c1d08 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -718,39 +718,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { - match &expr.kind { - ExprKind::Array(comps) | ExprKind::Tup(comps) => { - comps.iter().all(|e| self.is_destructuring_place_expr(e)) - } - ExprKind::Struct(_path, fields, rest) => { - rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) - && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr)) - } - _ => expr.is_syntactic_place_expr(), - } - } - pub(crate) fn check_lhs_assignable( &self, lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, expr_span: &Span, ) { - if !lhs.is_syntactic_place_expr() { - // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. - let mut err = self.tcx.sess.struct_span_err_with_code( - *expr_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); - err.span_label(lhs.span, "cannot assign to this expression"); - if self.is_destructuring_place_expr(lhs) { - err.note("destructuring assignments are not currently supported"); - err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372"); - } - err.emit(); + if lhs.is_syntactic_place_expr() { + return; } + + // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. + let mut err = self.tcx.sess.struct_span_err_with_code( + *expr_span, + "invalid left-hand side of assignment", + DiagnosticId::Error(err_code.into()), + ); + err.span_label(lhs.span, "cannot assign to this expression"); + err.emit(); } /// Type check assignment expression `expr` of form `lhs = rhs`. diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a820661d8432a..9f94d61cf0aba 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -17,6 +17,7 @@ use rustc_hir::{ExprKind, Node, QPath}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty}; +use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::{self, MultiSpan, Span}; @@ -515,6 +516,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check a `let` statement. pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Check for destructuring assignment. + match local.source { + hir::LocalSource::AssignDesugar(eq_sign_span) + if !self.tcx.features().destructuring_assignment => + { + feature_err( + &self.tcx.sess.parse_sess, + sym::destructuring_assignment, + eq_sign_span, + "destructuring assignments are unstable", + ) + .span_label(local.pat.span, "cannot assign to this expression") + .emit(); + } + _ => {} + } + // Determine and write the type which we'll check the pattern against. let ty = self.local_ty(local.span, local.hir_id).decl_ty; self.write_ty(local.hir_id, ty); diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 53bc2069b76ce..d0e6edfd9db30 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -270,6 +270,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> AdjustMode { + // When we perform destructuring assignment, we disable default match bindings, which are + // unintuitive in this context. + if !pat.default_binding_modes { + return AdjustMode::Reset; + } match &pat.kind { // Type checking these product-like types successfully always require // that the expected type be of those types and not reference types. diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index ba0f22513a146..7b31b9f3915f4 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) { debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); ignore_err!(self.with_mc(|mc| { - mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| { + mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| { // `ref x` pattern if let PatKind::Binding(..) = kind { if let Some(ty::BindByReference(mutbl)) = diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index caf55bec53ddd..bff92d8607ece 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -155,7 +155,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { }, 17 => { let path = Path::from_ident(Ident::from_str("S")); - g(ExprKind::Struct(path, vec![], Some(make_x()))); + g(ExprKind::Struct(path, vec![], StructRest::Base(make_x()))); }, 18 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs index d7cf1b7700514..39536f12e3bb5 100644 --- a/src/test/ui/bad/bad-expr-lhs.rs +++ b/src/test/ui/bad/bad-expr-lhs.rs @@ -1,10 +1,12 @@ fn main() { 1 = 2; //~ ERROR invalid left-hand side of assignment 1 += 2; //~ ERROR invalid left-hand side of assignment - (1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment + (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable + //~| ERROR invalid left-hand side of assignment + //~| ERROR invalid left-hand side of assignment let (a, b) = (1, 2); - (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment + (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable None = Some(3); //~ ERROR invalid left-hand side of assignment } diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr index a195e1054d099..584e77d0b8794 100644 --- a/src/test/ui/bad/bad-expr-lhs.stderr +++ b/src/test/ui/bad/bad-expr-lhs.stderr @@ -14,34 +14,53 @@ LL | 1 += 2; | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/bad-expr-lhs.rs:4:12 | LL | (1, 2) = (3, 4); | ------ ^ | | | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0070]: invalid left-hand side of assignment - --> $DIR/bad-expr-lhs.rs:7:12 + --> $DIR/bad-expr-lhs.rs:4:12 + | +LL | (1, 2) = (3, 4); + | - ^ + | | + | cannot assign to this expression + +error[E0070]: invalid left-hand side of assignment + --> $DIR/bad-expr-lhs.rs:4:12 + | +LL | (1, 2) = (3, 4); + | - ^ + | | + | cannot assign to this expression + +error[E0658]: destructuring assignments are unstable + --> $DIR/bad-expr-lhs.rs:9:12 | LL | (a, b) = (3, 4); | ------ ^ | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0070]: invalid left-hand side of assignment - --> $DIR/bad-expr-lhs.rs:9:10 + --> $DIR/bad-expr-lhs.rs:11:10 | LL | None = Some(3); | ---- ^ | | | cannot assign to this expression -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0067, E0070. +Some errors have detailed explanations: E0067, E0070, E0658. For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs index 74e9461803c52..46728d453df9a 100644 --- a/src/test/ui/cross/cross-file-errors/main.rs +++ b/src/test/ui/cross/cross-file-errors/main.rs @@ -4,4 +4,5 @@ mod underscore; fn main() { underscore!(); //~^ ERROR expected expression, found reserved identifier `_` + //~^^ ERROR destructuring assignments are unstable } diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr index f9101d8a583d3..e1bdb6fab71f1 100644 --- a/src/test/ui/cross/cross-file-errors/main.stderr +++ b/src/test/ui/cross/cross-file-errors/main.stderr @@ -1,3 +1,18 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore.rs:8:9 + | +LL | _ + | ^ + | + ::: $DIR/main.rs:5:5 + | +LL | underscore!(); + | -------------- in this macro invocation + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: expected expression, found reserved identifier `_` --> $DIR/underscore.rs:8:9 | @@ -11,5 +26,6 @@ LL | underscore!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs new file mode 100644 index 0000000000000..adecd0ff291f9 --- /dev/null +++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs @@ -0,0 +1,7 @@ +#![feature(destructuring_assignment)] + +fn main() { + let mut x = &0; + let mut y = &0; + (x, y) = &(1, 2); //~ ERROR mismatched types +} diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr new file mode 100644 index 0000000000000..e6161fdfa2441 --- /dev/null +++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/default-match-bindings-forbidden.rs:6:5 + | +LL | (x, y) = &(1, 2); + | ^^^^^^ ------- this expression has type `&({integer}, {integer})` + | | + | expected reference, found tuple + | + = note: expected type `&({integer}, {integer})` + found tuple `(_, _)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs new file mode 100644 index 0000000000000..0d45ff7da7249 --- /dev/null +++ b/src/test/ui/destructuring-assignment/nested_destructure.rs @@ -0,0 +1,20 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct Struct { + a: S, + b: T, +} + +struct TupleStruct(S, T); + +fn main() { + let (a, b, c, d); + Struct { a: TupleStruct((a, b), c), b: [d] } = + Struct { a: TupleStruct((0, 1), 2), b: [3] }; + assert_eq!((a, b, c, d), (0, 1, 2, 3)); + + // unnested underscore: just discard + _ = 1; +} diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index 876c9efea2647..249fba7f920bc 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -3,23 +3,25 @@ struct S { x: u8, y: u8 } fn main() { let (a, b) = (1, 2); - (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment + (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment - //~^ ERROR binary assignment operation `+=` cannot be applied + //~| ERROR binary assignment operation `+=` cannot be applied - [a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment + [a, b] = [3, 4]; //~ ERROR destructuring assignments are unstable [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment - //~^ ERROR binary assignment operation `+=` cannot be applied + //~| ERROR binary assignment operation `+=` cannot be applied let s = S { x: 3, y: 4 }; - S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment + S { x: a, y: b } = s; //~ ERROR destructuring assignments are unstable S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment - //~^ ERROR binary assignment operation `+=` cannot be applied + //~| ERROR binary assignment operation `+=` cannot be applied - S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment + S { x: a, ..s } = S { x: 3, y: 4 }; + //~^ ERROR functional record updates are not allowed in destructuring assignments + //~| ERROR destructuring assignments are unstable let c = 3; - ((a, b), c) = ((3, 4), 5); //~ ERROR invalid left-hand side of assignment + ((a, b), c) = ((3, 4), 5); //~ ERROR destructuring assignments are unstable } diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index d4e25930d22d7..e8a38a910c500 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,4 +1,10 @@ -error[E0070]: invalid left-hand side of assignment +error: functional record updates are not allowed in destructuring assignments + --> $DIR/note-unsupported.rs:20:17 + | +LL | S { x: a, ..s } = S { x: 3, y: 4 }; + | ^ help: consider removing the trailing pattern + +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:6:12 | LL | (a, b) = (3, 4); @@ -6,8 +12,8 @@ LL | (a, b) = (3, 4); | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})` --> $DIR/note-unsupported.rs:7:5 @@ -24,11 +30,8 @@ LL | (a, b) += (3, 4); | ------ ^^ | | | cannot assign to this expression - | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:10:12 | LL | [a, b] = [3, 4]; @@ -36,8 +39,8 @@ LL | [a, b] = [3, 4]; | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]` --> $DIR/note-unsupported.rs:11:5 @@ -54,11 +57,8 @@ LL | [a, b] += [3, 4]; | ------ ^^ | | | cannot assign to this expression - | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:16:22 | LL | S { x: a, y: b } = s; @@ -66,8 +66,8 @@ LL | S { x: a, y: b } = s; | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `S` --> $DIR/note-unsupported.rs:17:5 @@ -86,11 +86,8 @@ LL | S { x: a, y: b } += s; | ---------------- ^^ | | | cannot assign to this expression - | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:20:21 | LL | S { x: a, ..s } = S { x: 3, y: 4 }; @@ -98,21 +95,21 @@ LL | S { x: a, ..s } = S { x: 3, y: 4 }; | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:24:17 +error[E0658]: destructuring assignments are unstable + --> $DIR/note-unsupported.rs:26:17 | LL | ((a, b), c) = ((3, 4), 5); | ----------- ^ | | | cannot assign to this expression | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0067, E0070, E0368. +Some errors have detailed explanations: E0067, E0368, E0658. For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs new file mode 100644 index 0000000000000..76cdc1260fcde --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + [a, b] = [0, 1]; + assert_eq!((a, b), (0, 1)); + let mut c; + [a, .., b, c] = [1, 2, 3, 4, 5]; + assert_eq!((a, b, c), (1, 4, 5)); + [_, a, _] = [1, 2, 3]; + assert_eq!((a, b), (2, 4)); + [..] = [1, 2, 3]; + [c, ..] = [5, 6, 6]; + assert_eq!(c, 5); +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs new file mode 100644 index 0000000000000..90d93892f7f22 --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -0,0 +1,8 @@ +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern + [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2 + [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2 +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr new file mode 100644 index 0000000000000..cc412c72df51d --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -0,0 +1,23 @@ +error: `..` can only be used once per slice pattern + --> $DIR/slice_destructure_fail.rs:5:14 + | +LL | [a, .., b, ..] = [0, 1]; + | -- ^^ can only be used once per slice pattern + | | + | previously used here + +error[E0527]: pattern requires 3 elements but array has 2 + --> $DIR/slice_destructure_fail.rs:6:3 + | +LL | [a, a, b] = [1, 2]; + | ^^^^^^^^^ expected 2 elements + +error[E0527]: pattern requires 1 element but array has 2 + --> $DIR/slice_destructure_fail.rs:7:3 + | +LL | [_] = [1, 2]; + | ^^^ expected 2 elements + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0527`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs new file mode 100644 index 0000000000000..2bcbd9d0d742e --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -0,0 +1,21 @@ +// run-pass + +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (mut a, mut b); + Struct { a, b } = Struct { a: 0, b: 1 }; + assert_eq!((a, b), (0, 1)); + Struct { a: b, b: a } = Struct { a: 1, b: 2 }; + assert_eq!((a,b), (2, 1)); + Struct { a: _, b } = Struct { a: 1, b: 2 }; + assert_eq!((a, b), (2, 2)); + Struct { a, .. } = Struct { a: 1, b: 3 }; + assert_eq!((a, b), (1, 2)); + Struct { .. } = Struct { a: 1, b: 4 }; + assert_eq!((a, b), (1, 2)); +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs new file mode 100644 index 0000000000000..4aa327b61f497 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -0,0 +1,17 @@ +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (mut a, b); + let mut c; + let d = Struct { a: 0, b: 1 }; + Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` + Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` + //~| ERROR expected identifier, found reserved identifier `_` + Struct { a, ..d } = Struct { a: 1, b: 2 }; + //~^ ERROR functional record updates are not allowed in destructuring assignments + Struct { a, .. }; //~ ERROR base expression required after `..` +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr new file mode 100644 index 0000000000000..c8ddc4bafc17c --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -0,0 +1,45 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/struct_destructure_fail.rs:12:17 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ------ ^ expected identifier, found reserved identifier + | | + | while parsing this struct + +error: functional record updates are not allowed in destructuring assignments + --> $DIR/struct_destructure_fail.rs:14:19 + | +LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; + | ^ help: consider removing the trailing pattern + +error: base expression required after `..` + --> $DIR/struct_destructure_fail.rs:16:19 + | +LL | Struct { a, .. }; + | ^ add a base expression here + +error[E0026]: struct `Struct` does not have a field named `c` + --> $DIR/struct_destructure_fail.rs:11:20 + | +LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; + | ^ struct `Struct` does not have this field + +error[E0027]: pattern does not mention field `b` + --> $DIR/struct_destructure_fail.rs:12:5 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ^^^^^^^^^^^^^^^ missing field `b` + | +help: include the missing field in the pattern + | +LL | Struct { a, b, _ } = Struct { a: 1, b: 2 }; + | ^^^ +help: if you don't care about this missing field, you can explicitely ignore it + | +LL | Struct { a, .., _ } = Struct { a: 1, b: 2 }; + | ^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0026, E0027. +For more information about an error, try `rustc --explain E0026`. diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs new file mode 100644 index 0000000000000..b0f223072ba52 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -0,0 +1,19 @@ +// run-pass + +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + (a, b) = (0, 1); + assert_eq!((a, b), (0, 1)); + (b, a) = (a, b); + assert_eq!((a, b), (1, 0)); + (a, .., b) = (1, 2); + assert_eq!((a, b), (1, 2)); + (_, a) = (1, 2); + assert_eq!((a, b), (2, 2)); + (..) = (3, 4); + assert_eq!((a, b), (2, 2)); + (b, ..) = (5, 6, 7); + assert_eq!(b, 5); +} diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs new file mode 100644 index 0000000000000..a98d73b632da4 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -0,0 +1,8 @@ +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern + (a, a, b) = (1, 2); //~ ERROR mismatched types + (_,) = (1, 2); //~ ERROR mismatched types +} diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr new file mode 100644 index 0000000000000..9a83635d295d6 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -0,0 +1,33 @@ +error: `..` can only be used once per tuple pattern + --> $DIR/tuple_destructure_fail.rs:5:16 + | +LL | (a, .., b, ..) = (0, 1); + | -- ^^ can only be used once per tuple pattern + | | + | previously used here + +error[E0308]: mismatched types + --> $DIR/tuple_destructure_fail.rs:6:5 + | +LL | (a, a, b) = (1, 2); + | ^^^^^^^^^ ------ this expression has type `({integer}, {integer})` + | | + | expected a tuple with 2 elements, found one with 3 elements + | + = note: expected type `({integer}, {integer})` + found tuple `(_, _, _)` + +error[E0308]: mismatched types + --> $DIR/tuple_destructure_fail.rs:7:5 + | +LL | (_,) = (1, 2); + | ^^^^ ------ this expression has type `({integer}, {integer})` + | | + | expected a tuple with 2 elements, found one with 1 element + | + = note: expected type `({integer}, {integer})` + found tuple `(_,)` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs new file mode 100644 index 0000000000000..1cbdd3cca931f --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +fn main() { + let (mut a, mut b); + TupleStruct(a, b) = TupleStruct(0, 1); + assert_eq!((a, b), (0, 1)); + TupleStruct(a, .., b) = TupleStruct(1, 2); + assert_eq!((a, b), (1, 2)); + TupleStruct(_, a) = TupleStruct(2, 2); + assert_eq!((a, b), (2, 2)); + TupleStruct(..) = TupleStruct(3, 4); + assert_eq!((a, b), (2, 2)); +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs new file mode 100644 index 0000000000000..3b8a491d8dea9 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -0,0 +1,19 @@ +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +fn main() { + let (mut a, mut b); + TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + //~^ ERROR `..` can only be used once per tuple struct pattern + TupleStruct(a, a, b) = TupleStruct(1, 2); + //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields + // Check if `test` is recognized as not a tuple struct but a function call: + test() = TupleStruct(0, 0); //~ ERROR invalid left-hand side of assignment + TupleStruct(_) = TupleStruct(1, 2); + //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields +} + +fn test() -> TupleStruct { + TupleStruct(0, 0) +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr new file mode 100644 index 0000000000000..e50191a40d93c --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -0,0 +1,38 @@ +error: `..` can only be used once per tuple struct pattern + --> $DIR/tuple_struct_destructure_fail.rs:7:27 + | +LL | TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + | -- ^^ can only be used once per tuple struct pattern + | | + | previously used here + +error[E0023]: this pattern has 3 fields, but the corresponding tuple struct has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:9:5 + | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +LL | TupleStruct(a, a, b) = TupleStruct(1, 2); + | ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3 + +error[E0070]: invalid left-hand side of assignment + --> $DIR/tuple_struct_destructure_fail.rs:12:12 + | +LL | test() = TupleStruct(0, 0); + | ------ ^ + | | + | cannot assign to this expression + +error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:13:5 + | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +LL | TupleStruct(_) = TupleStruct(1, 2); + | ^^^^^^^^^^^^^^ expected 2 fields, found 1 + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0023, E0070. +For more information about an error, try `rustc --explain E0023`. diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs new file mode 100644 index 0000000000000..4ed4f56702c32 --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs @@ -0,0 +1,10 @@ +fn main() {} + +struct S { x : u32 } + +#[cfg(FALSE)] +fn foo() { + _; //~ ERROR destructuring assignments are unstable + + S { x: 5, .. }; //~ ERROR destructuring assignments are unstable +} diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr new file mode 100644 index 0000000000000..a5ed761a01c33 --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr @@ -0,0 +1,21 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore-range-expr-gating.rs:7:5 + | +LL | _; + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore-range-expr-gating.rs:9:15 + | +LL | S { x: 5, .. }; + | ^^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.rs b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs new file mode 100644 index 0000000000000..c1c5c2cd3cebb --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs @@ -0,0 +1,23 @@ +// run-pass + +#![feature(destructuring_assignment)] + +#![warn(unused_assignments)] + +fn main() { + let mut a; + // Assignment occurs left-to-right. + // However, we emit warnings when this happens, so it is clear that this is happening. + (a, a) = (0, 1); //~ WARN value assigned to `a` is never read + assert_eq!(a, 1); + + // We can't always tell when a variable is being assigned to twice, which is why we don't try + // to emit an error, which would be fallible. + let mut x = 1; + (*foo(&mut x), *foo(&mut x)) = (5, 6); + assert_eq!(x, 6); +} + +fn foo<'a>(x: &'a mut u32) -> &'a mut u32 { + x +} diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr new file mode 100644 index 0000000000000..b87ef6f1571c1 --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr @@ -0,0 +1,15 @@ +warning: value assigned to `a` is never read + --> $DIR/warn-unused-duplication.rs:11:6 + | +LL | (a, a) = (0, 1); + | ^ + | +note: the lint level is defined here + --> $DIR/warn-unused-duplication.rs:5:9 + | +LL | #![warn(unused_assignments)] + | ^^^^^^^^^^^^^^^^^^ + = help: maybe it is overwritten before being read? + +warning: 1 warning emitted + diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs new file mode 100644 index 0000000000000..e7801f0e8ec2b --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs @@ -0,0 +1,4 @@ +fn main() { + let (a, b) = (0, 1); + (a, b) = (2, 3); //~ ERROR destructuring assignments are unstable +} diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr new file mode 100644 index 0000000000000..62e71decb32a0 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr @@ -0,0 +1,14 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/feature-gate-destructuring_assignment.rs:3:12 + | +LL | (a, b) = (2, 3); + | ------ ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs index a8ea3faefe876..adff79682635b 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs @@ -10,10 +10,16 @@ fn main() { let _: usize = foo(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: S = S(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: usize = T::baz(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable } diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr index a6d1c4b859f2f..d45cb709422d0 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr @@ -1,3 +1,57 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 + | +LL | let _: usize = foo(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27 + | +LL | let _: usize = foo(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18 + | +LL | let _: S = S(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21 + | +LL | let _: S = S(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error: expected expression, found reserved identifier `_` --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 | @@ -11,28 +65,29 @@ LL | let _: usize = foo(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18 | LL | let _: S = S(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21 | LL | let _: S = S(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27 | LL | let _: usize = T::baz(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30 | LL | let _: usize = T::baz(_, _); | ^ expected expression -error: aborting due to 6 previous errors +error: aborting due to 12 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/if-let-typo.rs b/src/test/ui/suggestions/if-let-typo.rs index 87def13c476c7..688b6e8265826 100644 --- a/src/test/ui/suggestions/if-let-typo.rs +++ b/src/test/ui/suggestions/if-let-typo.rs @@ -2,7 +2,12 @@ fn main() { let foo = Some(0); let bar = None; if Some(x) = foo {} //~ ERROR cannot find value `x` in this scope + //~^ ERROR mismatched types + //~^^ ERROR destructuring assignments are unstable if Some(foo) = bar {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable if 3 = foo {} //~ ERROR mismatched types if Some(3) = foo {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable + //~^^ ERROR invalid left-hand side of assignment } diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr index d8e50cae55ad1..2c8ec11d39495 100644 --- a/src/test/ui/suggestions/if-let-typo.stderr +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -9,23 +9,42 @@ help: you might have meant to use pattern matching LL | if let Some(x) = foo {} | ^^^ +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:4:16 + | +LL | if Some(x) = foo {} + | ------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:5:8 + --> $DIR/if-let-typo.rs:4:8 | -LL | if Some(foo) = bar {} - | ^^^^^^^^^^^^^^^ expected `bool`, found `()` +LL | if Some(x) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` + +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:7:18 | -help: you might have meant to use pattern matching +LL | if Some(foo) = bar {} + | --------- ^ + | | + | cannot assign to this expression | -LL | if let Some(foo) = bar {} - | ^^^ -help: you might have meant to compare for equality + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:7:8 | -LL | if Some(foo) == bar {} - | ^^ +LL | if Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:6:8 + --> $DIR/if-let-typo.rs:9:8 | LL | if 3 = foo {} | ^^^^^^^ expected `bool`, found `()` @@ -35,22 +54,32 @@ help: you might have meant to use pattern matching LL | if let 3 = foo {} | ^^^ -error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:7:8 +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:10:16 | LL | if Some(3) = foo {} - | ^^^^^^^^^^^^^ expected `bool`, found `()` + | ------- ^ + | | + | cannot assign to this expression | -help: you might have meant to use pattern matching + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0070]: invalid left-hand side of assignment + --> $DIR/if-let-typo.rs:10:16 | -LL | if let Some(3) = foo {} - | ^^^ -help: you might have meant to compare for equality +LL | if Some(3) = foo {} + | - ^ + | | + | cannot assign to this expression + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:10:8 | -LL | if Some(3) == foo {} - | ^^ +LL | if Some(3) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` -error: aborting due to 4 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0070, E0308, E0425, E0658. +For more information about an error, try `rustc --explain E0070`. diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index 0e9feef3746e7..7b65e664867ff 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -107,6 +107,15 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { + match (l, r) { + (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), + (StructRest::Rest(_), StructRest::Rest(_)) => true, + (StructRest::None, StructRest::None) => true, + _ => false, + } +} + pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { @@ -150,7 +159,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { - eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) }, _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs index 625120b880eb5..1fcd41e4dbfed 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs @@ -170,6 +170,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Paren(..) + | ast::ExprKind::Underscore | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..)