Skip to content

Commit c9aff92

Browse files
committed
Support parentheses in patterns under feature gate
Improve recovery for trailing comma after `..`
1 parent 0ff9872 commit c9aff92

File tree

12 files changed

+166
-107
lines changed

12 files changed

+166
-107
lines changed

src/librustc/hir/lowering.rs

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,86 +2465,88 @@ impl<'a> LoweringContext<'a> {
24652465
}
24662466

24672467
fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
2468-
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
2468+
let node = match p.node {
2469+
PatKind::Wild => hir::PatKind::Wild,
2470+
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
2471+
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
2472+
// `None` can occur in body-less function signatures
2473+
def @ None | def @ Some(Def::Local(_)) => {
2474+
let canonical_id = match def {
2475+
Some(Def::Local(id)) => id,
2476+
_ => p.id
2477+
};
2478+
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
2479+
canonical_id,
2480+
respan(pth1.span, pth1.node.name),
2481+
sub.as_ref().map(|x| self.lower_pat(x)))
2482+
}
2483+
Some(def) => {
2484+
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
2485+
span: pth1.span,
2486+
def,
2487+
segments: hir_vec![
2488+
hir::PathSegment::from_name(pth1.node.name)
2489+
],
2490+
})))
2491+
}
2492+
}
2493+
}
2494+
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
2495+
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
2496+
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2497+
ImplTraitContext::Disallowed);
2498+
hir::PatKind::TupleStruct(qpath,
2499+
pats.iter().map(|x| self.lower_pat(x)).collect(),
2500+
ddpos)
2501+
}
2502+
PatKind::Path(ref qself, ref path) => {
2503+
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
2504+
ImplTraitContext::Disallowed))
2505+
}
2506+
PatKind::Struct(ref path, ref fields, etc) => {
2507+
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2508+
ImplTraitContext::Disallowed);
2509+
2510+
let fs = fields.iter()
2511+
.map(|f| {
2512+
Spanned {
2513+
span: f.span,
2514+
node: hir::FieldPat {
2515+
name: self.lower_ident(f.node.ident),
2516+
pat: self.lower_pat(&f.node.pat),
2517+
is_shorthand: f.node.is_shorthand,
2518+
},
2519+
}
2520+
})
2521+
.collect();
2522+
hir::PatKind::Struct(qpath, fs, etc)
2523+
}
2524+
PatKind::Tuple(ref elts, ddpos) => {
2525+
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
2526+
}
2527+
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
2528+
PatKind::Ref(ref inner, mutbl) => {
2529+
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
2530+
}
2531+
PatKind::Range(ref e1, ref e2, ref end) => {
2532+
hir::PatKind::Range(P(self.lower_expr(e1)),
2533+
P(self.lower_expr(e2)),
2534+
self.lower_range_end(end))
2535+
}
2536+
PatKind::Slice(ref before, ref slice, ref after) => {
2537+
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
2538+
slice.as_ref().map(|x| self.lower_pat(x)),
2539+
after.iter().map(|x| self.lower_pat(x)).collect())
2540+
}
2541+
PatKind::Paren(ref inner) => return self.lower_pat(inner),
2542+
PatKind::Mac(_) => panic!("Shouldn't exist here"),
2543+
};
24692544

2545+
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
24702546
P(hir::Pat {
24712547
id: node_id,
24722548
hir_id,
2473-
node: match p.node {
2474-
PatKind::Wild => hir::PatKind::Wild,
2475-
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
2476-
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
2477-
// `None` can occur in body-less function signatures
2478-
def @ None | def @ Some(Def::Local(_)) => {
2479-
let canonical_id = match def {
2480-
Some(Def::Local(id)) => id,
2481-
_ => p.id
2482-
};
2483-
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
2484-
canonical_id,
2485-
respan(pth1.span, pth1.node.name),
2486-
sub.as_ref().map(|x| self.lower_pat(x)))
2487-
}
2488-
Some(def) => {
2489-
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
2490-
span: pth1.span,
2491-
def,
2492-
segments: hir_vec![
2493-
hir::PathSegment::from_name(pth1.node.name)
2494-
],
2495-
})))
2496-
}
2497-
}
2498-
}
2499-
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
2500-
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
2501-
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2502-
ImplTraitContext::Disallowed);
2503-
hir::PatKind::TupleStruct(qpath,
2504-
pats.iter().map(|x| self.lower_pat(x)).collect(),
2505-
ddpos)
2506-
}
2507-
PatKind::Path(ref qself, ref path) => {
2508-
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
2509-
ImplTraitContext::Disallowed))
2510-
}
2511-
PatKind::Struct(ref path, ref fields, etc) => {
2512-
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2513-
ImplTraitContext::Disallowed);
2514-
2515-
let fs = fields.iter()
2516-
.map(|f| {
2517-
Spanned {
2518-
span: f.span,
2519-
node: hir::FieldPat {
2520-
name: self.lower_ident(f.node.ident),
2521-
pat: self.lower_pat(&f.node.pat),
2522-
is_shorthand: f.node.is_shorthand,
2523-
},
2524-
}
2525-
})
2526-
.collect();
2527-
hir::PatKind::Struct(qpath, fs, etc)
2528-
}
2529-
PatKind::Tuple(ref elts, ddpos) => {
2530-
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
2531-
}
2532-
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
2533-
PatKind::Ref(ref inner, mutbl) => {
2534-
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
2535-
}
2536-
PatKind::Range(ref e1, ref e2, ref end) => {
2537-
hir::PatKind::Range(P(self.lower_expr(e1)),
2538-
P(self.lower_expr(e2)),
2539-
self.lower_range_end(end))
2540-
}
2541-
PatKind::Slice(ref before, ref slice, ref after) => {
2542-
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
2543-
slice.as_ref().map(|x| self.lower_pat(x)),
2544-
after.iter().map(|x| self.lower_pat(x)).collect())
2545-
}
2546-
PatKind::Mac(_) => panic!("Shouldn't exist here"),
2547-
},
2549+
node,
25482550
span: p.span,
25492551
})
25502552
}

