Skip to content

Distinguish delim kind to decide whether to emit unexpected closing delimiter #138554

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions compiler/rustc_parse/src/lexer/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ pub(super) fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Sp

// When we get a `)` or `]` for `{`, we should emit help message here
// it's more friendly compared to report `unmatched error` in later phase
fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDelim]) -> bool {
fn report_missing_open_delim(
err: &mut Diag<'_>,
unmatched_delims: &mut Vec<UnmatchedDelim>,
) -> bool {
let mut reported_missing_open = false;
for unmatch_brace in unmatched_delims.iter() {
unmatched_delims.retain(|unmatch_brace| {
if let Some(delim) = unmatch_brace.found_delim
&& matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
{
Expand All @@ -50,18 +53,21 @@ fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDe
format!("missing open `{missed_open}` for this delimiter"),
);
reported_missing_open = true;
false
} else {
true
}
}
});
reported_missing_open
}

pub(super) fn report_suspicious_mismatch_block(
err: &mut Diag<'_>,
diag_info: &TokenTreeDiagInfo,
diag_info: &mut TokenTreeDiagInfo,
sm: &SourceMap,
delim: Delimiter,
) {
if report_missing_open_delim(err, &diag_info.unmatched_delims) {
if report_missing_open_delim(err, &mut diag_info.unmatched_delims) {
return;
}

Expand Down
29 changes: 21 additions & 8 deletions compiler/rustc_parse/src/lexer/tokentrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
);
}

if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
if let Some((delim, _)) = self.diag_info.open_delimiters.last().cloned() {
report_suspicious_mismatch_block(
&mut err,
&self.diag_info,
&mut self.diag_info,
self.psess.source_map(),
*delim,
delim,
)
}
err
Expand Down Expand Up @@ -154,15 +154,12 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
candidate = Some(*delimiter_span);
}
}
let (_, _) = self.diag_info.open_delimiters.pop().unwrap();
self.diag_info.unmatched_delims.push(UnmatchedDelim {
found_delim: Some(close_delim),
found_span: self.token.span,
unclosed_span: unclosed_delimiter,
candidate_span: candidate,
});
} else {
self.diag_info.open_delimiters.pop();
}

// If the incorrect delimiter matches an earlier opening
Expand Down Expand Up @@ -238,13 +235,29 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
this_spacing
}

fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
fn close_delim_err(&mut self, close_delim: Delimiter) -> Diag<'psess> {
// An unexpected closing delimiter (i.e., there is no matching opening delimiter).
let token_str = token_to_string(&self.token);
let msg = format!("unexpected closing delimiter: `{token_str}`");
let mut err = self.dcx().struct_span_err(self.token.span, msg);

report_suspicious_mismatch_block(&mut err, &self.diag_info, self.psess.source_map(), delim);
if let Some((open_delim, open_delim_span)) = self.diag_info.open_delimiters.last().cloned()
{
if open_delim == close_delim {
err.span_label(
open_delim_span,
format!("this closing `{}` may be intended to match here", token_str),
);
self.diag_info.open_delimiters.pop();
}
}

report_suspicious_mismatch_block(
&mut err,
&mut self.diag_info,
self.psess.source_map(),
close_delim,
);
err.span_label(self.token.span, "unexpected closing delimiter");
err
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/parser/deli-ident-issue-2.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
fn main() {
if 1 < 2 {
let _a = vec!]; //~ ERROR mismatched closing delimiter
let _a = vec!];
}
} //~ ERROR unexpected closing delimiter

Expand Down
13 changes: 4 additions & 9 deletions tests/ui/parser/deli-ident-issue-2.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
error: mismatched closing delimiter: `]`
--> $DIR/deli-ident-issue-2.rs:2:14
|
LL | if 1 < 2 {
| ^ unclosed delimiter
LL | let _a = vec!];
| ^ mismatched closing delimiter

