about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorDan Robertson <dan@dlrobertson.com>2019-07-14 01:05:52 +0000
committerDan Robertson <dan@dlrobertson.com>2019-08-17 15:55:40 +0000
commit1870537f2701e5aa47080a879b63a4d6b391553b (patch)
treec8d3e59938a5dc0aefca4de9ee95ab753099cd81 /src/libsyntax
parent1713ac4bf5c992f40d667c929c1e1ce9c3a51204 (diff)
downloadrust-1870537f2701e5aa47080a879b63a4d6b391553b.tar.gz
rust-1870537f2701e5aa47080a879b63a4d6b391553b.zip
initial implementation of or-pattern parsing
Initial implementation of parsing or-patterns e.g., `Some(Foo | Bar)`.
This is a partial implementation of RFC 2535.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs1
-rw-r--r--src/libsyntax/feature_gate.rs5
-rw-r--r--src/libsyntax/parse/mod.rs3
-rw-r--r--src/libsyntax/parse/parser/pat.rs41
-rw-r--r--src/libsyntax/print/pprust.rs47
-rw-r--r--src/libsyntax/visit.rs2
6 files changed, 59 insertions, 40 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 0136c4ff5f9..3d15782df34 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -650,6 +650,7 @@ pub enum PatKind {
     TupleStruct(Path, Vec<P<Pat>>),
 
     /// An or-pattern `A | B | C`.
+    /// Invariant: `pats.len() >= 2`.
     Or(Vec<P<Pat>>),
 
     /// A possibly qualified path pattern.
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 1a87a903156..bbc3ae28225 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -559,6 +559,9 @@ declare_features! (
     // Allows `impl Trait` to be used inside type aliases (RFC 2515).
     (active, type_alias_impl_trait, "1.38.0", Some(63063), None),
 
+    // Allows the use of or-patterns, e.g. `0 | 1`.
+    (active, or_patterns, "1.38.0", Some(54883), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -571,6 +574,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::impl_trait_in_bindings,
     sym::generic_associated_types,
     sym::const_generics,
+    sym::or_patterns,
     sym::let_chains,
 ];
 
@@ -2443,6 +2447,7 @@ pub fn check_crate(krate: &ast::Crate,
     gate_all!(let_chains_spans, let_chains, "`let` expressions in this position are experimental");
     gate_all!(async_closure_spans, async_closure, "async closures are unstable");
     gate_all!(yield_spans, generators, "yield syntax is experimental");
+    gate_all!(or_pattern_spans, or_patterns, "or-patterns syntax is experimental");
 
     let visitor = &mut PostExpansionVisitor {
         context: &ctx,
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 9088f929372..b1f3612a839 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -66,6 +66,8 @@ pub struct ParseSess {
     // Places where `yield e?` exprs were used and should be feature gated.
     pub yield_spans: Lock<Vec<Span>>,
     pub injected_crate_name: Once<Symbol>,
+    // Places where or-patterns e.g. `Some(Foo | Bar)` were used and should be feature gated.
+    pub or_pattern_spans: Lock<Vec<Span>>,
 }
 
 impl ParseSess {
@@ -96,6 +98,7 @@ impl ParseSess {
             async_closure_spans: Lock::new(Vec::new()),
             yield_spans: Lock::new(Vec::new()),
             injected_crate_name: Once::new(),
+            or_pattern_spans: Lock::new(Vec::new()),
         }
     }
 
diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index c3079d2da0c..fd458aec743 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -14,7 +14,10 @@ use errors::{Applicability, DiagnosticBuilder};
 
 impl<'a> Parser<'a> {
     /// Parses a pattern.
-    pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
+    pub fn parse_pat(
+        &mut self,
+        expected: Option<&'static str>
+    ) -> PResult<'a, P<Pat>> {
         self.parse_pat_with_range_pat(true, expected)
     }
 
@@ -97,6 +100,34 @@ impl<'a> Parser<'a> {
         Ok(())
     }
 
+    /// Parses a pattern, that may be a or-pattern (e.g. `Some(Foo | Bar)`).
+    fn parse_pat_with_or(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
+        // Parse the first pattern.
+        let first_pat = self.parse_pat(expected)?;
+
+        // If the next token is not a `|`, this is not an or-pattern and
+        // we should exit here.
+        if !self.check(&token::BinOp(token::Or)) {
+            return Ok(first_pat)
+        }
+
+        let lo = first_pat.span;
+
+        let mut pats = vec![first_pat];
+
+        while self.eat(&token::BinOp(token::Or)) {
+            pats.push(self.parse_pat_with_range_pat(
+                true, expected
+            )?);
+        }
+
+        let or_pattern_span = lo.to(self.prev_span);
+
+        self.sess.or_pattern_spans.borrow_mut().push(or_pattern_span);
+
+        Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats)))
+    }
+
     /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
     /// allowed).
     fn parse_pat_with_range_pat(
@@ -240,7 +271,9 @@ impl<'a> Parser<'a> {
 
     /// Parse a tuple or parenthesis pattern.
     fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
-        let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
+        let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
+            p.parse_pat_with_or(None)
+        })?;
 
         // Here, `(pat,)` is a tuple pattern.
         // For backward compatibility, `(..)` is a tuple pattern as well.
@@ -483,7 +516,7 @@ impl<'a> Parser<'a> {
             err.span_label(self.token.span, msg);
             return Err(err);
         }
-        let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
+        let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or(None))?;
         Ok(PatKind::TupleStruct(path, fields))
     }
 