src/librustc_lint/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ impl EarlyLintPass for IllegalFloatLiteralPattern {
737737
PatKind::TupleStruct(..) |
738738
PatKind::Ref(..) |
739739
PatKind::Box(..) |
740+
PatKind::Paren(..) |
740741
PatKind::Slice(..) => (),
741742

742743
// Extract the expressions and check them

src/libsyntax/ast.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ impl Pat {
562562
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
563563
s.iter().all(|p| p.walk(it))
564564
}
565-
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
565+
PatKind::Box(ref s) | PatKind::Ref(ref s, _) | PatKind::Paren(ref s) => {
566566
s.walk(it)
567567
}
568568
PatKind::Slice(ref before, ref slice, ref after) => {
@@ -656,6 +656,8 @@ pub enum PatKind {
656656
/// `[a, b, ..i, y, z]` is represented as:
657657
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
658658
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
659+
/// Parentheses in patters used for grouping, i.e. `(PAT)`.
660+
Paren(P<Pat>),
659661
/// A macro pattern; pre-expansion
660662
Mac(Mac),
661663
}

src/libsyntax/feature_gate.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ declare_features! (
449449

450450
// Multiple patterns with `|` in `if let` and `while let`
451451
(active, if_while_or_patterns, "1.26.0", Some(48215)),
452+
453+
// Parentheses in patterns
454+
(active, pattern_parentheses, "1.26.0", None),
452455
);
453456

454457
declare_features! (
@@ -1663,6 +1666,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
16631666
gate_feature_post!(&self, dotdoteq_in_patterns, pattern.span,
16641667
"`..=` syntax in patterns is experimental");
16651668
}
1669+
PatKind::Paren(..) => {
1670+
gate_feature_post!(&self, pattern_parentheses, pattern.span,
1671+
"parentheses in patterns are unstable");
1672+
}
16661673
_ => {}
16671674
}
16681675
visit::walk_pat(self, pattern)

src/libsyntax/fold.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
11481148
slice.map(|x| folder.fold_pat(x)),
11491149
after.move_map(|x| folder.fold_pat(x)))
11501150
}
1151+
PatKind::Paren(inner) => PatKind::Paren(folder.fold_pat(inner)),
11511152
PatKind::Mac(mac) => PatKind::Mac(folder.fold_mac(mac))
11521153
},
11531154
span: folder.new_span(span)

src/libsyntax/parse/parser.rs

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,33 +3484,47 @@ impl<'a> Parser<'a> {
34843484
};
34853485
}
34863486

3487-
fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
3488-
-> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
3489-
let mut fields = vec![];
3490-
let mut ddpos = None;
3487+
// Parses a parenthesized list of patterns like
3488+
// `()`, `(p)`, `(p,)`, `(p, q)`, or `(p, .., q)`. Returns:
3489+
// - a vector of the patterns that were parsed
3490+
// - an option indicating the index of the `..` element
3491+
// - a boolean indicating whether a trailing comma was present.
3492+
// Trailing commas are significant because (p) and (p,) are different patterns.
3493+
fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
3494+
self.expect(&token::OpenDelim(token::Paren))?;
34913495

