Skip to content

Improve messages for un-closed delimiter errors #53949

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

Merged
merged 4 commits into from
Sep 9, 2018
Merged
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
5 changes: 5 additions & 0 deletions src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ pub struct StringReader<'a> {
/// The raw source span which *does not* take `override_span` into account
span_src_raw: Span,
open_braces: Vec<(token::DelimToken, Span)>,
/// The type and spans for all braces that have different indentation.
///
/// Used only for error recovery when arriving to EOF with mismatched braces.
suspicious_open_spans: Vec<(token::DelimToken, Span, Span)>,
crate override_span: Option<Span>,
last_unclosed_found_span: Option<Span>,
}
Expand Down Expand Up @@ -216,6 +220,7 @@ impl<'a> StringReader<'a> {
span: syntax_pos::DUMMY_SP,
span_src_raw: syntax_pos::DUMMY_SP,
open_braces: Vec::new(),
suspicious_open_spans: Vec::new(),
override_span,
last_unclosed_found_span: None,
}
Expand Down
47 changes: 43 additions & 4 deletions src/libsyntax/parse/lexer/tokentrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,26 @@ impl<'a> StringReader<'a> {
let msg = "this file contains an un-closed delimiter";
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, msg);
for &(_, sp) in &self.open_braces {
err.span_help(sp, "did you mean to close this delimiter?");
err.span_label(sp, "un-closed delimiter");
}

