Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to recover from unmatched delimiters in the parser #54029

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,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
2 changes: 1 addition & 1 deletion src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ 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 = source_file_to_stream(&sess.parse_sess, source_file, None).0;

// Mark the attrs as used
let attrs = data.get_item_attrs(id.index, sess);
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct StringReader<'a> {
/// The raw source span which *does not* take `override_span` into account
span_src_raw: Span,
open_braces: Vec<(token::DelimToken, Span)>,
crate unmatched_braces: Vec<(token::DelimToken, Span)>,
/// The type and spans for all braces
///
/// Used only for error recovery when arriving to EOF with mismatched braces.
Expand Down Expand Up @@ -221,6 +222,7 @@ impl<'a> StringReader<'a> {
span_src_raw: syntax_pos::DUMMY_SP,
open_braces: Vec::new(),
matching_delim_spans: Vec::new(),
unmatched_braces: Vec::new(),
override_span,
last_unclosed_found_span: None,
}
Expand Down
5 changes: 3 additions & 2 deletions src/libsyntax/parse/lexer/tokentrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ impl<'a> StringReader<'a> {
token::Eof => {
let msg = "this file contains an un-closed delimiter";
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, msg);
for &(_, sp) in &self.open_braces {
for &(tok, sp) in &self.open_braces {
err.span_label(sp, "un-closed delimiter");
self.unmatched_braces.push((tok, sp));
}

if let Some((delim, _)) = self.open_braces.last() {
Expand Down Expand Up @@ -134,7 +135,7 @@ impl<'a> StringReader<'a> {
}
err.emit();
}
self.open_braces.pop().unwrap();
self.unmatched_braces.push(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
42 changes: 25 additions & 17 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,13 @@ crate fn parse_stmt_from_source_str(name: FileName, source: String, sess: &Parse
new_parser_from_source_str(sess, name, source).parse_stmt()
}

pub fn parse_stream_from_source_str(name: FileName, source: String, sess: &ParseSess,
override_span: Option<Span>)
-> TokenStream {
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
pub fn parse_stream_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
override_span: Option<Span>,
) -> TokenStream {
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span).0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean that for rustc invocations that don't read the source from a file but from stdin we don't get these suggestions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. On the one hand, those will not have any worse output. On the other, this was a bit explorative and want to nail it down before trying to get to to work in all cases.

}

// Create a new parser from a source string
Expand All @@ -191,26 +194,28 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a>
/// Given a session, a crate config, a path, and a span, add
/// the file at the given path to the source_map, and return a parser.
/// On an error, use the given span as the source of the problem.
crate fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
path: &Path,
directory_ownership: DirectoryOwnership,
module_name: Option<String>,
sp: Span) -> Parser<'a> {
crate fn new_sub_parser_from_file<'a>(
sess: &'a ParseSess,
path: &Path,
directory_ownership: DirectoryOwnership,
module_name: Option<String>,
sp: Span,
) -> Parser<'a> {
let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp)));
p.directory.ownership = directory_ownership;
p.root_module_name = module_name;
p
}

/// Given a source_file and config, return a parser
fn source_file_to_parser(sess: & ParseSess, source_file: Lrc<SourceFile>) -> Parser {
fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser {
let end_pos = source_file.end_pos;
let mut parser = stream_to_parser(sess, source_file_to_stream(sess, source_file, None));

let (tts, open_braces) = source_file_to_stream(sess, source_file, None);
let mut parser = stream_to_parser(sess, tts);
parser.open_braces = open_braces;
if parser.token == token::Eof && parser.span.is_dummy() {
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
}

parser
}

Expand Down Expand Up @@ -240,12 +245,15 @@ 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<(token::DelimToken, Span)>) {
let mut srdr = lexer::StringReader::new(sess, source_file, override_span);
srdr.real_token();
panictry!(srdr.parse_all_token_trees())
let tt = panictry!(srdr.parse_all_token_trees());
(tt, srdr.unmatched_braces)
}

/// Given stream and the `ParseSess`, produce a parser
Expand Down
Loading