Skip to content

Commit 008aa5a

Browse files
committed
Provide more context for unenclosed delimiters
* When encountering EOF, point at the last opening brace that does not have the same indentation level as its close delimiter. * When encountering the wrong type of close delimiter, point at the likely correct open delimiter to give a better idea of what went wrong.
1 parent 5238b52 commit 008aa5a

File tree

9 files changed

+124
-13
lines changed

9 files changed

+124
-13
lines changed

src/libsyntax/parse/lexer/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ pub struct StringReader<'a> {
6666
/// The raw source span which *does not* take `override_span` into account
6767
span_src_raw: Span,
6868
open_braces: Vec<(token::DelimToken, Span)>,
69+
/// The type and spans for all braces that have different indentation.
70+
///
71+
/// Used only for error recovery when arriving to EOF with mismatched braces.
72+
suspicious_open_spans: Vec<(token::DelimToken, Span, Span)>,
6973
crate override_span: Option<Span>,
7074
last_unclosed_found_span: Option<Span>,
7175
}
@@ -216,6 +220,7 @@ impl<'a> StringReader<'a> {
216220
span: syntax_pos::DUMMY_SP,
217221
span_src_raw: syntax_pos::DUMMY_SP,
218222
open_braces: Vec::new(),
223+
suspicious_open_spans: Vec::new(),
219224
override_span,
220225
last_unclosed_found_span: None,
221226
}

src/libsyntax/parse/lexer/tokentrees.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ impl<'a> StringReader<'a> {
5252
err.span_label(sp, "un-closed delimiter");
5353
}
5454

55+
if let Some((delim, _)) = self.open_braces.last() {
56+
if let Some((d, open_sp, close_sp)) = self.suspicious_open_spans.iter()
57+
.filter(|(d, _, _)| delim == d)
58+
.next() // these are in reverse order as they get inserted on close, but
59+
{ // we want the last open/first close
60+
if d == delim {
61+
err.span_label(*open_sp, "this might be the culprit...");
62+
err.span_label(
63+
*close_sp,
64+
"...as it matches this but it has different indentation",
65+
);
66+
}
67+
}
68+
}
5569
Err(err)
5670
},
5771
token::OpenDelim(delim) => {
@@ -70,11 +84,20 @@ impl<'a> StringReader<'a> {
7084
// Expand to cover the entire delimited token tree
7185
let span = pre_span.with_hi(self.span.hi());
7286

87+
let sm = self.sess.source_map();
7388
match self.token {
7489
// Correct delimiter.
7590
token::CloseDelim(d) if d == delim => {
76-
self.open_braces.pop().unwrap();
77-
91+
let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
92+
if let Some(current_padding) = sm.span_to_margin(self.span) {
93+
if let Some(padding) = sm.span_to_margin(open_brace_span) {
94+
if current_padding != padding {
95+
self.suspicious_open_spans.push(
96+
(open_brace, open_brace_span, self.span),
97+
);
98+
}
99+
}
100+
}
78101
// Parse the close delimiter.
79102
self.real_token();
80103
}
@@ -96,6 +119,19 @@ impl<'a> StringReader<'a> {
96119
if let Some(&(_, sp)) = self.open_braces.last() {
97120
err.span_label(sp, "un-closed delimiter");
98121
};
122+
if let Some(current_padding) = sm.span_to_margin(self.span) {
123+
for (brace, brace_span) in &self.open_braces {
124+
if let Some(padding) = sm.span_to_margin(*brace_span) {
125+
// high likelihood of these two corresponding
126+
if current_padding == padding && brace == &other {
127+
err.span_label(
128+
*brace_span,
129+
"close delimiter possibly meant for this",
130+
);
131+
}
132+
}
133+
}
134+
}
99135
err.emit();
100136
}
101137
self.open_braces.pop().unwrap();

src/libsyntax/source_map.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -251,17 +251,18 @@ impl SourceMap {
251251
/// crate. The source code of such an "imported source_file" is not available,
252252
/// but we still know enough to generate accurate debuginfo location
253253
/// information for things inlined from other crates.
254-
pub fn new_imported_source_file(&self,
255-
filename: FileName,
256-
name_was_remapped: bool,
257-
crate_of_origin: u32,
258-
src_hash: u128,
259-
name_hash: u128,
260-
source_len: usize,
261-
mut file_local_lines: Vec<BytePos>,
262-
mut file_local_multibyte_chars: Vec<MultiByteChar>,
263-
mut file_local_non_narrow_chars: Vec<NonNarrowChar>)
264-
-> Lrc<SourceFile> {
254+
pub fn new_imported_source_file(
255+
&self,
256+
filename: FileName,
257+
name_was_remapped: bool,
258+
crate_of_origin: u32,
259+
src_hash: u128,
260+
name_hash: u128,
261+
source_len: usize,
262+
mut file_local_lines: Vec<BytePos>,
263+
mut file_local_multibyte_chars: Vec<MultiByteChar>,
264+
mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
265+
) -> Lrc<SourceFile> {
265266
let start_pos = self.next_start_pos();
266267

267268
let end_pos = Pos::from_usize(start_pos + source_len);
@@ -578,6 +579,15 @@ impl SourceMap {
578579
.to_string())
579580
}
580581

582+
pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
583+
match self.span_to_prev_source(sp) {
584+
Err(_) => None,
585+
Ok(source) => source.split('\n').last().map(|last_line| {
586+
last_line.len() - last_line.trim_left().len()
587+
})
588+
}
589+
}
590+
581591
/// Return the source snippet as `String` before the given `Span`
582592
pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
583593
self.span_to_source(sp, |src, start_index, _| src[..start_index].to_string())

src/test/ui/parser-recovery-1.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error: this file contains an un-closed delimiter
33
|
44
LL | trait Foo {
55
| - un-closed delimiter
6+
LL | fn bar() {
7+
| - this might be the culprit...
8+
...
9+
LL | }
10+
| - ...as it matches this but it has different indentation
611
...
712
LL | } //~ ERROR this file contains an un-closed delimiter
813
| ^

src/test/ui/parser/unclosed-braces.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct S {
12+
x: [usize; 3],
13+
}
14+
15+
fn foo() {
16+
{
17+
{
18+
println!("hi");
19+
}
20+
}
21+
}
22+
23+
fn main() {
24+
//~^ NOTE un-closed delimiter
25+
{
26+
{
27+
//~^ NOTE this might be the culprit...
28+
foo();
29+
}
30+
//~^ NOTE ...as it matches this but it has different indentation
31+
}
32+
//~ ERROR this file contains an un-closed delimiter
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: this file contains an un-closed delimiter
2+
--> $DIR/unclosed-braces.rs:32:53
3+
|
4+
LL | fn main() {
5+
| - un-closed delimiter
6+
...
7+
LL | {
8+
| - this might be the culprit...
9+
...
10+
LL | }
11+
| - ...as it matches this but it has different indentation
12+
...
13+
LL | //~ ERROR this file contains an un-closed delimiter
14+
| ^
15+
16+
error: aborting due to previous error
17+

src/test/ui/resolve/token-error-correct-3.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error: incorrect close delimiter: `}`
22
--> $DIR/token-error-correct-3.rs:30:9
33
|
4+
LL | if !is_directory(path.as_ref()) { //~ ERROR: cannot find function `is_directory`
5+
| - close delimiter possibly meant for this
46
LL | callback(path.as_ref(); //~ ERROR expected one of
57
| - un-closed delimiter
68
...

src/test/ui/resolve/token-error-correct.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error: incorrect close delimiter: `}`
22
--> $DIR/token-error-correct.rs:16:1
33
|
4+
LL | fn main() {
5+
| - close delimiter possibly meant for this
46
LL | foo(bar(;
57
| - un-closed delimiter
68
LL | //~^ ERROR: expected expression, found `;`

src/test/ui/token/issue-10636-2.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error: incorrect close delimiter: `}`
22
--> $DIR/issue-10636-2.rs:18:1
33
|
4+
LL | pub fn trace_option(option: Option<isize>) {
5+
| - close delimiter possibly meant for this
46
LL | option.map(|some| 42;
57
| - un-closed delimiter
68
...

0 commit comments

Comments
 (0)