diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/mod.rs | 57 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/unescape_error_reporting.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr.rs | 51 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 162 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 26 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 63 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/nonterminal.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/path.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 2 |
13 files changed, 302 insertions, 179 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 32b56bb7e87..20ebfc6691b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -851,13 +851,6 @@ pub(crate) struct StructLiteralNotAllowedHereSugg { } #[derive(Diagnostic)] -#[diag(parse_invalid_interpolated_expression)] -pub(crate) struct InvalidInterpolatedExpression { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(parse_invalid_literal_suffix_on_tuple_index)] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[primary_span] @@ -1994,6 +1987,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] @@ -2205,12 +2209,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df..63b2b47630b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option<Span>, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a string literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -669,15 +698,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { + let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + if self.cursor.first() == '\'' + && let Some(start) = self.last_lifetime + && self.cursor.third() != '\'' + { + // An "unclosed `char`" error will be emitted already, silence redundant error. + silence = true; + Some(errors::UnknownPrefixSugg::MeantStr { + start, + end: self.mk_sp(self.pos, self.pos + BytePos(1)), + }) + } else { + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } } else { None }; - self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); + let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; + if silence { + self.dcx().create_err(err).delay_as_bug(); + } else { + self.dcx().emit_err(err); + } } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd7..fa242a32a18 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() || full_lit_span.is_empty() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index eb9a10f4bda..ab5f51eedc3 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -3,11 +3,12 @@ use crate::errors::{ SuffixedLiteralInAttribute, }; use crate::fluent_generated as fluent; +use crate::maybe_whole; use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; -use rustc_ast::token::{self, Delimiter, Nonterminal}; +use rustc_ast::token::{self, Delimiter}; use rustc_errors::{codes::*, Diag, PResult}; use rustc_span::{sym, BytePos, Span}; use thin_vec::ThinVec; @@ -251,25 +252,15 @@ impl<'a> Parser<'a> { /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { - let item = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - Nonterminal::NtMeta(item) => Some(item.clone().into_inner()), - _ => None, - }, - _ => None, + maybe_whole!(self, NtMeta, |attr| attr.into_inner()); + + let do_parse = |this: &mut Self| { + let path = this.parse_path(PathStyle::Mod)?; + let args = this.parse_attr_args()?; + Ok(ast::AttrItem { path, args, tokens: None }) }; - Ok(if let Some(item) = item { - self.bump(); - item - } else { - let do_parse = |this: &mut Self| { - let path = this.parse_path(PathStyle::Mod)?; - let args = this.parse_attr_args()?; - Ok(ast::AttrItem { path, args, tokens: None }) - }; - // Attr items don't have attributes - if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }? - }) + // Attr items don't have attributes + if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } } /// Parses attributes that appear after the opening of an item. These should @@ -371,22 +362,18 @@ impl<'a> Parser<'a> { /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { - let nt_meta = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - token::NtMeta(e) => Some(e.clone()), - _ => None, - }, - _ => None, - }; - - if let Some(item) = nt_meta { - return match item.meta(item.path.span) { + // We can't use `maybe_whole` here because it would bump in the `None` + // case, which we don't want. + if let token::Interpolated(nt) = &self.token.kind + && let token::NtMeta(attr_item) = &nt.0 + { + match attr_item.meta(attr_item.path.span) { Some(meta) => { self.bump(); - Ok(meta) + return Ok(meta); } - None => self.unexpected(), - }; + None => self.unexpected()?, + } } let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e27a5f93799..7e317c3df14 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -11,12 +11,11 @@ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; use ast::token::IdentIsRaw; -use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment}; use core::mem; use core::ops::ControlFlow; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::Spacing; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -943,7 +942,10 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - let res = self.parse_expr_dot_or_call_with_(e0, lo); + let res = ensure_sufficient_stack( + // this expr demonstrates the recursion it guards against + || self.parse_expr_dot_or_call_with_(e0, lo), + ); if attrs.is_empty() { res } else { @@ -996,13 +998,57 @@ impl<'a> Parser<'a> { } pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> { + // At this point we've consumed something like `expr.` and `self.token` holds the token + // after the dot. match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) + let ident_span = self.token.span; + self.bump(); + Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix)) + Ok(match self.break_up_float(symbol, self.token.span) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + // `foo.1e2`: a single complete dot access, fully consumed. We end up with + // the `1e2` token in `self.prev_token` and the following token in + // `self.token`. + let ident_span = self.token.span; + self.bump(); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + // `foo.1.`: a single complete dot access and the start of another. + // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in + // `self.token`. + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + sym1, + ident1_span, + _dot_span, + sym2, + ident2_span, + ) => { + // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with + // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following + // token in `self.token`. + let next_token2 = + Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span); + self.bump_with((next_token2, self.token_spacing)); + self.bump(); + let base1 = + self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); + self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) + } + DestructuredFloat::Error => base, + }) } _ => { self.error_unexpected_after_dot(); @@ -1116,41 +1162,6 @@ impl<'a> Parser<'a> { } } - 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, self.token.span) { - // 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, IdentIsRaw::No), 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, IdentIsRaw::No), 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, IdentIsRaw::No), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) - } - DestructuredFloat::Error => base, - } - } - /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. @@ -1252,24 +1263,18 @@ impl<'a> Parser<'a> { Ok(fields.into_iter().collect()) } - fn parse_expr_tuple_field_access( + fn mk_expr_tuple_field_access( &mut self, lo: Span, + ident_span: Span, base: P<Expr>, field: Symbol, suffix: Option<Symbol>, - next_token: Option<(Token, Spacing)>, ) -> P<Expr> { - match next_token { - Some(next_token) => self.bump_with(next_token), - None => self.bump(), - } - let span = self.prev_token.span; - let field = ExprKind::Field(base, Ident::new(field, span)); if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(span, suffix); + self.expect_no_tuple_index_suffix(ident_span, suffix); } - self.mk_expr(lo.to(span), field) + self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } /// Parse a function call expression, `expr(...)`. @@ -1376,6 +1381,13 @@ impl<'a> Parser<'a> { return Ok(self.mk_await_expr(self_arg, lo)); } + // Post-fix match + if self.eat_keyword(kw::Match) { + let match_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::postfix_match, match_span); + return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); @@ -1936,11 +1948,11 @@ impl<'a> Parser<'a> { /// Parse `builtin # ident(args,*)`. fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> { self.parse_builtin(|this, lo, ident| { - if ident.name == sym::offset_of { - return Ok(Some(this.parse_expr_offset_of(lo)?)); - } - - Ok(None) + Ok(match ident.name { + sym::offset_of => Some(this.parse_expr_offset_of(lo)?), + sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), + _ => None, + }) }) } @@ -1975,6 +1987,7 @@ impl<'a> Parser<'a> { ret } + /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> { let container = self.parse_ty()?; self.expect(&TokenKind::Comma)?; @@ -2004,6 +2017,15 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } + /// Built-in macro for type ascription expressions. + pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P<Expr>> { + let expr = self.parse_expr()?; + self.expect(&token::Comma)?; + let ty = self.parse_ty()?; + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2040,16 +2062,6 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { - if let token::Interpolated(nt) = &self.token.kind - && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 - && matches!(e.kind, ExprKind::Err(_)) - { - let mut err = self - .dcx() - .create_err(errors::InvalidInterpolatedExpression { span: self.token.span }); - err.downgrade_to_delayed_bug(); - return Err(err); - } let token = self.token.clone(); let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); @@ -2891,8 +2903,20 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> { let match_span = self.prev_token.span; - let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) + } + + /// Parses the block of a `match expr { ... }` or a `expr.match { ... }` + /// expression. This is after the match token and scrutinee are eaten + fn parse_match_block( + &mut self, + lo: Span, + match_span: Span, + scrutinee: P<Expr>, + match_kind: MatchKind, + ) -> PResult<'a, P<Expr>> { if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { if self.token == token::Semi { e.span_suggestion_short( @@ -2935,7 +2959,7 @@ impl<'a> Parser<'a> { }); return Ok(self.mk_expr_with_attrs( span, - ExprKind::Match(scrutinee, arms), + ExprKind::Match(scrutinee, arms, match_kind), attrs, )); } @@ -2943,7 +2967,7 @@ impl<'a> Parser<'a> { } let hi = self.token.span; self.bump(); - Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) + Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs)) } /// Attempt to recover from match arm body with statements and no surrounding braces. @@ -3952,7 +3976,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::While(_, _, _) | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) - | ExprKind::Match(_, _) + | ExprKind::Match(_, _, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) | ExprKind::Gen(_, _, _) diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 263b2a90643..fde16ac957d 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -481,7 +481,7 @@ impl<'a> Parser<'a> { })) } else { self.maybe_recover_bounds_doubled_colon(&ty)?; - self.unexpected() + self.unexpected_any() } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 79492fe62ed..d54eb8dc4c9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -6,6 +6,7 @@ use super::{ }; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; +use crate::maybe_whole; use ast::token::IdentIsRaw; use rustc_ast::ast::*; use rustc_ast::ptr::P; @@ -115,17 +116,10 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, ) -> PResult<'a, Option<Item>> { - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtItem(item) = &nt.0 - { - let mut item = item.clone(); - self.bump(); - + maybe_whole!(self, NtItem, |item| { attrs.prepend_to_nt_inner(&mut item.attrs); - return Ok(Some(item.into_inner())); - }; + Some(item.into_inner()) + }); let item = self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| { @@ -1514,7 +1508,7 @@ impl<'a> Parser<'a> { let ident = this.parse_field_ident("enum", vlo)?; if this.token == token::Not { - if let Err(err) = this.unexpected::<()>() { + if let Err(err) = this.unexpected() { err.with_note(fluent::parse_macro_expands_to_enum_variant).emit(); } @@ -1937,7 +1931,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; if self.token.kind == token::Not { - if let Err(mut err) = self.unexpected::<FieldDef>() { + if let Err(mut err) = self.unexpected() { // Encounter the macro invocation err.subdiagnostic(self.dcx(), MacroExpandsToAdtField { adt_ty }); return Err(err); @@ -2067,7 +2061,7 @@ impl<'a> Parser<'a> { let params = self.parse_token_tree(); // `MacParams` let pspan = params.span(); if !self.check(&token::OpenDelim(Delimiter::Brace)) { - return self.unexpected(); + self.unexpected()?; } let body = self.parse_token_tree(); // `MacBody` // Convert `MacParams MacBody` into `{ MacParams => MacBody }`. @@ -2077,7 +2071,7 @@ impl<'a> Parser<'a> { let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi()); P(DelimArgs { dspan, delim: Delimiter::Brace, tokens }) } else { - return self.unexpected(); + self.unexpected_any()? }; self.psess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span)); @@ -2692,7 +2686,7 @@ impl<'a> Parser<'a> { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); let (pat, colon) = this.parse_fn_param_pat_colon()?; if !colon { - let mut err = this.unexpected::<()>().unwrap_err(); + let mut err = this.unexpected().unwrap_err(); return if let Some(ident) = this.parameter_without_type(&mut err, pat, is_name_required, first_param) { @@ -2716,7 +2710,7 @@ impl<'a> Parser<'a> { { // This wasn't actually a type, but a pattern looking like a type, // so we are going to rollback and re-parse for recovery. - ty = this.unexpected(); + ty = this.unexpected_any(); } match ty { Ok(ty) => { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 12879dc429b..de83528b52c 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,7 +20,7 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; @@ -93,12 +93,13 @@ pub enum TrailingToken { #[macro_export] macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { - if let token::Interpolated(nt) = &$p.token.kind { - if let token::$constructor(x) = &nt.0 { - let $x = x.clone(); - $p.bump(); - return Ok($e); - } + if let token::Interpolated(nt) = &$p.token.kind + && let token::$constructor(x) = &nt.0 + { + #[allow(unused_mut)] + let mut $x = x.clone(); + $p.bump(); + return Ok($e); } }; } @@ -449,6 +450,7 @@ impl<'a> Parser<'a> { parser } + #[inline] pub fn recovery(mut self, recovery: Recovery) -> Self { self.recovery = recovery; self @@ -461,11 +463,14 @@ impl<'a> Parser<'a> { /// /// Technically, this only needs to restrict eager recovery by doing lookahead at more tokens. /// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold. + #[inline] fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) } - pub fn unexpected<T>(&mut self) -> PResult<'a, T> { + /// Version of [`unexpected`](Parser::unexpected) that "returns" any type in the `Ok` + /// (both those functions never return "Ok", and so can lie like that in the type). + pub fn unexpected_any<T>(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), // We can get `Ok(true)` from `recover_closing_delimiter` @@ -474,6 +479,10 @@ impl<'a> Parser<'a> { } } + pub fn unexpected(&mut self) -> PResult<'a, ()> { + self.unexpected_any() + } + /// Expects and consumes the token `t`. Signals an error if the next token is not `t`. pub fn expect(&mut self, t: &TokenKind) -> PResult<'a, Recovered> { if self.expected_tokens.is_empty() { @@ -542,6 +551,7 @@ impl<'a> Parser<'a> { /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. + #[inline] fn check(&mut self, tok: &TokenKind) -> bool { let is_present = self.token == *tok; if !is_present { @@ -550,6 +560,7 @@ impl<'a> Parser<'a> { is_present } + #[inline] fn check_noexpect(&self, tok: &TokenKind) -> bool { self.token == *tok } @@ -558,6 +569,7 @@ impl<'a> Parser<'a> { /// /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. + #[inline] pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { @@ -567,6 +579,7 @@ impl<'a> Parser<'a> { } /// Consumes a token 'tok' if it exists. Returns whether the given token was present. + #[inline] pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); if is_present { @@ -577,11 +590,13 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, returns `true` without eating it. /// An expectation is also added for diagnostics purposes. + #[inline] fn check_keyword(&mut self, kw: Symbol) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } + #[inline] fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; @@ -600,6 +615,7 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. + #[inline] pub fn eat_keyword(&mut self, kw: Symbol) -> bool { if self.check_keyword(kw) { self.bump(); @@ -612,6 +628,7 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. + #[inline] fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; @@ -629,6 +646,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -650,6 +668,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } + #[inline] fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool { if ok { true @@ -697,6 +716,7 @@ impl<'a> Parser<'a> { /// Checks to see if the next token is either `+` or `+=`. /// Otherwise returns `false`. + #[inline] fn check_plus(&mut self) -> bool { self.check_or_expected( self.token.is_like_plus(), @@ -1278,7 +1298,11 @@ impl<'a> Parser<'a> { } fn parse_delim_args(&mut self) -> PResult<'a, P<DelimArgs>> { - if let Some(args) = self.parse_delim_args_inner() { Ok(P(args)) } else { self.unexpected() } + if let Some(args) = self.parse_delim_args_inner() { + Ok(P(args)) + } else { + self.unexpected_any() + } } fn parse_attr_args(&mut self) -> PResult<'a, AttrArgs> { @@ -1384,7 +1408,7 @@ impl<'a> Parser<'a> { /// so emit a proper diagnostic. // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { - maybe_whole!(self, NtVis, |x| x.into_inner()); + maybe_whole!(self, NtVis, |vis| vis.into_inner()); if !self.eat_keyword(kw::Pub) { // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no @@ -1561,8 +1585,21 @@ pub enum FlatToken { Empty, } -#[derive(Debug)] -pub enum ParseNtResult { - Nt(Nonterminal), +// Metavar captures of various kinds. +#[derive(Clone, Debug)] +pub enum ParseNtResult<NtType> { Tt(TokenTree), + Nt(NtType), +} + +impl<T> ParseNtResult<T> { + pub fn map_nt<F, U>(self, mut f: F) -> ParseNtResult<U> + where + F: FnMut(T) -> U, + { + match self { + ParseNtResult::Tt(tt) => ParseNtResult::Tt(tt), + ParseNtResult::Nt(nt) => ParseNtResult::Nt(f(nt)), + } + } } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f1572a18a8b..36a00df7b44 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, Nonterminal, Nonterminal::*, NonterminalKind, Token}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_errors::PResult; @@ -66,15 +66,14 @@ impl<'a> Parser<'a> { token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { - match &token.kind { + NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern token::OpenDelim(Delimiter::Bracket) | // slice pattern token::BinOp(token::And) | // reference token::BinOp(token::Minus) | // negative literal token::AndAnd | // double reference - token::Literal(_) | // literal + token::Literal(_) | // literal token::DotDot | // range pattern (future compat) token::DotDotDot | // range pattern (future compat) token::ModSep | // path @@ -84,8 +83,7 @@ impl<'a> Parser<'a> { token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, - } - } + }, NonterminalKind::Lifetime => match &token.kind { token::Lifetime(_) => true, token::Interpolated(nt) => { @@ -102,7 +100,10 @@ impl<'a> Parser<'a> { /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call /// site. #[inline] - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> { + pub fn parse_nonterminal( + &mut self, + kind: NonterminalKind, + ) -> PResult<'a, ParseNtResult<Nonterminal>> { // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, // which requires having captured tokens available. Since we cannot determine // in advance whether or not a proc-macro will be (transitively) invoked, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 88ae1b5420f..fbc28859535 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -435,7 +435,7 @@ impl<'a> Parser<'a> { syntax_loc: Option<PatternLocation>, ) -> PResult<'a, P<Pat>> { maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole!(self, NtPat, |x| x); + maybe_whole!(self, NtPat, |pat| pat); let mut lo = self.token.span; @@ -498,11 +498,14 @@ impl<'a> Parser<'a> { } else { PatKind::Lit(const_expr) } + } else if self.is_builtin() { + self.parse_pat_builtin()? + } // Don't eagerly error on semantically invalid tokens when matching // declarative macros, as the input to those doesn't have to be // semantically valid. For attribute/derive proc macros this is not the // case, so doing the recovery for them is fine. - } else if self.can_be_ident_pat() + else if self.can_be_ident_pat() || (self.is_lit_bad_ident().is_some() && self.may_recover()) { // Parse `ident @ pat` @@ -1119,6 +1122,21 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) } + fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> { + self.parse_builtin(|self_, _lo, ident| { + Ok(match ident.name { + // builtin#deref(PAT) + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?)), + _ => None, + }) + }) + } + /// Parses `box pat` fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { let box_span = self.prev_token.span; diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 163d10d03f0..9153f2b9d06 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -660,7 +660,7 @@ impl<'a> Parser<'a> { // Add `>` to the list of expected tokens. self.check(&token::Gt); // Handle `,` to `;` substitution - let mut err = self.unexpected::<()>().unwrap_err(); + let mut err = self.unexpected().unwrap_err(); self.bump(); err.span_suggestion_verbose( self.prev_token.span.until(self.token.span), @@ -731,8 +731,6 @@ impl<'a> Parser<'a> { && matches!(args.output, ast::FnRetTy::Default(..)) { self.psess.gated_spans.gate(sym::return_type_notation, span); - } else { - self.psess.gated_spans.gate(sym::associated_type_bounds, span); } } let constraint = diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 73f5829adec..6601011665b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -40,8 +40,8 @@ impl<'a> Parser<'a> { })) } - /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether - /// or not we have attributes + /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of + /// whether or not we have attributes. // Public for `cfg_eval` macro expansion. pub fn parse_stmt_without_recovery( &mut self, @@ -51,18 +51,12 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtStmt(stmt) = &nt.0 - { - let mut stmt = stmt.clone(); - self.bump(); + maybe_whole!(self, NtStmt, |stmt| { stmt.visit_attrs(|stmt_attrs| { attrs.prepend_to_nt_inner(stmt_attrs); }); - return Ok(Some(stmt.into_inner())); - } + Some(stmt.into_inner()) + }); if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { self.bump(); @@ -254,7 +248,7 @@ impl<'a> Parser<'a> { let local = this.parse_local(attrs)?; // FIXME - maybe capture semicolon in recovery? Ok(( - this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), + this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), TrailingToken::None, )) })?; @@ -278,7 +272,7 @@ impl<'a> Parser<'a> { } else { TrailingToken::None }; - Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing)) + Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing)) }) } @@ -539,7 +533,7 @@ impl<'a> Parser<'a> { blk_mode: BlockCheckMode, can_be_struct_literal: bool, ) -> PResult<'a, (AttrVec, P<Block>)> { - maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x)); + maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block)); let maybe_ident = self.prev_token.clone(); self.maybe_recover_unexpected_block_label(); @@ -643,7 +637,7 @@ impl<'a> Parser<'a> { recover: AttemptLocalParseRecovery, ) -> PResult<'a, Option<Stmt>> { // Skip looking for a trailing semicolon when we have an interpolated statement. - maybe_whole!(self, NtStmt, |x| Some(x.into_inner())); + maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner())); let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else { return Ok(None); @@ -764,7 +758,7 @@ impl<'a> Parser<'a> { } } StmtKind::Expr(_) | StmtKind::MacCall(_) => {} - StmtKind::Local(local) if let Err(mut e) = self.expect_semi() => { + StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => { // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. match &mut local.kind { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { @@ -820,7 +814,7 @@ impl<'a> Parser<'a> { } eat_semi = false; } - StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => { + StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => { eat_semi = false } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2385c18c089..1cea32cb90f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -250,7 +250,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Ty>> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); - maybe_whole!(self, NtTy, |x| x); + maybe_whole!(self, NtTy, |ty| ty); let lo = self.token.span; let mut impl_dyn_multi = false; |
