Skip to content

Commit

Permalink
Auto merge of #57944 - estebank:unclosed-delim-the-quickening, r=<try>
Browse files Browse the repository at this point in the history
Deduplicate mismatched delimiter errors

Delay unmatched delimiter errors until after the parser has run to deduplicate them when parsing and attempt recovering intelligently.

Second attempt at #54029, follow up to #53949. Fix #31528.
  • Loading branch information
bors committed Feb 7, 2019
2 parents ad43389 + c54b230 commit dbcc2e8
Show file tree
Hide file tree
Showing 18 changed files with 331 additions and 156 deletions.
4 changes: 2 additions & 2 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,8 @@ impl EmitterWriter {
// | | something about `foo`
// | something about `fn foo()`
annotations_position.sort_by(|a, b| {
// Decreasing order
a.1.len().cmp(&b.1.len()).reverse()
// Decreasing order. When `a` and `b` are the same length, prefer `Primary`.
(a.1.len(), !a.1.is_primary).cmp(&(b.1.len(), !b.1.is_primary)).reverse()
});

// Write the underlines.
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use syntax::attr;
use syntax::source_map;
use syntax::edition::Edition;
use syntax::parse::source_file_to_stream;
use syntax::parse::parser::emit_unclosed_delims;
use syntax::symbol::Symbol;
use syntax_pos::{Span, NO_EXPANSION, FileName};
use rustc_data_structures::bit_set::BitSet;
Expand Down Expand Up @@ -436,7 +437,8 @@ impl cstore::CStore {

let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
let local_span = Span::new(source_file.start_pos, source_file.end_pos, NO_EXPANSION);
let body = source_file_to_stream(&sess.parse_sess, source_file, None);
let (body, errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
emit_unclosed_delims(&errors, &sess.diagnostic());

// Mark the attrs as used
let attrs = data.get_item_attrs(id.index, sess);
Expand Down
11 changes: 11 additions & 0 deletions src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ impl Default for TokenAndSpan {
}
}

#[derive(Clone, Debug)]
pub struct UnmatchedBrace {
pub expected_delim: token::DelimToken,
pub found_delim: token::DelimToken,
pub found_span: Span,
pub unclosed_span: Option<Span>,
pub candidate_span: Option<Span>,
}

pub struct StringReader<'a> {
pub sess: &'a ParseSess,
/// The absolute offset within the source_map of the next character to read
Expand All @@ -58,6 +67,7 @@ pub struct StringReader<'a> {
span_src_raw: Span,
/// Stack of open delimiters and their spans. Used for error message.
open_braces: Vec<(token::DelimToken, Span)>,
crate unmatched_braces: Vec<UnmatchedBrace>,
/// The type and spans for all braces
///
/// Used only for error recovery when arriving to EOF with mismatched braces.
Expand Down Expand Up @@ -222,6 +232,7 @@ impl<'a> StringReader<'a> {
span: syntax_pos::DUMMY_SP,
span_src_raw: syntax_pos::DUMMY_SP,
open_braces: Vec::new(),
unmatched_braces: Vec::new(),
matching_delim_spans: Vec::new(),
override_span,
last_unclosed_found_span: None,
Expand Down
30 changes: 15 additions & 15 deletions src/libsyntax/parse/lexer/tokentrees.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::print::pprust::token_to_string;
use crate::parse::lexer::StringReader;
use crate::parse::lexer::{StringReader, UnmatchedBrace};
use crate::parse::{token, PResult};
use crate::tokenstream::{DelimSpan, IsJoint::*, TokenStream, TokenTree, TreeAndJoint};

Expand Down Expand Up @@ -101,38 +101,38 @@ impl<'a> StringReader<'a> {
}
// Incorrect delimiter.
token::CloseDelim(other) => {
let token_str = token_to_string(&self.token);
let mut unclosed_delimiter = None;
let mut candidate = None;
if self.last_unclosed_found_span != Some(self.span) {
// do not complain about the same unclosed delimiter multiple times
self.last_unclosed_found_span = Some(self.span);
let msg = format!("incorrect close delimiter: `{}`", token_str);
let mut err = self.sess.span_diagnostic.struct_span_err(
self.span,
&msg,
);
err.span_label(self.span, "incorrect close delimiter");
// This is a conservative error: only report the last unclosed
// delimiter. The previous unclosed delimiters could actually be
// closed! The parser just hasn't gotten to them yet.
if let Some(&(_, sp)) = self.open_braces.last() {
err.span_label(sp, "un-closed delimiter");
unclosed_delimiter = Some(sp);
};
if let Some(current_padding) = sm.span_to_margin(self.span) {
for (brace, brace_span) in &self.open_braces {
if let Some(padding) = sm.span_to_margin(*brace_span) {
// high likelihood of these two corresponding
if current_padding == padding && brace == &other {
err.span_label(
*brace_span,
"close delimiter possibly meant for this",
);
candidate = Some(*brace_span);
}
}
}
}
err.emit();
let (tok, _) = self.open_braces.pop().unwrap();
self.unmatched_braces.push(UnmatchedBrace {
expected_delim: tok,
found_delim: other,
found_span: self.span,
unclosed_span: unclosed_delimiter,
candidate_span: candidate,
});
} else {
self.open_braces.pop();
}
self.open_braces.pop().unwrap();

// If the incorrect delimiter matches an earlier opening
// delimiter, then don't consume it (it can be used to
Expand Down
58 changes: 41 additions & 17 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::parse::parser::Parser;
use crate::symbol::Symbol;
use crate::tokenstream::{TokenStream, TokenTree};
use crate::diagnostics::plugin::ErrorMap;
use crate::print::pprust::token_to_string;

use rustc_data_structures::sync::{Lrc, Lock};
use syntax_pos::{Span, SourceFile, FileName, MultiSpan};
Expand Down Expand Up @@ -136,15 +137,17 @@ pub fn parse_crate_attrs_from_source_str(name: FileName, source: String, sess: &
new_parser_from_source_str(sess, name, source).parse_inner_attributes()
}

pub fn parse_stream_from_source_str(name: FileName, source: String, sess: &ParseSess,
override_span: Option<Span>)
-> TokenStream {
pub fn parse_stream_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
override_span: Option<Span>,
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
}

/// Create a new parser from a source string
pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String)
-> Parser<'_> {
pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source))
}

