From 09a0f10ee13fb7303b41c1f99c3c46a0f0e0afce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Jan 2020 18:28:33 -0800 Subject: [PATCH] Implement half-open range patterns --- src/expr.rs | 2 +- src/pat.rs | 101 ++++++++++++++++++++++++++++++++-------------- tests/repo/mod.rs | 3 -- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 11d22bd75c..e67c6366a5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1948,7 +1948,7 @@ pub(crate) mod parsing { return parse_expr(input, expr, allow_struct, Precedence::Any); }; - if input.peek(Token![.]) || input.peek(Token![?]) { + if input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) { expr = trailer_helper(input, expr)?; attrs.extend(expr.replace_attrs(Vec::new())); diff --git a/src/pat.rs b/src/pat.rs index 262129b5c4..fac651edab 100644 --- a/src/pat.rs +++ b/src/pat.rs @@ -386,11 +386,12 @@ mod parsing { use super::*; use crate::ext::IdentExt; - use crate::parse::{Parse, ParseStream, Result}; + use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; use crate::path; impl Parse for Pat { fn parse(input: ParseStream) -> Result { + let begin = input.fork(); let lookahead = input.lookahead1(); if lookahead.peek(Ident) && ({ @@ -434,7 +435,7 @@ mod parsing { } else if lookahead.peek(token::Bracket) { input.call(pat_slice).map(Pat::Slice) } else if lookahead.peek(Token![..]) && !input.peek(Token![...]) { - input.call(pat_rest).map(Pat::Rest) + pat_range_half_open(input, begin) } else { Err(lookahead.error()) } @@ -442,10 +443,11 @@ mod parsing { } fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result { + let begin = input.fork(); let (qself, path) = path::parsing::qpath(input, true)?; if input.peek(Token![..]) { - return pat_range(input, qself, path).map(Pat::Range); + return pat_range(input, begin, qself, path); } if qself.is_some() { @@ -487,7 +489,7 @@ mod parsing { } else if input.peek(token::Paren) { pat_tuple_struct(input, path).map(Pat::TupleStruct) } else if input.peek(Token![..]) { - pat_range(input, qself, path).map(Pat::Range) + pat_range(input, begin, qself, path) } else { Ok(Pat::Path(PatPath { attrs: Vec::new(), @@ -624,17 +626,44 @@ mod parsing { }) } - fn pat_range(input: ParseStream, qself: Option, path: Path) -> Result { - Ok(PatRange { - attrs: Vec::new(), - lo: Box::new(Expr::Path(ExprPath { + fn pat_range( + input: ParseStream, + begin: ParseBuffer, + qself: Option, + path: Path, + ) -> Result { + let limits: RangeLimits = input.parse()?; + let hi = input.call(pat_lit_expr)?; + if let Some(hi) = hi { + Ok(Pat::Range(PatRange { attrs: Vec::new(), - qself, - path, - })), - limits: input.parse()?, - hi: input.call(pat_lit_expr)?, - }) + lo: Box::new(Expr::Path(ExprPath { + attrs: Vec::new(), + qself, + path, + })), + limits, + hi, + })) + } else { + Ok(Pat::Verbatim(verbatim::between(begin, input))) + } + } + + fn pat_range_half_open(input: ParseStream, begin: ParseBuffer) -> Result { + let limits: RangeLimits = input.parse()?; + let hi = input.call(pat_lit_expr)?; + if hi.is_some() { + Ok(Pat::Verbatim(verbatim::between(begin, input))) + } else { + match limits { + RangeLimits::HalfOpen(dot2_token) => Ok(Pat::Rest(PatRest { + attrs: Vec::new(), + dot2_token, + })), + RangeLimits::Closed(_) => Err(input.error("expected range upper bound")), + } + } } fn pat_tuple(input: ParseStream) -> Result { @@ -669,14 +698,21 @@ mod parsing { } fn pat_lit_or_range(input: ParseStream) -> Result { - let lo = input.call(pat_lit_expr)?; + let begin = input.fork(); + let lo = input.call(pat_lit_expr)?.unwrap(); if input.peek(Token![..]) { - Ok(Pat::Range(PatRange { - attrs: Vec::new(), - lo, - limits: input.parse()?, - hi: input.call(pat_lit_expr)?, - })) + let limits: RangeLimits = input.parse()?; + let hi = input.call(pat_lit_expr)?; + if let Some(hi) = hi { + Ok(Pat::Range(PatRange { + attrs: Vec::new(), + lo, + limits, + hi, + })) + } else { + Ok(Pat::Verbatim(verbatim::between(begin, input))) + } } else { Ok(Pat::Lit(PatLit { attrs: Vec::new(), @@ -685,7 +721,17 @@ mod parsing { } } - fn pat_lit_expr(input: ParseStream) -> Result> { + fn pat_lit_expr(input: ParseStream) -> Result>> { + if input.is_empty() + || input.peek(Token![|]) + || input.peek(Token![=>]) + || input.peek(Token![:]) && !input.peek(Token![::]) + || input.peek(Token![,]) + || input.peek(Token![;]) + { + return Ok(None); + } + let neg: Option = input.parse()?; let lookahead = input.lookahead1(); @@ -705,7 +751,7 @@ mod parsing { return Err(lookahead.error()); }; - Ok(Box::new(if let Some(neg) = neg { + Ok(Some(Box::new(if let Some(neg) = neg { Expr::Unary(ExprUnary { attrs: Vec::new(), op: UnOp::Neg(neg), @@ -713,7 +759,7 @@ mod parsing { }) } else { expr - })) + }))) } fn pat_slice(input: ParseStream) -> Result { @@ -737,13 +783,6 @@ mod parsing { elems, }) } - - fn pat_rest(input: ParseStream) -> Result { - Ok(PatRest { - attrs: Vec::new(), - dot2_token: input.parse()?, - }) - } } #[cfg(feature = "printing")] diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index 52d608a880..1facc5c8ac 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -54,9 +54,6 @@ pub fn base_dir_filter(entry: &DirEntry) -> bool { // TODO: visibility on enum variants "tests/rust/src/test/pretty/enum-variant-vis.rs" | "tests/rust/src/test/ui/parser/issue-65041-empty-vis-matcher-in-enum.rs" | - // TODO: half open range patterns - "tests/rust/src/test/ui/half-open-range-patterns/half-open-range-pats-syntactic-pass.rs" | - "tests/rust/src/test/ui/half-open-range-patterns/pat-tuple-4.rs" | // TODO: inherent associated const "tests/rust/src/test/ui/parser/impl-item-const-pass.rs" | // TODO: inherent associated type