3492-
while !self.check(&token::CloseDelim(token::Paren)) {
3493-
if ddpos.is_none() && self.eat(&token::DotDot) {
3494-
ddpos = Some(fields.len());
3495-
if self.eat(&token::Comma) {
3496-
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
3497-
fields.push(self.parse_pat()?);
3496+
let mut fields = Vec::new();
3497+
let mut ddpos = None;
3498+
let mut trailing_comma = false;
3499+
loop {
3500+
if self.eat(&token::DotDot) {
3501+
if ddpos.is_none() {
3502+
ddpos = Some(fields.len());
3503+
} else {
3504+
// Emit a friendly error, ignore `..` and continue parsing
3505+
self.span_err(self.prev_span,
3506+
"`..` can only be used once per tuple or tuple struct pattern");
34983507
}
3499-
} else if ddpos.is_some() && self.eat(&token::DotDot) {
3500-
// Emit a friendly error, ignore `..` and continue parsing
3501-
self.span_err(self.prev_span, "`..` can only be used once per \
3502-
tuple or tuple struct pattern");
3503-
} else {
3508+
} else if !self.check(&token::CloseDelim(token::Paren)) {
35043509
fields.push(self.parse_pat()?);
3510+
} else {
3511+
break
35053512
}
35063513

3507-
if !self.check(&token::CloseDelim(token::Paren)) ||
3508-
(unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
3509-
self.expect(&token::Comma)?;
3514+
trailing_comma = self.eat(&token::Comma);
3515+
if !trailing_comma {
3516+
break
35103517
}
35113518
}
35123519

3513-
Ok((fields, ddpos))
3520+
if ddpos == Some(fields.len()) && trailing_comma {
3521+
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
3522+
self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
3523+
}
3524+
3525+
self.expect(&token::CloseDelim(token::Paren))?;
3526+
3527+
Ok((fields, ddpos, trailing_comma))
35143528
}
35153529

35163530
fn parse_pat_vec_elements(
@@ -3714,10 +3728,12 @@ impl<'a> Parser<'a> {
37143728
}
37153729
token::OpenDelim(token::Paren) => {
37163730
// Parse (pat,pat,pat,...) as tuple pattern
3717-
self.bump();
3718-
let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
3719-
self.expect(&token::CloseDelim(token::Paren))?;
3720-
pat = PatKind::Tuple(fields, ddpos);
3731+
let (fields, ddpos, trailing_comma) = self.parse_parenthesized_pat_list()?;
3732+
pat = if fields.len() == 1 && ddpos.is_none() && !trailing_comma {
3733+
PatKind::Paren(fields.into_iter().nth(0).unwrap())
3734+
} else {
3735+
PatKind::Tuple(fields, ddpos)
3736+
};
37213737
}
37223738
token::OpenDelim(token::Bracket) => {
37233739
// Parse [pat,pat,...] as slice pattern
@@ -3807,9 +3823,7 @@ impl<'a> Parser<'a> {
38073823
return Err(self.fatal("unexpected `(` after qualified path"));
38083824
}
38093825
// Parse tuple struct or enum pattern
3810-
self.bump();
3811-
let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
3812-
self.expect(&token::CloseDelim(token::Paren))?;
3826+
let (fields, ddpos, _) = self.parse_parenthesized_pat_list()?;
38133827
pat = PatKind::TupleStruct(path, fields, ddpos)
38143828
}
38153829
_ => pat = PatKind::Path(qself, path),

src/libsyntax/print/pprust.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,11 @@ impl<'a> State<'a> {
26592659
|s, p| s.print_pat(p))?;
26602660
self.s.word("]")?;
26612661
}
2662+
PatKind::Paren(ref inner) => {
2663+
self.popen()?;
2664+
self.print_pat(inner)?;
2665+
self.pclose()?;
2666+
}
26622667
PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
26632668
}
26642669
self.ann.post(self, NodePat(pat))

src/libsyntax/visit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
425425
walk_list!(visitor, visit_pat, tuple_elements);
426426
}
427427
PatKind::Box(ref subpattern) |
428-
PatKind::Ref(ref subpattern, _) => {
428+
PatKind::Ref(ref subpattern, _) |
429+
PatKind::Paren(ref subpattern) => {
429430
visitor.visit_pat(subpattern)
430431
}
431432
PatKind::Ident(_, ref pth1, ref optional_subpattern) => {

src/test/parse-fail/pat-tuple-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212

1313
fn main() {
1414
match 0 {
15-
(pat, ..,) => {} //~ ERROR expected pattern, found `)`
15+
(pat, ..,) => {} //~ ERROR trailing comma is not permitted after `..`
1616
}
1717
}

src/test/parse-fail/pat-tuple-6.rs renamed to src/test/run-pass/pat-tuple-7.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
11+
#![feature(pattern_parentheses)]
1212

1313
fn main() {
1414
match 0 {
15-
(pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
15+
(pat) => assert_eq!(pat, 0)
1616
}
1717
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
match 0 {
13+
(pat) => {} //~ ERROR parentheses in patterns are unstable
14+
}
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: parentheses in patterns are unstable
2+
--> $DIR/feature-gate-pattern_parentheses.rs:13:9
3+
|
4+
LL | (pat) => {} //~ ERROR parentheses in patterns are unstable
5+
| ^^^^^
6+
|
7+
= help: add #![feature(pattern_parentheses)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
If you want more information on this error, try using "rustc --explain E0658"

0 commit comments

Comments
 (0)