error: unexpected closing delimiter: `}`
--> $DIR/deli-ident-issue-2.rs:5:1
|
LL | fn main() {
| - this closing `}` may be intended to match here
LL | if 1 < 2 {
LL | let _a = vec!];
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we should still have a label pointing at this opening {, stating that the closing ] matches it.

Copy link
Contributor Author

@xizheyin xizheyin Jul 7, 2025

Choose a reason for hiding this comment

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

However, the first { is the most likely match, and the { below seems to have already been successfully matched in the if statement.

The current error report seems to be a combination of the previous two errors, and points to a more accurate match. The original first error seems to have no effect. :)

| - missing open `[` for this delimiter
LL | }
LL | }
| ^ unexpected closing delimiter

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-104367.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
struct S {
d: [u32; {
#![cfg] {
#![w,) //~ ERROR mismatched closing delimiter
#![w,)
//~ ERROR this file contains an unclosed delimiter
14 changes: 4 additions & 10 deletions tests/ui/parser/issues/issue-104367.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
error: mismatched closing delimiter: `)`
--> $DIR/issue-104367.rs:5:15
|
LL | #![w,)
| ^ ^ mismatched closing delimiter
| |
| unclosed delimiter

error: this file contains an unclosed delimiter
--> $DIR/issue-104367.rs:6:71
|
Expand All @@ -18,9 +10,11 @@ LL | d: [u32; {
LL | #![cfg] {
| - unclosed delimiter
LL | #![w,)
| - missing open `(` for this delimiter
| - - missing open `(` for this delimiter
| |
| unclosed delimiter
LL |
| ^

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

4 changes: 2 additions & 2 deletions tests/ui/parser/issues/issue-105209.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
//@ compile-flags: -Zunpretty=ast-tree
#![c={#![c[)x //~ ERROR mismatched closing delimiter
//~ ERROR this file contains an unclosed delimiter
//~v ERROR this file contains an unclosed delimiter
#![c={#![c[)x
20 changes: 6 additions & 14 deletions tests/ui/parser/issues/issue-105209.stderr
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
error: mismatched closing delimiter: `)`
--> $DIR/issue-105209.rs:2:11
|
LL | #![c={#![c[)x
| ^^ mismatched closing delimiter
| |
| unclosed delimiter

error: this file contains an unclosed delimiter
--> $DIR/issue-105209.rs:3:68
--> $DIR/issue-105209.rs:3:15
|
LL | #![c={#![c[)x
| - - - - missing open `(` for this delimiter
| | | |
| - - - -- ^
| | | | ||
| | | | |missing open `(` for this delimiter
| | | | unclosed delimiter
| | | unclosed delimiter
| | unclosed delimiter
| unclosed delimiter
LL |
| ^

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

6 changes: 3 additions & 3 deletions tests/ui/parser/issues/issue-62895.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ LL | (), w20);
| ^ mismatched closing delimiter

error: mismatched closing delimiter: `)`
--> $DIR/issue-62895.rs:4:7
--> $DIR/issue-62895.rs:5:22
|
LL | mod _ {
| ^ unclosed delimiter
LL | pub fn g() -> isizee {
| ^ unclosed delimiter
...
LL | (), w20);
| ^ mismatched closing delimiter
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/parser/issues/issue-62973.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

fn main() {}

//~vvv ERROR mismatched closing delimiter: `)`
//~vv ERROR mismatched closing delimiter: `)`
//~vvv ERROR this file contains an unclosed delimiter
fn p() { match s { v, E { [) {) }

Expand Down
28 changes: 7 additions & 21 deletions tests/ui/parser/issues/issue-62973.stderr
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
error: mismatched closing delimiter: `)`
--> $DIR/issue-62973.rs:8:27
|
LL | fn p() { match s { v, E { [) {) }
| ^^ mismatched closing delimiter
| |
| unclosed delimiter

error: mismatched closing delimiter: `)`
--> $DIR/issue-62973.rs:8:30
|
LL | fn p() { match s { v, E { [) {) }
| ^^ mismatched closing delimiter
| |
| unclosed delimiter

error: this file contains an unclosed delimiter
--> $DIR/issue-62973.rs:10:2
--> $DIR/issue-62973.rs:8:2
|
LL | fn p() { match s { v, E { [) {) }
| - - - - missing open `(` for this delimiter
| | | |
| | | missing open `(` for this delimiter
| - - - -- - missing open `(` for this delimiter
| | | | ||
| | | | |missing open `(` for this delimiter
| | | | unclosed delimiter
| | | unclosed delimiter
| | unclosed delimiter
| unclosed delimiter
LL |
LL |
| ^

error: aborting due to 3 previous errors
error: aborting due to 1 previous error

1 change: 0 additions & 1 deletion tests/ui/parser/issues/issue-63116.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// fixed by #66361
//~vv ERROR mismatched closing delimiter: `]`
//~v ERROR this file contains an unclosed delimiter
impl W <s(f;Y(;]
19 changes: 6 additions & 13 deletions tests/ui/parser/issues/issue-63116.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
error: mismatched closing delimiter: `]`
--> $DIR/issue-63116.rs:4:14
|
LL | impl W <s(f;Y(;]
| ^ ^ mismatched closing delimiter
| |
| unclosed delimiter

error: this file contains an unclosed delimiter
--> $DIR/issue-63116.rs:4:18
--> $DIR/issue-63116.rs:3:18
|
LL | impl W <s(f;Y(;]
| - -^
| | |
| | missing open `[` for this delimiter
| - - -^
| | | |
| | | missing open `[` for this delimiter
| | unclosed delimiter
| unclosed delimiter

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@ mod a {

enum Bug {
V = [PhantomData; { [ () ].len() ].len() as isize,
//~^ ERROR mismatched closing delimiter: `]`
}
}

mod b {
enum Bug {
V = [Vec::new; { [].len() ].len() as isize,
//~^ ERROR mismatched closing delimiter: `]`
}
}

mod c {
enum Bug {
V = [Vec::new; { [0].len() ].len() as isize,
//~^ ERROR mismatched closing delimiter: `]`
}

fn main() {} //~ ERROR this file contains an unclosed delimiter
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
error: mismatched closing delimiter: `]`
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:27
|
LL | V = [PhantomData; { [ () ].len() ].len() as isize,
| - ^ ^ mismatched closing delimiter
| | |
| | unclosed delimiter
| closing delimiter possibly meant for this

error: mismatched closing delimiter: `]`
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:12:24
|
LL | V = [Vec::new; { [].len() ].len() as isize,
| - ^ ^ mismatched closing delimiter
| | |
| | unclosed delimiter
| closing delimiter possibly meant for this

error: mismatched closing delimiter: `]`
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:19:24
|
LL | V = [Vec::new; { [0].len() ].len() as isize,
| - ^ ^ mismatched closing delimiter
| | |
| | unclosed delimiter
| closing delimiter possibly meant for this

error: this file contains an unclosed delimiter
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:23:65
--> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:20:65
|
LL | mod a {
| - unclosed delimiter
...
LL | V = [PhantomData; { [ () ].len() ].len() as isize,
| - missing open `[` for this delimiter
...
LL | mod b {
| - unclosed delimiter
LL | enum Bug {
LL | V = [Vec::new; { [].len() ].len() as isize,
| - missing open `[` for this delimiter
...
LL | mod c {
| - unclosed delimiter
LL | enum Bug {
| - unclosed delimiter
LL | V = [Vec::new; { [0].len() ].len() as isize,
| - missing open `[` for this delimiter
...
LL | fn main() {}
| ^

error: aborting due to 4 previous errors
error: aborting due to 1 previous error

2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-68987-unmatch-issue-2.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// FIXME: this case need more work to fix
// currently the TokenTree matching ')' with '{', which is not user friendly for diagnostics
async fn obstest() -> Result<> {
let obs_connect = || -> Result<(), MyError) { //~ ERROR mismatched closing delimiter
let obs_connect = || -> Result<(), MyError) {
async {
}
}
Expand Down
Loading
Loading