@@ -627,7 +660,7 @@ impl<'a> Parser<'a> {
             // Parsing a pattern of the form "fieldname: pat"
             let fieldname = self.parse_field_name()?;
             self.bump();
-            let pat = self.parse_pat(None)?;
+            let pat = self.parse_pat_with_or(None)?;
             hi = pat.span;
             (pat, fieldname, false)
         } else {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 8dcb7ecf881..4dc00af4860 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -431,46 +431,33 @@ impl std::ops::DerefMut for State<'_> {
     }
 }
 
-pub enum SeparatorSpacing {
-    After,
-    Both,
-}
-
 pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefMut {
     fn comments(&mut self) -> &mut Option<Comments<'a>>;
     fn print_ident(&mut self, ident: ast::Ident);
     fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
 
-    fn strsep<T, F>(
-        &mut self,
-        sep: &'static str,
-        spacing: SeparatorSpacing,
-        b: Breaks,
-        elts: &[T],
-        mut op: F
-    ) -> io::Result<()>
+    fn strsep<T, F>(&mut self, sep: &'static str, space_before: bool,
+                    b: Breaks, elts: &[T], mut op: F)
         where F: FnMut(&mut Self, &T),
     {
         self.rbox(0, b);
-        let mut first = true;
-        for elt in elts {
-            if first {
-                first = false;
-            } else {
-                if let SeparatorSpacing::Both = spacing {
-                    self.writer().space();
+        if let Some((first, rest)) = elts.split_first() {
+            op(self, first);
+            for elt in rest {
+                if space_before {
+                    self.space();
                 }
                 self.word_space(sep);
+                op(self, elt);
             }
-            op(self, elt);
         }
         self.end();
     }
 
-    fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F)
+    fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
         where F: FnMut(&mut Self, &T),
     {
-        self.strsep(",", SeparatorSpacing::After, b, elts, op)
+        self.strsep(",", false, b, elts, op)
     }
 
     fn maybe_print_comment(&mut self, pos: BytePos) {
@@ -2379,8 +2366,7 @@ impl<'a> State<'a> {
                 self.pclose();
             }
             PatKind::Or(ref pats) => {
-                let spacing = SeparatorSpacing::Both;
-                self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(p))?;
+                self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
             }
             PatKind::Path(None, ref path) => {
                 self.print_path(path, true, 0);
@@ -2458,16 +2444,7 @@ impl<'a> State<'a> {
     }
 
     fn print_pats(&mut self, pats: &[P<ast::Pat>]) {
-        let mut first = true;
-        for p in pats {
-            if first {
-                first = false;
-            } else {
-                self.s.space();
-                self.word_space("|");
-            }
-            self.print_pat(p);
-        }
+        self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
     }
 
     fn print_arm(&mut self, arm: &ast::Arm) {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index ce679a5db63..91b92d84a81 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -462,7 +462,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
             visitor.visit_expr(upper_bound);
         }
         PatKind::Wild | PatKind::Rest => {},
-        PatKind::Tuple(ref elems) => {
+        PatKind::Tuple(ref elems)
         | PatKind::Slice(ref elems)
         | PatKind::Or(ref elems) => {
             walk_list!(visitor, visit_pat, elems);