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.
This commit is contained in:
Esteban Küber 2022-04-23 19:44:25 -07:00
parent 1e9aa8a96b
commit 3587406967
3 changed files with 85 additions and 16 deletions

View File

@ -431,10 +431,11 @@ impl<'a> Parser<'a> {
return Ok(true); return Ok(true);
} else if self.look_ahead(0, |t| { } else if self.look_ahead(0, |t| {
t == &token::CloseDelim(token::Brace) t == &token::CloseDelim(token::Brace)
|| ( || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound)
t.can_begin_expr() && t != &token::Semi && t != &token::Pound // Avoid triggering with too many trailing `#` in raw string.
// 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 // Missing semicolon typo. This is triggered if the next token could either start a
// new statement or is a block close. For example: // 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) { 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 { 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 { 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) { match (&self.prev_token.kind, &self.token.kind) {
( (
TokenKind::Literal(Lit { TokenKind::Literal(Lit {
@ -545,15 +552,33 @@ impl<'a> Parser<'a> {
.. ..
}), }),
TokenKind::Pound, 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"); 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( err.span_suggestion(
self.token.span, span,
"remove the extra `#`", &format!("remove the extra `#`{}", pluralize!(count)),
String::new(), String::new(),
Applicability::MachineApplicable, 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 true
} }
_ => false, _ => false,

View File

@ -1,4 +1,22 @@
static s: &'static str = static s: &'static str =
r#" r#""## //~ ERROR too many `#` when terminating raw string
"## //~ too many `#` when terminating raw string
; ;
static s2: &'static str =
r#"
"#### //~ 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() {}

View File

@ -1,10 +1,36 @@
error: too many `#` when terminating raw string error: too many `#` when terminating raw string
--> $DIR/raw-str-unbalanced.rs:3:9 --> $DIR/raw-str-unbalanced.rs:2:10
| |
LL | "## LL | r#""##
| ^ help: remove the extra `#` | -----^ help: remove the extra `#`
| |
| this raw string started with 1 `#`
error: too many `#` when terminating raw string
--> $DIR/raw-str-unbalanced.rs:7:9
| |
= note: the raw string started with 1 `#`s LL | / r#"
LL | | "####
| | -^^^ help: remove the extra `#`s
| |________|
| this raw string started with 1 `#`
error: aborting due to previous error 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
|
LL | const B: &'static str = r""##
| ---^^ help: remove the extra `#`s
| |
| this raw string started with 0 `#`s
error: aborting due to 4 previous errors