Expand Down Expand Up @@ -195,12 +198,14 @@ fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Pars

/// Given a source_file and config, return a parser. Returns any buffered errors from lexing the
/// initial token stream.
fn maybe_source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>)
-> Result<Parser<'_>, Vec<Diagnostic>>
{
fn maybe_source_file_to_parser(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
) -> Result<Parser<'_>, Vec<Diagnostic>> {
let end_pos = source_file.end_pos;
let mut parser = stream_to_parser(sess, maybe_file_to_stream(sess, source_file, None)?);

let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
let mut parser = stream_to_parser(sess, stream);
parser.unclosed_delims = unclosed_delims;
if parser.token == token::Eof && parser.span.is_dummy() {
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
}
Expand Down Expand Up @@ -247,25 +252,44 @@ fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
}

/// Given a source_file, produce a sequence of token-trees
pub fn source_file_to_stream(sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>) -> TokenStream {
pub fn source_file_to_stream(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
}

/// Given a source file, produce a sequence of token-trees. Returns any buffered errors from
/// parsing the token tream.
pub fn maybe_file_to_stream(sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>) -> Result<TokenStream, Vec<Diagnostic>> {
pub fn maybe_file_to_stream(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
let mut srdr = lexer::StringReader::new_or_buffered_errs(sess, source_file, override_span)?;
srdr.real_token();

match srdr.parse_all_token_trees() {
Ok(stream) => Ok(stream),
Ok(stream) => Ok((stream, srdr.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 srdr.unmatched_braces {
let mut db = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!(
"incorrect close delimiter: `{}`",
token_to_string(&token::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(sp) = unmatched.unclosed_span {
db.span_label(sp, "un-closed delimiter");
}
db.buffer(&mut buffer);
}
Err(buffer)
}
}
Expand Down
Loading

0 comments on commit dbcc2e8

Please sign in to comment.