if let Some((delim, _)) = self.open_braces.last() {
if let Some((d, open_sp, close_sp)) = self.suspicious_open_spans.iter()
.filter(|(d, _, _)| delim == d)
.next() // these are in reverse order as they get inserted on close, but
{ // we want the last open/first close
if d == delim {
err.span_label(
*open_sp,
"this delimiter might not be properly closed...",
);
err.span_label(
*close_sp,
"...as it matches this but it has different indentation",
);
}
}
}
Err(err)
},
token::OpenDelim(delim) => {
Expand All @@ -70,11 +87,20 @@ impl<'a> StringReader<'a> {
// Expand to cover the entire delimited token tree
let span = pre_span.with_hi(self.span.hi());

let sm = self.sess.source_map();
match self.token {
// Correct delimiter.
token::CloseDelim(d) if d == delim => {
self.open_braces.pop().unwrap();

let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
if let Some(current_padding) = sm.span_to_margin(self.span) {
if let Some(padding) = sm.span_to_margin(open_brace_span) {
if current_padding != padding {
self.suspicious_open_spans.push(
(open_brace, open_brace_span, self.span),
);
}
}
}
// Parse the close delimiter.
self.real_token();
}
Expand All @@ -94,8 +120,21 @@ impl<'a> StringReader<'a> {
// delimiter. The previous unclosed delimiters could actually be
// closed! The parser just hasn't gotten to them yet.
if let Some(&(_, sp)) = self.open_braces.last() {
err.span_label(sp, "unclosed delimiter");
err.span_label(sp, "un-closed delimiter");
};
if let Some(current_padding) = sm.span_to_margin(self.span) {
for (brace, brace_span) in &self.open_braces {
if let Some(padding) = sm.span_to_margin(*brace_span) {
// high likelihood of these two corresponding
if current_padding == padding && brace == &other {
err.span_label(
*brace_span,
"close delimiter possibly meant for this",
);
}
}
}
}
err.emit();
}
self.open_braces.pop().unwrap();
Expand Down
32 changes: 21 additions & 11 deletions src/libsyntax/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,17 +251,18 @@ impl SourceMap {
/// crate. The source code of such an "imported source_file" is not available,
/// but we still know enough to generate accurate debuginfo location
/// information for things inlined from other crates.
pub fn new_imported_source_file(&self,
filename: FileName,
name_was_remapped: bool,
crate_of_origin: u32,
src_hash: u128,
name_hash: u128,
source_len: usize,
mut file_local_lines: Vec<BytePos>,
mut file_local_multibyte_chars: Vec<MultiByteChar>,
mut file_local_non_narrow_chars: Vec<NonNarrowChar>)
-> Lrc<SourceFile> {
pub fn new_imported_source_file(
&self,
filename: FileName,
name_was_remapped: bool,
crate_of_origin: u32,
src_hash: u128,
name_hash: u128,
source_len: usize,
mut file_local_lines: Vec<BytePos>,
mut file_local_multibyte_chars: Vec<MultiByteChar>,
mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
) -> Lrc<SourceFile> {
let start_pos = self.next_start_pos();

let end_pos = Pos::from_usize(start_pos + source_len);
Expand Down Expand Up @@ -578,6 +579,15 @@ impl SourceMap {
.to_string())
}

pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
match self.span_to_prev_source(sp) {
Err(_) => None,
Ok(source) => source.split('\n').last().map(|last_line| {
last_line.len() - last_line.trim_left().len()
})
}
}

/// Return the source snippet as `String` before the given `Span`
pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
self.span_to_source(sp, |src, start_index, _| src[..start_index].to_string())
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/issue-10636-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

// compile-flags: -Z parse-only

struct Obj { //~ NOTE: unclosed delimiter
struct Obj {
//~^ NOTE: un-closed delimiter
member: usize
)
//~^ ERROR incorrect close delimiter
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/issue-10636-1.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
error: incorrect close delimiter: `)`
--> $DIR/issue-10636-1.rs:15:1
--> $DIR/issue-10636-1.rs:16:1
|
LL | struct Obj { //~ NOTE: unclosed delimiter
| - unclosed delimiter
LL | member: usize
LL | struct Obj {
| - un-closed delimiter
...
LL | )
| ^ incorrect close delimiter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@

// compile-flags: -Z parse-only

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

fn bar() {
let mut i = 0;
Expand Down
16 changes: 16 additions & 0 deletions src/test/ui/issue-2354.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: this file contains an un-closed delimiter
--> $DIR/issue-2354.rs:26:66
|
LL | fn foo() { //~ NOTE un-closed delimiter
| - un-closed delimiter
LL | match Some(x) {
| - this delimiter might not be properly closed...
...
LL | }
| - ...as it matches this but it has different indentation
...
LL | fn main() {} //~ ERROR this file contains an un-closed delimiter
| ^

error: aborting due to previous error

9 changes: 5 additions & 4 deletions src/test/ui/parser-recovery-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@

trait Foo {
fn bar() {
let x = foo(); //~ ERROR cannot find function `foo` in this scope

let x = foo();
//~^ ERROR cannot find function `foo` in this scope
}

fn main() {
let x = y.; //~ ERROR unexpected token
//~^ ERROR cannot find value `y` in this scope
let x = y.;
//~^ ERROR unexpected token
//~| ERROR cannot find value `y` in this scope
} //~ ERROR this file contains an un-closed delimiter
22 changes: 12 additions & 10 deletions src/test/ui/parser-recovery-1.stderr
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
error: this file contains an un-closed delimiter
--> $DIR/parser-recovery-1.rs:24:55
--> $DIR/parser-recovery-1.rs:25:55
|
LL | trait Foo {
| - un-closed delimiter
LL | fn bar() {
| - this delimiter might not be properly closed...
...
LL | }
| - ...as it matches this but it has different indentation
...
LL | } //~ ERROR this file contains an un-closed delimiter
| ^
|
help: did you mean to close this delimiter?
--> $DIR/parser-recovery-1.rs:15:11
|
LL | trait Foo {
| ^

error: unexpected token: `;`
--> $DIR/parser-recovery-1.rs:22:15
|
LL | let x = y.; //~ ERROR unexpected token
LL | let x = y.;
| ^

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

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

error[E0601]: `main` function not found in crate `parser_recovery_1`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser-recovery-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: incorrect close delimiter: `)`
--> $DIR/parser-recovery-2.rs:18:5
|
LL | fn bar() {
| - unclosed delimiter
| - un-closed delimiter
LL | let x = foo(); //~ ERROR cannot find function `foo` in this scope
LL | ) //~ ERROR incorrect close delimiter: `)`
| ^ incorrect close delimiter
Expand Down
32 changes: 32 additions & 0 deletions src/test/ui/parser/unclosed-braces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct S {
x: [usize; 3],
}

fn foo() {
{
{
println!("hi");
}
}
}

fn main() {
//~^ NOTE un-closed delimiter
{
{
//~^ NOTE this delimiter might not be properly closed...
foo();
}
//~^ NOTE ...as it matches this but it has different indentation
}
//~ ERROR this file contains an un-closed delimiter
17 changes: 17 additions & 0 deletions src/test/ui/parser/unclosed-braces.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: this file contains an un-closed delimiter
--> $DIR/unclosed-braces.rs:32:53
|
LL | fn main() {
| - un-closed delimiter
...
LL | {
| - this delimiter might not be properly closed...
...
LL | }
| - ...as it matches this but it has different indentation
...
LL | //~ ERROR this file contains an un-closed delimiter
| ^

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/resolve/token-error-correct-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: incorrect close delimiter: `)`
--> $DIR/token-error-correct-2.rs:16:5
|
LL | if foo {
| - unclosed delimiter
| - un-closed delimiter
LL | //~^ ERROR: cannot find value `foo`
LL | ) //~ ERROR: incorrect close delimiter: `)`
| ^ incorrect close delimiter
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/resolve/token-error-correct-3.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
error: incorrect close delimiter: `}`
--> $DIR/token-error-correct-3.rs:30:9
|
LL | if !is_directory(path.as_ref()) { //~ ERROR: cannot find function `is_directory`
| - close delimiter possibly meant for this
LL | callback(path.as_ref(); //~ ERROR expected one of
| - unclosed delimiter
| - un-closed delimiter
...
LL | } else { //~ ERROR: incorrect close delimiter: `}`
| ^ incorrect close delimiter
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/resolve/token-error-correct.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
error: incorrect close delimiter: `}`
--> $DIR/token-error-correct.rs:16:1
|
LL | fn main() {
| - close delimiter possibly meant for this
LL | foo(bar(;
| - unclosed delimiter
| - un-closed delimiter
LL | //~^ ERROR: expected expression, found `;`
LL | }
| ^ incorrect close delimiter
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/token/issue-10636-2.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
error: incorrect close delimiter: `}`
--> $DIR/issue-10636-2.rs:18:1
|
LL | pub fn trace_option(option: Option<isize>) {
| - close delimiter possibly meant for this
LL | option.map(|some| 42;
| - unclosed delimiter
| - un-closed delimiter
...
LL | } //~ ERROR: incorrect close delimiter
| ^ incorrect close delimiter
Expand Down