From 35874069679cf394e8a93bd5764ce13f9696096b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 23 Apr 2022 19:44:25 -0700 Subject: [PATCH] Better handle too many `#` recovery in raw str Point at all the unnecessary trailing `#`. Better handle interaction with outer attributes when `;` is missing. Fix #95030. --- .../rustc_parse/src/parser/diagnostics.rs | 43 +++++++++++++++---- src/test/ui/parser/raw/raw-str-unbalanced.rs | 20 ++++++++- .../ui/parser/raw/raw-str-unbalanced.stderr | 36 +++++++++++++--- 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ed2640451705b..acc0d7a6ee05e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -431,10 +431,11 @@ impl<'a> Parser<'a> { return Ok(true); } else if self.look_ahead(0, |t| { t == &token::CloseDelim(token::Brace) - || ( - t.can_begin_expr() && t != &token::Semi && t != &token::Pound - // Avoid triggering with too many trailing `#` in raw string. - ) + || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound) + // Avoid triggering with too many trailing `#` in raw string. + || (sm.is_multiline( + self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()) + ) && t == &token::Pound) }) { // Missing semicolon typo. This is triggered if the next token could either start a // new statement or is a block close. For example: @@ -508,7 +509,12 @@ impl<'a> Parser<'a> { } if self.check_too_many_raw_str_terminators(&mut err) { - return Err(err); + if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) { + err.emit(); + return Ok(true); + } else { + return Err(err); + } } if self.prev_token.span == DUMMY_SP { @@ -538,6 +544,7 @@ impl<'a> Parser<'a> { } fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool { + let sm = self.sess.source_map(); match (&self.prev_token.kind, &self.token.kind) { ( TokenKind::Literal(Lit { @@ -545,15 +552,33 @@ impl<'a> Parser<'a> { .. }), TokenKind::Pound, - ) => { + ) if !sm.is_multiline( + self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()), + ) => + { + let n_hashes: u8 = *n_hashes; err.set_primary_message("too many `#` when terminating raw string"); + let str_span = self.prev_token.span; + let mut span = self.token.span; + let mut count = 0; + while self.token.kind == TokenKind::Pound + && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo())) + { + span = span.with_hi(self.token.span.hi()); + self.bump(); + count += 1; + } + err.set_span(span); err.span_suggestion( - self.token.span, - "remove the extra `#`", + span, + &format!("remove the extra `#`{}", pluralize!(count)), String::new(), Applicability::MachineApplicable, ); - err.note(&format!("the raw string started with {n_hashes} `#`s")); + err.span_label( + str_span, + &format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)), + ); true } _ => false, diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.rs b/src/test/ui/parser/raw/raw-str-unbalanced.rs index 35f118f5ce6ee..38537f8b31e64 100644 --- a/src/test/ui/parser/raw/raw-str-unbalanced.rs +++ b/src/test/ui/parser/raw/raw-str-unbalanced.rs @@ -1,4 +1,22 @@ static s: &'static str = + r#""## //~ ERROR too many `#` when terminating raw string +; + +static s2: &'static str = r#" - "## //~ too many `#` when terminating raw string + "#### //~ ERROR too many `#` when terminating raw string ; + +const A: &'static str = r"" //~ ERROR expected `;`, found `#` + +// Test +#[test] +fn test() {} + +const B: &'static str = r""## //~ ERROR too many `#` when terminating raw string + +// Test +#[test] +fn test2() {} + +fn main() {} diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.stderr b/src/test/ui/parser/raw/raw-str-unbalanced.stderr index bf8f3a7a5a4bd..eac8c06c1df5c 100644 --- a/src/test/ui/parser/raw/raw-str-unbalanced.stderr +++ b/src/test/ui/parser/raw/raw-str-unbalanced.stderr @@ -1,10 +1,36 @@ error: too many `#` when terminating raw string - --> $DIR/raw-str-unbalanced.rs:3:9 + --> $DIR/raw-str-unbalanced.rs:2:10 | -LL | "## - | ^ help: remove the extra `#` +LL | r#""## + | -----^ help: remove the extra `#` + | | + | this raw string started with 1 `#` + +error: too many `#` when terminating raw string + --> $DIR/raw-str-unbalanced.rs:7:9 + | +LL | / r#" +LL | | "#### + | | -^^^ help: remove the extra `#`s + | |________| + | this raw string started with 1 `#` + +error: expected `;`, found `#` + --> $DIR/raw-str-unbalanced.rs:10:28 + | +LL | const A: &'static str = r"" + | ^ help: add `;` here +... +LL | #[test] + | - unexpected token + +error: too many `#` when terminating raw string + --> $DIR/raw-str-unbalanced.rs:16:28 | - = note: the raw string started with 1 `#`s +LL | const B: &'static str = r""## + | ---^^ help: remove the extra `#`s + | | + | this raw string started with 0 `#`s -error: aborting due to previous error +error: aborting due to 4 previous errors