提交 3315728c 编写于 作者: B bors

Auto merge of #57944 - estebank:unclosed-delim-the-quickening, r=oli-obk

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.
......@@ -672,8 +672,8 @@ fn render_source_line(&self,
// | | 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.
......
......@@ -29,6 +29,7 @@
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;
......@@ -436,7 +437,8 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
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);
......
......@@ -33,6 +33,15 @@ fn default() -> Self {
}
}
#[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
......@@ -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.
......@@ -222,6 +232,7 @@ fn new_raw_internal(sess: &'a ParseSess, source_file: Lrc<syntax_pos::SourceFile
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,
......
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 @@ fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
}
// 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
......
......@@ -9,6 +9,7 @@
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<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))
}
......@@ -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());
}
......@@ -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)
}
}
......
此差异已折叠。
......@@ -10,6 +10,7 @@
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};
......@@ -501,8 +502,8 @@ pub fn is_reserved_ident(&self) -> bool {
/// Enables better error recovery when the wrong token is found.
crate fn similar_tokens(&self) -> Option<Vec<Token>> {
match *self {
Comma => Some(vec![Dot, Lt]),
Semi => Some(vec![Colon]),
Comma => Some(vec![Dot, Lt, Semi]),
Semi => Some(vec![Colon, Comma]),
_ => None
}
}
......@@ -559,7 +560,10 @@ pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
// 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));
emit_unclosed_delims(&errors, &sess.span_diagnostic);
tokens
});
// During early phases of the compiler the AST could get modified
......@@ -800,12 +804,13 @@ 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),
);
emit_unclosed_delims(&errors, &sess.span_diagnostic);
builder.push(stream);
continue
}
......@@ -822,12 +827,13 @@ 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),
);
emit_unclosed_delims(&errors, &sess.span_diagnostic);
brackets.push(stream);
}
......
......@@ -12,8 +12,11 @@
/// 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)
......
......@@ -12,6 +12,7 @@
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};
......@@ -409,12 +410,14 @@ fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
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),
)
);
emit_unclosed_delims(&errors, &self.sess.span_diagnostic);
tokens
}
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
stream.to_string()
......
......@@ -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
......
......@@ -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
......
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
|
......
......@@ -5,7 +5,7 @@ pub fn trace_option(option: Option<isize>) {
option.map(|some| 42;
//~^ ERROR: expected one of
} //~ ERROR: incorrect close delimiter
}
//~^ ERROR: expected expression, found `)`
fn main() {}
error: incorrect close delimiter: `}`
--> $DIR/issue-10636-2.rs:8:1
|
LL | pub fn trace_option(option: Option<isize>) {
| - 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
| unclosed delimiter
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
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
......@@ -17,7 +17,7 @@ pub fn ensure_dir_exists<P: AsRef<Path>, F: FnOnce(&Path)>(path: P,
//~| expected type `()`
//~| found type `std::result::Result<bool, std::io::Error>`
//~| expected one of
} else { //~ ERROR: incorrect close delimiter: `}`
} else {
//~^ ERROR: expected one of
//~| unexpected token
Ok(false);
......
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
| unclosed delimiter
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<bool, std::io::Error>`
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`.
......@@ -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) {}
......@@ -5,15 +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: expected expression, found `;`
--> $DIR/token-error-correct.rs:4:13
error[E0425]: cannot find function `bar` in this scope
--> $DIR/token-error-correct.rs:4:9
|
LL | foo(bar(;
| ^ expected expression
| ^^^ not found in this scope
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0425`.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册