Rollup merge of #87436 - ebobrow:suggest-semicolon, r=oli-obk
Suggest `;` on parse error where applicable fixes #87197
This commit is contained in:
commit
4205077537
6 changed files with 118 additions and 64 deletions
|
@ -242,6 +242,63 @@ impl<'a> Parser<'a> {
|
|||
expected.sort_by_cached_key(|x| x.to_string());
|
||||
expected.dedup();
|
||||
|
||||
let sm = self.sess.source_map();
|
||||
let msg = format!("expected `;`, found {}", super::token_descr(&self.token));
|
||||
let appl = Applicability::MachineApplicable;
|
||||
if expected.contains(&TokenType::Token(token::Semi)) {
|
||||
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
|
||||
// Likely inside a macro, can't provide meaningful suggestions.
|
||||
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
|
||||
// The current token is in the same line as the prior token, not recoverable.
|
||||
} else if [token::Comma, token::Colon].contains(&self.token.kind)
|
||||
&& self.prev_token.kind == token::CloseDelim(token::Paren)
|
||||
{
|
||||
// Likely typo: The current token is on a new line and is expected to be
|
||||
// `.`, `;`, `?`, or an operator after a close delimiter token.
|
||||
//
|
||||
// let a = std::process::Command::new("echo")
|
||||
// .arg("1")
|
||||
// ,arg("2")
|
||||
// ^
|
||||
// https://github.com/rust-lang/rust/issues/72253
|
||||
} else if self.look_ahead(1, |t| {
|
||||
t == &token::CloseDelim(token::Brace)
|
||||
|| t.can_begin_expr() && t.kind != token::Colon
|
||||
}) && [token::Comma, token::Colon].contains(&self.token.kind)
|
||||
{
|
||||
// Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
|
||||
// either `,` or `:`, and the next token could either start a new statement or is a
|
||||
// block close. For example:
|
||||
//
|
||||
// let x = 32:
|
||||
// let y = 42;
|
||||
self.bump();
|
||||
let sp = self.prev_token.span;
|
||||
self.struct_span_err(sp, &msg)
|
||||
.span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl)
|
||||
.emit();
|
||||
return Ok(false);
|
||||
} 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.
|
||||
)
|
||||
}) {
|
||||
// Missing semicolon typo. This is triggered if the next token could either start a
|
||||
// new statement or is a block close. For example:
|
||||
//
|
||||
// let x = 32
|
||||
// let y = 42;
|
||||
let sp = self.prev_token.span.shrink_to_hi();
|
||||
self.struct_span_err(sp, &msg)
|
||||
.span_label(self.token.span, "unexpected token")
|
||||
.span_suggestion_short(sp, "add `;` here", ";".to_string(), appl)
|
||||
.emit();
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
let expect = tokens_to_string(&expected[..]);
|
||||
let actual = super::token_descr(&self.token);
|
||||
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
|
||||
|
@ -303,7 +360,6 @@ impl<'a> Parser<'a> {
|
|||
return Err(err);
|
||||
}
|
||||
|
||||
let sm = self.sess.source_map();
|
||||
if self.prev_token.span == DUMMY_SP {
|
||||
// Account for macro context where the previous span might not be
|
||||
// available to avoid incorrect output (#54841).
|
||||
|
@ -1144,62 +1200,6 @@ impl<'a> Parser<'a> {
|
|||
if self.eat(&token::Semi) {
|
||||
return Ok(());
|
||||
}
|
||||
let sm = self.sess.source_map();
|
||||
let msg = format!("expected `;`, found {}", super::token_descr(&self.token));
|
||||
let appl = Applicability::MachineApplicable;
|
||||
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
|
||||
// Likely inside a macro, can't provide meaningful suggestions.
|
||||
return self.expect(&token::Semi).map(drop);
|
||||
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
|
||||
// The current token is in the same line as the prior token, not recoverable.
|
||||
} else if [token::Comma, token::Colon].contains(&self.token.kind)
|
||||
&& self.prev_token.kind == token::CloseDelim(token::Paren)
|
||||
{
|
||||
// Likely typo: The current token is on a new line and is expected to be
|
||||
// `.`, `;`, `?`, or an operator after a close delimiter token.
|
||||
//
|
||||
// let a = std::process::Command::new("echo")
|
||||
// .arg("1")
|
||||
// ,arg("2")
|
||||
// ^
|
||||
// https://github.com/rust-lang/rust/issues/72253
|
||||
self.expect(&token::Semi)?;
|
||||
return Ok(());
|
||||
} else if self.look_ahead(1, |t| {
|
||||
t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon
|
||||
}) && [token::Comma, token::Colon].contains(&self.token.kind)
|
||||
{
|
||||
// Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
|
||||
// either `,` or `:`, and the next token could either start a new statement or is a
|
||||
// block close. For example:
|
||||
//
|
||||
// let x = 32:
|
||||
// let y = 42;
|
||||
self.bump();
|
||||
let sp = self.prev_token.span;
|
||||
self.struct_span_err(sp, &msg)
|
||||
.span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl)
|
||||
.emit();
|
||||
return Ok(());
|
||||
} 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.
|
||||
)
|
||||
}) {
|
||||
// Missing semicolon typo. This is triggered if the next token could either start a
|
||||
// new statement or is a block close. For example:
|
||||
//
|
||||
// let x = 32
|
||||
// let y = 42;
|
||||
let sp = self.prev_token.span.shrink_to_hi();
|
||||
self.struct_span_err(sp, &msg)
|
||||
.span_label(self.token.span, "unexpected token")
|
||||
.span_suggestion_short(sp, "add `;` here", ";".to_string(), appl)
|
||||
.emit();
|
||||
return Ok(());
|
||||
}
|
||||
self.expect(&token::Semi).map(drop) // Error unconditionally
|
||||
}
|
||||
|
||||
|
|
10
src/test/ui/parser/issue-87197-missing-semicolon.fixed
Normal file
10
src/test/ui/parser/issue-87197-missing-semicolon.fixed
Normal file
|
@ -0,0 +1,10 @@
|
|||
// run-rustfix
|
||||
// Parser should know when a semicolon is missing.
|
||||
// https://github.com/rust-lang/rust/issues/87197
|
||||
|
||||
fn main() {
|
||||
let x = 100; //~ ERROR: expected `;`
|
||||
println!("{}", x); //~ ERROR: expected `;`
|
||||
let y = 200; //~ ERROR: expected `;`
|
||||
println!("{}", y);
|
||||
}
|
10
src/test/ui/parser/issue-87197-missing-semicolon.rs
Normal file
10
src/test/ui/parser/issue-87197-missing-semicolon.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// run-rustfix
|
||||
// Parser should know when a semicolon is missing.
|
||||
// https://github.com/rust-lang/rust/issues/87197
|
||||
|
||||
fn main() {
|
||||
let x = 100 //~ ERROR: expected `;`
|
||||
println!("{}", x) //~ ERROR: expected `;`
|
||||
let y = 200 //~ ERROR: expected `;`
|
||||
println!("{}", y);
|
||||
}
|
26
src/test/ui/parser/issue-87197-missing-semicolon.stderr
Normal file
26
src/test/ui/parser/issue-87197-missing-semicolon.stderr
Normal file
|
@ -0,0 +1,26 @@
|
|||
error: expected `;`, found `println`
|
||||
--> $DIR/issue-87197-missing-semicolon.rs:6:16
|
||||
|
|
||||
LL | let x = 100
|
||||
| ^ help: add `;` here
|
||||
LL | println!("{}", x)
|
||||
| ------- unexpected token
|
||||
|
||||
error: expected `;`, found keyword `let`
|
||||
--> $DIR/issue-87197-missing-semicolon.rs:7:22
|
||||
|
|
||||
LL | println!("{}", x)
|
||||
| ^ help: add `;` here
|
||||
LL | let y = 200
|
||||
| --- unexpected token
|
||||
|
||||
error: expected `;`, found `println`
|
||||
--> $DIR/issue-87197-missing-semicolon.rs:8:16
|
||||
|
|
||||
LL | let y = 200
|
||||
| ^ help: add `;` here
|
||||
LL | println!("{}", y);
|
||||
| ------- unexpected token
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fn main() {
|
||||
assert_eq!(1, 2)
|
||||
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
|
||||
assert_eq!(1, 2) //~ ERROR: expected `;`
|
||||
assert_eq!(3, 4) //~ ERROR: expected `;`
|
||||
println!("hello");
|
||||
}
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
|
||||
--> $DIR/macros-no-semicolon.rs:3:5
|
||||
error: expected `;`, found `assert_eq`
|
||||
--> $DIR/macros-no-semicolon.rs:2:21
|
||||
|
|
||||
LL | assert_eq!(1, 2)
|
||||
| - expected one of `.`, `;`, `?`, `}`, or an operator
|
||||
| ^ help: add `;` here
|
||||
LL | assert_eq!(3, 4)
|
||||
| ^^^^^^^^^ unexpected token
|
||||
| --------- unexpected token
|
||||
|
||||
error: aborting due to previous error
|
||||
error: expected `;`, found `println`
|
||||
--> $DIR/macros-no-semicolon.rs:3:21
|
||||
|
|
||||
LL | assert_eq!(3, 4)
|
||||
| ^ help: add `;` here
|
||||
LL | println!("hello");
|
||||
| ------- unexpected token
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue