Skip to content

Commit 1a8e69a

Browse files
committed
TokenTreesReader now can find the correct mismatch delimiter pairs
1 parent 6718ea1 commit 1a8e69a

13 files changed

+231
-62
lines changed

compiler/rustc_parse/src/lexer/tokentrees.rs

+69-46
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token};
33
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
44
use rustc_ast_pretty::pprust::token_to_string;
55
use rustc_data_structures::fx::FxHashMap;
6-
use rustc_errors::{PErr, PResult};
6+
use rustc_errors::{Diagnostic, PErr, PResult};
77
use rustc_span::Span;
88

99
pub(super) struct TokenTreesReader<'a> {
@@ -104,22 +104,7 @@ impl<'a> TokenTreesReader<'a> {
104104
}
105105

106106
if let Some((delim, _)) = self.open_braces.last() {
107-
if let Some((_, open_sp, close_sp)) =
108-
self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
109-
let sm = self.string_reader.sess.source_map();
110-
if let Some(close_padding) = sm.span_to_margin(*close_sp) {
111-
if let Some(open_padding) = sm.span_to_margin(*open_sp) {
112-
return delim == d && close_padding != open_padding;
113-
}
114-
}
115-
false
116-
})
117-
// these are in reverse order as they get inserted on close, but
118-
{
119-
// we want the last open/first close
120-
err.span_label(*open_sp, "this delimiter might not be properly closed...");
121-
err.span_label(*close_sp, "...as it matches this but it has different indentation");
122-
}
107+
self.report_error_prone_delim_block(*delim, &mut err);
123108
}
124109
err
125110
}
@@ -157,15 +142,11 @@ impl<'a> TokenTreesReader<'a> {
157142
//only add braces
158143
if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) {
159144
self.matching_block_spans.push((open_brace_span, close_brace_span));
160-
}
161145

162-
if self.open_braces.is_empty() {
163-
// Clear up these spans to avoid suggesting them as we've found
164-
// properly matched delimiters so far for an entire block.
165-
self.matching_delim_spans.clear();
166-
} else {
146+
// Add all the matching spans, we will sort by span later
167147
self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span));
168148
}
149+
169150
// Move past the closing delimiter.
170151
self.token = self.string_reader.next_token().0;
171152
}
@@ -183,15 +164,12 @@ impl<'a> TokenTreesReader<'a> {
183164
if let Some(&(_, sp)) = self.open_braces.last() {
184165
unclosed_delimiter = Some(sp);
185166
};
186-
let sm = self.string_reader.sess.source_map();
187-
if let Some(current_padding) = sm.span_to_margin(self.token.span) {
188-
for (brace, brace_span) in &self.open_braces {
189-
if let Some(padding) = sm.span_to_margin(*brace_span) {
190-
// high likelihood of these two corresponding
191-
if current_padding == padding && brace == &close_delim {
192-
candidate = Some(*brace_span);
193-
}
194-
}
167+
for (brace, brace_span) in &self.open_braces {
168+
if self.same_identation_level(self.token.span, *brace_span)
169+
&& brace == &close_delim
170+
{
171+
// high likelihood of these two corresponding
172+
candidate = Some(*brace_span);
195173
}
196174
}
197175
let (tok, _) = self.open_braces.pop().unwrap();
@@ -236,23 +214,68 @@ impl<'a> TokenTreesReader<'a> {
236214
let mut err =
237215
self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
238216

239-
// Braces are added at the end, so the last element is the biggest block
240-
if let Some(parent) = self.matching_block_spans.last() {
241-
if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
242-
// Check if the (empty block) is in the last properly closed block
243-
if (parent.0.to(parent.1)).contains(span) {
244-
err.span_label(span, "block is empty, you might have not meant to close it");
245-
} else {
246-
err.span_label(parent.0, "this opening brace...");
247-
err.span_label(parent.1, "...matches this closing brace");
217+
self.report_error_prone_delim_block(delim, &mut err);
218+
err.span_label(self.token.span, "unexpected closing delimiter");
219+
err
220+
}
221+
222+
fn same_identation_level(&self, open_sp: Span, close_sp: Span) -> bool {
223+
let sm = self.string_reader.sess.source_map();
224+
if let (Some(open_padding), Some(close_padding)) =
225+
(sm.span_to_margin(open_sp), sm.span_to_margin(close_sp))
226+
{
227+
open_padding == close_padding
228+
} else {
229+
false
230+
}
231+
}
232+
233+
fn report_error_prone_delim_block(&self, delim: Delimiter, err: &mut Diagnostic) {
234+
let mut matched_spans = vec![];
235+
let mut candidate_span = None;
236+
237+
for &(d, open_sp, close_sp) in &self.matching_delim_spans {
238+
if d == delim {
239+
let block_span = open_sp.with_hi(close_sp.lo());
240+
let same_ident = self.same_identation_level(open_sp, close_sp);
241+
matched_spans.push((block_span, same_ident));
242+
}
243+
}
244+
245+
// sort by `lo`, so the large block spans in the front
246+
matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo()));
247+
248+
// We use larger block whose identation is well to cover those innert blocks
249+
// O(N^2) here, but we are on error reporting path, so it is fine
250+
for i in 0..matched_spans.len() {
251+
let (block_span, same_ident) = matched_spans[i];
252+
if same_ident {
253+
for j in i + 1..matched_spans.len() {
254+
let (inner_block, innert_same_ident) = matched_spans[j];
255+
if block_span.contains(inner_block) && !innert_same_ident {
256+
matched_spans[j] = (inner_block, true);
257+
}
248258
}
249-
} else {
250-
err.span_label(parent.0, "this opening brace...");
251-
err.span_label(parent.1, "...matches this closing brace");
252259
}
253260
}
254261

255-
err.span_label(self.token.span, "unexpected closing delimiter");
256-
err
262+
// Find the innermost span candidate for final report
263+
for (block_span, same_ident) in matched_spans.into_iter().rev() {
264+
if !same_ident {
265+
candidate_span = Some(block_span);
266+
break;
267+
}
268+
}
269+
270+
if let Some(block_span) = candidate_span {
271+
err.span_label(
272+
block_span.shrink_to_lo(),
273+
"this delimiter might not be properly closed...",
274+
);
275+
err.span_label(
276+
block_span.shrink_to_hi(),
277+
"...as it matches this but it has different indentation",
278+
);
279+
}
257280
}
258281
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// ignore-tidy-trailing-newlines
2+
//
3+
// error-pattern: this file contains an unclosed delimiter
4+
#![feature(let_chains)]
5+
trait Demo {}
6+
7+
impl dyn Demo {
8+
pub fn report(&self) -> u32 {
9+
let sum = |a: u32,
10+
b: u32,
11+
c: u32| {
12+
a + b + c
13+
};
14+
sum(1, 2, 3)
15+
}
16+
17+
fn check(&self, val: Option<u32>, num: Option<u32>) {
18+
if let Some(b) = val
19+
&& let Some(c) = num {
20+
&& b == c {
21+
//~^ ERROR expected struct
22+
//~| ERROR mismatched types
23+
}
24+
}
25+
}
26+
27+
fn main() { } //~ ERROR this file contains an unclosed delimiter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: this file contains an unclosed delimiter
2+
--> $DIR/deli-ident-issue-1.rs:27:65
3+
|
4+
LL | impl dyn Demo {
5+
| - unclosed delimiter
6+
...
7+
LL | && let Some(c) = num {
8+
| - this delimiter might not be properly closed...
9+
...
10+
LL | }
11+
| - ...as it matches this but it has different indentation
12+
...
13+
LL | fn main() { }
14+
| ^
15+
16+
error[E0574]: expected struct, variant or union type, found local variable `c`
17+
--> $DIR/deli-ident-issue-1.rs:20:17
18+
|
19+
LL | && b == c {
20+
| ^ not a struct, variant or union type
21+
22+
error[E0308]: mismatched types
23+
--> $DIR/deli-ident-issue-1.rs:20:9
24+
|
25+
LL | fn check(&self, val: Option<u32>, num: Option<u32>) {
26+
| - expected `()` because of default return type
27+
...
28+
LL | / && b == c {
29+
LL | |
30+
LL | |
31+
LL | | }
32+
| |_________^ expected `()`, found `bool`
33+
34+
error: aborting due to 3 previous errors
35+
36+
Some errors have detailed explanations: E0308, E0574.
37+
For more information about an error, try `rustc --explain E0308`.
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ignore-tidy-trailing-newlines
2+
//
3+
// error-pattern: this file contains an unclosed delimiter
4+
#![feature(let_chains)]
5+
trait Demo {}
6+
7+
impl dyn Demo {
8+
pub fn report(&self,
9+
a: u32,
10+
b: u32,
11+
c: u32) -> u32 {
12+
return a + b + c;
13+
}
14+
15+
fn check(&self, val: Option<u32>, num: Option<u32>) {
16+
if let Some(b) = val
17+
&& let Some(c) = num {
18+
&& b == c {
19+
//~^ ERROR expected struct
20+
//~| ERROR mismatched types
21+
}
22+
}
23+
}
24+
25+
fn main() { } //~ ERROR this file contains an unclosed delimiter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: this file contains an unclosed delimiter
2+
--> $DIR/deli-ident-issue-2.rs:25:65
3+
|
4+
LL | impl dyn Demo {
5+
| - unclosed delimiter
6+
...
7+
LL | && let Some(c) = num {
8+
| - this delimiter might not be properly closed...
9+
...
10+
LL | }
11+
| - ...as it matches this but it has different indentation
12+
...
13+
LL | fn main() { }
14+
| ^
15+
16+
error[E0574]: expected struct, variant or union type, found local variable `c`
17+
--> $DIR/deli-ident-issue-2.rs:18:17
18+
|
19+
LL | && b == c {
20+
| ^ not a struct, variant or union type
21+
22+
error[E0308]: mismatched types
23+
--> $DIR/deli-ident-issue-2.rs:18:9
24+
|
25+
LL | fn check(&self, val: Option<u32>, num: Option<u32>) {
26+
| - expected `()` because of default return type
27+
...
28+
LL | / && b == c {
29+
LL | |
30+
LL | |
31+
LL | | }
32+
| |_________^ expected `()`, found `bool`
33+
34+
error: aborting due to 3 previous errors
35+
36+
Some errors have detailed explanations: E0308, E0574.
37+
For more information about an error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This file has unexpected closing delimiter,
2+
3+
fn func(o: Option<u32>) {
4+
match o {
5+
Some(_x) =>
6+
let _ = if true {};
7+
}
8+
None => {}
9+
}
10+
} //~ ERROR unexpected closing delimiter
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: unexpected closing delimiter: `}`
2+
--> $DIR/issue-68987-unmatch-issue.rs:10:1
3+
|
4+
LL | match o {
5+
| - this delimiter might not be properly closed...
6+
...
7+
LL | }
8+
| - ...as it matches this but it has different indentation
9+
...
10+
LL | }
11+
| ^ unexpected closing delimiter
12+
13+
error: aborting due to previous error
14+

src/test/ui/parser/issues/issue-2354.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
fn foo() { //~ NOTE unclosed delimiter
2-
match Some(10) {
3-
//~^ NOTE this delimiter might not be properly closed...
2+
match Some(10) { //~ NOTE this delimiter might not be properly closed
43
Some(y) => { panic!(); }
54
None => { panic!(); }
6-
}
7-
//~^ NOTE ...as it matches this but it has different indentation
5+
} //~ NOTE ...as it matches this but it has different indentation
86

97
fn bar() {
108
let mut i = 0;

src/test/ui/parser/issues/issue-2354.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this file contains an unclosed delimiter
2-
--> $DIR/issue-2354.rs:15:52
2+
--> $DIR/issue-2354.rs:13:52
33
|
44
LL | fn foo() {
55
| - unclosed delimiter

src/test/ui/parser/issues/issue-70583-block-is-empty-1.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}`
22
--> $DIR/issue-70583-block-is-empty-1.rs:20:1
33
|
44
LL | fn struct_generic(x: Vec<i32>) {
5-
| - this opening brace...
5+
| - this delimiter might not be properly closed...
66
...
77
LL | }
8-
| - ...matches this closing brace
8+
| - ...as it matches this but it has different indentation
99
LL | }
1010
| ^ unexpected closing delimiter
1111

src/test/ui/parser/issues/issue-70583-block-is-empty-2.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
error: unexpected closing delimiter: `}`
22
--> $DIR/issue-70583-block-is-empty-2.rs:14:1
33
|
4+
LL | match self {
5+
| - this delimiter might not be properly closed...
46
LL | ErrorHandled::Reported => {}}
5-
| -- block is empty, you might have not meant to close it
7+
| - ...as it matches this but it has different indentation
68
...
79
LL | }
810
| ^ unexpected closing delimiter

src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}`
22
--> $DIR/macro-mismatched-delim-paren-brace.rs:5:1
33
|
44
LL | fn main() {
5-
| - this opening brace...
5+
| - this delimiter might not be properly closed...
66
...
77
LL | }
8-
| - ...matches this closing brace
8+
| - ...as it matches this but it has different indentation
99
LL | }
1010
| ^ unexpected closing delimiter
1111

src/test/ui/parser/mismatched-delim-brace-empty-block.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
error: unexpected closing delimiter: `}`
22
--> $DIR/mismatched-delim-brace-empty-block.rs:5:1
33
|
4-
LL | fn main() {
5-
| - this opening brace...
6-
LL |
7-
LL | }
8-
| - ...matches this closing brace
9-
LL | let _ = ();
104
LL | }
115
| ^ unexpected closing delimiter
126

0 commit comments

Comments
 (0)