diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b2701817d489..7691ba76cc7b 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{PErr, PResult}; +use rustc_errors::{Diagnostic, PErr, PResult}; use rustc_span::Span; pub(super) struct TokenTreesReader<'a> { @@ -104,22 +104,7 @@ impl<'a> TokenTreesReader<'a> { } if let Some((delim, _)) = self.open_braces.last() { - if let Some((_, open_sp, close_sp)) = - self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| { - let sm = self.string_reader.sess.source_map(); - if let Some(close_padding) = sm.span_to_margin(*close_sp) { - if let Some(open_padding) = sm.span_to_margin(*open_sp) { - return delim == d && close_padding != open_padding; - } - } - false - }) - // these are in reverse order as they get inserted on close, but - { - // we want the last open/first close - 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"); - } + self.report_error_prone_delim_block(*delim, &mut err); } err } @@ -157,15 +142,11 @@ impl<'a> TokenTreesReader<'a> { //only add braces if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { self.matching_block_spans.push((open_brace_span, close_brace_span)); - } - if self.open_braces.is_empty() { - // Clear up these spans to avoid suggesting them as we've found - // properly matched delimiters so far for an entire block. - self.matching_delim_spans.clear(); - } else { + // Add all the matching spans, we will sort by span later self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span)); } + // Move past the closing delimiter. self.token = self.string_reader.next_token().0; } @@ -183,15 +164,12 @@ impl<'a> TokenTreesReader<'a> { if let Some(&(_, sp)) = self.open_braces.last() { unclosed_delimiter = Some(sp); }; - let sm = self.string_reader.sess.source_map(); - if let Some(current_padding) = sm.span_to_margin(self.token.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 == &close_delim { - candidate = Some(*brace_span); - } - } + for (brace, brace_span) in &self.open_braces { + if self.same_identation_level(self.token.span, *brace_span) + && brace == &close_delim + { + // high likelihood of these two corresponding + candidate = Some(*brace_span); } } let (tok, _) = self.open_braces.pop().unwrap(); @@ -236,23 +214,68 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - // Braces are added at the end, so the last element is the biggest block - if let Some(parent) = self.matching_block_spans.last() { - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - // Check if the (empty block) is in the last properly closed block - if (parent.0.to(parent.1)).contains(span) { - err.span_label(span, "block is empty, you might have not meant to close it"); - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); + self.report_error_prone_delim_block(delim, &mut err); + err.span_label(self.token.span, "unexpected closing delimiter"); + err + } + + fn same_identation_level(&self, open_sp: Span, close_sp: Span) -> bool { + let sm = self.string_reader.sess.source_map(); + if let (Some(open_padding), Some(close_padding)) = + (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) + { + open_padding == close_padding + } else { + false + } + } + + fn report_error_prone_delim_block(&self, delim: Delimiter, err: &mut Diagnostic) { + let mut matched_spans = vec![]; + let mut candidate_span = None; + + for &(d, open_sp, close_sp) in &self.matching_delim_spans { + if d == delim { + let block_span = open_sp.with_hi(close_sp.lo()); + let same_ident = self.same_identation_level(open_sp, close_sp); + matched_spans.push((block_span, same_ident)); + } + } + + // sort by `lo`, so the large block spans in the front + matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo())); + + // We use larger block whose identation is well to cover those innert blocks + // O(N^2) here, but we are on error reporting path, so it is fine + for i in 0..matched_spans.len() { + let (block_span, same_ident) = matched_spans[i]; + if same_ident { + for j in i + 1..matched_spans.len() { + let (inner_block, innert_same_ident) = matched_spans[j]; + if block_span.contains(inner_block) && !innert_same_ident { + matched_spans[j] = (inner_block, true); + } } - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); } } - err.span_label(self.token.span, "unexpected closing delimiter"); - err + // Find the innermost span candidate for final report + for (block_span, same_ident) in matched_spans.into_iter().rev() { + if !same_ident { + candidate_span = Some(block_span); + break; + } + } + + if let Some(block_span) = candidate_span { + err.span_label( + block_span.shrink_to_lo(), + "this delimiter might not be properly closed...", + ); + err.span_label( + block_span.shrink_to_hi(), + "...as it matches this but it has different indentation", + ); + } } } diff --git a/src/test/ui/parser/deli-ident-issue-1.rs b/src/test/ui/parser/deli-ident-issue-1.rs new file mode 100644 index 000000000000..73a69382bb86 --- /dev/null +++ b/src/test/ui/parser/deli-ident-issue-1.rs @@ -0,0 +1,27 @@ +// ignore-tidy-trailing-newlines +// +// error-pattern: this file contains an unclosed delimiter +#![feature(let_chains)] +trait Demo {} + +impl dyn Demo { + pub fn report(&self) -> u32 { + let sum = |a: u32, + b: u32, + c: u32| { + a + b + c + }; + sum(1, 2, 3) + } + + fn check(&self, val: Option, num: Option) { + if let Some(b) = val + && let Some(c) = num { + && b == c { + //~^ ERROR expected struct + //~| ERROR mismatched types + } + } +} + +fn main() { } //~ ERROR this file contains an unclosed delimiter \ No newline at end of file diff --git a/src/test/ui/parser/deli-ident-issue-1.stderr b/src/test/ui/parser/deli-ident-issue-1.stderr new file mode 100644 index 000000000000..bdc458482366 --- /dev/null +++ b/src/test/ui/parser/deli-ident-issue-1.stderr @@ -0,0 +1,37 @@ +error: this file contains an unclosed delimiter + --> $DIR/deli-ident-issue-1.rs:27:65 + | +LL | impl dyn Demo { + | - unclosed delimiter +... +LL | && let Some(c) = num { + | - this delimiter might not be properly closed... +... +LL | } + | - ...as it matches this but it has different indentation +... +LL | fn main() { } + | ^ + +error[E0574]: expected struct, variant or union type, found local variable `c` + --> $DIR/deli-ident-issue-1.rs:20:17 + | +LL | && b == c { + | ^ not a struct, variant or union type + +error[E0308]: mismatched types + --> $DIR/deli-ident-issue-1.rs:20:9 + | +LL | fn check(&self, val: Option, num: Option) { + | - expected `()` because of default return type +... +LL | / && b == c { +LL | | +LL | | +LL | | } + | |_________^ expected `()`, found `bool` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0574. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/deli-ident-issue-2.rs b/src/test/ui/parser/deli-ident-issue-2.rs new file mode 100644 index 000000000000..ba3c4c5c8af7 --- /dev/null +++ b/src/test/ui/parser/deli-ident-issue-2.rs @@ -0,0 +1,25 @@ +// ignore-tidy-trailing-newlines +// +// error-pattern: this file contains an unclosed delimiter +#![feature(let_chains)] +trait Demo {} + +impl dyn Demo { + pub fn report(&self, + a: u32, + b: u32, + c: u32) -> u32 { + return a + b + c; + } + + fn check(&self, val: Option, num: Option) { + if let Some(b) = val + && let Some(c) = num { + && b == c { + //~^ ERROR expected struct + //~| ERROR mismatched types + } + } +} + +fn main() { } //~ ERROR this file contains an unclosed delimiter \ No newline at end of file diff --git a/src/test/ui/parser/deli-ident-issue-2.stderr b/src/test/ui/parser/deli-ident-issue-2.stderr new file mode 100644 index 000000000000..38a8ff666f2b --- /dev/null +++ b/src/test/ui/parser/deli-ident-issue-2.stderr @@ -0,0 +1,37 @@ +error: this file contains an unclosed delimiter + --> $DIR/deli-ident-issue-2.rs:25:65 + | +LL | impl dyn Demo { + | - unclosed delimiter +... +LL | && let Some(c) = num { + | - this delimiter might not be properly closed... +... +LL | } + | - ...as it matches this but it has different indentation +... +LL | fn main() { } + | ^ + +error[E0574]: expected struct, variant or union type, found local variable `c` + --> $DIR/deli-ident-issue-2.rs:18:17 + | +LL | && b == c { + | ^ not a struct, variant or union type + +error[E0308]: mismatched types + --> $DIR/deli-ident-issue-2.rs:18:9 + | +LL | fn check(&self, val: Option, num: Option) { + | - expected `()` because of default return type +... +LL | / && b == c { +LL | | +LL | | +LL | | } + | |_________^ expected `()`, found `bool` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0574. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/issue-68987-unmatch-issue.rs b/src/test/ui/parser/issue-68987-unmatch-issue.rs new file mode 100644 index 000000000000..5a7bc8827bea --- /dev/null +++ b/src/test/ui/parser/issue-68987-unmatch-issue.rs @@ -0,0 +1,12 @@ +// This file has unexpected closing delimiter, + +fn func(o: Option) { + match o { + Some(_x) => + let _ = if true {}; + } + None => {} + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/src/test/ui/parser/issue-68987-unmatch-issue.stderr b/src/test/ui/parser/issue-68987-unmatch-issue.stderr new file mode 100644 index 000000000000..b5dae57c0503 --- /dev/null +++ b/src/test/ui/parser/issue-68987-unmatch-issue.stderr @@ -0,0 +1,14 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue.rs:10:1 + | +LL | match o { + | - this delimiter might not be properly closed... +... +LL | } + | - ...as it matches this but it has different indentation +... +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issues/issue-2354.rs b/src/test/ui/parser/issues/issue-2354.rs index c422040cbe30..d06b94356a7e 100644 --- a/src/test/ui/parser/issues/issue-2354.rs +++ b/src/test/ui/parser/issues/issue-2354.rs @@ -1,10 +1,8 @@ fn foo() { //~ NOTE unclosed delimiter - match Some(10) { - //~^ NOTE this delimiter might not be properly closed... + match Some(10) { //~ NOTE this delimiter might not be properly closed Some(y) => { panic!(); } None => { panic!(); } -} -//~^ NOTE ...as it matches this but it has different indentation +} //~ NOTE ...as it matches this but it has different indentation fn bar() { let mut i = 0; diff --git a/src/test/ui/parser/issues/issue-2354.stderr b/src/test/ui/parser/issues/issue-2354.stderr index b89ed3958357..611264ed71a5 100644 --- a/src/test/ui/parser/issues/issue-2354.stderr +++ b/src/test/ui/parser/issues/issue-2354.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-2354.rs:15:52 + --> $DIR/issue-2354.rs:13:52 | LL | fn foo() { | - unclosed delimiter diff --git a/src/test/ui/parser/issues/issue-70583-block-is-empty-1.stderr b/src/test/ui/parser/issues/issue-70583-block-is-empty-1.stderr index 39bf113ef83d..46cbb056d1d8 100644 --- a/src/test/ui/parser/issues/issue-70583-block-is-empty-1.stderr +++ b/src/test/ui/parser/issues/issue-70583-block-is-empty-1.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-1.rs:20:1 | LL | fn struct_generic(x: Vec) { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/issues/issue-70583-block-is-empty-2.stderr b/src/test/ui/parser/issues/issue-70583-block-is-empty-2.stderr index 5d37b216427f..bb3a4d8a6bb4 100644 --- a/src/test/ui/parser/issues/issue-70583-block-is-empty-2.stderr +++ b/src/test/ui/parser/issues/issue-70583-block-is-empty-2.stderr @@ -1,8 +1,10 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-2.rs:14:1 | +LL | match self { + | - this delimiter might not be properly closed... LL | ErrorHandled::Reported => {}} - | -- block is empty, you might have not meant to close it + | - ...as it matches this but it has different indentation ... LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr index 967a3e6fdc11..689ce1eb6b70 100644 --- a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | LL | fn main() { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr index 165eb8ae9328..f1be5dc5ba7f 100644 --- a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr +++ b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr @@ -1,12 +1,6 @@ error: unexpected closing delimiter: `}` --> $DIR/mismatched-delim-brace-empty-block.rs:5:1 | -LL | fn main() { - | - this opening brace... -LL | -LL | } - | - ...matches this closing brace -LL | let _ = (); LL | } | ^ unexpected closing delimiter