diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/mod.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/tokentrees.rs | 64 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 38 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 34 |
8 files changed, 162 insertions, 54 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f22d70ec809..20b4292701e 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -521,6 +521,14 @@ pub(crate) struct CatchAfterTry { } #[derive(Diagnostic)] +#[diag(parse_gen_fn)] +#[help] +pub(crate) struct GenFn { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_comma_after_base_struct)] #[note] pub(crate) struct CommaAfterBaseStruct { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index a375a1d69cd..f2eed5c9be5 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -64,10 +64,10 @@ pub(crate) fn parse_token_trees<'a>( override_span, nbsp_is_whitespace: false, }; - let (token_trees, unmatched_delims) = + let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); - match token_trees { - Ok(stream) if unmatched_delims.is_empty() => Ok(stream), + match res { + Ok(()) if unmatched_delims.is_empty() => Ok(stream), _ => { // Return error if there are unmatched delimiters or unclosed delimiters. // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch @@ -79,9 +79,11 @@ pub(crate) fn parse_token_trees<'a>( err.buffer(&mut buffer); } } - if let Err(err) = token_trees { - // Add unclosing delimiter error - err.buffer(&mut buffer); + if let Err(errs) = res { + // Add unclosing delimiter or diff marker errors + for err in errs { + err.buffer(&mut buffer); + } } Err(buffer) } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 1d9dbfe4b89..31d91fe80bd 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -5,7 +5,7 @@ use super::{StringReader, UnmatchedDelim}; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_errors::{PErr, PResult}; +use rustc_errors::PErr; pub(super) struct TokenTreesReader<'a> { string_reader: StringReader<'a>, @@ -18,36 +18,42 @@ pub(super) struct TokenTreesReader<'a> { impl<'a> TokenTreesReader<'a> { pub(super) fn parse_all_token_trees( string_reader: StringReader<'a>, - ) -> (PResult<'a, TokenStream>, Vec<UnmatchedDelim>) { + ) -> (TokenStream, Result<(), Vec<PErr<'a>>>, Vec<UnmatchedDelim>) { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), diag_info: TokenTreeDiagInfo::default(), }; - let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.diag_info.unmatched_delims) + let (stream, res) = tt_reader.parse_token_trees(/* is_delimited */ false); + (stream, res, tt_reader.diag_info.unmatched_delims) } // Parse a stream of tokens into a list of `TokenTree`s. - fn parse_token_trees(&mut self, is_delimited: bool) -> PResult<'a, TokenStream> { + fn parse_token_trees( + &mut self, + is_delimited: bool, + ) -> (TokenStream, Result<(), Vec<PErr<'a>>>) { self.token = self.string_reader.next_token().0; let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?), + token::OpenDelim(delim) => { + buf.push(match self.parse_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return (TokenStream::new(buf), Err(errs)), + }) + } token::CloseDelim(delim) => { - return if is_delimited { - Ok(TokenStream::new(buf)) - } else { - Err(self.close_delim_err(delim)) - }; + return ( + TokenStream::new(buf), + if is_delimited { Ok(()) } else { Err(vec![self.close_delim_err(delim)]) }, + ); } token::Eof => { - return if is_delimited { - Err(self.eof_err()) - } else { - Ok(TokenStream::new(buf)) - }; + return ( + TokenStream::new(buf), + if is_delimited { Err(vec![self.eof_err()]) } else { Ok(()) }, + ); } _ => { // Get the next normal token. This might require getting multiple adjacent @@ -97,7 +103,10 @@ impl<'a> TokenTreesReader<'a> { err } - fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> { + fn parse_token_tree_open_delim( + &mut self, + open_delim: Delimiter, + ) -> Result<TokenTree, Vec<PErr<'a>>> { // The span for beginning of the delimited section let pre_span = self.token.span; @@ -106,7 +115,26 @@ impl<'a> TokenTreesReader<'a> { // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let tts = self.parse_token_trees(/* is_delimited */ true)?; + let (tts, res) = self.parse_token_trees(/* is_delimited */ true); + if let Err(mut errs) = res { + // If there are unclosed delims, see if there are diff markers and if so, point them + // out instead of complaining about the unclosed delims. + let mut parser = crate::stream_to_parser(self.string_reader.sess, tts, None); + let mut diff_errs = vec![]; + while parser.token != token::Eof { + if let Err(diff_err) = parser.err_diff_marker() { + diff_errs.push(diff_err); + } + parser.bump(); + } + if !diff_errs.is_empty() { + errs.iter_mut().for_each(|err| { + err.delay_as_bug(); + }); + return Err(diff_errs); + } + return Err(errs); + } // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9f8361a4b1e..2a8eb6edd23 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -504,8 +504,10 @@ impl<'a> Parser<'a> { // Special-case "expected `;`" errors if expected.contains(&TokenType::Token(token::Semi)) { - if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() { - return Ok(true); + // If the user is trying to write a ternary expression, recover it and + // return an Err to prevent a cascade of irrelevant diagnostics + if self.prev_token == token::Question && let Err(e) = self.maybe_recover_from_ternary_operator() { + return Err(e); } if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { @@ -1428,10 +1430,10 @@ impl<'a> Parser<'a> { /// Rust has no ternary operator (`cond ? then : else`). Parse it and try /// to recover from it if `then` and `else` are valid expressions. Returns - /// whether it was a ternary operator. - pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool { + /// an err if this appears to be a ternary expression. + pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> { if self.prev_token != token::Question { - return false; + return PResult::Ok(()); } let lo = self.prev_token.span.lo(); @@ -1449,20 +1451,18 @@ impl<'a> Parser<'a> { if self.eat_noexpect(&token::Colon) { match self.parse_expr() { Ok(_) => { - self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) }); - return true; + return Err(self + .sess + .create_err(TernaryOperator { span: self.token.span.with_lo(lo) })); } Err(err) => { err.cancel(); - self.restore_snapshot(snapshot); } }; } - } else { - self.restore_snapshot(snapshot); - }; - - false + } + self.restore_snapshot(snapshot); + Ok(()) } pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> { @@ -2808,8 +2808,15 @@ impl<'a> Parser<'a> { } pub fn recover_diff_marker(&mut self) { + if let Err(mut err) = self.err_diff_marker() { + err.emit(); + FatalError.raise(); + } + } + + pub fn err_diff_marker(&mut self) -> PResult<'a, ()> { let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { - return; + return Ok(()); }; let mut spans = Vec::with_capacity(3); spans.push(start); @@ -2856,8 +2863,7 @@ impl<'a> Parser<'a> { "for an explanation on these markers from the `git` documentation, visit \ <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>", ); - err.emit(); - FatalError.raise() + Err(err) } /// Parse and throw away a parenthesized comma separated diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5157106f4e2..0b5ec9b59ea 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -9,7 +9,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{Path, PathSegment}; +use ast::{GenBlockKind, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -1441,14 +1441,20 @@ impl<'a> Parser<'a> { } else if this.token.uninterpolated_span().at_least_rust_2018() { // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. if this.check_keyword(kw::Async) { - if this.is_async_block() { + if this.is_gen_block(kw::Async) { // Check for `async {` and `async move {`. - this.parse_async_block() + this.parse_gen_block() } else { this.parse_expr_closure() } } else if this.eat_keyword(kw::Await) { this.recover_incorrect_await_syntax(lo, this.prev_token.span) + } else if this.token.uninterpolated_span().at_least_rust_2024() { + if this.is_gen_block(kw::Gen) { + this.parse_gen_block() + } else { + this.parse_expr_lit() + } } else { this.parse_expr_lit() } @@ -1848,7 +1854,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let kind = ExprKind::Yield(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::coroutines, span); + self.sess.gated_spans.gate(sym::yield_expr, span); let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -3059,18 +3065,24 @@ impl<'a> Parser<'a> { && self.token.uninterpolated_span().at_least_rust_2018() } - /// Parses an `async move? {...}` expression. - fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> { + /// Parses an `async move? {...}` or `gen move? {...}` expression. + fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; - self.expect_keyword(kw::Async)?; + let kind = if self.eat_keyword(kw::Async) { + GenBlockKind::Async + } else { + assert!(self.eat_keyword(kw::Gen)); + self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span)); + GenBlockKind::Gen + }; let capture_clause = self.parse_capture_clause()?; let (attrs, body) = self.parse_inner_attrs_and_block()?; - let kind = ExprKind::Async(capture_clause, body); + let kind = ExprKind::Gen(capture_clause, body, kind); Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } - fn is_async_block(&self) -> bool { - self.token.is_keyword(kw::Async) + fn is_gen_block(&self, kw: Symbol) -> bool { + self.token.is_keyword(kw) && (( // `async move {` self.is_keyword_ahead(1, &[kw::Move]) @@ -3596,7 +3608,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Match(_, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) - | ExprKind::Async(_, _) + | ExprKind::Gen(_, _, _) | ExprKind::TryBlock(_) | ExprKind::Underscore | ExprKind::Path(_, _) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d4f7083c397..f0cf6bb58ca 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2297,9 +2297,9 @@ impl<'a> Parser<'a> { // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] } else { - &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: @@ -2353,6 +2353,9 @@ impl<'a> Parser<'a> { let async_start_sp = self.token.span; let asyncness = self.parse_asyncness(case); + let _gen_start_sp = self.token.span; + let genness = self.parse_genness(case); + let unsafe_start_sp = self.token.span; let unsafety = self.parse_unsafety(case); @@ -2368,6 +2371,10 @@ impl<'a> Parser<'a> { } } + if let Gen::Yes { span, .. } = genness { + self.sess.emit_err(errors::GenFn { span }); + } + if !self.eat_keyword_case(kw::Fn, case) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't @@ -2504,9 +2511,11 @@ impl<'a> Parser<'a> { let (mut params, _) = self.parse_paren_comma_seq(|p| { p.recover_diff_marker(); + let snapshot = p.create_snapshot_for_diagnostic(); let param = p.parse_param_general(req_name, first_param).or_else(|mut e| { e.emit(); let lo = p.prev_token.span; + p.restore_snapshot(snapshot); // Skip every token until next possible arg or end. p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(Delimiter::Parenthesis)]); // Create a placeholder argument for proper arg count (issue #34264). diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 41b19ecb63a..59b51954542 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,6 +11,7 @@ mod stmt; mod ty; use crate::lexer::UnmatchedDelim; +use ast::Gen; pub use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; @@ -1128,6 +1129,16 @@ impl<'a> Parser<'a> { } } + /// Parses genness: `gen` or nothing. + fn parse_genness(&mut self, case: Case) -> Gen { + if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) { + let span = self.prev_token.uninterpolated_span(); + Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } + } else { + Gen::No + } + } + /// Parses unsafety: `unsafe` or nothing. fn parse_unsafety(&mut self, case: Case) -> Unsafe { if self.eat_keyword_case(kw::Unsafe, case) { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 3e4e9278910..3378e4d46b7 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -967,11 +967,12 @@ impl<'a> Parser<'a> { // check that a comma comes after every field if !ate_comma { - let err = ExpectedCommaAfterPatternField { span: self.token.span } + let mut err = ExpectedCommaAfterPatternField { span: self.token.span } .into_diagnostic(&self.sess.span_diagnostic); if let Some(mut delayed) = delayed_err { delayed.emit(); } + self.recover_misplaced_pattern_modifiers(&fields, &mut err); return Err(err); } ate_comma = false; @@ -1109,6 +1110,37 @@ impl<'a> Parser<'a> { Ok((fields, etc)) } + /// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest + /// the correct code. + fn recover_misplaced_pattern_modifiers( + &self, + fields: &ThinVec<PatField>, + err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>, + ) { + if let Some(last) = fields.iter().last() + && last.is_shorthand + && let PatKind::Ident(binding, ident, None) = last.pat.kind + && binding != BindingAnnotation::NONE + && self.token == token::Colon + // We found `ref mut? ident:`, try to parse a `name,` or `name }`. + && let Some(name_span) = self.look_ahead(1, |t| t.is_ident().then(|| t.span)) + && self.look_ahead(2, |t| { + t == &token::Comma || t == &token::CloseDelim(Delimiter::Brace) + }) + { + let span = last.pat.span.with_hi(ident.span.lo()); + // We have `S { ref field: name }` instead of `S { field: ref name }` + err.multipart_suggestion( + "the pattern modifiers belong after the `:`", + vec![ + (span, String::new()), + (name_span.shrink_to_lo(), binding.prefix_str().to_string()), + ], + Applicability::MachineApplicable, + ); + } + } + /// Recover on `...` or `_` as if it were `..` to avoid further errors. /// See issue #46718. fn recover_bad_dot_dot(&self) { |
