From 4a0f8d517529cdaca6750966536052b5104b05be Mon Sep 17 00:00:00 2001 From: rainy-me Date: Thu, 14 Apr 2022 03:22:02 +0900 Subject: [PATCH] improve diagnostics for unterminated nested block comment --- compiler/rustc_parse/src/lexer/mod.rs | 63 ++++++++++++++++--- src/test/ui/unterminated-nested-comment.rs | 4 ++ .../ui/unterminated-nested-comment.stderr | 21 +++++++ 3 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/unterminated-nested-comment.rs create mode 100644 src/test/ui/unterminated-nested-comment.stderr diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 5ab412dc777de..96513958eb06b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -182,16 +182,7 @@ impl<'a> StringReader<'a> { } rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { if !terminated { - let msg = match doc_style { - Some(_) => "unterminated block doc-comment", - None => "unterminated block comment", - }; - let last_bpos = self.pos; - self.sess.span_diagnostic.span_fatal_with_code( - self.mk_sp(start, last_bpos), - msg, - error_code!(E0758), - ); + self.report_unterminated_block_comment(start, doc_style); } // Skip non-doc comments @@ -553,6 +544,58 @@ impl<'a> StringReader<'a> { err.emit() } + fn report_unterminated_block_comment(&self, start: BytePos, doc_style: Option) { + let msg = match doc_style { + Some(_) => "unterminated block doc-comment", + None => "unterminated block comment", + }; + let last_bpos = self.pos; + let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code( + self.mk_sp(start, last_bpos), + msg, + error_code!(E0758), + ); + let mut nested_block_comment_open_idxs = vec![]; + let mut last_nested_block_comment_idxs = None; + let mut content_chars = self.str_from(start).char_indices(); + + if let Some((_, mut last_char)) = content_chars.next() { + while let Some((idx, c)) = content_chars.next() { + match c { + '*' if last_char == '/' => { + nested_block_comment_open_idxs.push(idx); + } + '/' if last_char == '*' => { + last_nested_block_comment_idxs = + nested_block_comment_open_idxs.pop().map(|open_idx| (open_idx, idx)); + } + _ => {} + }; + last_char = c; + } + } + + if let Some((nested_open_idx, nested_close_idx)) = last_nested_block_comment_idxs { + err.span_label(self.mk_sp(start, start + BytePos(2)), msg) + .span_label( + self.mk_sp( + start + BytePos(nested_open_idx as u32 - 1), + start + BytePos(nested_open_idx as u32 + 1), + ), + "...as last nested comment starts here, maybe you want to close this instead?", + ) + .span_label( + self.mk_sp( + start + BytePos(nested_close_idx as u32 - 1), + start + BytePos(nested_close_idx as u32 + 1), + ), + "...and last nested comment terminates here", + ); + } + + err.emit(); + } + // RFC 3101 introduced the idea of (reserved) prefixes. As of Rust 2021, // using a (unknown) prefix is an error. In earlier editions, however, they // only result in a (allowed by default) lint, and are treated as regular diff --git a/src/test/ui/unterminated-nested-comment.rs b/src/test/ui/unterminated-nested-comment.rs new file mode 100644 index 0000000000000..db5f2f3ba1358 --- /dev/null +++ b/src/test/ui/unterminated-nested-comment.rs @@ -0,0 +1,4 @@ +/* //~ ERROR E0758 +/* */ +/* +*/ diff --git a/src/test/ui/unterminated-nested-comment.stderr b/src/test/ui/unterminated-nested-comment.stderr new file mode 100644 index 0000000000000..eda8f2dcd2469 --- /dev/null +++ b/src/test/ui/unterminated-nested-comment.stderr @@ -0,0 +1,21 @@ +error[E0758]: unterminated block comment + --> $DIR/unterminated-nested-comment.rs:1:1 + | +LL | /* + | ^- + | | + | _unterminated block comment + | | +LL | | /* */ +LL | | /* + | | -- + | | | + | | ...as last nested comment starts here, maybe you want to close this instead? +LL | | */ + | |_--^ + | | + | ...and last nested comment terminates here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0758`.