diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 151 |
1 files changed, 119 insertions, 32 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1b28f3c97..7ede4fbc3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -91,6 +91,18 @@ impl From<P<Expr>> for LhsExpr { } } +#[derive(Debug)] +enum DestructuredFloat { + /// 1e2 + Single(Symbol, Span), + /// 1. + TrailingDot(Symbol, Span, Span), + /// 1.2 | 1.2e3 + MiddleDot(Symbol, Span, Span, Symbol, Span), + /// Invalid + Error, +} + impl<'a> Parser<'a> { /// Parses an expression. #[inline] @@ -1001,9 +1013,15 @@ impl<'a> Parser<'a> { } fn error_unexpected_after_dot(&self) { - // FIXME Could factor this out into non_fatal_unexpected or something. let actual = pprust::token_to_string(&self.token); - self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual }); + let span = self.token.span; + let sm = self.sess.source_map(); + let (span, actual) = match (&self.token.kind, self.subparser_name) { + (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => + (span.shrink_to_hi(), actual.into()), + _ => (span, actual), + }; + self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } // We need an identifier or integer, but the next token is a float. @@ -1013,13 +1031,8 @@ 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 parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P<Expr>, - float: Symbol, - suffix: Option<Symbol>, - ) -> P<Expr> { + // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`. + fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), @@ -1056,7 +1069,7 @@ impl<'a> Parser<'a> { match &*components { // 1e2 [IdentLike(i)] => { - self.parse_expr_tuple_field_access(lo, base, Symbol::intern(&i), suffix, None) + DestructuredFloat::Single(Symbol::intern(&i), span) } // 1. [IdentLike(i), Punct('.')] => { @@ -1068,11 +1081,8 @@ impl<'a> Parser<'a> { } else { (span, span) }; - assert!(suffix.is_none()); let symbol = Symbol::intern(&i); - self.token = Token::new(token::Ident(symbol, false), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, symbol, None, Some(next_token)) + DestructuredFloat::TrailingDot(symbol, ident_span, dot_span) } // 1.2 | 1.2e3 [IdentLike(i1), Punct('.'), IdentLike(i2)] => { @@ -1088,16 +1098,8 @@ impl<'a> Parser<'a> { (span, span, span) }; let symbol1 = Symbol::intern(&i1); - self.token = Token::new(token::Ident(symbol1, false), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); - let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) } // 1e+ | 1e- (recovered) [IdentLike(_), Punct('+' | '-')] | @@ -1109,12 +1111,83 @@ impl<'a> Parser<'a> { [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. self.error_unexpected_after_dot(); - base + DestructuredFloat::Error } _ => panic!("unexpected components in a float token: {:?}", components), } } + fn parse_expr_tuple_field_access_float( + &mut self, + lo: Span, + base: P<Expr>, + float: Symbol, + suffix: Option<Symbol>, + ) -> P<Expr> { + match self.break_up_float(float) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, false), ident_span); + let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); + self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { + self.token = Token::new(token::Ident(symbol1, false), ident1_span); + // This needs to be `Spacing::Alone` to prevent regressions. + // See issue #76399 and PR #76285 for more details + let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); + let base1 = + self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); + let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); + self.bump_with((next_token2, self.token_spacing)); // `.` + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + } + DestructuredFloat::Error => base, + } + } + + fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec<Ident>> { + let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind + else { + return Ok(thin_vec![self.parse_field_name()?]); + }; + Ok(match self.break_up_float(symbol) { + // 1e2 + DestructuredFloat::Single(sym, sp) => { + self.bump(); + thin_vec![Ident::new(sym, sp)] + } + // 1. + DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { + assert!(suffix.is_none()); + // Analogous to `Self::break_and_eat` + self.token_cursor.break_last_token = true; + // This might work, in cases like `1. 2`, and might not, + // in cases like `offset_of!(Ty, 1.)`. It depends on what comes + // after the float-like token, and therefore we have to make + // the other parts of the parser think that there is a dot literal. + self.token = Token::new(token::Ident(sym, false), sym_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + thin_vec![Ident::new(sym, sym_span)] + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => { + self.bump(); + thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)] + } + DestructuredFloat::Error => { + self.bump(); + thin_vec![Ident::new(symbol, self.prev_token.span)] + } + }) + } + fn parse_expr_tuple_field_access( &mut self, lo: Span, @@ -1363,6 +1436,8 @@ impl<'a> Parser<'a> { self.parse_expr_yield() } else if self.is_do_yeet() { self.parse_expr_yeet() + } else if self.eat_keyword(kw::Become) { + self.parse_expr_become() } else if self.check_keyword(kw::Let) { self.parse_expr_let() } else if self.eat_keyword(kw::Underscore) { @@ -1679,6 +1754,16 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } + /// Parse `"become" expr`, with `"become"` token already eaten. + fn parse_expr_become(&mut self) -> PResult<'a, P<Expr>> { + let lo = self.prev_token.span; + let kind = ExprKind::Become(self.parse_expr()?); + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::explicit_tail_calls, span); + let expr = self.mk_expr(span, kind); + self.maybe_recover_from_bad_qpath(expr) + } + /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten. /// If the label is followed immediately by a `:` token, the label and `:` are /// parsed as part of the expression (i.e. a labeled loop). The language team has @@ -1821,10 +1906,11 @@ impl<'a> Parser<'a> { let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( &TokenKind::CloseDelim(Delimiter::Parenthesis), seq_sep, - Parser::parse_field_name, + Parser::parse_field_name_maybe_tuple, )?; + let fields = fields.into_iter().flatten().collect::<Vec<_>>(); let span = lo.to(self.token.span); - Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into()))) + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into()))) } /// Returns a string literal if the next token is a string literal. @@ -1955,17 +2041,14 @@ impl<'a> Parser<'a> { let recovered = self.recover_after_dot(); let token = recovered.as_ref().unwrap_or(&self.token); match token::Lit::from_token(token) { - Some(token_lit) => { - match MetaItemLit::from_token_lit(token_lit, token.span) { + Some(lit) => { + match MetaItemLit::from_token_lit(lit, token.span) { Ok(lit) => { self.bump(); Some(lit) } Err(err) => { - let span = token.span; - let token::Literal(lit) = token.kind else { - unreachable!(); - }; + let span = token.uninterpolated_span(); self.bump(); report_lit_error(&self.sess, err, lit, span); // Pack possible quotes and prefixes from the original literal into @@ -2109,6 +2192,10 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::InvalidBlockMacroSegment { span: self.token.span, context: lo.to(self.token.span), + wrap: errors::WrapInExplicitBlock { + lo: self.token.span.shrink_to_lo(), + hi: self.token.span.shrink_to_hi(), + }, }); } |