From 7451cd8dc0b20c012fad1886b321c3dee2052577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 27 Jan 2019 21:04:50 -0800 Subject: [PATCH 01/11] Deduplicate mismatched delimiter errors Delay unmatched delimiter errors until after the parser has run to deduplicate them when parsing and attempt recovering intelligently. --- src/librustc_errors/emitter.rs | 4 +- src/librustc_metadata/cstore_impl.rs | 8 +- src/libsyntax/parse/lexer/mod.rs | 11 + src/libsyntax/parse/lexer/tokentrees.rs | 30 +- src/libsyntax/parse/mod.rs | 57 ++-- src/libsyntax/parse/parser.rs | 261 +++++++++++++----- src/libsyntax/parse/token.rs | 30 +- src/libsyntax_ext/proc_macro_server.rs | 2 +- src/test/ui/parser-recovery-2.stderr | 12 +- src/test/ui/parser/issue-10636-2.rs | 2 +- src/test/ui/parser/issue-10636-2.stderr | 20 +- src/test/ui/parser/issue-2354.rs | 3 +- src/test/ui/parser/issue-2354.stderr | 7 +- .../macro-mismatched-delim-paren-brace.stderr | 12 +- src/test/ui/resolve/token-error-correct-3.rs | 2 +- .../ui/resolve/token-error-correct-3.stderr | 20 +- .../ui/resolve/token-error-correct.stderr | 12 +- 17 files changed, 335 insertions(+), 158 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 25d09a33c154f..f04bd7b8f021b 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -674,8 +674,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. diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index e61229db86ddb..ecbc900215f7b 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -436,7 +436,13 @@ 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); + for err in errors { + sess.struct_span_err( + err.found_span, + "unclosed delimiter cstore", + ).emit(); + } // Mark the attrs as used let attrs = data.get_item_attrs(id.index, sess); diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 2e3233c8ed8fa..d3fc1c03634ea 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -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, + pub candidate_span: Option, +} + pub struct StringReader<'a> { pub sess: &'a ParseSess, /// The absolute offset within the source_map of the next character to read @@ -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, /// The type and spans for all braces /// /// Used only for error recovery when arriving to EOF with mismatched braces. @@ -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, diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs index 7699d9eab2225..0db36c84cdfeb 100644 --- a/src/libsyntax/parse/lexer/tokentrees.rs +++ b/src/libsyntax/parse/lexer/tokentrees.rs @@ -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}; @@ -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 diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index c723d591f2fb2..52c7e774ab64a 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -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}; @@ -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) - -> TokenStream { +pub fn parse_stream_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, + override_span: Option, +) -> (TokenStream, Vec) { 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)) } @@ -195,12 +198,14 @@ fn source_file_to_parser(sess: &ParseSess, source_file: Lrc) -> 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) - -> Result, Vec> -{ +fn maybe_source_file_to_parser( + sess: &ParseSess, + source_file: Lrc, +) -> Result, Vec> { 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()); } @@ -247,25 +252,43 @@ fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option) } /// Given a source_file, produce a sequence of token-trees -pub fn source_file_to_stream(sess: &ParseSess, - source_file: Lrc, - override_span: Option) -> TokenStream { +pub fn source_file_to_stream( + sess: &ParseSess, + source_file: Lrc, + override_span: Option, +) -> (TokenStream, Vec) { 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, - override_span: Option) -> Result> { +pub fn maybe_file_to_stream( + sess: &ParseSess, + source_file: Lrc, + override_span: Option, +) -> Result<(TokenStream, Vec), Vec> { 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); + 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) } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index cacdab980facd..2e605ab6544df 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -35,7 +35,7 @@ use crate::ext::base::DummyResult; use crate::source_map::{self, SourceMap, Spanned, respan}; use crate::errors::{self, Applicability, DiagnosticBuilder, DiagnosticId}; use crate::parse::{self, SeqSep, classify, token}; -use crate::parse::lexer::TokenAndSpan; +use crate::parse::lexer::{TokenAndSpan, UnmatchedBrace}; use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use crate::parse::token::DelimToken; use crate::parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership}; @@ -251,6 +251,8 @@ pub struct Parser<'a> { /// /// See the comments in the `parse_path_segment` function for more details. crate unmatched_angle_bracket_count: u32, + crate max_angle_bracket_count: u32, + crate unclosed_delims: Vec, } @@ -573,6 +575,8 @@ impl<'a> Parser<'a> { desugar_doc_comments, cfg_mods: true, unmatched_angle_bracket_count: 0, + max_angle_bracket_count: 0, + unclosed_delims: Vec::new(), }; let tok = parser.next_tok(); @@ -642,11 +646,11 @@ impl<'a> Parser<'a> { /// Expect and consume the token t. Signal an error if /// the next token is not t. - pub fn expect(&mut self, t: &token::Token) -> PResult<'a, ()> { + pub fn expect(&mut self, t: &token::Token) -> PResult<'a, bool /* recovered */> { if self.expected_tokens.is_empty() { if self.token == *t { self.bump(); - Ok(()) + Ok(false) } else { let token_str = pprust::token_to_string(t); let this_token_str = self.this_token_descr(); @@ -661,6 +665,12 @@ impl<'a> Parser<'a> { self.sess.source_map().next_point(self.prev_span) }; let label_exp = format!("expected `{}`", token_str); + match self.recover_closing_delimiter(&[t.clone()], err) { + Err(e) => err = e, + Ok(recovered) => { + return Ok(recovered); + } + } let cm = self.sess.source_map(); match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) { (Ok(ref a), Ok(ref b)) if a.line == b.line => { @@ -680,12 +690,62 @@ impl<'a> Parser<'a> { } } + fn recover_closing_delimiter( + &mut self, + tokens: &[token::Token], + mut err: DiagnosticBuilder<'a>, + ) -> PResult<'a, bool> { + let mut pos = None; + // we want to use the last closing delim that would apply + for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { + if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) + && Some(self.span) > unmatched.unclosed_span + { + pos = Some(i); + } + } + match pos { + Some(pos) => { + // Recover and assume that the detected unclosed delimiter was meant for + // this location. Emit the diagnostic and act as if the delimiter was + // present for the parser's sake. + + // 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)); + + // We want to suggest the inclusion of the closing delimiter where it makes + // the most sense, which is immediately after the last token: + // + // {foo(bar {}} + // - ^ help: `)` may belong here + // | + // in order to close this... + if let Some(sp) = unmatched.unclosed_span { + err.span_label(sp, "in order to close this..."); + } + err.span_suggestion_short_with_applicability( + self.sess.source_map().next_point(self.prev_span), + &format!("{} may belong here", delim.to_string()), + delim.to_string(), + Applicability::MaybeIncorrect, + ); + err.emit(); + // self.expected_tokens.clear(); // reduce errors + Ok(true) + } + _ => Err(err), + } + } + /// Expect next token to be edible or inedible token. If edible, /// then consume it; if inedible, then return without consuming /// anything. Signal a fatal error if next token is unexpected. - pub fn expect_one_of(&mut self, - edible: &[token::Token], - inedible: &[token::Token]) -> PResult<'a, ()>{ + pub fn expect_one_of( + &mut self, + edible: &[token::Token], + inedible: &[token::Token], + ) -> PResult<'a, bool /* recovered */> { fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on Iterator. @@ -705,10 +765,10 @@ impl<'a> Parser<'a> { } if edible.contains(&self.token) { self.bump(); - Ok(()) + Ok(false) } else if inedible.contains(&self.token) { // leave it in the input - Ok(()) + Ok(false) } else { let mut expected = edible.iter() .map(|x| TokenType::Token(x.clone())) @@ -759,6 +819,15 @@ impl<'a> Parser<'a> { } else { label_sp }; + match self.recover_closing_delimiter(&expected.iter().filter_map(|tt| match tt { + TokenType::Token(t) => Some(t.clone()), + _ => None, + }).collect::>(), err) { + Err(e) => err = e, + Ok(recovered) => { + return Ok(recovered); + } + } let cm = self.sess.source_map(); match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) { @@ -1053,6 +1122,7 @@ impl<'a> Parser<'a> { if ate { // See doc comment for `unmatched_angle_bracket_count`. self.unmatched_angle_bracket_count += 1; + self.max_angle_bracket_count += 1; debug!("eat_lt: (increment) count={:?}", self.unmatched_angle_bracket_count); } @@ -1093,14 +1163,30 @@ impl<'a> Parser<'a> { }; match ate { - Some(x) => { + Some(_) => { // See doc comment for `unmatched_angle_bracket_count`. self.unmatched_angle_bracket_count -= 1; debug!("expect_gt: (decrement) count={:?}", self.unmatched_angle_bracket_count); - Ok(x) + Ok(()) }, - None => self.unexpected(), + None => { + match ( + &self.token, + self.unmatched_angle_bracket_count, + self.max_angle_bracket_count > 1, + ) { + // (token::OpenDelim(_), 1, true) | (token::Semi, 1, true) => { + // self.struct_span_err( + // self.span, + // &format!("expected `>`, found `{}`", self.this_token_to_string()), + // // ).span_suggestion_short_with_applicability( + // ).emit(); + // Ok(()) + // } + _ => self.unexpected(), + } + } } } @@ -1127,19 +1213,22 @@ impl<'a> Parser<'a> { -> PResult<'a, Vec> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { - let val = self.parse_seq_to_before_end(ket, sep, f)?; - self.bump(); + let (val, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; + if !recovered { + self.bump(); + } Ok(val) } /// Parse a sequence, not including the closing delimiter. The function /// f must consume tokens until reaching the next separator or /// closing bracket. - pub fn parse_seq_to_before_end(&mut self, - ket: &token::Token, - sep: SeqSep, - f: F) - -> PResult<'a, Vec> + pub fn parse_seq_to_before_end( + &mut self, + ket: &token::Token, + sep: SeqSep, + f: F, + ) -> PResult<'a, (Vec, bool)> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> { self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) @@ -1151,10 +1240,11 @@ impl<'a> Parser<'a> { sep: SeqSep, expect: TokenExpectType, mut f: F, - ) -> PResult<'a, Vec> + ) -> PResult<'a, (Vec, bool /* recovered */)> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> { - let mut first: bool = true; + let mut first = true; + let mut recovered = false; let mut v = vec![]; while !kets.iter().any(|k| { match expect { @@ -1170,23 +1260,30 @@ impl<'a> Parser<'a> { if first { first = false; } else { - if let Err(mut e) = self.expect(t) { - // Attempt to keep parsing if it was a similar separator - if let Some(ref tokens) = t.similar_tokens() { - if tokens.contains(&self.token) { - self.bump(); - } + match self.expect(t) { + Ok(false) => {} + Ok(true) => { + recovered = true; + break; } - e.emit(); - // Attempt to keep parsing if it was an omitted separator - match f(self) { - Ok(t) => { - v.push(t); - continue; - }, - Err(mut e) => { - e.cancel(); - break; + Err(mut e) => { + // Attempt to keep parsing if it was a similar separator + if let Some(ref tokens) = t.similar_tokens() { + if tokens.contains(&self.token) { + self.bump(); + } + } + e.emit(); + // Attempt to keep parsing if it was an omitted separator + match f(self) { + Ok(t) => { + v.push(t); + continue; + }, + Err(mut e) => { + e.cancel(); + break; + } } } } @@ -1205,23 +1302,26 @@ impl<'a> Parser<'a> { v.push(t); } - Ok(v) + Ok((v, recovered)) } /// Parse a sequence, including the closing delimiter. The function /// f must consume tokens until reaching the next separator or /// closing bracket. - fn parse_unspanned_seq(&mut self, - bra: &token::Token, - ket: &token::Token, - sep: SeqSep, - f: F) - -> PResult<'a, Vec> where + fn parse_unspanned_seq( + &mut self, + bra: &token::Token, + ket: &token::Token, + sep: SeqSep, + f: F, + ) -> PResult<'a, Vec> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { self.expect(bra)?; - let result = self.parse_seq_to_before_end(ket, sep, f)?; - self.eat(ket); + let (result, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; + if !recovered { + self.eat(ket); + } Ok(result) } @@ -2273,7 +2373,10 @@ impl<'a> Parser<'a> { // We use `style == PathStyle::Expr` to check if this is in a recursion or not. If // it isn't, then we reset the unmatched angle bracket count as we're about to start // parsing a new path. - if style == PathStyle::Expr { self.unmatched_angle_bracket_count = 0; } + if style == PathStyle::Expr { + self.unmatched_angle_bracket_count = 0; + self.max_angle_bracket_count = 0; + } let args = if self.eat_lt() { // `<'a, T, A = U>` @@ -2285,12 +2388,14 @@ impl<'a> Parser<'a> { } else { // `(T, U) -> R` self.bump(); // `(` - let inputs = self.parse_seq_to_before_tokens( + let (inputs, recovered) = self.parse_seq_to_before_tokens( &[&token::CloseDelim(token::Paren)], SeqSep::trailing_allowed(token::Comma), TokenExpectType::Expect, |p| p.parse_ty())?; - self.bump(); // `)` + if !recovered { + self.bump(); // `)` + } let span = lo.to(self.prev_span); let output = if self.eat(&token::RArrow) { Some(self.parse_ty_common(false, false)?) @@ -2496,9 +2601,13 @@ impl<'a> Parser<'a> { // (e,) is a tuple with only one field, e let mut es = vec![]; let mut trailing_comma = false; + let mut recovered = false; while self.token != token::CloseDelim(token::Paren) { es.push(self.parse_expr()?); - self.expect_one_of(&[], &[token::Comma, token::CloseDelim(token::Paren)])?; + recovered = self.expect_one_of( + &[], + &[token::Comma, token::CloseDelim(token::Paren)], + )?; if self.eat(&token::Comma) { trailing_comma = true; } else { @@ -2506,7 +2615,9 @@ impl<'a> Parser<'a> { break; } } - self.bump(); + if !recovered { + self.bump(); + } hi = self.prev_span; ex = if es.len() == 1 && !trailing_comma { @@ -2802,7 +2913,7 @@ impl<'a> Parser<'a> { match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) { - Ok(()) => if let Some(f) = parsed_field.or(recovery_field) { + Ok(_) => if let Some(f) = parsed_field.or(recovery_field) { // only include the field if there's no parse error for the field name fields.push(f); } @@ -6011,7 +6122,7 @@ impl<'a> Parser<'a> { let sp = self.span; let mut variadic = false; - let args: Vec> = + let (args, recovered): (Vec>, bool) = self.parse_seq_to_before_end( &token::CloseDelim(token::Paren), SeqSep::trailing_allowed(token::Comma), @@ -6059,7 +6170,9 @@ impl<'a> Parser<'a> { } )?; - self.eat(&token::CloseDelim(token::Paren)); + if !recovered { + self.eat(&token::CloseDelim(token::Paren)); + } let args: Vec<_> = args.into_iter().filter_map(|x| x).collect(); @@ -6204,15 +6317,15 @@ impl<'a> Parser<'a> { // Parse the rest of the function parameter list. let sep = SeqSep::trailing_allowed(token::Comma); - let fn_inputs = if let Some(self_arg) = self_arg { + let (fn_inputs, recovered) = if let Some(self_arg) = self_arg { if self.check(&token::CloseDelim(token::Paren)) { - vec![self_arg] + (vec![self_arg], false) } else if self.eat(&token::Comma) { let mut fn_inputs = vec![self_arg]; - fn_inputs.append(&mut self.parse_seq_to_before_end( - &token::CloseDelim(token::Paren), sep, parse_arg_fn)? - ); - fn_inputs + let (mut input, recovered) = self.parse_seq_to_before_end( + &token::CloseDelim(token::Paren), sep, parse_arg_fn)?; + fn_inputs.append(&mut input); + (fn_inputs, recovered) } else { return self.unexpected(); } @@ -6220,8 +6333,10 @@ impl<'a> Parser<'a> { self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)? }; - // Parse closing paren and return type. - self.expect(&token::CloseDelim(token::Paren))?; + if !recovered { + // Parse closing paren and return type. + self.expect(&token::CloseDelim(token::Paren))?; + } Ok(P(FnDecl { inputs: fn_inputs, output: self.parse_ret_ty(true)?, @@ -6241,7 +6356,7 @@ impl<'a> Parser<'a> { SeqSep::trailing_allowed(token::Comma), TokenExpectType::NoExpect, |p| p.parse_fn_block_arg() - )?; + )?.0; self.expect_or()?; args } @@ -8238,7 +8353,7 @@ impl<'a> Parser<'a> { // eat a matched-delimiter token tree: let (delim, tts) = self.expect_delimited_token_tree()?; if delim != MacDelimiter::Brace { - self.expect(&token::Semi)? + self.expect(&token::Semi)?; } Ok(Some(respan(lo.to(self.prev_span), Mac_ { path: pth, tts, delim }))) @@ -8383,11 +8498,27 @@ impl<'a> Parser<'a> { /// entry point for the parser. pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> { let lo = self.span; - Ok(ast::Crate { + let krate = Ok(ast::Crate { attrs: self.parse_inner_attributes()?, module: self.parse_mod_items(&token::Eof, lo)?, span: lo.to(self.span), - }) + }); + for unmatched in &self.unclosed_delims { + let mut err = self.struct_span_err(unmatched.found_span, &format!( + "incorrect close delimiter: `{}`", + pprust::token_to_string(&token::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(); + } + self.unclosed_delims.clear(); + krate } pub fn parse_optional_str(&mut self) -> Option<(Symbol, ast::StrStyle, Option)> { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 3b1fa5ea01f54..a0f3113a1cb7f 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -487,8 +487,8 @@ impl Token { /// Enables better error recovery when the wrong token is found. crate fn similar_tokens(&self) -> Option> { match *self { - Comma => Some(vec![Dot, Lt]), - Semi => Some(vec![Colon]), + Comma => Some(vec![Dot, Lt, Semi]), + Semi => Some(vec![Colon, Comma]), _ => None } } @@ -545,7 +545,15 @@ impl Token { // FIXME(#43081): Avoid this pretty-print + reparse hack let source = pprust::token_to_string(self); let filename = FileName::macro_expansion_source_code(&source); - parse_stream_from_source_str(filename, source, sess, Some(span)) + let (tokens, errors) = parse_stream_from_source_str( + filename, source, sess, Some(span)); + for err in errors { + sess.span_diagnostic.struct_span_err( + err.found_span, + "unclosed delimiter for_real", + ).emit(); + } + tokens }); // During early phases of the compiler the AST could get modified @@ -786,12 +794,18 @@ fn prepend_attrs(sess: &ParseSess, let source = pprust::attr_to_string(attr); let macro_filename = FileName::macro_expansion_source_code(&source); if attr.is_sugared_doc { - let stream = parse_stream_from_source_str( + let (stream, errors) = parse_stream_from_source_str( macro_filename, source, sess, Some(span), ); + for err in errors { + sess.span_diagnostic.struct_span_err( + err.found_span, + "unclosed delimiter attrs", + ).emit(); + } builder.push(stream); continue } @@ -808,12 +822,18 @@ fn prepend_attrs(sess: &ParseSess, // ... and for more complicated paths, fall back to a reparse hack that // should eventually be removed. } else { - let stream = parse_stream_from_source_str( + let (stream, errors) = parse_stream_from_source_str( macro_filename, source, sess, Some(span), ); + for err in errors { + sess.span_diagnostic.struct_span_err( + err.found_span, + "unclosed delimiter attrs 2", + ).emit(); + } brackets.push(stream); } diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs index 7de9b9343a8fa..ef291e2102b4c 100644 --- a/src/libsyntax_ext/proc_macro_server.rs +++ b/src/libsyntax_ext/proc_macro_server.rs @@ -413,7 +413,7 @@ impl server::TokenStream for Rustc<'_> { src.to_string(), self.sess, Some(self.call_site), - ) + ).0 } fn to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() diff --git a/src/test/ui/parser-recovery-2.stderr b/src/test/ui/parser-recovery-2.stderr index 92d8cbc100a03..76f7af38e776d 100644 --- a/src/test/ui/parser-recovery-2.stderr +++ b/src/test/ui/parser-recovery-2.stderr @@ -1,3 +1,9 @@ +error: unexpected token: `;` + --> $DIR/parser-recovery-2.rs:12:15 + | +LL | let x = y.; //~ ERROR unexpected token + | ^ + error: incorrect close delimiter: `)` --> $DIR/parser-recovery-2.rs:8:5 | @@ -7,12 +13,6 @@ LL | let x = foo(); //~ ERROR cannot find function `foo` in this scope LL | ) //~ ERROR incorrect close delimiter: `)` | ^ incorrect close delimiter -error: unexpected token: `;` - --> $DIR/parser-recovery-2.rs:12:15 - | -LL | let x = y.; //~ ERROR unexpected token - | ^ - error[E0425]: cannot find function `foo` in this scope --> $DIR/parser-recovery-2.rs:7:17 | diff --git a/src/test/ui/parser/issue-10636-2.rs b/src/test/ui/parser/issue-10636-2.rs index a02fd41b349c2..6fb63639d5f60 100644 --- a/src/test/ui/parser/issue-10636-2.rs +++ b/src/test/ui/parser/issue-10636-2.rs @@ -5,7 +5,7 @@ pub fn trace_option(option: Option) { option.map(|some| 42; //~^ ERROR: expected one of -} //~ ERROR: incorrect close delimiter +} //~^ ERROR: expected expression, found `)` fn main() {} diff --git a/src/test/ui/parser/issue-10636-2.stderr b/src/test/ui/parser/issue-10636-2.stderr index 9b3115cb3f4b7..670a116eb51f0 100644 --- a/src/test/ui/parser/issue-10636-2.stderr +++ b/src/test/ui/parser/issue-10636-2.stderr @@ -1,25 +1,17 @@ -error: incorrect close delimiter: `}` - --> $DIR/issue-10636-2.rs:8:1 - | -LL | pub fn trace_option(option: Option) { - | - close delimiter possibly meant for this -LL | option.map(|some| 42; - | - un-closed delimiter -... -LL | } //~ ERROR: incorrect close delimiter - | ^ incorrect close delimiter - error: expected one of `)`, `,`, `.`, `?`, or an operator, found `;` --> $DIR/issue-10636-2.rs:5:25 | LL | option.map(|some| 42; - | ^ expected one of `)`, `,`, `.`, `?`, or an operator here + | - ^ + | | | + | | help: `)` may belong here + | in order to close this... error: expected expression, found `)` --> $DIR/issue-10636-2.rs:8:1 | -LL | } //~ ERROR: incorrect close delimiter +LL | } | ^ expected expression -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/issue-2354.rs b/src/test/ui/parser/issue-2354.rs index 565f84822f7d6..b383bc00f911d 100644 --- a/src/test/ui/parser/issue-2354.rs +++ b/src/test/ui/parser/issue-2354.rs @@ -1,4 +1,5 @@ -fn foo() { //~ NOTE un-closed delimiter +fn foo() { + //~^ NOTE un-closed delimiter match Some(10) { //~^ NOTE this delimiter might not be properly closed... Some(y) => { panic!(); } diff --git a/src/test/ui/parser/issue-2354.stderr b/src/test/ui/parser/issue-2354.stderr index 0f4cd5724ce1e..f1b0905d86600 100644 --- a/src/test/ui/parser/issue-2354.stderr +++ b/src/test/ui/parser/issue-2354.stderr @@ -1,8 +1,9 @@ error: this file contains an un-closed delimiter - --> $DIR/issue-2354.rs:15:66 + --> $DIR/issue-2354.rs:16:66 | -LL | fn foo() { //~ NOTE un-closed delimiter +LL | fn foo() { | - un-closed delimiter +LL | //~^ NOTE un-closed delimiter LL | match Some(10) { | - this delimiter might not be properly closed... ... @@ -16,7 +17,7 @@ error[E0601]: `main` function not found in crate `issue_2354` | = note: the main function must be defined at the crate level but you have one or more functions named 'main' that are not defined at the crate level. Either move the definition or attach the `#[main]` attribute to override this behavior. note: here is a function named 'main' - --> $DIR/issue-2354.rs:14:1 + --> $DIR/issue-2354.rs:15:1 | LL | fn main() {} //~ NOTE here is a function named 'main' | ^^^^^^^^^^^^ diff --git a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr index 805ba8b6baa60..abb0820979532 100644 --- a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -1,3 +1,9 @@ +error: unexpected close delimiter: `}` + --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 + | +LL | } //~ ERROR unexpected close delimiter: `}` + | ^ unexpected close delimiter + error: incorrect close delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:4:5 | @@ -7,11 +13,5 @@ LL | bar, "baz", 1, 2.0 LL | } //~ ERROR incorrect close delimiter | ^ incorrect close delimiter -error: unexpected close delimiter: `}` - --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 - | -LL | } //~ ERROR unexpected close delimiter: `}` - | ^ unexpected close delimiter - error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/token-error-correct-3.rs b/src/test/ui/resolve/token-error-correct-3.rs index 86cf71117a6f8..b1ca0bbfc57c1 100644 --- a/src/test/ui/resolve/token-error-correct-3.rs +++ b/src/test/ui/resolve/token-error-correct-3.rs @@ -17,7 +17,7 @@ pub mod raw { //~| expected type `()` //~| found type `std::result::Result` //~| expected one of - } else { //~ ERROR: incorrect close delimiter: `}` + } else { //~^ ERROR: expected one of //~| unexpected token Ok(false); diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr index 2164d27a05174..fcc1c34d1fc53 100644 --- a/src/test/ui/resolve/token-error-correct-3.stderr +++ b/src/test/ui/resolve/token-error-correct-3.stderr @@ -1,19 +1,11 @@ -error: incorrect close delimiter: `}` - --> $DIR/token-error-correct-3.rs:20:9 - | -LL | if !is_directory(path.as_ref()) { //~ ERROR: cannot find function `is_directory` - | - close delimiter possibly meant for this -LL | callback(path.as_ref(); //~ ERROR expected one of - | - un-closed delimiter -... -LL | } else { //~ ERROR: incorrect close delimiter: `}` - | ^ incorrect close delimiter - error: expected one of `)`, `,`, `.`, `?`, or an operator, found `;` --> $DIR/token-error-correct-3.rs:14:35 | LL | callback(path.as_ref(); //~ ERROR expected one of - | ^ expected one of `)`, `,`, `.`, `?`, or an operator here + | - ^ + | | | + | | help: `)` may belong here + | in order to close this... error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` --> $DIR/token-error-correct-3.rs:20:9 @@ -21,7 +13,7 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` LL | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types | - expected one of `.`, `;`, `?`, `}`, or an operator here ... -LL | } else { //~ ERROR: incorrect close delimiter: `}` +LL | } else { | ^ unexpected token error[E0425]: cannot find function `is_directory` in this scope @@ -41,7 +33,7 @@ LL | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mis = note: expected type `()` found type `std::result::Result` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors occurred: E0308, E0425. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/resolve/token-error-correct.stderr b/src/test/ui/resolve/token-error-correct.stderr index 0a4590461b5ab..48a247a5898ef 100644 --- a/src/test/ui/resolve/token-error-correct.stderr +++ b/src/test/ui/resolve/token-error-correct.stderr @@ -1,3 +1,9 @@ +error: expected expression, found `;` + --> $DIR/token-error-correct.rs:4:13 + | +LL | foo(bar(; + | ^ expected expression + error: incorrect close delimiter: `}` --> $DIR/token-error-correct.rs:6:1 | @@ -9,11 +15,5 @@ LL | //~^ ERROR: expected expression, found `;` LL | } | ^ incorrect close delimiter -error: expected expression, found `;` - --> $DIR/token-error-correct.rs:4:13 - | -LL | foo(bar(; - | ^ expected expression - error: aborting due to 2 previous errors From 99be87aac3b9af941e74b8681643e1963ce75671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 01:35:25 -0800 Subject: [PATCH 02/11] unify error handling to single method --- src/librustc_metadata/cstore_impl.rs | 8 ++---- src/libsyntax/parse/mod.rs | 1 + src/libsyntax/parse/parser.rs | 36 ++++++++++++++------------ src/libsyntax/parse/token.rs | 22 +++------------- src/libsyntax_ext/proc_macro_server.rs | 7 +++-- 5 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index ecbc900215f7b..fd4089dfdb9db 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -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; @@ -437,12 +438,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, errors) = source_file_to_stream(&sess.parse_sess, source_file, None); - for err in errors { - sess.struct_span_err( - err.found_span, - "unclosed delimiter cstore", - ).emit(); - } + emit_unclosed_delims(&errors, &sess.diagnostic()); // Mark the attrs as used let attrs = data.get_item_attrs(id.index, sess); diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 52c7e774ab64a..317d693320786 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -275,6 +275,7 @@ pub fn maybe_file_to_stream( 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: `{}`", diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2e605ab6544df..4a932ab7bd19c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -724,7 +724,7 @@ impl<'a> Parser<'a> { if let Some(sp) = unmatched.unclosed_span { err.span_label(sp, "in order to close this..."); } - err.span_suggestion_short_with_applicability( + err.span_suggestion_short( self.sess.source_map().next_point(self.prev_span), &format!("{} may belong here", delim.to_string()), delim.to_string(), @@ -1180,7 +1180,7 @@ impl<'a> Parser<'a> { // self.struct_span_err( // self.span, // &format!("expected `>`, found `{}`", self.this_token_to_string()), - // // ).span_suggestion_short_with_applicability( + // // ).span_suggestion_short( // ).emit(); // Ok(()) // } @@ -8503,20 +8503,7 @@ impl<'a> Parser<'a> { module: self.parse_mod_items(&token::Eof, lo)?, span: lo.to(self.span), }); - for unmatched in &self.unclosed_delims { - let mut err = self.struct_span_err(unmatched.found_span, &format!( - "incorrect close delimiter: `{}`", - pprust::token_to_string(&token::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(); - } + emit_unclosed_delims(&self.unclosed_delims, self.diagnostic()); self.unclosed_delims.clear(); krate } @@ -8547,3 +8534,20 @@ impl<'a> Parser<'a> { } } } + +pub fn emit_unclosed_delims(unclosed_delims: &[UnmatchedBrace], handler: &errors::Handler) { + for unmatched in unclosed_delims { + let mut err = handler.struct_span_err(unmatched.found_span, &format!( + "incorrect close delimiter: `{}`", + pprust::token_to_string(&token::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(); + } +} \ No newline at end of file diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a0f3113a1cb7f..d144223d1b8eb 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -10,6 +10,7 @@ use crate::print::pprust; use crate::ptr::P; use crate::symbol::keywords; use crate::syntax::parse::parse_stream_from_source_str; +use crate::syntax::parse::parser::emit_unclosed_delims; use crate::tokenstream::{self, DelimSpan, TokenStream, TokenTree}; use serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -547,12 +548,7 @@ impl Token { let filename = FileName::macro_expansion_source_code(&source); let (tokens, errors) = parse_stream_from_source_str( filename, source, sess, Some(span)); - for err in errors { - sess.span_diagnostic.struct_span_err( - err.found_span, - "unclosed delimiter for_real", - ).emit(); - } + emit_unclosed_delims(&errors, &sess.span_diagnostic); tokens }); @@ -800,12 +796,7 @@ fn prepend_attrs(sess: &ParseSess, sess, Some(span), ); - for err in errors { - sess.span_diagnostic.struct_span_err( - err.found_span, - "unclosed delimiter attrs", - ).emit(); - } + emit_unclosed_delims(&errors, &sess.span_diagnostic); builder.push(stream); continue } @@ -828,12 +819,7 @@ fn prepend_attrs(sess: &ParseSess, sess, Some(span), ); - for err in errors { - sess.span_diagnostic.struct_span_err( - err.found_span, - "unclosed delimiter attrs 2", - ).emit(); - } + emit_unclosed_delims(&errors, &sess.span_diagnostic); brackets.push(stream); } diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs index ef291e2102b4c..38d12db13ef7f 100644 --- a/src/libsyntax_ext/proc_macro_server.rs +++ b/src/libsyntax_ext/proc_macro_server.rs @@ -11,6 +11,7 @@ use syntax::ast; use syntax::ext::base::ExtCtxt; use syntax::parse::lexer::comments; use syntax::parse::{self, token, ParseSess}; +use syntax::parse::parser::emit_unclosed_delims; use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; use syntax_pos::hygiene::{SyntaxContext, Transparency}; use syntax_pos::symbol::{keywords, Symbol}; @@ -408,12 +409,14 @@ impl server::TokenStream for Rustc<'_> { stream.is_empty() } fn from_str(&mut self, src: &str) -> Self::TokenStream { - parse::parse_stream_from_source_str( + let (tokens, errors) = parse::parse_stream_from_source_str( FileName::proc_macro_source_code(src.clone()), src.to_string(), self.sess, Some(self.call_site), - ).0 + ); + emit_unclosed_delims(&errors, &self.sess.span_diagnostic); + tokens } fn to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() From 1495d304483bb73c408295523ca458f2691e9732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 02:25:06 -0800 Subject: [PATCH 03/11] Remove spurious complaint about missing expression for bare semicolons --- src/libsyntax/parse/parser.rs | 17 ++++++++++++++++- src/test/ui/resolve/token-error-correct.rs | 4 +++- src/test/ui/resolve/token-error-correct.stderr | 15 ++++++++------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4a932ab7bd19c..742a493a2f92b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -731,7 +731,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); err.emit(); - // self.expected_tokens.clear(); // reduce errors + self.expected_tokens.clear(); // reduce errors Ok(true) } _ => Err(err), @@ -2814,6 +2814,21 @@ impl<'a> Parser<'a> { hi = pth.span; ex = ExprKind::Path(None, pth); } else { + if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { + // Don't complain about bare semicolons after unclosed braces + // recovery in order to keep the error count down. Fixing the + // delimiters will possibly also fix the bare semicolon found in + // expression context. For example, silence the following error: + // ``` + // error: expected expression, found `;` + // --> file.rs:2:13 + // | + // 2 | foo(bar(; + // | ^ expected expression + // ``` + self.bump(); + return Ok(self.mk_expr(self.span, ExprKind::Err, ThinVec::new())); + } match self.parse_literal_maybe_minus() { Ok(expr) => { hi = expr.span; diff --git a/src/test/ui/resolve/token-error-correct.rs b/src/test/ui/resolve/token-error-correct.rs index b97e22f7d910b..c03ea61beb8a7 100644 --- a/src/test/ui/resolve/token-error-correct.rs +++ b/src/test/ui/resolve/token-error-correct.rs @@ -2,6 +2,8 @@ fn main() { foo(bar(; - //~^ ERROR: expected expression, found `;` + //~^ ERROR cannot find function `bar` in this scope } //~^ ERROR: incorrect close delimiter: `}` + +fn foo(_: usize) {} \ No newline at end of file diff --git a/src/test/ui/resolve/token-error-correct.stderr b/src/test/ui/resolve/token-error-correct.stderr index 48a247a5898ef..b0827ea7367c2 100644 --- a/src/test/ui/resolve/token-error-correct.stderr +++ b/src/test/ui/resolve/token-error-correct.stderr @@ -1,9 +1,3 @@ -error: expected expression, found `;` - --> $DIR/token-error-correct.rs:4:13 - | -LL | foo(bar(; - | ^ expected expression - error: incorrect close delimiter: `}` --> $DIR/token-error-correct.rs:6:1 | @@ -11,9 +5,16 @@ LL | fn main() { | - close delimiter possibly meant for this LL | foo(bar(; | - un-closed delimiter -LL | //~^ ERROR: expected expression, found `;` +LL | //~^ ERROR cannot find function `bar` in this scope LL | } | ^ incorrect close delimiter +error[E0425]: cannot find function `bar` in this scope + --> $DIR/token-error-correct.rs:4:9 + | +LL | foo(bar(; + | ^^^ not found in this scope + error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0425`. From 60459429c584d9ef98fc03b4a93518e3dd1307ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 02:26:26 -0800 Subject: [PATCH 04/11] Add missing trailing newline --- src/libsyntax/parse/parser.rs | 2 +- src/test/ui/resolve/token-error-correct.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 742a493a2f92b..949033b6a5e1f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -8565,4 +8565,4 @@ pub fn emit_unclosed_delims(unclosed_delims: &[UnmatchedBrace], handler: &errors } err.emit(); } -} \ No newline at end of file +} diff --git a/src/test/ui/resolve/token-error-correct.rs b/src/test/ui/resolve/token-error-correct.rs index c03ea61beb8a7..d64907780efb5 100644 --- a/src/test/ui/resolve/token-error-correct.rs +++ b/src/test/ui/resolve/token-error-correct.rs @@ -6,4 +6,4 @@ fn main() { } //~^ ERROR: incorrect close delimiter: `}` -fn foo(_: usize) {} \ No newline at end of file +fn foo(_: usize) {} From 80c0b326d6513bb7812a85bc4c753cb211a0033d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 03:22:25 -0800 Subject: [PATCH 05/11] Remove unused `match` --- src/libsyntax/parse/parser.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 949033b6a5e1f..c1dd75b47c865 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1170,23 +1170,7 @@ impl<'a> Parser<'a> { Ok(()) }, - None => { - match ( - &self.token, - self.unmatched_angle_bracket_count, - self.max_angle_bracket_count > 1, - ) { - // (token::OpenDelim(_), 1, true) | (token::Semi, 1, true) => { - // self.struct_span_err( - // self.span, - // &format!("expected `>`, found `{}`", self.this_token_to_string()), - // // ).span_suggestion_short( - // ).emit(); - // Ok(()) - // } - _ => self.unexpected(), - } - } + None => self.unexpected(), } } @@ -5846,11 +5830,12 @@ impl<'a> Parser<'a> { .span_suggestion( span, &format!( - "remove extra angle bracket{}", + "{}remove extra angle bracket{}", + if plural { "" } else { "maybe " }, // account for `S:: Date: Tue, 5 Feb 2019 04:44:02 -0800 Subject: [PATCH 06/11] Update test output after rebase --- src/test/ui/issues/issue-52891.stderr | 2 +- src/test/ui/issues/issue-57819.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/ui/issues/issue-52891.stderr b/src/test/ui/issues/issue-52891.stderr index 55d611070d9dc..65b2b9460bc94 100644 --- a/src/test/ui/issues/issue-52891.stderr +++ b/src/test/ui/issues/issue-52891.stderr @@ -90,7 +90,7 @@ LL | use issue_52891::a; LL | m, | ______- LL | | a}; //~ ERROR `a` is defined multiple times - | | - + | | ^ | | | | |_____`a` reimported here | help: remove unnecessary import diff --git a/src/test/ui/issues/issue-57819.stderr b/src/test/ui/issues/issue-57819.stderr index 493e9835b1ca9..c64f2fbbf60bf 100644 --- a/src/test/ui/issues/issue-57819.stderr +++ b/src/test/ui/issues/issue-57819.stderr @@ -14,7 +14,7 @@ error: unmatched angle bracket --> $DIR/issue-57819.rs:25:10 | LL | bar::<<::Output>(); - | ^ help: remove extra angle bracket + | ^ help: maybe remove extra angle bracket error: unmatched angle brackets --> $DIR/issue-57819.rs:34:48 @@ -38,7 +38,7 @@ error: unmatched angle bracket --> $DIR/issue-57819.rs:43:48 | LL | let _ = vec![1, 2, 3].into_iter().collect::<>(); - | ^ help: remove extra angle bracket + | ^ help: maybe remove extra angle bracket error: aborting due to 7 previous errors From cba96613de1c6e3978dbb39ef689cf997cef0ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 15:20:26 -0800 Subject: [PATCH 07/11] fix test --- src/libsyntax/util/parser_testing.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs index dbe2b8d39f2d0..bcf1da66c04b6 100644 --- a/src/libsyntax/util/parser_testing.rs +++ b/src/libsyntax/util/parser_testing.rs @@ -12,8 +12,11 @@ use std::path::PathBuf; /// Map a string to tts, using a made-up filename: pub fn string_to_stream(source_str: String) -> TokenStream { let ps = ParseSess::new(FilePathMapping::empty()); - source_file_to_stream(&ps, ps.source_map() - .new_source_file(PathBuf::from("bogofile").into(), source_str), None) + source_file_to_stream( + &ps, + ps.source_map().new_source_file(PathBuf::from("bogofile").into(), + source_str, + ), None).0 } /// Map string to parser (via tts) From 336c48cc7b76865ca0d8608c89831d2669a8e368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 5 Feb 2019 15:41:29 -0800 Subject: [PATCH 08/11] tweak wording based on in person feedback --- src/libsyntax/parse/parser.rs | 4 ++-- src/test/ui/parser/issue-10636-2.stderr | 2 +- src/test/ui/parser/issue-2354.rs | 3 +-- src/test/ui/parser/issue-2354.stderr | 7 +++---- src/test/ui/resolve/token-error-correct-3.stderr | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c1dd75b47c865..c0e5d6f1397ca 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -720,9 +720,9 @@ impl<'a> Parser<'a> { // {foo(bar {}} // - ^ help: `)` may belong here // | - // in order to close this... + // unclosed delimiter if let Some(sp) = unmatched.unclosed_span { - err.span_label(sp, "in order to close this..."); + err.span_label(sp, "unclosed delimiter"); } err.span_suggestion_short( self.sess.source_map().next_point(self.prev_span), diff --git a/src/test/ui/parser/issue-10636-2.stderr b/src/test/ui/parser/issue-10636-2.stderr index 670a116eb51f0..38d57ce572365 100644 --- a/src/test/ui/parser/issue-10636-2.stderr +++ b/src/test/ui/parser/issue-10636-2.stderr @@ -5,7 +5,7 @@ LL | option.map(|some| 42; | - ^ | | | | | help: `)` may belong here - | in order to close this... + | unclosed delimiter error: expected expression, found `)` --> $DIR/issue-10636-2.rs:8:1 diff --git a/src/test/ui/parser/issue-2354.rs b/src/test/ui/parser/issue-2354.rs index b383bc00f911d..565f84822f7d6 100644 --- a/src/test/ui/parser/issue-2354.rs +++ b/src/test/ui/parser/issue-2354.rs @@ -1,5 +1,4 @@ -fn foo() { - //~^ NOTE un-closed delimiter +fn foo() { //~ NOTE un-closed delimiter match Some(10) { //~^ NOTE this delimiter might not be properly closed... Some(y) => { panic!(); } diff --git a/src/test/ui/parser/issue-2354.stderr b/src/test/ui/parser/issue-2354.stderr index f1b0905d86600..0f4cd5724ce1e 100644 --- a/src/test/ui/parser/issue-2354.stderr +++ b/src/test/ui/parser/issue-2354.stderr @@ -1,9 +1,8 @@ error: this file contains an un-closed delimiter - --> $DIR/issue-2354.rs:16:66 + --> $DIR/issue-2354.rs:15:66 | -LL | fn foo() { +LL | fn foo() { //~ NOTE un-closed delimiter | - un-closed delimiter -LL | //~^ NOTE un-closed delimiter LL | match Some(10) { | - this delimiter might not be properly closed... ... @@ -17,7 +16,7 @@ error[E0601]: `main` function not found in crate `issue_2354` | = note: the main function must be defined at the crate level but you have one or more functions named 'main' that are not defined at the crate level. Either move the definition or attach the `#[main]` attribute to override this behavior. note: here is a function named 'main' - --> $DIR/issue-2354.rs:15:1 + --> $DIR/issue-2354.rs:14:1 | LL | fn main() {} //~ NOTE here is a function named 'main' | ^^^^^^^^^^^^ diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr index fcc1c34d1fc53..a6bb83c71f313 100644 --- a/src/test/ui/resolve/token-error-correct-3.stderr +++ b/src/test/ui/resolve/token-error-correct-3.stderr @@ -5,7 +5,7 @@ LL | callback(path.as_ref(); //~ ERROR expected one of | - ^ | | | | | help: `)` may belong here - | in order to close this... + | unclosed delimiter error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` --> $DIR/token-error-correct-3.rs:20:9 From 6e62129b363a1c10b08e0012ca5978d13f9c18e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 6 Feb 2019 02:24:07 -0800 Subject: [PATCH 09/11] add doc comment and revert angle bracket change --- src/libsyntax/parse/parser.rs | 8 +++++--- src/test/ui/issues/issue-57819.stderr | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c0e5d6f1397ca..6dd4bfbb8d5f5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -252,6 +252,9 @@ pub struct Parser<'a> { /// See the comments in the `parse_path_segment` function for more details. crate unmatched_angle_bracket_count: u32, crate max_angle_bracket_count: u32, + /// 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. crate unclosed_delims: Vec, } @@ -5830,12 +5833,11 @@ impl<'a> Parser<'a> { .span_suggestion( span, &format!( - "{}remove extra angle bracket{}", - if plural { "" } else { "maybe " }, // account for `S:: $DIR/issue-57819.rs:25:10 | LL | bar::<<::Output>(); - | ^ help: maybe remove extra angle bracket + | ^ help: remove extra angle bracket error: unmatched angle brackets --> $DIR/issue-57819.rs:34:48 @@ -38,7 +38,7 @@ error: unmatched angle bracket --> $DIR/issue-57819.rs:43:48 | LL | let _ = vec![1, 2, 3].into_iter().collect::<>(); - | ^ help: maybe remove extra angle bracket + | ^ help: remove extra angle bracket error: aborting due to 7 previous errors From c54b230fa1a7365dd648e9dece87e40e838b45f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 7 Feb 2019 06:20:23 -0800 Subject: [PATCH 10/11] Add fixme --- src/libsyntax/parse/parser.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6dd4bfbb8d5f5..0ac3b8f6bd018 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -721,7 +721,9 @@ impl<'a> Parser<'a> { // the most sense, which is immediately after the last token: // // {foo(bar {}} - // - ^ help: `)` may belong here + // - ^ + // | | + // | help: `)` may belong here (FIXME: #58270) // | // unclosed delimiter if let Some(sp) = unmatched.unclosed_span { From fb3c4fbfc33b77b7beeeaf4a749a2081a8bfbc2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Feb 2019 01:16:56 -0800 Subject: [PATCH 11/11] Fix nll test output --- src/test/ui/augmented-assignments.nll.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/augmented-assignments.nll.stderr b/src/test/ui/augmented-assignments.nll.stderr index 840b377263db9..33c94d6e3a59e 100644 --- a/src/test/ui/augmented-assignments.nll.stderr +++ b/src/test/ui/augmented-assignments.nll.stderr @@ -9,7 +9,7 @@ LL | x //~ error: use of moved value: `x` LL | | //~^ value used here after move LL | | += LL | | x; //~ value moved here - | | - + | | ^ | | | | |_____move out of `x` occurs here | borrow later used here