From f54c336c8019432803dd9a67b575053ffc03c372 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 22 Oct 2022 07:56:26 +0800 Subject: fix #103425, remove extra type error after missing semicolon error --- compiler/rustc_parse/src/parser/stmt.rs | 65 ++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 29 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index a61e77b7c3b..12753c6785c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -553,39 +553,46 @@ impl<'a> Parser<'a> { match stmt.kind { // Expression without semicolon. StmtKind::Expr(ref mut expr) - if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => - { + if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => { // Just check for errors and recover; do not eat semicolon yet. - if let Err(mut e) = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) - { - if let TokenKind::DocComment(..) = self.token.kind { - if let Ok(snippet) = self.span_to_snippet(self.token.span) { - let sp = self.token.span; - let marker = &snippet[..3]; - let (comment_marker, doc_comment_marker) = marker.split_at(2); - - e.span_suggestion( - sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), - &format!( - "add a space before `{}` to use a regular comment", - doc_comment_marker, - ), - format!("{} {}", comment_marker, doc_comment_marker), - Applicability::MaybeIncorrect, - ); + // `expect_one_of` returns PResult<'a, bool /* recovered */> + let replace_with_err = + match self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) { + // Recover from parser, skip type error to avoid extra errors. + Ok(true) => true, + Err(mut e) => { + if let TokenKind::DocComment(..) = self.token.kind && + let Ok(snippet) = self.span_to_snippet(self.token.span) { + let sp = self.token.span; + let marker = &snippet[..3]; + let (comment_marker, doc_comment_marker) = marker.split_at(2); + + e.span_suggestion( + sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), + &format!( + "add a space before `{}` to use a regular comment", + doc_comment_marker, + ), + format!("{} {}", comment_marker, doc_comment_marker), + Applicability::MaybeIncorrect, + ); } - } - if let Err(mut e) = - self.check_mistyped_turbofish_with_multiple_type_params(e, expr) - { - if recover.no() { - return Err(e); + + if let Err(mut e) = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + { + if recover.no() { + return Err(e); + } + e.emit(); + self.recover_stmt(); } - e.emit(); - self.recover_stmt(); + true } - // Don't complain about type errors in body tail after parse error (#57383). + _ => false + }; + if replace_with_err { + // We already emitted an error, so don't emit another type error let sp = expr.span.to(self.prev_token.span); *expr = self.mk_expr_err(sp); } -- cgit 1.4.1-3-g733a5 From ed142028641079918b95b539f0570e92469687fe Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:24:01 +0200 Subject: Add flag to forbid recovery in the parser --- compiler/rustc_expand/src/mbe/macro_rules.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 2 ++ compiler/rustc_parse/src/parser/mod.rs | 23 +++++++++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1e268542bcd..f6fe38174f7 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -250,6 +250,7 @@ fn expand_macro<'cx>( // hacky, but speeds up the `html5ever` benchmark significantly. (Issue // 68836 suggests a more comprehensive but more complex change to deal with // this situation.) + // FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match. let parser = parser_from_cx(sess, arg.clone()); // Try each arm's matchers. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ca216b1cd10..a781748efc5 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2112,6 +2112,8 @@ impl<'a> Parser<'a> { // HACK: This is needed so we can detect whether we're inside a macro, // where regular assumptions about what tokens can follow other tokens // don't necessarily apply. + && self.may_recover() + // FIXME(Nilstrieb): Remove this check once `may_recover` actually stops recovery && self.subparser_name.is_none() { // It is likely that the closure body is a block but where the diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 89c24920f85..89f7ab930b1 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -115,6 +115,12 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { }; } +#[derive(Clone, Copy)] +pub enum Recovery { + Allowed, + Forbidden, +} + #[derive(Clone)] pub struct Parser<'a> { pub sess: &'a ParseSess, @@ -152,12 +158,15 @@ pub struct Parser<'a> { /// This allows us to recover when the user forget to add braces around /// multiple statements in the closure body. pub current_closure: Option, + /// Whether the parser is allowed to recover and parse invalid code successfully (and emit a diagnostic as a side effect). + /// This is disabled when parsing macro arguments, see #103534 + pub recovery: Recovery, } -// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure +// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 328); +rustc_data_structures::static_assert_size!(Parser<'_>, 336); /// Stores span information about a closure. #[derive(Clone)] @@ -483,6 +492,7 @@ impl<'a> Parser<'a> { inner_attr_ranges: Default::default(), }, current_closure: None, + recovery: Recovery::Allowed, }; // Make parser point to the first token. @@ -491,6 +501,15 @@ impl<'a> Parser<'a> { parser } + pub fn forbid_recovery(mut self) -> Self { + self.recovery = Recovery::Forbidden; + self + } + + fn may_recover(&self) -> bool { + matches!(self.recovery, Recovery::Allowed) + } + pub fn unexpected(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), -- cgit 1.4.1-3-g733a5 From 796114a5b0c66abbb2527257b8a38c4cda964a66 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:09:28 +0200 Subject: Add documentation --- compiler/rustc_parse/src/parser/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 89f7ab930b1..4376e5832ef 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -158,7 +158,7 @@ pub struct Parser<'a> { /// This allows us to recover when the user forget to add braces around /// multiple statements in the closure body. pub current_closure: Option, - /// Whether the parser is allowed to recover and parse invalid code successfully (and emit a diagnostic as a side effect). + /// Whether the parser is allowed to do recovery. /// This is disabled when parsing macro arguments, see #103534 pub recovery: Recovery, } @@ -506,6 +506,13 @@ impl<'a> Parser<'a> { self } + /// Whether the parser is allowed to recover from broken code. + /// + /// If this returns false, recovering broken code into valid code (especially if this recovery does lookahead) + /// is not allowed. All recovery done by the parser must be gated behind this check. + /// + /// Technically, this only needs to restruct 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. fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) } -- cgit 1.4.1-3-g733a5 From da407ed38f6bcb79683379d59d18e615d2b8dfaa Mon Sep 17 00:00:00 2001 From: nils <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:06:35 +0200 Subject: Fix typo Co-authored-by: Esteban Kuber --- compiler/rustc_parse/src/parser/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 4376e5832ef..5fe29062b85 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -511,7 +511,7 @@ impl<'a> Parser<'a> { /// If this returns false, recovering broken code into valid code (especially if this recovery does lookahead) /// is not allowed. All recovery done by the parser must be gated behind this check. /// - /// Technically, this only needs to restruct eager recovery by doing lookahead at more tokens. + /// 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. fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) -- cgit 1.4.1-3-g733a5