Skip to content

Commit 3315728

Browse files
committed
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.
2 parents 4c9233c + fb3c4fb commit 3315728

19 files changed

+332
-157
lines changed

src/librustc_errors/emitter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -672,8 +672,8 @@ impl EmitterWriter {
672672
// | | something about `foo`
673673
// | something about `fn foo()`
674674
annotations_position.sort_by(|a, b| {
675-
// Decreasing order
676-
a.1.len().cmp(&b.1.len()).reverse()
675+
// Decreasing order. When `a` and `b` are the same length, prefer `Primary`.
676+
(a.1.len(), !a.1.is_primary).cmp(&(b.1.len(), !b.1.is_primary)).reverse()
677677
});
678678

679679
// Write the underlines.

src/librustc_metadata/cstore_impl.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use syntax::attr;
2929
use syntax::source_map;
3030
use syntax::edition::Edition;
3131
use syntax::parse::source_file_to_stream;
32+
use syntax::parse::parser::emit_unclosed_delims;
3233
use syntax::symbol::Symbol;
3334
use syntax_pos::{Span, NO_EXPANSION, FileName};
3435
use rustc_data_structures::bit_set::BitSet;
@@ -436,7 +437,8 @@ impl cstore::CStore {
436437

437438
let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
438439
let local_span = Span::new(source_file.start_pos, source_file.end_pos, NO_EXPANSION);
439-
let body = source_file_to_stream(&sess.parse_sess, source_file, None);
440+
let (body, errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
441+
emit_unclosed_delims(&errors, &sess.diagnostic());
440442

441443
// Mark the attrs as used
442444
let attrs = data.get_item_attrs(id.index, sess);

src/libsyntax/parse/lexer/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ impl Default for TokenAndSpan {
3333
}
3434
}
3535

36+
#[derive(Clone, Debug)]
37+
pub struct UnmatchedBrace {
38+
pub expected_delim: token::DelimToken,
39+
pub found_delim: token::DelimToken,
40+
pub found_span: Span,
41+
pub unclosed_span: Option<Span>,
42+
pub candidate_span: Option<Span>,
43+
}
44+
3645
pub struct StringReader<'a> {
3746
pub sess: &'a ParseSess,
3847
/// The absolute offset within the source_map of the next character to read
@@ -58,6 +67,7 @@ pub struct StringReader<'a> {
5867
span_src_raw: Span,
5968
/// Stack of open delimiters and their spans. Used for error message.
6069
open_braces: Vec<(token::DelimToken, Span)>,
70+
crate unmatched_braces: Vec<UnmatchedBrace>,
6171
/// The type and spans for all braces
6272
///
6373
/// Used only for error recovery when arriving to EOF with mismatched braces.
@@ -222,6 +232,7 @@ impl<'a> StringReader<'a> {
222232
span: syntax_pos::DUMMY_SP,
223233
span_src_raw: syntax_pos::DUMMY_SP,
224234
open_braces: Vec::new(),
235+
unmatched_braces: Vec::new(),
225236
matching_delim_spans: Vec::new(),
226237
override_span,
227238
last_unclosed_found_span: None,

src/libsyntax/parse/lexer/tokentrees.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::print::pprust::token_to_string;
2-
use crate::parse::lexer::StringReader;
2+
use crate::parse::lexer::{StringReader, UnmatchedBrace};
33
use crate::parse::{token, PResult};
44
use crate::tokenstream::{DelimSpan, IsJoint::*, TokenStream, TokenTree, TreeAndJoint};
55

@@ -101,38 +101,38 @@ impl<'a> StringReader<'a> {
101101
}
102102
// Incorrect delimiter.
103103
token::CloseDelim(other) => {
104-
let token_str = token_to_string(&self.token);
104+
let mut unclosed_delimiter = None;
105+
let mut candidate = None;
105106
if self.last_unclosed_found_span != Some(self.span) {
106107
// do not complain about the same unclosed delimiter multiple times
107108
self.last_unclosed_found_span = Some(self.span);
108-
let msg = format!("incorrect close delimiter: `{}`", token_str);
109-
let mut err = self.sess.span_diagnostic.struct_span_err(
110-
self.span,
111-
&msg,
112-
);
113-
err.span_label(self.span, "incorrect close delimiter");
114109
// This is a conservative error: only report the last unclosed
115110
// delimiter. The previous unclosed delimiters could actually be
116111
// closed! The parser just hasn't gotten to them yet.
117112
if let Some(&(_, sp)) = self.open_braces.last() {
118-
err.span_label(sp, "un-closed delimiter");
113+
unclosed_delimiter = Some(sp);
119114
};
120115
if let Some(current_padding) = sm.span_to_margin(self.span) {
121116
for (brace, brace_span) in &self.open_braces {
122117
if let Some(padding) = sm.span_to_margin(*brace_span) {
123118
// high likelihood of these two corresponding
124119
if current_padding == padding && brace == &other {
125-
err.span_label(
126-
*brace_span,
127-
"close delimiter possibly meant for this",
128-
);
120+
candidate = Some(*brace_span);
129121
}
130122
}
131123
}
132124
}
133-
err.emit();
125+
let (tok, _) = self.open_braces.pop().unwrap();
126+
self.unmatched_braces.push(UnmatchedBrace {
127+
expected_delim: tok,
128+
found_delim: other,
129+
found_span: self.span,
130+
unclosed_span: unclosed_delimiter,
131+
candidate_span: candidate,
132+
});
133+
} else {
134+
self.open_braces.pop();
134135
}
135-
self.open_braces.pop().unwrap();
136136

137137
// If the incorrect delimiter matches an earlier opening
138138
// delimiter, then don't consume it (it can be used to

src/libsyntax/parse/mod.rs

+41-17
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::parse::parser::Parser;
99
use crate::symbol::Symbol;
1010
use crate::tokenstream::{TokenStream, TokenTree};
1111
use crate::diagnostics::plugin::ErrorMap;
12+
use crate::print::pprust::token_to_string;
1213

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

139-
pub fn parse_stream_from_source_str(name: FileName, source: String, sess: &ParseSess,
140-
override_span: Option<Span>)
141-
-> TokenStream {
140+
pub fn parse_stream_from_source_str(
141+
name: FileName,
142+
source: String,
143+
sess: &ParseSess,
144+
override_span: Option<Span>,
145+
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
142146
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
143147
}
144148

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

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

196199
/// Given a source_file and config, return a parser. Returns any buffered errors from lexing the
197200
/// initial token stream.
198-
fn maybe_source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>)
199-
-> Result<Parser<'_>, Vec<Diagnostic>>
200-
{
201+
fn maybe_source_file_to_parser(
202+
sess: &ParseSess,
203+
source_file: Lrc<SourceFile>,
204+
) -> Result<Parser<'_>, Vec<Diagnostic>> {
201205
let end_pos = source_file.end_pos;
202-
let mut parser = stream_to_parser(sess, maybe_file_to_stream(sess, source_file, None)?);
203-
206+
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
207+
let mut parser = stream_to_parser(sess, stream);
208+
parser.unclosed_delims = unclosed_delims;
204209
if parser.token == token::Eof && parser.span.is_dummy() {
205210
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
206211
}
@@ -247,25 +252,44 @@ fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
247252
}
248253

249254
/// Given a source_file, produce a sequence of token-trees
250-
pub fn source_file_to_stream(sess: &ParseSess,
251-
source_file: Lrc<SourceFile>,
252-
override_span: Option<Span>) -> TokenStream {
255+
pub fn source_file_to_stream(
256+
sess: &ParseSess,
257+
source_file: Lrc<SourceFile>,
258+
override_span: Option<Span>,
259+
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
253260
panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
254261
}
255262

256263
/// Given a source file, produce a sequence of token-trees. Returns any buffered errors from
257264
/// parsing the token tream.
258-
pub fn maybe_file_to_stream(sess: &ParseSess,
259-
source_file: Lrc<SourceFile>,
260-
override_span: Option<Span>) -> Result<TokenStream, Vec<Diagnostic>> {
265+
pub fn maybe_file_to_stream(
266+
sess: &ParseSess,
267+
source_file: Lrc<SourceFile>,
268+
override_span: Option<Span>,
269+
) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
261270
let mut srdr = lexer::StringReader::new_or_buffered_errs(sess, source_file, override_span)?;
262271
srdr.real_token();
263272

264273
match srdr.parse_all_token_trees() {
265-
Ok(stream) => Ok(stream),
274+
Ok(stream) => Ok((stream, srdr.unmatched_braces)),
266275
Err(err) => {
267276
let mut buffer = Vec::with_capacity(1);
268277
err.buffer(&mut buffer);
278+
// Not using `emit_unclosed_delims` to use `db.buffer`
279+
for unmatched in srdr.unmatched_braces {
280+
let mut db = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!(
281+
"incorrect close delimiter: `{}`",
282+
token_to_string(&token::Token::CloseDelim(unmatched.found_delim)),
283+
));
284+
db.span_label(unmatched.found_span, "incorrect close delimiter");
285+
if let Some(sp) = unmatched.candidate_span {
286+
db.span_label(sp, "close delimiter possibly meant for this");
287+
}
288+
if let Some(sp) = unmatched.unclosed_span {
289+
db.span_label(sp, "un-closed delimiter");
290+
}
291+
db.buffer(&mut buffer);
292+
}
269293
Err(buffer)
270294
}
271295
}

0 commit comments

Comments
 (0)