From c4d00d710c86aa062f2e08fc90a14345f55a8b4b Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Jan 2023 10:52:57 +0800 Subject: [PATCH 1/2] add testcase for #104012 --- tests/ui/parser/deli-ident-issue-1.rs | 25 +++++++++++++ tests/ui/parser/deli-ident-issue-1.stderr | 37 +++++++++++++++++++ tests/ui/parser/deli-ident-issue-2.rs | 7 ++++ tests/ui/parser/deli-ident-issue-2.stderr | 21 +++++++++++ .../ui/parser/issue-68987-unmatch-issue-1.rs | 12 ++++++ .../parser/issue-68987-unmatch-issue-1.stderr | 11 ++++++ .../ui/parser/issue-68987-unmatch-issue-2.rs | 14 +++++++ .../parser/issue-68987-unmatch-issue-2.stderr | 21 +++++++++++ .../ui/parser/issue-68987-unmatch-issue-3.rs | 8 ++++ .../parser/issue-68987-unmatch-issue-3.stderr | 21 +++++++++++ tests/ui/parser/issue-68987-unmatch-issue.rs | 12 ++++++ .../parser/issue-68987-unmatch-issue.stderr | 11 ++++++ tests/ui/parser/issues/issue-69259.rs | 3 ++ tests/ui/parser/issues/issue-69259.stderr | 13 +++++++ 14 files changed, 216 insertions(+) create mode 100644 tests/ui/parser/deli-ident-issue-1.rs create mode 100644 tests/ui/parser/deli-ident-issue-1.stderr create mode 100644 tests/ui/parser/deli-ident-issue-2.rs create mode 100644 tests/ui/parser/deli-ident-issue-2.stderr create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-1.rs create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-1.stderr create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-2.rs create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-2.stderr create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-3.rs create mode 100644 tests/ui/parser/issue-68987-unmatch-issue-3.stderr create mode 100644 tests/ui/parser/issue-68987-unmatch-issue.rs create mode 100644 tests/ui/parser/issue-68987-unmatch-issue.stderr create mode 100644 tests/ui/parser/issues/issue-69259.rs create mode 100644 tests/ui/parser/issues/issue-69259.stderr diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs new file mode 100644 index 0000000000000..a8cbf8640f7a3 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -0,0 +1,25 @@ +// 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 diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr new file mode 100644 index 0000000000000..784f598f9d05c --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -0,0 +1,37 @@ +error: this file contains an unclosed delimiter + --> $DIR/deli-ident-issue-1.rs:25:66 + | +LL | impl dyn Demo { + | - unclosed delimiter +... +LL | c: u32| { + | - this delimiter might not be properly closed... +LL | a + b + c +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:18:17 + | +LL | && b == c { + | ^ not a struct, variant or union type + +error[E0308]: mismatched types + --> $DIR/deli-ident-issue-1.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/tests/ui/parser/deli-ident-issue-2.rs b/tests/ui/parser/deli-ident-issue-2.rs new file mode 100644 index 0000000000000..5394760df7026 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-2.rs @@ -0,0 +1,7 @@ +fn main() { + if 1 < 2 { + let _a = vec!]; //~ ERROR mismatched closing delimiter + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr new file mode 100644 index 0000000000000..782dac8f32d35 --- /dev/null +++ b/tests/ui/parser/deli-ident-issue-2.stderr @@ -0,0 +1,21 @@ +error: unexpected closing delimiter: `}` + --> $DIR/deli-ident-issue-2.rs:5:1 + | +LL | fn main() { + | - this opening brace... +... +LL | } + | - ...matches this closing brace +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `]` + --> $DIR/deli-ident-issue-2.rs:2:14 + | +LL | if 1 < 2 { + | ^ unclosed delimiter +LL | let _a = vec!]; + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.rs b/tests/ui/parser/issue-68987-unmatch-issue-1.rs new file mode 100644 index 0000000000000..30e7ef467368a --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.rs @@ -0,0 +1,12 @@ +// This file has unexpected closing delimiter, + +fn func(o: Option) { + match o { + Some(_x) => {} // Extra '}' + let _ = if true {}; + } + None => {} + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr new file mode 100644 index 0000000000000..b5c1887f76f08 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr @@ -0,0 +1,11 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-1.rs:10:1 + | +LL | None => {} + | -- block is empty, you might have not meant to close it +LL | } +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.rs b/tests/ui/parser/issue-68987-unmatch-issue-2.rs new file mode 100644 index 0000000000000..89aaa68ba4095 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.rs @@ -0,0 +1,14 @@ +// FIXME: this case need more work to fix +// currently the TokenTree matching ')' with '{', which is not user friendly for diagnostics +async fn obstest() -> Result<> { + let obs_connect = || -> Result<(), MyError) { //~ ERROR mismatched closing delimiter + async { + } + } + + if let Ok(version, scene_list) = obs_connect() { + + } else { + + } +} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr new file mode 100644 index 0000000000000..253f12d44b83f --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr @@ -0,0 +1,21 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-2.rs:14:1 + | +LL | } else { + | - this opening brace... +LL | +LL | } + | - ...matches this closing brace +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-2.rs:3:32 + | +LL | async fn obstest() -> Result<> { + | ^ unclosed delimiter +LL | let obs_connect = || -> Result<(), MyError) { + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.rs b/tests/ui/parser/issue-68987-unmatch-issue-3.rs new file mode 100644 index 0000000000000..e98df8d7c3c4e --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.rs @@ -0,0 +1,8 @@ +// the `{` is closed with `)`, there is a missing `(` +fn f(i: u32, j: u32) { + let res = String::new(); + let mut cnt = i; + while cnt < j { + write!&mut res, " "); //~ ERROR mismatched closing delimiter + } +} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr new file mode 100644 index 0000000000000..258a692c8557b --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr @@ -0,0 +1,21 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue-3.rs:8:1 + | +LL | fn f(i: u32, j: u32) { + | - this opening brace... +... +LL | } + | - ...matches this closing brace +LL | } + | ^ unexpected closing delimiter + +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-3.rs:5:19 + | +LL | while cnt < j { + | ^ unclosed delimiter +LL | write!&mut res, " "); + | ^ mismatched closing delimiter + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue.rs b/tests/ui/parser/issue-68987-unmatch-issue.rs new file mode 100644 index 0000000000000..5a3620bf24bd4 --- /dev/null +++ b/tests/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) => // Missing '{' + let _ = if true {}; + } + None => {} + } +} //~ ERROR unexpected closing delimiter + +fn main() {} diff --git a/tests/ui/parser/issue-68987-unmatch-issue.stderr b/tests/ui/parser/issue-68987-unmatch-issue.stderr new file mode 100644 index 0000000000000..00e9c9a5ccee0 --- /dev/null +++ b/tests/ui/parser/issue-68987-unmatch-issue.stderr @@ -0,0 +1,11 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-68987-unmatch-issue.rs:10:1 + | +LL | None => {} + | -- block is empty, you might have not meant to close it +LL | } +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issues/issue-69259.rs b/tests/ui/parser/issues/issue-69259.rs new file mode 100644 index 0000000000000..01fc2c0854647 --- /dev/null +++ b/tests/ui/parser/issues/issue-69259.rs @@ -0,0 +1,3 @@ +fn main() {} + +fn f) {} //~ ERROR unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-69259.stderr b/tests/ui/parser/issues/issue-69259.stderr new file mode 100644 index 0000000000000..f4cb391cb1ff2 --- /dev/null +++ b/tests/ui/parser/issues/issue-69259.stderr @@ -0,0 +1,13 @@ +error: unexpected closing delimiter: `)` + --> $DIR/issue-69259.rs:3:5 + | +LL | fn main() {} + | -- ...matches this closing brace + | | + | this opening brace... +LL | +LL | fn f) {} + | ^ unexpected closing delimiter + +error: aborting due to previous error + From cd233231aa215b7a5642e6a80869959b375e0725 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Jan 2023 11:02:19 +0800 Subject: [PATCH 2/2] Improve unexpected close and mismatch delimiter hint in TokenTreesReader --- compiler/rustc_parse/src/lexer/diagnostics.rs | 119 ++++++++++++++++ compiler/rustc_parse/src/lexer/mod.rs | 1 + compiler/rustc_parse/src/lexer/tokentrees.rs | 129 ++++++------------ tests/ui/parser/deli-ident-issue-1.rs | 1 - tests/ui/parser/deli-ident-issue-1.stderr | 16 +-- tests/ui/parser/deli-ident-issue-2.stderr | 6 +- .../parser/issue-68987-unmatch-issue-1.stderr | 11 +- .../parser/issue-68987-unmatch-issue-2.stderr | 8 +- .../parser/issue-68987-unmatch-issue-3.stderr | 6 +- .../parser/issue-68987-unmatch-issue.stderr | 11 +- tests/ui/parser/issue-81827.stderr | 10 +- tests/ui/parser/issues/issue-62973.stderr | 12 +- tests/ui/parser/issues/issue-63116.stderr | 5 +- tests/ui/parser/issues/issue-69259.stderr | 5 - .../issue-70583-block-is-empty-1.stderr | 4 +- .../issue-70583-block-is-empty-2.stderr | 6 +- .../macro-mismatched-delim-paren-brace.stderr | 4 +- tests/ui/typeck/issue-91334.stderr | 10 +- 18 files changed, 225 insertions(+), 139 deletions(-) create mode 100644 compiler/rustc_parse/src/lexer/diagnostics.rs diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs new file mode 100644 index 0000000000000..386bf026bb4af --- /dev/null +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -0,0 +1,119 @@ +use super::UnmatchedBrace; +use rustc_ast::token::Delimiter; +use rustc_errors::Diagnostic; +use rustc_span::source_map::SourceMap; +use rustc_span::Span; + +#[derive(Default)] +pub struct TokenTreeDiagInfo { + /// Stack of open delimiters and their spans. Used for error message. + pub open_braces: Vec<(Delimiter, Span)>, + pub unmatched_braces: Vec, + + /// Used only for error recovery when arriving to EOF with mismatched braces. + pub last_unclosed_found_span: Option, + + /// Collect empty block spans that might have been auto-inserted by editors. + pub empty_block_spans: Vec, + + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + pub matching_block_spans: Vec<(Span, Span)>, +} + +pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool { + match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) { + (Some(open_padding), Some(close_padding)) => open_padding == close_padding, + _ => false, + } +} + +// When we get a `)` or `]` for `{`, we should emit help message here +// it's more friendly compared to report `unmatched error` in later phase +pub fn report_missing_open_delim( + err: &mut Diagnostic, + unmatched_braces: &[UnmatchedBrace], +) -> bool { + let mut reported_missing_open = false; + for unmatch_brace in unmatched_braces.iter() { + if let Some(delim) = unmatch_brace.found_delim + && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket) + { + let missed_open = match delim { + Delimiter::Parenthesis => "(", + Delimiter::Bracket => "[", + _ => unreachable!(), + }; + err.span_label( + unmatch_brace.found_span.shrink_to_lo(), + format!("missing open `{}` for this delimiter", missed_open), + ); + reported_missing_open = true; + } + } + reported_missing_open +} + +pub fn report_suspicious_mismatch_block( + err: &mut Diagnostic, + diag_info: &TokenTreeDiagInfo, + sm: &SourceMap, + delim: Delimiter, +) { + if report_missing_open_delim(err, &diag_info.unmatched_braces) { + return; + } + + let mut matched_spans: Vec<(Span, bool)> = diag_info + .matching_block_spans + .iter() + .map(|&(open, close)| (open.with_hi(close.lo()), same_identation_level(sm, open, close))) + .collect(); + + // 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 inner mismatched 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, inner_same_ident) = matched_spans[j]; + if block_span.contains(inner_block) && !inner_same_ident { + matched_spans[j] = (inner_block, true); + } + } + } + } + + // Find the inner-most span candidate for final report + let candidate_span = + matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span); + + 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", + ); + + // If there is a empty block in the mismatched span, note it + if delim == Delimiter::Brace { + for span in diag_info.empty_block_spans.iter() { + if block_span.contains(*span) { + err.span_label(*span, "block is empty, you might have not meant to close it"); + break; + } + } + } + } else { + // If there is no suspicious span, give the last properly closed block may help + if let Some(parent) = diag_info.matching_block_spans.last() + && diag_info.open_braces.last().is_none() + && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { + err.span_label(parent.0, "this opening brace..."); + err.span_label(parent.1, "...matches this closing brace"); + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 9fe8d9836ba60..e957224a03377 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -17,6 +17,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{edition::Edition, BytePos, Pos, Span}; +mod diagnostics; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b2701817d489b..0de8f79112c65 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,29 +1,18 @@ +use super::diagnostics::report_suspicious_mismatch_block; +use super::diagnostics::same_identation_level; +use super::diagnostics::TokenTreeDiagInfo; use super::{StringReader, UnmatchedBrace}; 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_span::Span; pub(super) struct TokenTreesReader<'a> { string_reader: StringReader<'a>, /// The "next" token, which has been obtained from the `StringReader` but /// not yet handled by the `TokenTreesReader`. token: Token, - /// Stack of open delimiters and their spans. Used for error message. - open_braces: Vec<(Delimiter, Span)>, - unmatched_braces: Vec, - /// The type and spans for all braces - /// - /// Used only for error recovery when arriving to EOF with mismatched braces. - matching_delim_spans: Vec<(Delimiter, Span, Span)>, - last_unclosed_found_span: Option, - /// Collect empty block spans that might have been auto-inserted by editors. - last_delim_empty_block_spans: FxHashMap, - /// Collect the spans of braces (Open, Close). Used only - /// for detecting if blocks are empty and only braces. - matching_block_spans: Vec<(Span, Span)>, + diag_info: TokenTreeDiagInfo, } impl<'a> TokenTreesReader<'a> { @@ -33,15 +22,10 @@ impl<'a> TokenTreesReader<'a> { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), - open_braces: Vec::new(), - unmatched_braces: Vec::new(), - matching_delim_spans: Vec::new(), - last_unclosed_found_span: None, - last_delim_empty_block_spans: FxHashMap::default(), - matching_block_spans: Vec::new(), + diag_info: TokenTreeDiagInfo::default(), }; let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.unmatched_braces) + (res, tt_reader.diag_info.unmatched_braces) } // Parse a stream of tokens into a list of `TokenTree`s. @@ -92,9 +76,9 @@ impl<'a> TokenTreesReader<'a> { fn eof_err(&mut self) -> PErr<'a> { let msg = "this file contains an unclosed delimiter"; let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); - for &(_, sp) in &self.open_braces { + for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); - self.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, @@ -103,23 +87,13 @@ 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"); - } + if let Some((delim, _)) = self.diag_info.open_braces.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + *delim, + ) } err } @@ -128,7 +102,7 @@ impl<'a> TokenTreesReader<'a> { // The span for beginning of the delimited section let pre_span = self.token.span; - self.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_braces.push((open_delim, self.token.span)); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -137,35 +111,29 @@ impl<'a> TokenTreesReader<'a> { // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); + let sm = self.string_reader.sess.source_map(); match self.token.kind { // Correct delimiter. token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.open_braces.pop().unwrap(); + let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); let close_brace_span = self.token.span; - if tts.is_empty() { + if tts.is_empty() && close_delim == Delimiter::Brace { let empty_block_span = open_brace_span.to(close_brace_span); - let sm = self.string_reader.sess.source_map(); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. - self.last_delim_empty_block_spans.insert(open_delim, empty_block_span); + self.diag_info.empty_block_spans.push(empty_block_span); } } - //only add braces + // only add braces if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { - self.matching_block_spans.push((open_brace_span, close_brace_span)); + // Add all the matching spans, we will sort by span later + self.diag_info.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 { - 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; } @@ -174,28 +142,25 @@ impl<'a> TokenTreesReader<'a> { let mut unclosed_delimiter = None; let mut candidate = None; - if self.last_unclosed_found_span != Some(self.token.span) { + if self.diag_info.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times - self.last_unclosed_found_span = Some(self.token.span); + self.diag_info.last_unclosed_found_span = Some(self.token.span); // This is a conservative error: only report the last unclosed // 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() { + if let Some(&(_, sp)) = self.diag_info.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.diag_info.open_braces { + if same_identation_level(&sm, 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(); - self.unmatched_braces.push(UnmatchedBrace { + let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, @@ -203,7 +168,7 @@ impl<'a> TokenTreesReader<'a> { candidate_span: candidate, }); } else { - self.open_braces.pop(); + self.diag_info.open_braces.pop(); } // If the incorrect delimiter matches an earlier opening @@ -213,7 +178,7 @@ impl<'a> TokenTreesReader<'a> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { self.token = self.string_reader.next_token().0; } } @@ -236,22 +201,12 @@ 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"); - } - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } - + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + delim, + ); err.span_label(self.token.span, "unexpected closing delimiter"); err } diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs index a8cbf8640f7a3..54485262a0cc8 100644 --- a/tests/ui/parser/deli-ident-issue-1.rs +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -1,4 +1,3 @@ -// error-pattern: this file contains an unclosed delimiter #![feature(let_chains)] trait Demo {} diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr index 784f598f9d05c..1119edb199f05 100644 --- a/tests/ui/parser/deli-ident-issue-1.stderr +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -1,26 +1,26 @@ error: this file contains an unclosed delimiter - --> $DIR/deli-ident-issue-1.rs:25:66 + --> $DIR/deli-ident-issue-1.rs:24:66 | LL | impl dyn Demo { | - unclosed delimiter ... -LL | c: u32| { - | - this delimiter might not be properly closed... -LL | a + b + c -LL | }; - | - ...as it matches this but it has different indentation +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:18:17 + --> $DIR/deli-ident-issue-1.rs:17:17 | LL | && b == c { | ^ not a struct, variant or union type error[E0308]: mismatched types - --> $DIR/deli-ident-issue-1.rs:18:9 + --> $DIR/deli-ident-issue-1.rs:17:9 | LL | fn check(&self, val: Option, num: Option) { | - expected `()` because of default return type diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr index 782dac8f32d35..c8f59c9d32b5f 100644 --- a/tests/ui/parser/deli-ident-issue-2.stderr +++ b/tests/ui/parser/deli-ident-issue-2.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/deli-ident-issue-2.rs:5:1 | -LL | fn main() { - | - this opening brace... -... +LL | let _a = vec!]; + | - missing open `[` for this delimiter LL | } - | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr index b5c1887f76f08..2d873b46193ce 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr @@ -1,9 +1,14 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-1.rs:10:1 | -LL | None => {} - | -- block is empty, you might have not meant to close it -LL | } +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => {} // Extra '}' + | -- block is empty, you might have not meant to close it +LL | let _ = if true {}; +LL | } + | - ...as it matches this but it has different indentation +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr index 253f12d44b83f..2c08d41a15f1b 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-2.rs:14:1 | -LL | } else { - | - this opening brace... -LL | -LL | } - | - ...matches this closing brace +LL | let obs_connect = || -> Result<(), MyError) { + | - missing open `(` for this delimiter +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr index 258a692c8557b..a3fc46a1e883c 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-3.rs:8:1 | -LL | fn f(i: u32, j: u32) { - | - this opening brace... -... +LL | write!&mut res, " "); + | - missing open `(` for this delimiter LL | } - | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue.stderr b/tests/ui/parser/issue-68987-unmatch-issue.stderr index 00e9c9a5ccee0..cabd133242f60 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue.stderr @@ -1,9 +1,14 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue.rs:10:1 | -LL | None => {} - | -- block is empty, you might have not meant to close it -LL | } +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => // Missing '{' +LL | let _ = if true {}; + | -- block is empty, you might have not meant to close it +LL | } + | - ...as it matches this but it has different indentation +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-81827.stderr b/tests/ui/parser/issue-81827.stderr index 069de33919494..867244b72e849 100644 --- a/tests/ui/parser/issue-81827.stderr +++ b/tests/ui/parser/issue-81827.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr index 4737bc71860c2..3cb6d75a6754b 100644 --- a/tests/ui/parser/issues/issue-62973.stderr +++ b/tests/ui/parser/issues/issue-62973.stderr @@ -2,8 +2,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | @@ -13,8 +15,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | diff --git a/tests/ui/parser/issues/issue-63116.stderr b/tests/ui/parser/issues/issue-63116.stderr index cfdd99d1434ae..a1f8a77ffa7c6 100644 --- a/tests/ui/parser/issues/issue-63116.stderr +++ b/tests/ui/parser/issues/issue-63116.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-63116.rs:3:18 | LL | impl W $DIR/issue-69259.rs:3:5 | -LL | fn main() {} - | -- ...matches this closing brace - | | - | this opening brace... -LL | LL | fn f) {} | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr index 39bf113ef83de..46cbb056d1d88 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr +++ b/tests/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/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr index 5d37b216427f6..9ae94c701869b 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr +++ b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr @@ -1,8 +1,12 @@ 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 + | | + | block is empty, you might have not meant to close it ... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr index 967a3e6fdc11b..689ce1eb6b704 100644 --- a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/tests/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/tests/ui/typeck/issue-91334.stderr b/tests/ui/typeck/issue-91334.stderr index 8508f7a38e239..78f392c9a8acc 100644 --- a/tests/ui/typeck/issue-91334.stderr +++ b/tests/ui/typeck/issue-91334.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter