Skip to content

Commit 004bc5a

Browse files
committedSep 9, 2018
Auto merge of #53949 - estebank:unclosed-delim, r=nikomatsakis
Improve messages for un-closed delimiter errors
2 parents 0198a1e + 3192d3d commit 004bc5a

16 files changed

+171
-40
lines changed
 

Diff for: ‎src/libsyntax/parse/lexer/mod.rs

+5
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
}

Diff for: ‎src/libsyntax/parse/lexer/tokentrees.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,26 @@ impl<'a> StringReader<'a> {
4949
let msg = "this file contains an un-closed delimiter";
5050
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, msg);
5151
for &(_, sp) in &self.open_braces {
52-
err.span_help(sp, "did you mean to close this delimiter?");
52+
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(
62+
*open_sp,
63+
"this delimiter might not be properly closed...",
64+
);
65+
err.span_label(
66+
*close_sp,
67+
"...as it matches this but it has different indentation",
68+
);
69+
}
70+
}
71+
}
5572
Err(err)
5673
},
5774
token::OpenDelim(delim) => {
@@ -70,11 +87,20 @@ impl<'a> StringReader<'a> {
7087
// Expand to cover the entire delimited token tree
7188
let span = pre_span.with_hi(self.span.hi());
7289

90+
let sm = self.sess.source_map();
7391
match self.token {
7492
// Correct delimiter.
7593
token::CloseDelim(d) if d == delim => {
76-
self.open_braces.pop().unwrap();
77-
94+
let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
95+
if let Some(current_padding) = sm.span_to_margin(self.span) {
96+
if let Some(padding) = sm.span_to_margin(open_brace_span) {
97+
if current_padding != padding {
98+
self.suspicious_open_spans.push(
99+
(open_brace, open_brace_span, self.span),
100+
);
101+
}
102+
}
103+
}
78104
// Parse the close delimiter.
79105
self.real_token();
80106
}
@@ -94,8 +120,21 @@ impl<'a> StringReader<'a> {
94120
// delimiter. The previous unclosed delimiters could actually be
95121
// closed! The parser just hasn't gotten to them yet.
96122
if let Some(&(_, sp)) = self.open_braces.last() {
97-
err.span_label(sp, "unclosed delimiter");
123+
err.span_label(sp, "un-closed delimiter");
98124
};
125+
if let Some(current_padding) = sm.span_to_margin(self.span) {
126+
for (brace, brace_span) in &self.open_braces {
127+
if let Some(padding) = sm.span_to_margin(*brace_span) {
128+
// high likelihood of these two corresponding
129+
if current_padding == padding && brace == &other {
130+
err.span_label(
131+
*brace_span,
132+
"close delimiter possibly meant for this",
133+
);
134+
}
135+
}
136+
}
137+
}
99138
err.emit();
100139
}
101140
self.open_braces.pop().unwrap();

Diff for: ‎src/libsyntax/source_map.rs

+21-11
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())

Diff for: ‎src/test/ui/issue-10636-1.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
struct Obj { //~ NOTE: unclosed delimiter
13+
struct Obj {
14+
//~^ NOTE: un-closed delimiter
1415
member: usize
1516
)
1617
//~^ ERROR incorrect close delimiter

Diff for: ‎src/test/ui/issue-10636-1.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
error: incorrect close delimiter: `)`
2-
--> $DIR/issue-10636-1.rs:15:1
2+
--> $DIR/issue-10636-1.rs:16:1
33
|
4-
LL | struct Obj { //~ NOTE: unclosed delimiter
5-
| - unclosed delimiter
6-
LL | member: usize
4+
LL | struct Obj {
5+
| - un-closed delimiter
6+
...
77
LL | )
88
| ^ incorrect close delimiter
99

Diff for: ‎src/test/parse-fail/issue-2354.rs renamed to ‎src/test/ui/issue-2354.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
fn foo() { //~ HELP did you mean to close this delimiter?
13+
fn foo() { //~ NOTE un-closed delimiter
1414
match Some(x) {
15+
//~^ NOTE this delimiter might not be properly closed...
1516
Some(y) => { panic!(); }
1617
None => { panic!(); }
1718
}
19+
//~^ NOTE ...as it matches this but it has different indentation
1820

1921
fn bar() {
2022
let mut i = 0;

Diff for: ‎src/test/ui/issue-2354.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: this file contains an un-closed delimiter
2+
--> $DIR/issue-2354.rs:26:66
3+
|
4+
LL | fn foo() { //~ NOTE un-closed delimiter
5+
| - un-closed delimiter
6+
LL | match Some(x) {
7+
| - this delimiter might not be properly closed...
8+
...
9+
LL | }
10+
| - ...as it matches this but it has different indentation
11+
...
12+
LL | fn main() {} //~ ERROR this file contains an un-closed delimiter
13+
| ^
14+
15+
error: aborting due to previous error
16+

Diff for: ‎src/test/ui/parser-recovery-1.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414

1515
trait Foo {
1616
fn bar() {
17-
let x = foo(); //~ ERROR cannot find function `foo` in this scope
18-
17+
let x = foo();
18+
//~^ ERROR cannot find function `foo` in this scope
1919
}
2020

2121
fn main() {
22-
let x = y.; //~ ERROR unexpected token
23-
//~^ ERROR cannot find value `y` in this scope
22+
let x = y.;
23+
//~^ ERROR unexpected token
24+
//~| ERROR cannot find value `y` in this scope
2425
} //~ ERROR this file contains an un-closed delimiter

Diff for: ‎src/test/ui/parser-recovery-1.stderr

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
error: this file contains an un-closed delimiter
2-
--> $DIR/parser-recovery-1.rs:24:55
2+
--> $DIR/parser-recovery-1.rs:25:55
33
|
4+
LL | trait Foo {
5+
| - un-closed delimiter
6+
LL | fn bar() {
7+
| - this delimiter might not be properly closed...
8+
...
9+
LL | }
10+
| - ...as it matches this but it has different indentation
11+
...
412
LL | } //~ ERROR this file contains an un-closed delimiter
513
| ^
6-
|
7-
help: did you mean to close this delimiter?
8-
--> $DIR/parser-recovery-1.rs:15:11
9-
|
10-
LL | trait Foo {
11-
| ^
1214

1315
error: unexpected token: `;`
1416
--> $DIR/parser-recovery-1.rs:22:15
1517
|
16-
LL | let x = y.; //~ ERROR unexpected token
18+
LL | let x = y.;
1719
| ^
1820

1921
error[E0425]: cannot find function `foo` in this scope
2022
--> $DIR/parser-recovery-1.rs:17:17
2123
|
22-
LL | let x = foo(); //~ ERROR cannot find function `foo` in this scope
24+
LL | let x = foo();
2325
| ^^^ not found in this scope
2426

2527
error[E0425]: cannot find value `y` in this scope
2628
--> $DIR/parser-recovery-1.rs:22:13
2729
|
28-
LL | let x = y.; //~ ERROR unexpected token
30+
LL | let x = y.;
2931
| ^ not found in this scope
3032

3133
error[E0601]: `main` function not found in crate `parser_recovery_1`

Diff for: ‎src/test/ui/parser-recovery-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: incorrect close delimiter: `)`
22
--> $DIR/parser-recovery-2.rs:18:5
33
|
44
LL | fn bar() {
5-
| - unclosed delimiter
5+
| - un-closed delimiter
66
LL | let x = foo(); //~ ERROR cannot find function `foo` in this scope
77
LL | ) //~ ERROR incorrect close delimiter: `)`
88
| ^ incorrect close delimiter

Diff for: ‎src/test/ui/parser/unclosed-braces.rs

+32
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 delimiter might not be properly closed...
28+
foo();
29+
}
30+
//~^ NOTE ...as it matches this but it has different indentation
31+
}
32+
//~ ERROR this file contains an un-closed delimiter

Diff for: ‎src/test/ui/parser/unclosed-braces.stderr

+17
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 delimiter might not be properly closed...
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+

Diff for: ‎src/test/ui/resolve/token-error-correct-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: incorrect close delimiter: `)`
22
--> $DIR/token-error-correct-2.rs:16:5
33
|
44
LL | if foo {
5-
| - unclosed delimiter
5+
| - un-closed delimiter
66
LL | //~^ ERROR: cannot find value `foo`
77
LL | ) //~ ERROR: incorrect close delimiter: `)`
88
| ^ incorrect close delimiter

Diff for: ‎src/test/ui/resolve/token-error-correct-3.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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
5-
| - unclosed delimiter
7+
| - un-closed delimiter
68
...
79
LL | } else { //~ ERROR: incorrect close delimiter: `}`
810
| ^ incorrect close delimiter

Diff for: ‎src/test/ui/resolve/token-error-correct.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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(;
5-
| - unclosed delimiter
7+
| - un-closed delimiter
68
LL | //~^ ERROR: expected expression, found `;`
79
LL | }
810
| ^ incorrect close delimiter

Diff for: ‎src/test/ui/token/issue-10636-2.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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;
5-
| - unclosed delimiter
7+
| - un-closed delimiter
68
...
79
LL | } //~ ERROR: incorrect close delimiter
810
| ^ incorrect close delimiter

0 commit comments

Comments
 (0)
Please sign in to comment.