mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 14:31:55 +00:00
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.
This commit is contained in:
parent
5238b523c3
commit
008aa5a24e
@ -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>,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
|
@ -52,6 +52,20 @@ impl<'a> StringReader<'a> {
|
||||
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 might be the culprit...");
|
||||
err.span_label(
|
||||
*close_sp,
|
||||
"...as it matches this but it has different indentation",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
},
|
||||
token::OpenDelim(delim) => {
|
||||
@ -70,11 +84,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();
|
||||
}
|
||||
@ -96,6 +119,19 @@ impl<'a> StringReader<'a> {
|
||||
if let Some(&(_, sp)) = self.open_braces.last() {
|
||||
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();
|
||||
|
@ -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);
|
||||
@ -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())
|
||||
|
@ -3,6 +3,11 @@ error: this file contains an un-closed delimiter
|
||||
|
|
||||
LL | trait Foo {
|
||||
| - un-closed delimiter
|
||||
LL | fn bar() {
|
||||
| - this might be the culprit...
|
||||
...
|
||||
LL | }
|
||||
| - ...as it matches this but it has different indentation
|
||||
...
|
||||
LL | } //~ ERROR this file contains an un-closed delimiter
|
||||
| ^
|
||||
|
32
src/test/ui/parser/unclosed-braces.rs
Normal file
32
src/test/ui/parser/unclosed-braces.rs
Normal file
@ -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 might be the culprit...
|
||||
foo();
|
||||
}
|
||||
//~^ NOTE ...as it matches this but it has different indentation
|
||||
}
|
||||
//~ ERROR this file contains an un-closed delimiter
|
17
src/test/ui/parser/unclosed-braces.stderr
Normal file
17
src/test/ui/parser/unclosed-braces.stderr
Normal file
@ -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 might be the culprit...
|
||||
...
|
||||
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
|
||||
|
@ -1,6 +1,8 @@
|
||||
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
|
||||
| - un-closed delimiter
|
||||
...
|
||||
|
@ -1,6 +1,8 @@
|
||||
error: incorrect close delimiter: `}`
|
||||
--> $DIR/token-error-correct.rs:16:1
|
||||
|
|
||||
LL | fn main() {
|
||||
| - close delimiter possibly meant for this
|
||||
LL | foo(bar(;
|
||||
| - un-closed delimiter
|
||||
LL | //~^ ERROR: expected expression, found `;`
|
||||
|
@ -1,6 +1,8 @@
|
||||
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;
|
||||
| - un-closed delimiter
|
||||
...
|
||||
|
Loading…
Reference in New Issue
Block a user