Skip to content

Commit

Permalink
Recover parentheses in range patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
ShE3py committed Dec 28, 2023
1 parent 3ee6710 commit d9e140e
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 27 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,9 @@ parse_unexpected_if_with_if = unexpected `if` in the condition expression
parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern
.suggestion = remove the lifetime
parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses
parse_unexpected_paren_in_range_pat_sugg = remove these parentheses
parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head
.suggestion = remove parentheses in `for` loop
Expand Down
23 changes: 22 additions & 1 deletion compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use rustc_ast::token::Token;
use rustc_ast::{Path, Visibility};
use rustc_errors::{
AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, IntoDiagnostic, Level,
AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, IntoDiagnostic, Level, MultiSpan,
SubdiagnosticMessage,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
Expand Down Expand Up @@ -2378,6 +2378,27 @@ pub(crate) struct ExpectedCommaAfterPatternField {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_unexpected_paren_in_range_pat)]
pub(crate) struct UnexpectedParenInRangePat {
#[primary_span]
pub span: MultiSpan,
#[subdiagnostic]
pub sugg: UnexpectedParenInRangePatSugg,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
parse_unexpected_paren_in_range_pat_sugg,
applicability = "machine-applicable"
)]
pub(crate) struct UnexpectedParenInRangePatSugg {
#[suggestion_part(code = "")]
pub start_span: Span,
#[suggestion_part(code = "")]
pub end_span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_return_types_use_thin_arrow)]
pub(crate) struct ReturnTypesUseThinArrow {
Expand Down
87 changes: 76 additions & 11 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use super::{ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern,
AmbiguousRangePattern, BoxNotPat, DotDotDotForRemainingFields,
DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier,
ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField,
GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect,
RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed,
TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedLifetimeInPattern,
UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern,
};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
Expand All @@ -18,7 +19,7 @@ use rustc_ast::{
PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident};
Expand Down Expand Up @@ -579,6 +580,8 @@ impl<'a> Parser<'a> {

/// Parse a tuple or parenthesis pattern.
fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
let open_paren = self.token.span;

let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
p.parse_pat_allow_top_alt(
None,
Expand All @@ -591,7 +594,26 @@ impl<'a> Parser<'a> {
// Here, `(pat,)` is a tuple pattern.
// For backward compatibility, `(..)` is a tuple pattern as well.
Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) {
PatKind::Paren(fields.into_iter().next().unwrap())
let pat = fields.into_iter().next().unwrap();
let close_paren = self.prev_token.span;

match &pat.kind {
// recover ranges with parentheses around the `(start)..`
PatKind::Lit(begin) if let Some(form) = self.parse_range_end() => {
self.dcx().emit_err(UnexpectedParenInRangePat {
span: MultiSpan::from_spans(vec![open_paren, close_paren]),
sugg: UnexpectedParenInRangePatSugg {
start_span: open_paren,
end_span: close_paren,
},
});

self.parse_pat_range_begin_with(begin.clone(), form)?
}

// (pat) with optional parentheses
_ => PatKind::Paren(pat),
}
} else {
PatKind::Tuple(fields)
})
Expand Down Expand Up @@ -727,6 +749,13 @@ impl<'a> Parser<'a> {
begin: P<Expr>,
re: Spanned<RangeEnd>,
) -> PResult<'a, PatKind> {
// recover from `(`
let open_paren =
matches!(self.token.kind, token::OpenDelim(Delimiter::Parenthesis)).then(|| {
self.bump();
self.prev_token.span
});

let end = if self.is_pat_range_end_start(0) {
// Parsing e.g. `X..=Y`.
Some(self.parse_pat_range_end()?)
Expand All @@ -738,6 +767,19 @@ impl<'a> Parser<'a> {
}
None
};

if let Some(span) = open_paren {
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;

self.dcx().emit_err(UnexpectedParenInRangePat {
span: MultiSpan::from_spans(vec![span, self.prev_token.span]),
sugg: UnexpectedParenInRangePatSugg {
start_span: span,
end_span: self.prev_token.span,
},
});
}

Ok(PatKind::Range(Some(begin), end, re))
}

Expand Down Expand Up @@ -777,11 +819,31 @@ impl<'a> Parser<'a> {
/// The form `...X` is prohibited to reduce confusion with the potential
/// expression syntax `...expr` for splatting in expressions.
fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> {
// recover from `(`
let open_paren =
matches!(self.token.kind, token::OpenDelim(Delimiter::Parenthesis)).then(|| {
self.bump();
self.prev_token.span
});

let end = self.parse_pat_range_end()?;
if let RangeEnd::Included(syn @ RangeSyntax::DotDotDot) = &mut re.node {
*syn = RangeSyntax::DotDotEq;
self.dcx().emit_err(DotDotDotRangeToPatternNotAllowed { span: re.span });
}

if let Some(span) = open_paren {
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;

self.dcx().emit_err(UnexpectedParenInRangePat {
span: MultiSpan::from_spans(vec![span, self.prev_token.span]),
sugg: UnexpectedParenInRangePatSugg {
start_span: span,
end_span: self.prev_token.span,
},
});
}

Ok(PatKind::Range(None, Some(end), re))
}

Expand All @@ -794,6 +856,9 @@ impl<'a> Parser<'a> {
|| t.can_begin_literal_maybe_minus() // e.g. `42`.
|| t.is_whole_expr()
|| t.is_lifetime() // recover `'a` instead of `'a'`
|| (t.kind == token::OpenDelim(Delimiter::Parenthesis) // recover leading `(`
&& self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis))
&& self.is_pat_range_end_start(dist + 1))
})
}

