Rollup merge of #108297 - chenyukang:yukang/delim-error-exit, r=petrochenkov
Exit when there are unmatched delims to avoid noisy diagnostics From https://github.com/rust-lang/rust/pull/104012#issuecomment-1311764832 r? ``@petrochenkov``
This commit is contained in:
commit
371904bba6
106 changed files with 325 additions and 1586 deletions
|
@ -1,4 +1,4 @@
|
|||
use super::UnmatchedBrace;
|
||||
use super::UnmatchedDelim;
|
||||
use rustc_ast::token::Delimiter;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
|
@ -8,7 +8,7 @@ use rustc_span::Span;
|
|||
pub struct TokenTreeDiagInfo {
|
||||
/// Stack of open delimiters and their spans. Used for error message.
|
||||
pub open_braces: Vec<(Delimiter, Span)>,
|
||||
pub unmatched_braces: Vec<UnmatchedBrace>,
|
||||
pub unmatched_delims: Vec<UnmatchedDelim>,
|
||||
|
||||
/// Used only for error recovery when arriving to EOF with mismatched braces.
|
||||
pub last_unclosed_found_span: Option<Span>,
|
||||
|
@ -32,10 +32,10 @@ pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> b
|
|||
// it's more friendly compared to report `unmatched error` in later phase
|
||||
pub fn report_missing_open_delim(
|
||||
err: &mut Diagnostic,
|
||||
unmatched_braces: &[UnmatchedBrace],
|
||||
unmatched_delims: &[UnmatchedDelim],
|
||||
) -> bool {
|
||||
let mut reported_missing_open = false;
|
||||
for unmatch_brace in unmatched_braces.iter() {
|
||||
for unmatch_brace in unmatched_delims.iter() {
|
||||
if let Some(delim) = unmatch_brace.found_delim
|
||||
&& matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ pub fn report_suspicious_mismatch_block(
|
|||
sm: &SourceMap,
|
||||
delim: Delimiter,
|
||||
) {
|
||||
if report_missing_open_delim(err, &diag_info.unmatched_braces) {
|
||||
if report_missing_open_delim(err, &diag_info.unmatched_delims) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::errors;
|
||||
use crate::lexer::unicode_chars::UNICODE_ARRAY;
|
||||
use crate::make_unclosed_delims_error;
|
||||
use rustc_ast::ast::{self, AttrStyle};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::util::unicode::contains_text_flow_control_chars;
|
||||
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, PResult, StashKey};
|
||||
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
|
||||
use rustc_lexer::unescape::{self, Mode};
|
||||
use rustc_lexer::Cursor;
|
||||
use rustc_lexer::{Base, DocStyle, RawStrError};
|
||||
|
@ -31,7 +32,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char};
|
|||
rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UnmatchedBrace {
|
||||
pub struct UnmatchedDelim {
|
||||
pub expected_delim: Delimiter,
|
||||
pub found_delim: Option<Delimiter>,
|
||||
pub found_span: Span,
|
||||
|
@ -44,7 +45,7 @@ pub(crate) fn parse_token_trees<'a>(
|
|||
mut src: &'a str,
|
||||
mut start_pos: BytePos,
|
||||
override_span: Option<Span>,
|
||||
) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
|
||||
) -> Result<TokenStream, Vec<Diagnostic>> {
|
||||
// Skip `#!`, if present.
|
||||
if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
|
||||
src = &src[shebang_len..];
|
||||
|
@ -61,7 +62,29 @@ pub(crate) fn parse_token_trees<'a>(
|
|||
override_span,
|
||||
nbsp_is_whitespace: false,
|
||||
};
|
||||
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader)
|
||||
let (token_trees, unmatched_delims) =
|
||||
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader);
|
||||
match token_trees {
|
||||
Ok(stream) if unmatched_delims.is_empty() => Ok(stream),
|
||||
_ => {
|
||||
// Return error if there are unmatched delimiters or unclosng delimiters.
|
||||
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
|
||||
// because the delimiter mismatch is more likely to be the root cause of error
|
||||
|
||||
let mut buffer = Vec::with_capacity(1);
|
||||
// Not using `emit_unclosed_delims` to use `db.buffer`
|
||||
for unmatched in unmatched_delims {
|
||||
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
|
||||
err.buffer(&mut buffer);
|
||||
}
|
||||
}
|
||||
if let Err(err) = token_trees {
|
||||
// Add unclosing delimiter error
|
||||
err.buffer(&mut buffer);
|
||||
}
|
||||
Err(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StringReader<'a> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::diagnostics::report_suspicious_mismatch_block;
|
||||
use super::diagnostics::same_identation_level;
|
||||
use super::diagnostics::TokenTreeDiagInfo;
|
||||
use super::{StringReader, UnmatchedBrace};
|
||||
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;
|
||||
|
@ -18,14 +18,14 @@ pub(super) struct TokenTreesReader<'a> {
|
|||
impl<'a> TokenTreesReader<'a> {
|
||||
pub(super) fn parse_all_token_trees(
|
||||
string_reader: StringReader<'a>,
|
||||
) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
|
||||
) -> (PResult<'a, TokenStream>, 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_braces)
|
||||
(res, tt_reader.diag_info.unmatched_delims)
|
||||
}
|
||||
|
||||
// Parse a stream of tokens into a list of `TokenTree`s.
|
||||
|
@ -34,7 +34,7 @@ impl<'a> TokenTreesReader<'a> {
|
|||
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(self.parse_token_tree_open_delim(delim)?),
|
||||
token::CloseDelim(delim) => {
|
||||
return if is_delimited {
|
||||
Ok(TokenStream::new(buf))
|
||||
|
@ -43,10 +43,11 @@ impl<'a> TokenTreesReader<'a> {
|
|||
};
|
||||
}
|
||||
token::Eof => {
|
||||
if is_delimited {
|
||||
self.eof_err().emit();
|
||||
}
|
||||
return Ok(TokenStream::new(buf));
|
||||
return if is_delimited {
|
||||
Err(self.eof_err())
|
||||
} else {
|
||||
Ok(TokenStream::new(buf))
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
// Get the next normal token. This might require getting multiple adjacent
|
||||
|
@ -78,7 +79,7 @@ impl<'a> TokenTreesReader<'a> {
|
|||
let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
|
||||
for &(_, sp) in &self.diag_info.open_braces {
|
||||
err.span_label(sp, "unclosed delimiter");
|
||||
self.diag_info.unmatched_braces.push(UnmatchedBrace {
|
||||
self.diag_info.unmatched_delims.push(UnmatchedDelim {
|
||||
expected_delim: Delimiter::Brace,
|
||||
found_delim: None,
|
||||
found_span: self.token.span,
|
||||
|
@ -98,7 +99,7 @@ impl<'a> TokenTreesReader<'a> {
|
|||
err
|
||||
}
|
||||
|
||||
fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> TokenTree {
|
||||
fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> {
|
||||
// The span for beginning of the delimited section
|
||||
let pre_span = self.token.span;
|
||||
|
||||
|
@ -107,7 +108,7 @@ 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).unwrap();
|
||||
let tts = self.parse_token_trees(/* is_delimited */ true)?;
|
||||
|
||||
// Expand to cover the entire delimited token tree
|
||||
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
|
||||
|
@ -160,7 +161,7 @@ impl<'a> TokenTreesReader<'a> {
|
|||
}
|
||||
}
|
||||
let (tok, _) = self.diag_info.open_braces.pop().unwrap();
|
||||
self.diag_info.unmatched_braces.push(UnmatchedBrace {
|
||||
self.diag_info.unmatched_delims.push(UnmatchedDelim {
|
||||
expected_delim: tok,
|
||||
found_delim: Some(close_delim),
|
||||
found_span: self.token.span,
|
||||
|
@ -190,7 +191,7 @@ impl<'a> TokenTreesReader<'a> {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
TokenTree::Delimited(delim_span, open_delim, tts)
|
||||
Ok(TokenTree::Delimited(delim_span, open_delim, tts))
|
||||
}
|
||||
|
||||
fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
|
|||
|
||||
#[macro_use]
|
||||
pub mod parser;
|
||||
use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
|
||||
use parser::{make_unclosed_delims_error, Parser};
|
||||
pub mod lexer;
|
||||
pub mod validate_attr;
|
||||
|
||||
|
@ -96,10 +96,7 @@ pub fn parse_stream_from_source_str(
|
|||
sess: &ParseSess,
|
||||
override_span: Option<Span>,
|
||||
) -> TokenStream {
|
||||
let (stream, mut errors) =
|
||||
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span);
|
||||
emit_unclosed_delims(&mut errors, &sess);
|
||||
stream
|
||||
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
|
||||
}
|
||||
|
||||
/// Creates a new parser from a source string.
|
||||
|
@ -135,9 +132,8 @@ fn maybe_source_file_to_parser(
|
|||
source_file: Lrc<SourceFile>,
|
||||
) -> Result<Parser<'_>, Vec<Diagnostic>> {
|
||||
let end_pos = source_file.end_pos;
|
||||
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
|
||||
let stream = maybe_file_to_stream(sess, source_file, None)?;
|
||||
let mut parser = stream_to_parser(sess, stream, None);
|
||||
parser.unclosed_delims = unclosed_delims;
|
||||
if parser.token == token::Eof {
|
||||
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
|
||||
}
|
||||
|
@ -182,7 +178,7 @@ pub fn source_file_to_stream(
|
|||
sess: &ParseSess,
|
||||
source_file: Lrc<SourceFile>,
|
||||
override_span: Option<Span>,
|
||||
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
|
||||
) -> TokenStream {
|
||||
panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
|
||||
}
|
||||
|
||||
|
@ -192,7 +188,7 @@ pub fn maybe_file_to_stream(
|
|||
sess: &ParseSess,
|
||||
source_file: Lrc<SourceFile>,
|
||||
override_span: Option<Span>,
|
||||
) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
|
||||
) -> Result<TokenStream, Vec<Diagnostic>> {
|
||||
let src = source_file.src.as_ref().unwrap_or_else(|| {
|
||||
sess.span_diagnostic.bug(&format!(
|
||||
"cannot lex `source_file` without source: {}",
|
||||
|
@ -200,23 +196,7 @@ pub fn maybe_file_to_stream(
|
|||
));
|
||||
});
|
||||
|
||||
let (token_trees, unmatched_braces) =
|
||||
lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span);
|
||||
|
||||
match token_trees {
|
||||
Ok(stream) => Ok((stream, unmatched_braces)),
|
||||
Err(err) => {
|
||||
let mut buffer = Vec::with_capacity(1);
|
||||
err.buffer(&mut buffer);
|
||||
// Not using `emit_unclosed_delims` to use `db.buffer`
|
||||
for unmatched in unmatched_braces {
|
||||
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
|
||||
err.buffer(&mut buffer);
|
||||
}
|
||||
}
|
||||
Err(buffer)
|
||||
}
|
||||
}
|
||||
lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span)
|
||||
}
|
||||
|
||||
/// Given a stream and the `ParseSess`, produces a parser.
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::errors::{
|
|||
};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::lexer::UnmatchedBrace;
|
||||
use crate::lexer::UnmatchedDelim;
|
||||
use crate::parser;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::ptr::P;
|
||||
|
@ -222,7 +222,7 @@ impl MultiSugg {
|
|||
/// is dropped.
|
||||
pub struct SnapshotParser<'a> {
|
||||
parser: Parser<'a>,
|
||||
unclosed_delims: Vec<UnmatchedBrace>,
|
||||
unclosed_delims: Vec<UnmatchedDelim>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for SnapshotParser<'a> {
|
||||
|
@ -264,7 +264,7 @@ impl<'a> Parser<'a> {
|
|||
self.unclosed_delims.extend(snapshot.unclosed_delims);
|
||||
}
|
||||
|
||||
pub fn unclosed_delims(&self) -> &[UnmatchedBrace] {
|
||||
pub fn unclosed_delims(&self) -> &[UnmatchedDelim] {
|
||||
&self.unclosed_delims
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ mod path;
|
|||
mod stmt;
|
||||
mod ty;
|
||||
|
||||
use crate::lexer::UnmatchedBrace;
|
||||
use crate::lexer::UnmatchedDelim;
|
||||
pub use attr_wrapper::AttrWrapper;
|
||||
pub use diagnostics::AttemptLocalParseRecovery;
|
||||
pub(crate) use item::FnParseMode;
|
||||
|
@ -149,7 +149,7 @@ pub struct Parser<'a> {
|
|||
/// A list of all unclosed delimiters found by the lexer. If an entry is used for error recovery
|
||||
/// it gets removed from here. Every entry left at the end gets emitted as an independent
|
||||
/// error.
|
||||
pub(super) unclosed_delims: Vec<UnmatchedBrace>,
|
||||
pub(super) unclosed_delims: Vec<UnmatchedDelim>,
|
||||
last_unexpected_token_span: Option<Span>,
|
||||
/// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
|
||||
/// looked like it could have been a mistyped path or literal `Option:Some(42)`).
|
||||
|
@ -1521,11 +1521,11 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn make_unclosed_delims_error(
|
||||
unmatched: UnmatchedBrace,
|
||||
unmatched: UnmatchedDelim,
|
||||
sess: &ParseSess,
|
||||
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
|
||||
// `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`.
|
||||
// `unmatched_delims` only for error recovery in the `Parser`.
|
||||
let found_delim = unmatched.found_delim?;
|
||||
let mut spans = vec![unmatched.found_span];
|
||||
if let Some(sp) = unmatched.unclosed_span {
|
||||
|
@ -1542,7 +1542,7 @@ pub(crate) fn make_unclosed_delims_error(
|
|||
Some(err)
|
||||
}
|
||||
|
||||
pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &ParseSess) {
|
||||
pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedDelim>, 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(..) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue