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

Implement half-open range patterns #753

Merged
merged 1 commit into from
Jan 26, 2020
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
2 changes: 1 addition & 1 deletion src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
101 changes: 70 additions & 31 deletions src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
let begin = input.fork();
let lookahead = input.lookahead1();
if lookahead.peek(Ident)
&& ({
Expand Down Expand Up @@ -434,18 +435,19 @@ 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())
}
}
}

fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result<Pat> {
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() {
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -624,17 +626,44 @@ mod parsing {
})
}

fn pat_range(input: ParseStream, qself: Option<QSelf>, path: Path) -> Result<PatRange> {
Ok(PatRange {
attrs: Vec::new(),
lo: Box::new(Expr::Path(ExprPath {
fn pat_range(
input: ParseStream,
begin: ParseBuffer,
qself: Option<QSelf>,
path: Path,
) -> Result<Pat> {
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<Pat> {
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<PatTuple> {
Expand Down Expand Up @@ -669,14 +698,21 @@ mod parsing {
}

fn pat_lit_or_range(input: ParseStream) -> Result<Pat> {
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(),
Expand All @@ -685,7 +721,17 @@ mod parsing {
}
}

fn pat_lit_expr(input: ParseStream) -> Result<Box<Expr>> {
fn pat_lit_expr(input: ParseStream) -> Result<Option<Box<Expr>>> {
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<Token![-]> = input.parse()?;

let lookahead = input.lookahead1();
Expand All @@ -705,15 +751,15 @@ 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),
expr: Box::new(expr),
})
} else {
expr
}))
})))
}

fn pat_slice(input: ParseStream) -> Result<PatSlice> {
Expand All @@ -737,13 +783,6 @@ mod parsing {
elems,
})
}

fn pat_rest(input: ParseStream) -> Result<PatRest> {
Ok(PatRest {
attrs: Vec::new(),
dot2_token: input.parse()?,
})
}
}

#[cfg(feature = "printing")]
Expand Down
3 changes: 0 additions & 3 deletions tests/repo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down