Expand Down Expand Up @@ -942,7 +1007,7 @@ impl<'a> Parser<'a> {

if self.isnt_pattern_start() {
let descr = super::token_descr(&self.token);
self.dcx().emit_err(errors::BoxNotPat {
self.dcx().emit_err(BoxNotPat {
span: self.token.span,
kw: box_span,
lo: box_span.shrink_to_lo(),
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/half-open-range-patterns/range_pat_interactions2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ fn main() {
for x in -9 + 1..=(9 - 2) {
match x as i32 {
0..=(5+1) => errors_only.push(x),
//~^ error: inclusive range with no end
//~| error: expected one of `=>`, `if`, or `|`, found `(`
//~^ error: expected `)`, found `+`
1 | -3..0 => first_or.push(x),
y @ (0..5 | 6) => or_two.push(y),
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
Expand Down
17 changes: 4 additions & 13 deletions tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
error[E0586]: inclusive range with no end
--> $DIR/range_pat_interactions2.rs:10:14
error: expected `)`, found `+`
--> $DIR/range_pat_interactions2.rs:10:19
|
LL | 0..=(5+1) => errors_only.push(x),
| ^^^ help: use `..` instead
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)

error: expected one of `=>`, `if`, or `|`, found `(`
--> $DIR/range_pat_interactions2.rs:10:17
|
LL | 0..=(5+1) => errors_only.push(x),
| ^ expected one of `=>`, `if`, or `|`
| ^ expected `)`

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0586`.
12 changes: 12 additions & 0 deletions tests/ui/parser/pat-recover-ranges.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
match -1 {
0..=1 => (),
0..=(1) => (),
//~^ error: range pattern bounds cannot have parentheses
(-12)..=4 => (),
//~^ error: range pattern bounds cannot have parentheses
(0)..=(-4) => (),
//~^ error: range pattern bounds cannot have parentheses
//~| error: range pattern bounds cannot have parentheses
};
}
50 changes: 50 additions & 0 deletions tests/ui/parser/pat-recover-ranges.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
error: range pattern bounds cannot have parentheses
--> $DIR/pat-recover-ranges.rs:4:13
|
LL | 0..=(1) => (),
| ^ ^
|
help: remove these parentheses
|
LL - 0..=(1) => (),
LL + 0..=1 => (),
|

error: range pattern bounds cannot have parentheses
--> $DIR/pat-recover-ranges.rs:6:9
|
LL | (-12)..=4 => (),
| ^ ^
|
help: remove these parentheses
|
LL - (-12)..=4 => (),
LL + -12..=4 => (),
|

error: range pattern bounds cannot have parentheses
--> $DIR/pat-recover-ranges.rs:8:9
|
LL | (0)..=(-4) => (),
| ^ ^
|
help: remove these parentheses
|
LL - (0)..=(-4) => (),
LL + 0..=(-4) => (),
|

error: range pattern bounds cannot have parentheses
--> $DIR/pat-recover-ranges.rs:8:15
|
LL | (0)..=(-4) => (),
| ^ ^
|
help: remove these parentheses
|
LL - (0)..=(-4) => (),
LL + (0)..=-4 => (),
|

error: aborting due to 4 previous errors

0 comments on commit d9e140e

Please sign in to comment.