Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support parentheses in patterns under feature gate #48500

Merged
merged 1 commit into from
Mar 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 78 additions & 76 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2465,86 +2465,88 @@ impl<'a> LoweringContext<'a> {
}

fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
let node = match p.node {
PatKind::Wild => hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
// `None` can occur in body-less function signatures
def @ None | def @ Some(Def::Local(_)) => {
let canonical_id = match def {
Some(Def::Local(id)) => id,
_ => p.id
};
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
canonical_id,
respan(pth1.span, pth1.node.name),
sub.as_ref().map(|x| self.lower_pat(x)))
}
Some(def) => {
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
span: pth1.span,
def,
segments: hir_vec![
hir::PathSegment::from_name(pth1.node.name)
],
})))
}
}
}
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
ImplTraitContext::Disallowed);
hir::PatKind::TupleStruct(qpath,
pats.iter().map(|x| self.lower_pat(x)).collect(),
ddpos)
}
PatKind::Path(ref qself, ref path) => {
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
ImplTraitContext::Disallowed))
}
PatKind::Struct(ref path, ref fields, etc) => {
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
ImplTraitContext::Disallowed);

let fs = fields.iter()
.map(|f| {
Spanned {
span: f.span,
node: hir::FieldPat {
name: self.lower_ident(f.node.ident),
pat: self.lower_pat(&f.node.pat),
is_shorthand: f.node.is_shorthand,
},
}
})
.collect();
hir::PatKind::Struct(qpath, fs, etc)
}
PatKind::Tuple(ref elts, ddpos) => {
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
PatKind::Ref(ref inner, mutbl) => {
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
}
PatKind::Range(ref e1, ref e2, ref end) => {
hir::PatKind::Range(P(self.lower_expr(e1)),
P(self.lower_expr(e2)),
self.lower_range_end(end))
}
PatKind::Slice(ref before, ref slice, ref after) => {
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
slice.as_ref().map(|x| self.lower_pat(x)),
after.iter().map(|x| self.lower_pat(x)).collect())
}
PatKind::Paren(ref inner) => return self.lower_pat(inner),
PatKind::Mac(_) => panic!("Shouldn't exist here"),
};

let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
P(hir::Pat {
id: node_id,
hir_id,
node: match p.node {
PatKind::Wild => hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
// `None` can occur in body-less function signatures
def @ None | def @ Some(Def::Local(_)) => {
let canonical_id = match def {
Some(Def::Local(id)) => id,
_ => p.id
};
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
canonical_id,
respan(pth1.span, pth1.node.name),
sub.as_ref().map(|x| self.lower_pat(x)))
}
Some(def) => {
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
span: pth1.span,
def,
segments: hir_vec![
hir::PathSegment::from_name(pth1.node.name)
],
})))
}
}
}
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
ImplTraitContext::Disallowed);
hir::PatKind::TupleStruct(qpath,
pats.iter().map(|x| self.lower_pat(x)).collect(),
ddpos)
}
PatKind::Path(ref qself, ref path) => {
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
ImplTraitContext::Disallowed))
}
PatKind::Struct(ref path, ref fields, etc) => {
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
ImplTraitContext::Disallowed);

let fs = fields.iter()
.map(|f| {
Spanned {
span: f.span,
node: hir::FieldPat {
name: self.lower_ident(f.node.ident),
pat: self.lower_pat(&f.node.pat),
is_shorthand: f.node.is_shorthand,
},
}
})
.collect();
hir::PatKind::Struct(qpath, fs, etc)
}
PatKind::Tuple(ref elts, ddpos) => {
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
PatKind::Ref(ref inner, mutbl) => {
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
}
PatKind::Range(ref e1, ref e2, ref end) => {
hir::PatKind::Range(P(self.lower_expr(e1)),
P(self.lower_expr(e2)),
self.lower_range_end(end))
}
PatKind::Slice(ref before, ref slice, ref after) => {
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
slice.as_ref().map(|x| self.lower_pat(x)),
after.iter().map(|x| self.lower_pat(x)).collect())
}
PatKind::Mac(_) => panic!("Shouldn't exist here"),
},
node,
span: p.span,
})
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ impl EarlyLintPass for IllegalFloatLiteralPattern {
PatKind::TupleStruct(..) |
PatKind::Ref(..) |
PatKind::Box(..) |
PatKind::Paren(..) |
PatKind::Slice(..) => (),

// Extract the expressions and check them
Expand Down
4 changes: 3 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ impl Pat {
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
s.iter().all(|p| p.walk(it))
}
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
PatKind::Box(ref s) | PatKind::Ref(ref s, _) | PatKind::Paren(ref s) => {
s.walk(it)
}
PatKind::Slice(ref before, ref slice, ref after) => {
Expand Down Expand Up @@ -656,6 +656,8 @@ pub enum PatKind {
/// `[a, b, ..i, y, z]` is represented as:
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
/// Parentheses in patters used for grouping, i.e. `(PAT)`.
Paren(P<Pat>),
/// A macro pattern; pre-expansion
Mac(Mac),
}
Expand Down
7 changes: 7 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ declare_features! (

// Multiple patterns with `|` in `if let` and `while let`
(active, if_while_or_patterns, "1.26.0", Some(48215)),

// Parentheses in patterns
(active, pattern_parentheses, "1.26.0", None),
);

declare_features! (
Expand Down Expand Up @@ -1663,6 +1666,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_feature_post!(&self, dotdoteq_in_patterns, pattern.span,
"`..=` syntax in patterns is experimental");
}
PatKind::Paren(..) => {
gate_feature_post!(&self, pattern_parentheses, pattern.span,
"parentheses in patterns are unstable");
}
_ => {}
}
visit::walk_pat(self, pattern)
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
slice.map(|x| folder.fold_pat(x)),
after.move_map(|x| folder.fold_pat(x)))
}
PatKind::Paren(inner) => PatKind::Paren(folder.fold_pat(inner)),
PatKind::Mac(mac) => PatKind::Mac(folder.fold_mac(mac))
},
span: folder.new_span(span)
Expand Down
66 changes: 40 additions & 26 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3484,33 +3484,47 @@ impl<'a> Parser<'a> {
};
}

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

