mirror of
https://github.com/rust-lang/rust
synced 2024-10-01 22:34:35 +00:00
Rollup merge of #48490 - petrochenkov:orpat, r=eddyb
Implement multiple patterns with `|` in `if let` and `while let` (RFC 2175) cc https://github.com/rust-lang/rust/issues/48215
This commit is contained in:
commit
9523c82543
|
@ -2956,7 +2956,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||||
|
|
||||||
// Desugar ExprIfLet
|
// Desugar ExprIfLet
|
||||||
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
|
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
|
||||||
ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
|
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
|
||||||
// to:
|
// to:
|
||||||
//
|
//
|
||||||
// match <sub_expr> {
|
// match <sub_expr> {
|
||||||
|
@ -2970,8 +2970,8 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||||
{
|
{
|
||||||
let body = self.lower_block(body, false);
|
let body = self.lower_block(body, false);
|
||||||
let body_expr = P(self.expr_block(body, ThinVec::new()));
|
let body_expr = P(self.expr_block(body, ThinVec::new()));
|
||||||
let pat = self.lower_pat(pat);
|
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
|
||||||
arms.push(self.arm(hir_vec![pat], body_expr));
|
arms.push(self.arm(pats, body_expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// _ => [<else_opt>|()]
|
// _ => [<else_opt>|()]
|
||||||
|
@ -3000,7 +3000,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||||
|
|
||||||
// Desugar ExprWhileLet
|
// Desugar ExprWhileLet
|
||||||
// From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
|
// From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
|
||||||
ExprKind::WhileLet(ref pat, ref sub_expr, ref body, opt_label) => {
|
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
|
||||||
// to:
|
// to:
|
||||||
//
|
//
|
||||||
// [opt_ident]: loop {
|
// [opt_ident]: loop {
|
||||||
|
@ -3021,8 +3021,8 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||||
// `<pat> => <body>`
|
// `<pat> => <body>`
|
||||||
let pat_arm = {
|
let pat_arm = {
|
||||||
let body_expr = P(self.expr_block(body, ThinVec::new()));
|
let body_expr = P(self.expr_block(body, ThinVec::new()));
|
||||||
let pat = self.lower_pat(pat);
|
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
|
||||||
self.arm(hir_vec![pat], body_expr)
|
self.arm(pats, body_expr)
|
||||||
};
|
};
|
||||||
|
|
||||||
// `_ => break`
|
// `_ => break`
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
|
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
|
||||||
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue};
|
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue};
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
|
use syntax::ptr::P;
|
||||||
|
|
||||||
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
|
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
|
||||||
use errors::{DiagnosticBuilder, DiagnosticId};
|
use errors::{DiagnosticBuilder, DiagnosticId};
|
||||||
|
@ -2329,17 +2330,17 @@ fn binding_mode_map(&mut self, pat: &Pat) -> BindingMap {
|
||||||
|
|
||||||
// check that all of the arms in an or-pattern have exactly the
|
// check that all of the arms in an or-pattern have exactly the
|
||||||
// same set of bindings, with the same binding modes for each.
|
// same set of bindings, with the same binding modes for each.
|
||||||
fn check_consistent_bindings(&mut self, arm: &Arm) {
|
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) {
|
||||||
if arm.pats.is_empty() {
|
if pats.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut missing_vars = FxHashMap();
|
let mut missing_vars = FxHashMap();
|
||||||
let mut inconsistent_vars = FxHashMap();
|
let mut inconsistent_vars = FxHashMap();
|
||||||
for (i, p) in arm.pats.iter().enumerate() {
|
for (i, p) in pats.iter().enumerate() {
|
||||||
let map_i = self.binding_mode_map(&p);
|
let map_i = self.binding_mode_map(&p);
|
||||||
|
|
||||||
for (j, q) in arm.pats.iter().enumerate() {
|
for (j, q) in pats.iter().enumerate() {
|
||||||
if i == j {
|
if i == j {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2404,9 +2405,8 @@ fn resolve_arm(&mut self, arm: &Arm) {
|
||||||
self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
|
self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This has to happen *after* we determine which
|
// This has to happen *after* we determine which pat_idents are variants
|
||||||
// pat_idents are variants
|
self.check_consistent_bindings(&arm.pats);
|
||||||
self.check_consistent_bindings(arm);
|
|
||||||
|
|
||||||
walk_list!(self, visit_expr, &arm.guard);
|
walk_list!(self, visit_expr, &arm.guard);
|
||||||
self.visit_expr(&arm.body);
|
self.visit_expr(&arm.body);
|
||||||
|
@ -2490,7 +2490,9 @@ fn fresh_binding(&mut self,
|
||||||
&ident.node.name.as_str())
|
&ident.node.name.as_str())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(..) if pat_src == PatternSource::Match => {
|
Some(..) if pat_src == PatternSource::Match ||
|
||||||
|
pat_src == PatternSource::IfLet ||
|
||||||
|
pat_src == PatternSource::WhileLet => {
|
||||||
// `Variant1(a) | Variant2(a)`, ok
|
// `Variant1(a) | Variant2(a)`, ok
|
||||||
// Reuse definition from the first `a`.
|
// Reuse definition from the first `a`.
|
||||||
def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident.node];
|
def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident.node];
|
||||||
|
@ -3480,11 +3482,16 @@ fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) {
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
|
ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
|
||||||
self.visit_expr(subexpression);
|
self.visit_expr(subexpression);
|
||||||
|
|
||||||
self.ribs[ValueNS].push(Rib::new(NormalRibKind));
|
self.ribs[ValueNS].push(Rib::new(NormalRibKind));
|
||||||
self.resolve_pattern(pattern, PatternSource::IfLet, &mut FxHashMap());
|
let mut bindings_list = FxHashMap();
|
||||||
|
for pat in pats {
|
||||||
|
self.resolve_pattern(pat, PatternSource::IfLet, &mut bindings_list);
|
||||||
|
}
|
||||||
|
// This has to happen *after* we determine which pat_idents are variants
|
||||||
|
self.check_consistent_bindings(pats);
|
||||||
self.visit_block(if_block);
|
self.visit_block(if_block);
|
||||||
self.ribs[ValueNS].pop();
|
self.ribs[ValueNS].pop();
|
||||||
|
|
||||||
|
@ -3500,11 +3507,16 @@ fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::WhileLet(ref pattern, ref subexpression, ref block, label) => {
|
ExprKind::WhileLet(ref pats, ref subexpression, ref block, label) => {
|
||||||
self.with_resolved_label(label, expr.id, |this| {
|
self.with_resolved_label(label, expr.id, |this| {
|
||||||
this.visit_expr(subexpression);
|
this.visit_expr(subexpression);
|
||||||
this.ribs[ValueNS].push(Rib::new(NormalRibKind));
|
this.ribs[ValueNS].push(Rib::new(NormalRibKind));
|
||||||
this.resolve_pattern(pattern, PatternSource::WhileLet, &mut FxHashMap());
|
let mut bindings_list = FxHashMap();
|
||||||
|
for pat in pats {
|
||||||
|
this.resolve_pattern(pat, PatternSource::WhileLet, &mut bindings_list);
|
||||||
|
}
|
||||||
|
// This has to happen *after* we determine which pat_idents are variants
|
||||||
|
this.check_consistent_bindings(pats);
|
||||||
this.visit_block(block);
|
this.visit_block(block);
|
||||||
this.ribs[ValueNS].pop();
|
this.ribs[ValueNS].pop();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1031,6 +1031,81 @@ fn process_pat(&mut self, p: &'l ast::Pat) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_var_decl_multi(&mut self, pats: &'l [P<ast::Pat>]) {
|
||||||
|
let mut collector = PathCollector::new();
|
||||||
|
for pattern in pats {
|
||||||
|
// collect paths from the arm's patterns
|
||||||
|
collector.visit_pat(&pattern);
|
||||||
|
self.visit_pat(&pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// process collected paths
|
||||||
|
for (id, i, sp, immut) in collector.collected_idents {
|
||||||
|
match self.save_ctxt.get_path_def(id) {
|
||||||
|
HirDef::Local(id) => {
|
||||||
|
let mut value = if immut == ast::Mutability::Immutable {
|
||||||
|
self.span.snippet(sp).to_string()
|
||||||
|
} else {
|
||||||
|
"<mutable>".to_string()
|
||||||
|
};
|
||||||
|
let hir_id = self.tcx.hir.node_to_hir_id(id);
|
||||||
|
let typ = self.save_ctxt
|
||||||
|
.tables
|
||||||
|
.node_id_to_type_opt(hir_id)
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.unwrap_or(String::new());
|
||||||
|
value.push_str(": ");
|
||||||
|
value.push_str(&typ);
|
||||||
|
|
||||||
|
if !self.span.filter_generated(Some(sp), sp) {
|
||||||
|
let qualname = format!("{}${}", i.to_string(), id);
|
||||||
|
let id = ::id_from_node_id(id, &self.save_ctxt);
|
||||||
|
let span = self.span_from_span(sp);
|
||||||
|
|
||||||
|
self.dumper.dump_def(
|
||||||
|
&Access {
|
||||||
|
public: false,
|
||||||
|
reachable: false,
|
||||||
|
},
|
||||||
|
Def {
|
||||||
|
kind: DefKind::Local,
|
||||||
|
id,
|
||||||
|
span,
|
||||||
|
name: i.to_string(),
|
||||||
|
qualname,
|
||||||
|
value: typ,
|
||||||
|
parent: None,
|
||||||
|
children: vec![],
|
||||||
|
decl_id: None,
|
||||||
|
docs: String::new(),
|
||||||
|
sig: None,
|
||||||
|
attributes: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirDef::StructCtor(..) |
|
||||||
|
HirDef::VariantCtor(..) |
|
||||||
|
HirDef::Const(..) |
|
||||||
|
HirDef::AssociatedConst(..) |
|
||||||
|
HirDef::Struct(..) |
|
||||||
|
HirDef::Variant(..) |
|
||||||
|
HirDef::TyAlias(..) |
|
||||||
|
HirDef::AssociatedTy(..) |
|
||||||
|
HirDef::SelfTy(..) => {
|
||||||
|
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
|
||||||
|
}
|
||||||
|
def => error!(
|
||||||
|
"unexpected definition kind when processing collected idents: {:?}",
|
||||||
|
def
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id, ref path) in collector.collected_paths {
|
||||||
|
self.process_path(id, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process_var_decl(&mut self, p: &'l ast::Pat, value: String) {
|
fn process_var_decl(&mut self, p: &'l ast::Pat, value: String) {
|
||||||
// The local could declare multiple new vars, we must walk the
|
// The local could declare multiple new vars, we must walk the
|
||||||
|
@ -1622,17 +1697,21 @@ fn visit_expr(&mut self, ex: &'l ast::Expr) {
|
||||||
v.nest_scope(ex.id, |v| v.visit_expr(body))
|
v.nest_scope(ex.id, |v| v.visit_expr(body))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
|
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) => {
|
||||||
ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
|
|
||||||
let value = self.span.snippet(subexpression.span);
|
let value = self.span.snippet(subexpression.span);
|
||||||
self.process_var_decl(pattern, value);
|
self.process_var_decl(pattern, value);
|
||||||
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
|
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
|
||||||
self.visit_expr(subexpression);
|
self.visit_expr(subexpression);
|
||||||
visit::walk_block(self, block);
|
visit::walk_block(self, block);
|
||||||
}
|
}
|
||||||
ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => {
|
ast::ExprKind::WhileLet(ref pats, ref subexpression, ref block, _) => {
|
||||||
let value = self.span.snippet(subexpression.span);
|
self.process_var_decl_multi(pats);
|
||||||
self.process_var_decl(pattern, value);
|
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
|
||||||
|
self.visit_expr(subexpression);
|
||||||
|
visit::walk_block(self, block);
|
||||||
|
}
|
||||||
|
ast::ExprKind::IfLet(ref pats, ref subexpression, ref block, ref opt_else) => {
|
||||||
|
self.process_var_decl_multi(pats);
|
||||||
self.visit_expr(subexpression);
|
self.visit_expr(subexpression);
|
||||||
visit::walk_block(self, block);
|
visit::walk_block(self, block);
|
||||||
opt_else.as_ref().map(|el| self.visit_expr(el));
|
opt_else.as_ref().map(|el| self.visit_expr(el));
|
||||||
|
@ -1661,79 +1740,7 @@ fn visit_pat(&mut self, p: &'l ast::Pat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_arm(&mut self, arm: &'l ast::Arm) {
|
fn visit_arm(&mut self, arm: &'l ast::Arm) {
|
||||||
let mut collector = PathCollector::new();
|
self.process_var_decl_multi(&arm.pats);
|
||||||
for pattern in &arm.pats {
|
|
||||||
// collect paths from the arm's patterns
|
|
||||||
collector.visit_pat(&pattern);
|
|
||||||
self.visit_pat(&pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// process collected paths
|
|
||||||
for (id, i, sp, immut) in collector.collected_idents {
|
|
||||||
match self.save_ctxt.get_path_def(id) {
|
|
||||||
HirDef::Local(id) => {
|
|
||||||
let mut value = if immut == ast::Mutability::Immutable {
|
|
||||||
self.span.snippet(sp).to_string()
|
|
||||||
} else {
|
|
||||||
"<mutable>".to_string()
|
|
||||||
};
|
|
||||||
let hir_id = self.tcx.hir.node_to_hir_id(id);
|
|
||||||
let typ = self.save_ctxt
|
|
||||||
.tables
|
|
||||||
.node_id_to_type_opt(hir_id)
|
|
||||||
.map(|t| t.to_string())
|
|
||||||
.unwrap_or(String::new());
|
|
||||||
value.push_str(": ");
|
|
||||||
value.push_str(&typ);
|
|
||||||
|
|
||||||
if !self.span.filter_generated(Some(sp), sp) {
|
|
||||||
let qualname = format!("{}${}", i.to_string(), id);
|
|
||||||
let id = ::id_from_node_id(id, &self.save_ctxt);
|
|
||||||
let span = self.span_from_span(sp);
|
|
||||||
|
|
||||||
self.dumper.dump_def(
|
|
||||||
&Access {
|
|
||||||
public: false,
|
|
||||||
reachable: false,
|
|
||||||
},
|
|
||||||
Def {
|
|
||||||
kind: DefKind::Local,
|
|
||||||
id,
|
|
||||||
span,
|
|
||||||
name: i.to_string(),
|
|
||||||
qualname,
|
|
||||||
value: typ,
|
|
||||||
parent: None,
|
|
||||||
children: vec![],
|
|
||||||
decl_id: None,
|
|
||||||
docs: String::new(),
|
|
||||||
sig: None,
|
|
||||||
attributes: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HirDef::StructCtor(..) |
|
|
||||||
HirDef::VariantCtor(..) |
|
|
||||||
HirDef::Const(..) |
|
|
||||||
HirDef::AssociatedConst(..) |
|
|
||||||
HirDef::Struct(..) |
|
|
||||||
HirDef::Variant(..) |
|
|
||||||
HirDef::TyAlias(..) |
|
|
||||||
HirDef::AssociatedTy(..) |
|
|
||||||
HirDef::SelfTy(..) => {
|
|
||||||
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
|
|
||||||
}
|
|
||||||
def => error!(
|
|
||||||
"unexpected definition kind when processing collected idents: {:?}",
|
|
||||||
def
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (id, ref path) in collector.collected_paths {
|
|
||||||
self.process_path(id, path);
|
|
||||||
}
|
|
||||||
walk_list!(self, visit_expr, &arm.guard);
|
walk_list!(self, visit_expr, &arm.guard);
|
||||||
self.visit_expr(&arm.body);
|
self.visit_expr(&arm.body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1085,7 +1085,7 @@ pub enum ExprKind {
|
||||||
/// `if let pat = expr { block } else { expr }`
|
/// `if let pat = expr { block } else { expr }`
|
||||||
///
|
///
|
||||||
/// This is desugared to a `match` expression.
|
/// This is desugared to a `match` expression.
|
||||||
IfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>),
|
IfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>),
|
||||||
/// A while loop, with an optional label
|
/// A while loop, with an optional label
|
||||||
///
|
///
|
||||||
/// `'label: while expr { block }`
|
/// `'label: while expr { block }`
|
||||||
|
@ -1095,7 +1095,7 @@ pub enum ExprKind {
|
||||||
/// `'label: while let pat = expr { block }`
|
/// `'label: while let pat = expr { block }`
|
||||||
///
|
///
|
||||||
/// This is desugared to a combination of `loop` and `match` expressions.
|
/// This is desugared to a combination of `loop` and `match` expressions.
|
||||||
WhileLet(P<Pat>, P<Expr>, P<Block>, Option<Label>),
|
WhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Label>),
|
||||||
/// A for loop, with an optional label
|
/// A for loop, with an optional label
|
||||||
///
|
///
|
||||||
/// `'label: for pat in expr { block }`
|
/// `'label: for pat in expr { block }`
|
||||||
|
|
|
@ -446,6 +446,9 @@ pub fn new() -> Features {
|
||||||
|
|
||||||
// Use `?` as the Kleene "at most one" operator
|
// Use `?` as the Kleene "at most one" operator
|
||||||
(active, macro_at_most_once_rep, "1.25.0", Some(48075)),
|
(active, macro_at_most_once_rep, "1.25.0", Some(48075)),
|
||||||
|
|
||||||
|
// Multiple patterns with `|` in `if let` and `while let`
|
||||||
|
(active, if_while_or_patterns, "1.26.0", Some(48215)),
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_features! (
|
declare_features! (
|
||||||
|
@ -1618,6 +1621,12 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
|
||||||
ast::ExprKind::Catch(_) => {
|
ast::ExprKind::Catch(_) => {
|
||||||
gate_feature_post!(&self, catch_expr, e.span, "`catch` expression is experimental");
|
gate_feature_post!(&self, catch_expr, e.span, "`catch` expression is experimental");
|
||||||
}
|
}
|
||||||
|
ast::ExprKind::IfLet(ref pats, ..) | ast::ExprKind::WhileLet(ref pats, ..) => {
|
||||||
|
if pats.len() > 1 {
|
||||||
|
gate_feature_post!(&self, if_while_or_patterns, e.span,
|
||||||
|
"multiple patterns in `if let` and `while let` are unstable");
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
visit::walk_expr(self, e);
|
visit::walk_expr(self, e);
|
||||||
|
|
|
@ -1210,8 +1210,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
|
||||||
folder.fold_block(tr),
|
folder.fold_block(tr),
|
||||||
fl.map(|x| folder.fold_expr(x)))
|
fl.map(|x| folder.fold_expr(x)))
|
||||||
}
|
}
|
||||||
ExprKind::IfLet(pat, expr, tr, fl) => {
|
ExprKind::IfLet(pats, expr, tr, fl) => {
|
||||||
ExprKind::IfLet(folder.fold_pat(pat),
|
ExprKind::IfLet(pats.move_map(|pat| folder.fold_pat(pat)),
|
||||||
folder.fold_expr(expr),
|
folder.fold_expr(expr),
|
||||||
folder.fold_block(tr),
|
folder.fold_block(tr),
|
||||||
fl.map(|x| folder.fold_expr(x)))
|
fl.map(|x| folder.fold_expr(x)))
|
||||||
|
@ -1221,8 +1221,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
|
||||||
folder.fold_block(body),
|
folder.fold_block(body),
|
||||||
opt_label.map(|label| folder.fold_label(label)))
|
opt_label.map(|label| folder.fold_label(label)))
|
||||||
}
|
}
|
||||||
ExprKind::WhileLet(pat, expr, body, opt_label) => {
|
ExprKind::WhileLet(pats, expr, body, opt_label) => {
|
||||||
ExprKind::WhileLet(folder.fold_pat(pat),
|
ExprKind::WhileLet(pats.move_map(|pat| folder.fold_pat(pat)),
|
||||||
folder.fold_expr(expr),
|
folder.fold_expr(expr),
|
||||||
folder.fold_block(body),
|
folder.fold_block(body),
|
||||||
opt_label.map(|label| folder.fold_label(label)))
|
opt_label.map(|label| folder.fold_label(label)))
|
||||||
|
|
|
@ -3228,7 +3228,7 @@ pub fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>)
|
||||||
-> PResult<'a, P<Expr>> {
|
-> PResult<'a, P<Expr>> {
|
||||||
let lo = self.prev_span;
|
let lo = self.prev_span;
|
||||||
self.expect_keyword(keywords::Let)?;
|
self.expect_keyword(keywords::Let)?;
|
||||||
let pat = self.parse_pat()?;
|
let pats = self.parse_pats()?;
|
||||||
self.expect(&token::Eq)?;
|
self.expect(&token::Eq)?;
|
||||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
let thn = self.parse_block()?;
|
let thn = self.parse_block()?;
|
||||||
|
@ -3238,7 +3238,7 @@ pub fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>)
|
||||||
} else {
|
} else {
|
||||||
(thn.span, None)
|
(thn.span, None)
|
||||||
};
|
};
|
||||||
Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pat, expr, thn, els), attrs))
|
Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pats, expr, thn, els), attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// `move |args| expr`
|
// `move |args| expr`
|
||||||
|
@ -3329,13 +3329,13 @@ pub fn parse_while_let_expr(&mut self, opt_label: Option<Label>,
|
||||||
span_lo: Span,
|
span_lo: Span,
|
||||||
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
|
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
|
||||||
self.expect_keyword(keywords::Let)?;
|
self.expect_keyword(keywords::Let)?;
|
||||||
let pat = self.parse_pat()?;
|
let pats = self.parse_pats()?;
|
||||||
self.expect(&token::Eq)?;
|
self.expect(&token::Eq)?;
|
||||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
let (iattrs, body) = self.parse_inner_attrs_and_block()?;
|
let (iattrs, body) = self.parse_inner_attrs_and_block()?;
|
||||||
attrs.extend(iattrs);
|
attrs.extend(iattrs);
|
||||||
let span = span_lo.to(body.span);
|
let span = span_lo.to(body.span);
|
||||||
return Ok(self.mk_expr(span, ExprKind::WhileLet(pat, expr, body, opt_label), attrs));
|
return Ok(self.mk_expr(span, ExprKind::WhileLet(pats, expr, body, opt_label), attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse `loop {...}`, `loop` token already eaten
|
// parse `loop {...}`, `loop` token already eaten
|
||||||
|
|
|
@ -1767,11 +1767,11 @@ fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
|
||||||
self.print_else(e.as_ref().map(|e| &**e))
|
self.print_else(e.as_ref().map(|e| &**e))
|
||||||
}
|
}
|
||||||
// "another else-if-let"
|
// "another else-if-let"
|
||||||
ast::ExprKind::IfLet(ref pat, ref expr, ref then, ref e) => {
|
ast::ExprKind::IfLet(ref pats, ref expr, ref then, ref e) => {
|
||||||
self.cbox(INDENT_UNIT - 1)?;
|
self.cbox(INDENT_UNIT - 1)?;
|
||||||
self.ibox(0)?;
|
self.ibox(0)?;
|
||||||
self.s.word(" else if let ")?;
|
self.s.word(" else if let ")?;
|
||||||
self.print_pat(pat)?;
|
self.print_pats(pats)?;
|
||||||
self.s.space()?;
|
self.s.space()?;
|
||||||
self.word_space("=")?;
|
self.word_space("=")?;
|
||||||
self.print_expr_as_cond(expr)?;
|
self.print_expr_as_cond(expr)?;
|
||||||
|
@ -1805,10 +1805,10 @@ pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block,
|
||||||
self.print_else(elseopt)
|
self.print_else(elseopt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block,
|
pub fn print_if_let(&mut self, pats: &[P<ast::Pat>], expr: &ast::Expr, blk: &ast::Block,
|
||||||
elseopt: Option<&ast::Expr>) -> io::Result<()> {
|
elseopt: Option<&ast::Expr>) -> io::Result<()> {
|
||||||
self.head("if let")?;
|
self.head("if let")?;
|
||||||
self.print_pat(pat)?;
|
self.print_pats(pats)?;
|
||||||
self.s.space()?;
|
self.s.space()?;
|
||||||
self.word_space("=")?;
|
self.word_space("=")?;
|
||||||
self.print_expr_as_cond(expr)?;
|
self.print_expr_as_cond(expr)?;
|
||||||
|
@ -2109,8 +2109,8 @@ fn print_expr_outer_attr_style(&mut self,
|
||||||
ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
||||||
self.print_if(test, blk, elseopt.as_ref().map(|e| &**e))?;
|
self.print_if(test, blk, elseopt.as_ref().map(|e| &**e))?;
|
||||||
}
|
}
|
||||||
ast::ExprKind::IfLet(ref pat, ref expr, ref blk, ref elseopt) => {
|
ast::ExprKind::IfLet(ref pats, ref expr, ref blk, ref elseopt) => {
|
||||||
self.print_if_let(pat, expr, blk, elseopt.as_ref().map(|e| &**e))?;
|
self.print_if_let(pats, expr, blk, elseopt.as_ref().map(|e| &**e))?;
|
||||||
}
|
}
|
||||||
ast::ExprKind::While(ref test, ref blk, opt_label) => {
|
ast::ExprKind::While(ref test, ref blk, opt_label) => {
|
||||||
if let Some(label) = opt_label {
|
if let Some(label) = opt_label {
|
||||||
|
@ -2122,13 +2122,13 @@ fn print_expr_outer_attr_style(&mut self,
|
||||||
self.s.space()?;
|
self.s.space()?;
|
||||||
self.print_block_with_attrs(blk, attrs)?;
|
self.print_block_with_attrs(blk, attrs)?;
|
||||||
}
|
}
|
||||||
ast::ExprKind::WhileLet(ref pat, ref expr, ref blk, opt_label) => {
|
ast::ExprKind::WhileLet(ref pats, ref expr, ref blk, opt_label) => {
|
||||||
if let Some(label) = opt_label {
|
if let Some(label) = opt_label {
|
||||||
self.print_ident(label.ident)?;
|
self.print_ident(label.ident)?;
|
||||||
self.word_space(":")?;
|
self.word_space(":")?;
|
||||||
}
|
}
|
||||||
self.head("while let")?;
|
self.head("while let")?;
|
||||||
self.print_pat(pat)?;
|
self.print_pats(pats)?;
|
||||||
self.s.space()?;
|
self.s.space()?;
|
||||||
self.word_space("=")?;
|
self.word_space("=")?;
|
||||||
self.print_expr_as_cond(expr)?;
|
self.print_expr_as_cond(expr)?;
|
||||||
|
@ -2664,6 +2664,20 @@ pub fn print_pat(&mut self, pat: &ast::Pat) -> io::Result<()> {
|
||||||
self.ann.post(self, NodePat(pat))
|
self.ann.post(self, NodePat(pat))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_pats(&mut self, pats: &[P<ast::Pat>]) -> io::Result<()> {
|
||||||
|
let mut first = true;
|
||||||
|
for p in pats {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
self.s.space()?;
|
||||||
|
self.word_space("|")?;
|
||||||
|
}
|
||||||
|
self.print_pat(p)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
|
fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
|
||||||
// I have no idea why this check is necessary, but here it
|
// I have no idea why this check is necessary, but here it
|
||||||
// is :(
|
// is :(
|
||||||
|
@ -2674,16 +2688,7 @@ fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
|
||||||
self.ibox(0)?;
|
self.ibox(0)?;
|
||||||
self.maybe_print_comment(arm.pats[0].span.lo())?;
|
self.maybe_print_comment(arm.pats[0].span.lo())?;
|
||||||
self.print_outer_attributes(&arm.attrs)?;
|
self.print_outer_attributes(&arm.attrs)?;
|
||||||
let mut first = true;
|
self.print_pats(&arm.pats)?;
|
||||||
for p in &arm.pats {
|
|
||||||
if first {
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
self.s.space()?;
|
|
||||||
self.word_space("|")?;
|
|
||||||
}
|
|
||||||
self.print_pat(p)?;
|
|
||||||
}
|
|
||||||
self.s.space()?;
|
self.s.space()?;
|
||||||
if let Some(ref e) = arm.guard {
|
if let Some(ref e) = arm.guard {
|
||||||
self.word_space("if")?;
|
self.word_space("if")?;
|
||||||
|
|
|
@ -705,15 +705,15 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||||
visitor.visit_expr(subexpression);
|
visitor.visit_expr(subexpression);
|
||||||
visitor.visit_block(block);
|
visitor.visit_block(block);
|
||||||
}
|
}
|
||||||
ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
|
ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
|
||||||
visitor.visit_pat(pattern);
|
walk_list!(visitor, visit_pat, pats);
|
||||||
visitor.visit_expr(subexpression);
|
visitor.visit_expr(subexpression);
|
||||||
visitor.visit_block(if_block);
|
visitor.visit_block(if_block);
|
||||||
walk_list!(visitor, visit_expr, optional_else);
|
walk_list!(visitor, visit_expr, optional_else);
|
||||||
}
|
}
|
||||||
ExprKind::WhileLet(ref pattern, ref subexpression, ref block, ref opt_label) => {
|
ExprKind::WhileLet(ref pats, ref subexpression, ref block, ref opt_label) => {
|
||||||
walk_list!(visitor, visit_label, opt_label);
|
walk_list!(visitor, visit_label, opt_label);
|
||||||
visitor.visit_pat(pattern);
|
walk_list!(visitor, visit_pat, pats);
|
||||||
visitor.visit_expr(subexpression);
|
visitor.visit_expr(subexpression);
|
||||||
visitor.visit_block(block);
|
visitor.visit_block(block);
|
||||||
}
|
}
|
||||||
|
|
30
src/test/run-pass/rfc-2175-or-if-while-let/basic.rs
Normal file
30
src/test/run-pass/rfc-2175-or-if-while-let/basic.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(if_while_or_patterns)]
|
||||||
|
|
||||||
|
enum E {
|
||||||
|
V(u8),
|
||||||
|
U(u8),
|
||||||
|
W,
|
||||||
|
}
|
||||||
|
use E::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut e = V(10);
|
||||||
|
|
||||||
|
if let V(x) | U(x) = e {
|
||||||
|
assert_eq!(x, 10);
|
||||||
|
}
|
||||||
|
while let V(x) | U(x) = e {
|
||||||
|
assert_eq!(x, 10);
|
||||||
|
e = W;
|
||||||
|
}
|
||||||
|
}
|
18
src/test/ui/feature-gate-if_while_or_patterns.rs
Normal file
18
src/test/ui/feature-gate-if_while_or_patterns.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let 0 | 1 = 0 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
|
||||||
|
;
|
||||||
|
}
|
||||||
|
while let 0 | 1 = 1 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
22
src/test/ui/feature-gate-if_while_or_patterns.stderr
Normal file
22
src/test/ui/feature-gate-if_while_or_patterns.stderr
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
error[E0658]: multiple patterns in `if let` and `while let` are unstable (see issue #48215)
|
||||||
|
--> $DIR/feature-gate-if_while_or_patterns.rs:12:5
|
||||||
|
|
|
||||||
|
12 | / if let 0 | 1 = 0 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
|
||||||
|
13 | | ;
|
||||||
|
14 | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= help: add #![feature(if_while_or_patterns)] to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: multiple patterns in `if let` and `while let` are unstable (see issue #48215)
|
||||||
|
--> $DIR/feature-gate-if_while_or_patterns.rs:15:5
|
||||||
|
|
|
||||||
|
15 | / while let 0 | 1 = 1 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
|
||||||
|
16 | | break;
|
||||||
|
17 | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= help: add #![feature(if_while_or_patterns)] to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
Loading…
Reference in a new issue