diff options
| author | Lieselotte <52315535+she3py@users.noreply.github.com> | 2024-07-04 14:10:17 +0200 | 
|---|---|---|
| committer | Lieselotte <52315535+she3py@users.noreply.github.com> | 2024-09-18 20:37:56 +0200 | 
| commit | c2047219b59ac7830c4315653858f28cf0d57b9d (patch) | |
| tree | ebf28f0147468ed69fab37e67d93d3fad4952d0c /compiler/rustc_parse/src | |
| parent | 7fc70f870a1d11c3b104274d3a18112996cdec9a (diff) | |
| download | rust-c2047219b59ac7830c4315653858f28cf0d57b9d.tar.gz rust-c2047219b59ac7830c4315653858f28cf0d57b9d.zip | |
Recover more expressions in patterns
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 143 | 
3 files changed, 88 insertions, 61 deletions
| diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index da1103a4fe5..a91aa1f5018 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2597,8 +2597,6 @@ pub(crate) struct UnexpectedExpressionInPattern { pub span: Span, /// Was a `RangePatternBound` expected? pub is_bound: bool, - /// Was the unexpected expression a `MethodCallExpression`? - pub is_method_call: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2d5a1914fa6..f19cba4c1bf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -41,7 +41,7 @@ use super::{ use crate::{errors, maybe_recover_from_interpolated_ty_qpath}; #[derive(Debug)] -enum DestructuredFloat { +pub(super) enum DestructuredFloat { /// 1e2 Single(Symbol, Span), /// 1. @@ -1041,7 +1041,7 @@ impl<'a> Parser<'a> { // support pushing "future tokens" (would be also helpful to `break_and_eat`), or // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). - fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat { + pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index daced411b8f..94c01f7e0d4 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -25,7 +25,7 @@ use crate::errors::{ UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; -use crate::parser::expr::could_be_unclosed_char_literal; +use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; #[derive(PartialEq, Copy, Clone)] @@ -342,7 +342,7 @@ impl<'a> Parser<'a> { } } - /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator. + /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression. /// /// `is_end_bound` indicates whether the last parsed thing was the end bound of a range pattern (see [`parse_pat_range_end`](Self::parse_pat_range_end)) /// in order to say "expected a pattern range bound" instead of "expected a pattern"; @@ -350,38 +350,64 @@ impl<'a> Parser<'a> { /// 0..=1 + 2 /// ^^^^^ /// ``` - /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter. + /// Only the end bound is spanned in this case, and this function has no idea if there was a `..=` before `pat_span`, hence the parameter. + /// + /// This function returns `Some` if a trailing expression was recovered, and said expression's span. #[must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some"] fn maybe_recover_trailing_expr( &mut self, pat_span: Span, is_end_bound: bool, - ) -> Option<ErrorGuaranteed> { + ) -> Option<(ErrorGuaranteed, Span)> { if self.prev_token.is_keyword(kw::Underscore) || !self.may_recover() { // Don't recover anything after an `_` or if recovery is disabled. return None; } - // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`. - let has_trailing_method = self.check_noexpect(&token::Dot) + // Returns `true` iff `token` is an unsuffixed integer. + let is_one_tuple_index = |_: &Self, token: &Token| -> bool { + use token::{Lit, LitKind}; + + matches!( + token.kind, + token::Literal(Lit { kind: LitKind::Integer, symbol: _, suffix: None }) + ) + }; + + // Returns `true` iff `token` is an unsuffixed `x.y` float. + let is_two_tuple_indexes = |this: &Self, token: &Token| -> bool { + use token::{Lit, LitKind}; + + if let token::Literal(Lit { kind: LitKind::Float, symbol, suffix: None }) = token.kind + && let DestructuredFloat::MiddleDot(..) = this.break_up_float(symbol, token.span) + { + true + } else { + false + } + }; + + // Check for `.hello` or `.0`. + let has_dot_expr = self.check_noexpect(&token::Dot) // `.` && self.look_ahead(1, |tok| { - tok.ident() - .and_then(|(ident, _)| ident.name.as_str().chars().next()) - .is_some_and(char::is_lowercase) - }) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Parenthesis)); + tok.is_ident() // `hello` + || is_one_tuple_index(&self, &tok) // `0` + || is_two_tuple_indexes(&self, &tok) // `0.0` + }); // Check for operators. // `|` is excluded as it is used in pattern alternatives and lambdas, // `?` is included for error propagation, // `[` is included for indexing operations, - // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`) + // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`), + // `as` is included for type casts let has_trailing_operator = matches!(self.token.kind, token::BinOp(op) if op != BinOpToken::Or) || self.token == token::Question || (self.token == token::OpenDelim(Delimiter::Bracket) - && self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket))); + && self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket))) // excludes `[]` + || self.token.is_keyword(kw::As); - if !has_trailing_method && !has_trailing_operator { + if !has_dot_expr && !has_trailing_operator { // Nothing to recover here. return None; } @@ -391,44 +417,41 @@ impl<'a> Parser<'a> { snapshot.restrictions.insert(Restrictions::IS_PAT); // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten. - if let Ok(expr) = snapshot + let Ok(expr) = snapshot .parse_expr_dot_or_call_with( AttrVec::new(), self.mk_expr(pat_span, ExprKind::Dummy), // equivalent to transforming the parsed pattern into an `Expr` pat_span, ) .map_err(|err| err.cancel()) - { - let non_assoc_span = expr.span; + else { + // We got a trailing method/operator, but that wasn't an expression. + return None; + }; - // Parse an associative expression such as `+ expr`, `% expr`, ... - // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - if let Ok((expr, _)) = - snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) - { - // We got a valid expression. - self.restore_snapshot(snapshot); - self.restrictions.remove(Restrictions::IS_PAT); - - let is_bound = is_end_bound - // is_start_bound: either `..` or `)..` - || self.token.is_range_separator() - || self.token == token::CloseDelim(Delimiter::Parenthesis) - && self.look_ahead(1, Token::is_range_separator); - - // Check that `parse_expr_assoc_with` didn't eat a rhs. - let is_method_call = has_trailing_method && non_assoc_span == expr.span; - - return Some(self.dcx().emit_err(UnexpectedExpressionInPattern { - span: expr.span, - is_bound, - is_method_call, - })); - } - } + // Parse an associative expression such as `+ expr`, `% expr`, ... + // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. + let Ok((expr, _)) = + snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) + else { + // We got a trailing method/operator, but that wasn't an expression. + return None; + }; - // We got a trailing method/operator, but we couldn't parse an expression. - None + // We got a valid expression. + self.restore_snapshot(snapshot); + self.restrictions.remove(Restrictions::IS_PAT); + + let is_bound = is_end_bound + // is_start_bound: either `..` or `)..` + || self.token.is_range_separator() + || self.token == token::CloseDelim(Delimiter::Parenthesis) + && self.look_ahead(1, Token::is_range_separator); + + Some(( + self.dcx().emit_err(UnexpectedExpressionInPattern { span: expr.span, is_bound }), + expr.span, + )) } /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are @@ -544,7 +567,7 @@ impl<'a> Parser<'a> { self.parse_pat_tuple_struct(qself, path)? } else { match self.maybe_recover_trailing_expr(span, false) { - Some(guar) => PatKind::Err(guar), + Some((guar, _)) => PatKind::Err(guar), None => PatKind::Path(qself, path), } } @@ -577,10 +600,10 @@ impl<'a> Parser<'a> { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { Ok(begin) => { - let begin = match self.maybe_recover_trailing_expr(begin.span, false) { - Some(guar) => self.mk_expr_err(begin.span, guar), - None => begin, - }; + let begin = self + .maybe_recover_trailing_expr(begin.span, false) + .map(|(guar, sp)| self.mk_expr_err(sp, guar)) + .unwrap_or(begin); match self.parse_range_end() { Some(form) => self.parse_pat_range_begin_with(begin, form)?, @@ -721,7 +744,8 @@ impl<'a> Parser<'a> { // For backward compatibility, `(..)` is a tuple pattern as well. let paren_pattern = fields.len() == 1 && !(matches!(trailing_comma, Trailing::Yes) || fields[0].is_rest()); - if paren_pattern { + + let pat = if paren_pattern { let pat = fields.into_iter().next().unwrap(); let close_paren = self.prev_token.span; @@ -739,7 +763,7 @@ impl<'a> Parser<'a> { }, }); - self.parse_pat_range_begin_with(begin.clone(), form) + self.parse_pat_range_begin_with(begin.clone(), form)? } // recover ranges with parentheses around the `(start)..` PatKind::Err(guar) @@ -754,15 +778,20 @@ impl<'a> Parser<'a> { }, }); - self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form) + self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)? } // (pat) with optional parentheses - _ => Ok(PatKind::Paren(pat)), + _ => PatKind::Paren(pat), } } else { - Ok(PatKind::Tuple(fields)) - } + PatKind::Tuple(fields) + }; + + Ok(match self.maybe_recover_trailing_expr(open_paren.to(self.prev_token.span), false) { + None => pat, + Some((guar, _)) => PatKind::Err(guar), + }) } /// Parse a mutable binding with the `mut` token already eaten. @@ -1015,7 +1044,7 @@ impl<'a> Parser<'a> { } Ok(match recovered { - Some(guar) => self.mk_expr_err(bound.span, guar), + Some((guar, sp)) => self.mk_expr_err(sp, guar), None => bound, }) } @@ -1084,7 +1113,7 @@ impl<'a> Parser<'a> { // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`. let pat = if sub.is_none() - && let Some(guar) = self.maybe_recover_trailing_expr(ident.span, false) + && let Some((guar, _)) = self.maybe_recover_trailing_expr(ident.span, false) { PatKind::Err(guar) } else { | 
