Skip to content

Commit cde05d3

Browse files
committed
Recover on '..X' / '..=X' / '...X' range patterns.
1 parent 6bfba39 commit cde05d3

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

src/libsyntax/parse/parser.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,6 +3729,13 @@ impl<'a> Parser<'a> {
37293729
}
37303730
}
37313731

3732+
/// Is the current token suitable as the start of a range patterns end?
3733+
fn is_pat_range_end_start(&self) -> bool {
3734+
self.token.is_path_start() // e.g. `MY_CONST`;
3735+
|| self.token == token::Dot // e.g. `.5` for recovery;
3736+
|| self.token.can_begin_literal_or_bool() // e.g. `42`.
3737+
}
3738+
37323739
// helper function to decide whether to parse as ident binding or to try to do
37333740
// something more complex like range patterns
37343741
fn parse_as_ident(&mut self) -> bool {
@@ -3801,6 +3808,26 @@ impl<'a> Parser<'a> {
38013808
self.parse_pat_with_range_pat(true, expected)
38023809
}
38033810

3811+
/// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery.
3812+
fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> {
3813+
let lo = self.prev_span;
3814+
let end = self.parse_pat_range_end()?;
3815+
let range_span = lo.to(end.span);
3816+
let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new());
3817+
3818+
self.diagnostic()
3819+
.struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form))
3820+
.span_suggestion(
3821+
range_span,
3822+
"try using the minimum value for the type",
3823+
format!("MIN{}{}", form, pprust::expr_to_string(&end)),
3824+
Applicability::HasPlaceholders,
3825+
)
3826+
.emit();
3827+
3828+
Ok(PatKind::Range(begin, end, respan(lo, re)))
3829+
}
3830+
38043831
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
38053832
/// allowed).
38063833
fn parse_pat_with_range_pat(
@@ -3842,9 +3869,24 @@ impl<'a> Parser<'a> {
38423869
pat = PatKind::Slice(slice);
38433870
}
38443871
token::DotDot => {
3845-
// Parse `..`.
38463872
self.bump();
3847-
pat = PatKind::Rest;
3873+
pat = if self.is_pat_range_end_start() {
3874+
// Parse `..42` for recovery.
3875+
self.parse_pat_range_to(RangeEnd::Excluded, "..")?
3876+
} else {
3877+
// A rest pattern `..`.
3878+
PatKind::Rest
3879+
};
3880+
}
3881+
token::DotDotEq => {
3882+
// Parse `..=42` for recovery.
3883+
self.bump();
3884+
pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?;
3885+
}
3886+
token::DotDotDot => {
3887+
// Parse `...42` for recovery.
3888+
self.bump();
3889+
pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?;
38483890
}
38493891
// At this point, token != &, &&, (, [
38503892
_ => if self.eat_keyword(kw::Underscore) {
@@ -3914,8 +3956,7 @@ impl<'a> Parser<'a> {
39143956
let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new());
39153957
self.bump();
39163958
let end = self.parse_pat_range_end()?;
3917-
let op = Spanned { span: op_span, node: end_kind };
3918-
pat = PatKind::Range(begin, end, op);
3959+
pat = PatKind::Range(begin, end, respan(op_span, end_kind));
39193960
}
39203961
token::OpenDelim(token::Brace) => {
39213962
if qself.is_some() {
@@ -3965,8 +4006,7 @@ impl<'a> Parser<'a> {
39654006
on a range-operator token")
39664007
};
39674008
let end = self.parse_pat_range_end()?;
3968-
let op = Spanned { span: op_span, node: end_kind };
3969-
pat = PatKind::Range(begin, end, op);
4009+
pat = PatKind::Range(begin, end, respan(op_span, end_kind))
39704010
} else {
39714011
pat = PatKind::Lit(begin);
39724012
}

0 commit comments

Comments
 (0)