while !self.check(&token::CloseDelim(token::Paren)) {
if ddpos.is_none() && self.eat(&token::DotDot) {
ddpos = Some(fields.len());
if self.eat(&token::Comma) {
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
fields.push(self.parse_pat()?);
let mut fields = Vec::new();
let mut ddpos = None;
let mut trailing_comma = false;
loop {
if self.eat(&token::DotDot) {
if ddpos.is_none() {
ddpos = Some(fields.len());
} else {
// Emit a friendly error, ignore `..` and continue parsing
self.span_err(self.prev_span,
"`..` can only be used once per tuple or tuple struct pattern");
}
} else if ddpos.is_some() && self.eat(&token::DotDot) {
// Emit a friendly error, ignore `..` and continue parsing
self.span_err(self.prev_span, "`..` can only be used once per \
tuple or tuple struct pattern");
} else {
} else if !self.check(&token::CloseDelim(token::Paren)) {
fields.push(self.parse_pat()?);
} else {
break
}

if !self.check(&token::CloseDelim(token::Paren)) ||
(unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
self.expect(&token::Comma)?;
trailing_comma = self.eat(&token::Comma);
if !trailing_comma {
break
}
}

Ok((fields, ddpos))
if ddpos == Some(fields.len()) && trailing_comma {
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
}

self.expect(&token::CloseDelim(token::Paren))?;

Ok((fields, ddpos, trailing_comma))
}

fn parse_pat_vec_elements(
Expand Down Expand Up @@ -3714,10 +3728,12 @@ impl<'a> Parser<'a> {
}
token::OpenDelim(token::Paren) => {
// Parse (pat,pat,pat,...) as tuple pattern
self.bump();
let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
self.expect(&token::CloseDelim(token::Paren))?;
pat = PatKind::Tuple(fields, ddpos);
let (fields, ddpos, trailing_comma) = self.parse_parenthesized_pat_list()?;
pat = if fields.len() == 1 && ddpos.is_none() && !trailing_comma {
PatKind::Paren(fields.into_iter().nth(0).unwrap())
} else {
PatKind::Tuple(fields, ddpos)
};
}
token::OpenDelim(token::Bracket) => {
// Parse [pat,pat,...] as slice pattern
Expand Down Expand Up @@ -3807,9 +3823,7 @@ impl<'a> Parser<'a> {
return Err(self.fatal("unexpected `(` after qualified path"));
}
// Parse tuple struct or enum pattern
self.bump();
let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
self.expect(&token::CloseDelim(token::Paren))?;
let (fields, ddpos, _) = self.parse_parenthesized_pat_list()?;
pat = PatKind::TupleStruct(path, fields, ddpos)
}
_ => pat = PatKind::Path(qself, path),
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2659,6 +2659,11 @@ impl<'a> State<'a> {
|s, p| s.print_pat(p))?;
self.s.word("]")?;
}
PatKind::Paren(ref inner) => {
self.popen()?;
self.print_pat(inner)?;
self.pclose()?;
}
PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
}
self.ann.post(self, NodePat(pat))
Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
walk_list!(visitor, visit_pat, tuple_elements);
}
PatKind::Box(ref subpattern) |
PatKind::Ref(ref subpattern, _) => {
PatKind::Ref(ref subpattern, _) |
PatKind::Paren(ref subpattern) => {
visitor.visit_pat(subpattern)
}
PatKind::Ident(_, ref pth1, ref optional_subpattern) => {
Expand Down
2 changes: 1 addition & 1 deletion src/test/parse-fail/pat-tuple-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@

fn main() {
match 0 {
(pat, ..,) => {} //~ ERROR expected pattern, found `)`
(pat, ..,) => {} //~ ERROR trailing comma is not permitted after `..`
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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

fn main() {
match 0 {
(pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
(pat) => assert_eq!(pat, 0)
}
}
15 changes: 15 additions & 0 deletions src/test/ui/feature-gate-pattern_parentheses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.

fn main() {
match 0 {
(pat) => {} //~ ERROR parentheses in patterns are unstable
}
}
11 changes: 11 additions & 0 deletions src/test/ui/feature-gate-pattern_parentheses.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0658]: parentheses in patterns are unstable
--> $DIR/feature-gate-pattern_parentheses.rs:13:9
|
LL | (pat) => {} //~ ERROR parentheses in patterns are unstable
| ^^^^^
|
= help: add #![feature(pattern_parentheses)] to the crate attributes to enable

error: aborting due to previous error

If you want more information on this error, try using "rustc --explain E0658"