diff options
| author | bors <bors@rust-lang.org> | 2019-11-04 02:30:45 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-11-04 02:30:45 +0000 |
| commit | f49f38871389041671cc710a044e8360091840a3 (patch) | |
| tree | ad7ea355839acdbf19ec6cc9f3af8b1cb12e864e /src/libsyntax/parse | |
| parent | 0d5264a03c2d873d9e23a22def748b9c6937c537 (diff) | |
| parent | 454e2aa8c99850c9393fb2314e1a71da08120063 (diff) | |
| download | rust-f49f38871389041671cc710a044e8360091840a3.tar.gz rust-f49f38871389041671cc710a044e8360091840a3.zip | |
Auto merge of #65838 - estebank:resilient-recovery, r=Centril
Reduce amount of errors given unclosed delimiter When in a file with a non-terminated item, catch the error and consume the block instead of trying to recover it on a more granular way in order to reduce the amount of unrelated errors that would be fixed after adding the missing closing brace. Also point out the possible location of the missing closing brace. Fix #63690.
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/tokentrees.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 46 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/diagnostics.rs | 50 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/item.rs | 17 |
6 files changed, 93 insertions, 48 deletions
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index e6dc9a4c134..48cf117f65a 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -23,7 +23,7 @@ mod unicode_chars; #[derive(Clone, Debug)] pub struct UnmatchedBrace { pub expected_delim: token::DelimToken, - pub found_delim: token::DelimToken, + pub found_delim: Option<token::DelimToken>, pub found_span: Span, pub unclosed_span: Option<Span>, pub candidate_span: Option<Span>, diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs index 853723de14f..de8ac2c71e8 100644 --- a/src/libsyntax/parse/lexer/tokentrees.rs +++ b/src/libsyntax/parse/lexer/tokentrees.rs @@ -80,6 +80,13 @@ impl<'a> TokenTreesReader<'a> { .struct_span_err(self.token.span, msg); for &(_, sp) in &self.open_braces { err.span_label(sp, "un-closed delimiter"); + self.unmatched_braces.push(UnmatchedBrace { + expected_delim: token::DelimToken::Brace, + found_delim: None, + found_span: self.token.span, + unclosed_span: Some(sp), + candidate_span: None, + }); } if let Some((delim, _)) = self.open_braces.last() { @@ -170,7 +177,7 @@ impl<'a> TokenTreesReader<'a> { let (tok, _) = self.open_braces.pop().unwrap(); self.unmatched_braces.push(UnmatchedBrace { expected_delim: tok, - found_delim: other, + found_delim: Some(other), found_span: self.token.span, unclosed_span: unclosed_delimiter, candidate_span: candidate, diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index f5e416b722b..6d8ecdf805b 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -1,7 +1,7 @@ //! The main parser interface. use crate::ast; -use crate::parse::parser::{Parser, emit_unclosed_delims}; +use crate::parse::parser::{Parser, emit_unclosed_delims, make_unclosed_delims_error}; use crate::parse::token::Nonterminal; use crate::tokenstream::{self, TokenStream, TokenTree}; use crate::print::pprust; @@ -108,7 +108,7 @@ pub fn parse_stream_from_source_str( sess.source_map().new_source_file(name, source), override_span, ); - emit_unclosed_delims(&mut errors, &sess.span_diagnostic); + emit_unclosed_delims(&mut errors, &sess); stream } @@ -242,18 +242,9 @@ pub fn maybe_file_to_stream( err.buffer(&mut buffer); // Not using `emit_unclosed_delims` to use `db.buffer` for unmatched in unmatched_braces { - let mut db = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!( - "incorrect close delimiter: `{}`", - pprust::token_kind_to_string(&token::CloseDelim(unmatched.found_delim)), - )); - db.span_label(unmatched.found_span, "incorrect close delimiter"); - if let Some(sp) = unmatched.candidate_span { - db.span_label(sp, "close delimiter possibly meant for this"); + if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { + err.buffer(&mut buffer); } - if let Some(sp) = unmatched.unclosed_span { - db.span_label(sp, "un-closed delimiter"); - } - db.buffer(&mut buffer); } Err(buffer) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6ead1ce811d..e81d4573b73 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -27,7 +27,7 @@ use crate::symbol::{kw, sym, Symbol}; use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint}; use crate::ThinVec; -use errors::{Applicability, DiagnosticId, FatalError}; +use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError}; use rustc_target::spec::abi::{self, Abi}; use syntax_pos::{Span, BytePos, DUMMY_SP, FileName}; use log::debug; @@ -148,8 +148,7 @@ pub struct Parser<'a> { impl<'a> Drop for Parser<'a> { fn drop(&mut self) { - let diag = self.diagnostic(); - emit_unclosed_delims(&mut self.unclosed_delims, diag); + emit_unclosed_delims(&mut self.unclosed_delims, &self.sess); } } @@ -1370,20 +1369,31 @@ impl<'a> Parser<'a> { } } -pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, handler: &errors::Handler) { - for unmatched in unclosed_delims.iter() { - let mut err = handler.struct_span_err(unmatched.found_span, &format!( - "incorrect close delimiter: `{}`", - pprust::token_kind_to_string(&token::CloseDelim(unmatched.found_delim)), - )); - err.span_label(unmatched.found_span, "incorrect close delimiter"); - if let Some(sp) = unmatched.candidate_span { - err.span_label(sp, "close delimiter possibly meant for this"); - } - if let Some(sp) = unmatched.unclosed_span { - err.span_label(sp, "un-closed delimiter"); - } - err.emit(); +crate fn make_unclosed_delims_error( + unmatched: UnmatchedBrace, + sess: &ParseSess, +) -> Option<DiagnosticBuilder<'_>> { + // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to + // `unmatched_braces` only for error recovery in the `Parser`. + let found_delim = unmatched.found_delim?; + let mut err = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!( + "incorrect close delimiter: `{}`", + pprust::token_kind_to_string(&token::CloseDelim(found_delim)), + )); + err.span_label(unmatched.found_span, "incorrect close delimiter"); + if let Some(sp) = unmatched.candidate_span { + err.span_label(sp, "close delimiter possibly meant for this"); + } + if let Some(sp) = unmatched.unclosed_span { + err.span_label(sp, "un-closed delimiter"); + } + Some(err) +} + +pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &ParseSess) { + *sess.reached_eof.borrow_mut() |= unclosed_delims.iter() + .any(|unmatched_delim| unmatched_delim.found_delim.is_none()); + for unmatched in unclosed_delims.drain(..) { + make_unclosed_delims_error(unmatched, sess).map(|mut e| e.emit()); } - unclosed_delims.clear(); } diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs index ab2b4519cb7..fcf3b4c0aa8 100644 --- a/src/libsyntax/parse/parser/diagnostics.rs +++ b/src/libsyntax/parse/parser/diagnostics.rs @@ -171,6 +171,12 @@ impl RecoverQPath for Expr { } } +/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`. +crate enum ConsumeClosingDelim { + Yes, + No, +} + impl<'a> Parser<'a> { pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> { self.span_fatal(self.token.span, m) @@ -1105,8 +1111,8 @@ impl<'a> Parser<'a> { Ok(x) => x, Err(mut err) => { err.emit(); - // Recover from parse error. - self.consume_block(delim); + // Recover from parse error, callers expect the closing delim to be consumed. + self.consume_block(delim, ConsumeClosingDelim::Yes); self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new()) } } @@ -1135,6 +1141,11 @@ impl<'a> Parser<'a> { // Don't attempt to recover from this unclosed delimiter more than once. let unmatched = self.unclosed_delims.remove(pos); let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); + if unmatched.found_delim.is_none() { + // We encountered `Eof`, set this fact here to avoid complaining about missing + // `fn main()` when we found place to suggest the closing brace. + *self.sess.reached_eof.borrow_mut() = true; + } // We want to suggest the inclusion of the closing delimiter where it makes // the most sense, which is immediately after the last token: @@ -1154,9 +1165,16 @@ impl<'a> Parser<'a> { delim.to_string(), Applicability::MaybeIncorrect, ); - err.emit(); - self.expected_tokens.clear(); // reduce errors - Ok(true) + if unmatched.found_delim.is_none() { + // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown + // errors which would be emitted elsewhere in the parser and let other error + // recovery consume the rest of the file. + Err(err) + } else { + err.emit(); + self.expected_tokens.clear(); // Reduce the number of errors. + Ok(true) + } } _ => Err(err), } @@ -1164,7 +1182,12 @@ impl<'a> Parser<'a> { /// Recovers from `pub` keyword in places where it seems _reasonable_ but isn't valid. pub(super) fn eat_bad_pub(&mut self) { - if self.token.is_keyword(kw::Pub) { + // When `unclosed_delims` is populated, it means that the code being parsed is already + // quite malformed, which might mean that, for example, a pub struct definition could be + // parsed as being a trait item, which is invalid and this error would trigger + // unconditionally, resulting in misleading diagnostics. Because of this, we only attempt + // this nice to have recovery for code that is otherwise well formed. + if self.token.is_keyword(kw::Pub) && self.unclosed_delims.is_empty() { match self.parse_visibility(false) { Ok(vis) => { self.diagnostic() @@ -1422,15 +1445,26 @@ impl<'a> Parser<'a> { Ok(param) } - pub(super) fn consume_block(&mut self, delim: token::DelimToken) { + pub(super) fn consume_block( + &mut self, + delim: token::DelimToken, + consume_close: ConsumeClosingDelim, + ) { let mut brace_depth = 0; loop { if self.eat(&token::OpenDelim(delim)) { brace_depth += 1; - } else if self.eat(&token::CloseDelim(delim)) { + } else if self.check(&token::CloseDelim(delim)) { if brace_depth == 0 { + if let ConsumeClosingDelim::Yes = consume_close { + // Some of the callers of this method expect to be able to parse the + // closing delimiter themselves, so we leave it alone. Otherwise we advance + // the parser. + self.bump(); + } return; } else { + self.bump(); brace_depth -= 1; continue; } diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs index fe125336190..5b60e7e6dba 100644 --- a/src/libsyntax/parse/parser/item.rs +++ b/src/libsyntax/parse/parser/item.rs @@ -1,5 +1,5 @@ -use super::{Parser, PResult, PathStyle, SemiColonMode, BlockMode}; -use super::diagnostics::{Error, dummy_arg}; +use super::{Parser, PResult, PathStyle}; +use super::diagnostics::{Error, dummy_arg, ConsumeClosingDelim}; use crate::maybe_whole; use crate::ptr::P; @@ -339,7 +339,7 @@ impl<'a> Parser<'a> { let ident = self.parse_ident().unwrap(); self.bump(); // `(` let kw_name = self.recover_first_param(); - self.consume_block(token::Paren); + self.consume_block(token::Paren, ConsumeClosingDelim::Yes); let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) { self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]); self.bump(); // `{` @@ -357,7 +357,7 @@ impl<'a> Parser<'a> { let msg = format!("missing `{}` for {} definition", kw, kw_name); let mut err = self.diagnostic().struct_span_err(sp, &msg); if !ambiguous { - self.consume_block(token::Brace); + self.consume_block(token::Brace, ConsumeClosingDelim::Yes); let suggestion = format!("add `{}` here to parse `{}` as a public {}", kw, ident, @@ -672,7 +672,8 @@ impl<'a> Parser<'a> { Err(mut err) => { err.emit(); if !at_end { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Break); + self.consume_block(token::Brace, ConsumeClosingDelim::Yes); + break; } } } @@ -861,7 +862,8 @@ impl<'a> Parser<'a> { Err(mut e) => { e.emit(); if !at_end { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Break); + self.consume_block(token::Brace, ConsumeClosingDelim::Yes); + break; } } } @@ -1520,7 +1522,7 @@ impl<'a> Parser<'a> { if self.eat(&token::OpenDelim(token::Brace)) { while self.token != token::CloseDelim(token::Brace) { let field = self.parse_struct_decl_field().map_err(|e| { - self.recover_stmt(); + self.consume_block(token::Brace, ConsumeClosingDelim::No); recovered = true; e }); @@ -1528,6 +1530,7 @@ impl<'a> Parser<'a> { Ok(field) => fields.push(field), Err(mut err) => { err.emit(); + break; } } } |
