Skip to content

Commit 371904b

Browse files
authored
Rollup merge of #108297 - chenyukang:yukang/delim-error-exit, r=petrochenkov
Exit when there are unmatched delims to avoid noisy diagnostics From #104012 (comment) r? ``@petrochenkov``
2 parents 31f858d + f808877 commit 371904b

File tree

106 files changed

+317
-1578
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+317
-1578
lines changed

compiler/rustc_expand/src/tests.rs

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
4343
ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str),
4444
None,
4545
)
46-
.0
4746
}
4847

4948
/// Parses a string, returns a crate.

compiler/rustc_parse/src/lexer/diagnostics.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::UnmatchedBrace;
1+
use super::UnmatchedDelim;
22
use rustc_ast::token::Delimiter;
33
use rustc_errors::Diagnostic;
44
use rustc_span::source_map::SourceMap;
@@ -8,7 +8,7 @@ use rustc_span::Span;
88
pub struct TokenTreeDiagInfo {
99
/// Stack of open delimiters and their spans. Used for error message.
1010
pub open_braces: Vec<(Delimiter, Span)>,
11-
pub unmatched_braces: Vec<UnmatchedBrace>,
11+
pub unmatched_delims: Vec<UnmatchedDelim>,
1212

1313
/// Used only for error recovery when arriving to EOF with mismatched braces.
1414
pub last_unclosed_found_span: Option<Span>,
@@ -32,10 +32,10 @@ pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> b
3232
// it's more friendly compared to report `unmatched error` in later phase
3333
pub fn report_missing_open_delim(
3434
err: &mut Diagnostic,
35-
unmatched_braces: &[UnmatchedBrace],
35+
unmatched_delims: &[UnmatchedDelim],
3636
) -> bool {
3737
let mut reported_missing_open = false;
38-
for unmatch_brace in unmatched_braces.iter() {
38+
for unmatch_brace in unmatched_delims.iter() {
3939
if let Some(delim) = unmatch_brace.found_delim
4040
&& matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
4141
{
@@ -60,7 +60,7 @@ pub fn report_suspicious_mismatch_block(
6060
sm: &SourceMap,
6161
delim: Delimiter,
6262
) {
63-
if report_missing_open_delim(err, &diag_info.unmatched_braces) {
63+
if report_missing_open_delim(err, &diag_info.unmatched_delims) {
6464
return;
6565
}
6666

compiler/rustc_parse/src/lexer/mod.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::errors;
22
use crate::lexer::unicode_chars::UNICODE_ARRAY;
3+
use crate::make_unclosed_delims_error;
34
use rustc_ast::ast::{self, AttrStyle};
45
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
56
use rustc_ast::tokenstream::TokenStream;
67
use rustc_ast::util::unicode::contains_text_flow_control_chars;
7-
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, PResult, StashKey};
8+
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
89
use rustc_lexer::unescape::{self, Mode};
910
use rustc_lexer::Cursor;
1011
use rustc_lexer::{Base, DocStyle, RawStrError};
@@ -31,7 +32,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char};
3132
rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
3233

3334
#[derive(Clone, Debug)]
34-
pub struct UnmatchedBrace {
35+
pub struct UnmatchedDelim {
3536
pub expected_delim: Delimiter,
3637
pub found_delim: Option<Delimiter>,
3738
pub found_span: Span,
@@ -44,7 +45,7 @@ pub(crate) fn parse_token_trees<'a>(
4445
mut src: &'a str,
4546
mut start_pos: BytePos,
4647
override_span: Option<Span>,
47-
) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
48+
) -> Result<TokenStream, Vec<Diagnostic>> {
4849
// Skip `#!`, if present.
4950
if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
5051
src = &src[shebang_len..];
@@ -61,7 +62,29 @@ pub(crate) fn parse_token_trees<'a>(
6162
override_span,
6263
nbsp_is_whitespace: false,
6364
};
64-
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader)
65+
let (token_trees, unmatched_delims) =
66+
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader);
67+
match token_trees {
68+
Ok(stream) if unmatched_delims.is_empty() => Ok(stream),
69+
_ => {
70+
// Return error if there are unmatched delimiters or unclosng delimiters.
71+
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
72+
// because the delimiter mismatch is more likely to be the root cause of error
73+
74+
let mut buffer = Vec::with_capacity(1);
75+
// Not using `emit_unclosed_delims` to use `db.buffer`
76+
for unmatched in unmatched_delims {
77+
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
78+
err.buffer(&mut buffer);
79+
}
80+
}
81+
if let Err(err) = token_trees {
82+
// Add unclosing delimiter error
83+
err.buffer(&mut buffer);
84+
}
85+
Err(buffer)
86+
}
87+
}
6588
}
6689

6790
struct StringReader<'a> {

compiler/rustc_parse/src/lexer/tokentrees.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::diagnostics::report_suspicious_mismatch_block;
22
use super::diagnostics::same_identation_level;
33
use super::diagnostics::TokenTreeDiagInfo;
4-
use super::{StringReader, UnmatchedBrace};
4+
use super::{StringReader, UnmatchedDelim};
55
use rustc_ast::token::{self, Delimiter, Token};
66
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
77
use rustc_ast_pretty::pprust::token_to_string;
@@ -18,14 +18,14 @@ pub(super) struct TokenTreesReader<'a> {
1818
impl<'a> TokenTreesReader<'a> {
1919
pub(super) fn parse_all_token_trees(
2020
string_reader: StringReader<'a>,
21-
) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
21+
) -> (PResult<'a, TokenStream>, Vec<UnmatchedDelim>) {
2222
let mut tt_reader = TokenTreesReader {
2323
string_reader,
2424
token: Token::dummy(),
2525
diag_info: TokenTreeDiagInfo::default(),
2626
};
2727
let res = tt_reader.parse_token_trees(/* is_delimited */ false);
28-
(res, tt_reader.diag_info.unmatched_braces)
28+
(res, tt_reader.diag_info.unmatched_delims)
2929
}
3030

3131
// Parse a stream of tokens into a list of `TokenTree`s.
@@ -34,7 +34,7 @@ impl<'a> TokenTreesReader<'a> {
3434
let mut buf = Vec::new();
3535
loop {
3636
match self.token.kind {
37-
token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)),
37+
token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?),
3838
token::CloseDelim(delim) => {
3939
return if is_delimited {
4040
Ok(TokenStream::new(buf))
@@ -43,10 +43,11 @@ impl<'a> TokenTreesReader<'a> {
4343
};
4444
}
4545
token::Eof => {
46-
if is_delimited {
47-
self.eof_err().emit();
48-
}
49-
return Ok(TokenStream::new(buf));
46+
return if is_delimited {
47+
Err(self.eof_err())
48+
} else {
49+
Ok(TokenStream::new(buf))
50+
};
5051
}
5152
_ => {
5253
// Get the next normal token. This might require getting multiple adjacent
@@ -78,7 +79,7 @@ impl<'a> TokenTreesReader<'a> {
7879
let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
7980
for &(_, sp) in &self.diag_info.open_braces {
8081
err.span_label(sp, "unclosed delimiter");
81-
self.diag_info.unmatched_braces.push(UnmatchedBrace {
82+
self.diag_info.unmatched_delims.push(UnmatchedDelim {
8283
expected_delim: Delimiter::Brace,
8384
found_delim: None,
8485
found_span: self.token.span,
@@ -98,7 +99,7 @@ impl<'a> TokenTreesReader<'a> {
9899
err
99100
}
100101

101-
fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> TokenTree {
102+
fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> {
102103
// The span for beginning of the delimited section
103104
let pre_span = self.token.span;
104105

@@ -107,7 +108,7 @@ impl<'a> TokenTreesReader<'a> {
107108
// Parse the token trees within the delimiters.
108109
// We stop at any delimiter so we can try to recover if the user
109110
// uses an incorrect delimiter.
110-
let tts = self.parse_token_trees(/* is_delimited */ true).unwrap();
111+
let tts = self.parse_token_trees(/* is_delimited */ true)?;
111112

112113
// Expand to cover the entire delimited token tree
113114
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
@@ -160,7 +161,7 @@ impl<'a> TokenTreesReader<'a> {
160161
}
161162
}
162163
let (tok, _) = self.diag_info.open_braces.pop().unwrap();
163-
self.diag_info.unmatched_braces.push(UnmatchedBrace {
164+
self.diag_info.unmatched_delims.push(UnmatchedDelim {
164165
expected_delim: tok,
165166
found_delim: Some(close_delim),
166167
found_span: self.token.span,
@@ -190,7 +191,7 @@ impl<'a> TokenTreesReader<'a> {
190191
_ => unreachable!(),
191192
}
192193

193-
TokenTree::Delimited(delim_span, open_delim, tts)
194+
Ok(TokenTree::Delimited(delim_span, open_delim, tts))
194195
}
195196

196197
fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> {

compiler/rustc_parse/src/lib.rs

+6-26
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
3030

3131
#[macro_use]
3232
pub mod parser;
33-
use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
33+
use parser::{make_unclosed_delims_error, Parser};
3434
pub mod lexer;
3535
pub mod validate_attr;
3636

@@ -96,10 +96,7 @@ pub fn parse_stream_from_source_str(
9696
sess: &ParseSess,
9797
override_span: Option<Span>,
9898
) -> TokenStream {
99-
let (stream, mut errors) =
100-
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span);
101-
emit_unclosed_delims(&mut errors, &sess);
102-
stream
99+
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
103100
}
104101

105102
/// Creates a new parser from a source string.
@@ -135,9 +132,8 @@ fn maybe_source_file_to_parser(
135132
source_file: Lrc<SourceFile>,
136133
) -> Result<Parser<'_>, Vec<Diagnostic>> {
137134
let end_pos = source_file.end_pos;
138-
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
135+
let stream = maybe_file_to_stream(sess, source_file, None)?;
139136
let mut parser = stream_to_parser(sess, stream, None);
140-
parser.unclosed_delims = unclosed_delims;
141137
if parser.token == token::Eof {
142138
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
143139
}
@@ -182,7 +178,7 @@ pub fn source_file_to_stream(
182178
sess: &ParseSess,
183179
source_file: Lrc<SourceFile>,
184180
override_span: Option<Span>,
185-
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
181+
) -> TokenStream {
186182
panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
187183
}
188184

@@ -192,31 +188,15 @@ pub fn maybe_file_to_stream(
192188
sess: &ParseSess,
193189
source_file: Lrc<SourceFile>,
194190
override_span: Option<Span>,
195-
) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
191+
) -> Result<TokenStream, Vec<Diagnostic>> {
196192
let src = source_file.src.as_ref().unwrap_or_else(|| {
197193
sess.span_diagnostic.bug(&format!(
198194
"cannot lex `source_file` without source: {}",
199195
sess.source_map().filename_for_diagnostics(&source_file.name)
200196
));
201197
});
202198

203-
let (token_trees, unmatched_braces) =
204-
lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span);
205-
206-
match token_trees {
207-
Ok(stream) => Ok((stream, unmatched_braces)),
208-
Err(err) => {
209-
let mut buffer = Vec::with_capacity(1);
210-
err.buffer(&mut buffer);
211-
// Not using `emit_unclosed_delims` to use `db.buffer`
212-
for unmatched in unmatched_braces {
213-
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
214-
err.buffer(&mut buffer);
215-
}
216-
}
217-
Err(buffer)
218-
}
219-
}
199+
lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span)
220200
}
221201

222202
/// Given a stream and the `ParseSess`, produces a parser.

compiler/rustc_parse/src/parser/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::errors::{
1919
};
2020

2121
use crate::fluent_generated as fluent;
22-
use crate::lexer::UnmatchedBrace;
22+
use crate::lexer::UnmatchedDelim;
2323
use crate::parser;
2424
use rustc_ast as ast;
2525
use rustc_ast::ptr::P;
@@ -222,7 +222,7 @@ impl MultiSugg {
222222
/// is dropped.
223223
pub struct SnapshotParser<'a> {
224224
parser: Parser<'a>,
225-
unclosed_delims: Vec<UnmatchedBrace>,
225+
unclosed_delims: Vec<UnmatchedDelim>,
226226
}
227227

228228
impl<'a> Deref for SnapshotParser<'a> {
@@ -264,7 +264,7 @@ impl<'a> Parser<'a> {
264264
self.unclosed_delims.extend(snapshot.unclosed_delims);
265265
}
266266

267-
pub fn unclosed_delims(&self) -> &[UnmatchedBrace] {
267+
pub fn unclosed_delims(&self) -> &[UnmatchedDelim] {
268268
&self.unclosed_delims
269269
}
270270

compiler/rustc_parse/src/parser/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod path;
1010
mod stmt;
1111
mod ty;
1212

13-
use crate::lexer::UnmatchedBrace;
13+
use crate::lexer::UnmatchedDelim;
1414
pub use attr_wrapper::AttrWrapper;
1515
pub use diagnostics::AttemptLocalParseRecovery;
1616
pub(crate) use item::FnParseMode;
@@ -149,7 +149,7 @@ pub struct Parser<'a> {
149149
/// A list of all unclosed delimiters found by the lexer. If an entry is used for error recovery
150150
/// it gets removed from here. Every entry left at the end gets emitted as an independent
151151
/// error.
152-
pub(super) unclosed_delims: Vec<UnmatchedBrace>,
152+
pub(super) unclosed_delims: Vec<UnmatchedDelim>,
153153
last_unexpected_token_span: Option<Span>,
154154
/// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
155155
/// looked like it could have been a mistyped path or literal `Option:Some(42)`).
@@ -1521,11 +1521,11 @@ impl<'a> Parser<'a> {
15211521
}
15221522

15231523
pub(crate) fn make_unclosed_delims_error(
1524-
unmatched: UnmatchedBrace,
1524+
unmatched: UnmatchedDelim,
15251525
sess: &ParseSess,
15261526
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
15271527
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
1528-
// `unmatched_braces` only for error recovery in the `Parser`.
1528+
// `unmatched_delims` only for error recovery in the `Parser`.
15291529
let found_delim = unmatched.found_delim?;
15301530
let mut spans = vec![unmatched.found_span];
15311531
if let Some(sp) = unmatched.unclosed_span {
@@ -1542,7 +1542,7 @@ pub(crate) fn make_unclosed_delims_error(
15421542
Some(err)
15431543
}
15441544

1545-
pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &ParseSess) {
1545+
pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedDelim>, sess: &ParseSess) {
15461546
*sess.reached_eof.borrow_mut() |=
15471547
unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none());
15481548
for unmatched in unclosed_delims.drain(..) {

src/librustdoc/doctest.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -769,24 +769,16 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
769769
match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
770770
Ok(p) => p,
771771
Err(_) => {
772-
debug!("Cannot build a parser to check mod attr so skipping...");
773-
return true;
772+
// If there is an unclosed delimiter, an error will be returned by the tokentrees.
773+
return false;
774774
}
775775
};
776776
// If a parsing error happened, it's very likely that the attribute is incomplete.
777777
if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
778778
e.cancel();
779779
return false;
780780
}
781-
// We now check if there is an unclosed delimiter for the attribute. To do so, we look at
782-
// the `unclosed_delims` and see if the opening square bracket was closed.
783-
parser
784-
.unclosed_delims()
785-
.get(0)
786-
.map(|unclosed| {
787-
unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2)
788-
})
789-
.unwrap_or(true)
781+
true
790782
})
791783
})
792784
.unwrap_or(false)

tests/ui/lint/issue-104897.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// error-pattern: this file contains an unclosed delimiter
22
// error-pattern: this file contains an unclosed delimiter
33
// error-pattern: this file contains an unclosed delimiter
4-
// error-pattern: format argument must be a string literal
54

65
fn f(){(print!(á

0 commit comments

Comments
 (0)