fix code to suggest ;
on parse error
This commit is contained in:
parent
b543e0dc03
commit
e0995a5a8d
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.sort_by_cached_key(|x| x.to_string());
|
||||||
expected.dedup();
|
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 expect = tokens_to_string(&expected[..]);
|
||||||
let actual = super::token_descr(&self.token);
|
let actual = super::token_descr(&self.token);
|
||||||
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
|
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
|
||||||
|
@ -303,7 +360,6 @@ impl<'a> Parser<'a> {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sm = self.sess.source_map();
|
|
||||||
if self.prev_token.span == DUMMY_SP {
|
if self.prev_token.span == DUMMY_SP {
|
||||||
// Account for macro context where the previous span might not be
|
// Account for macro context where the previous span might not be
|
||||||
// available to avoid incorrect output (#54841).
|
// available to avoid incorrect output (#54841).
|
||||||
|
@ -1144,62 +1200,6 @@ impl<'a> Parser<'a> {
|
||||||
if self.eat(&token::Semi) {
|
if self.eat(&token::Semi) {
|
||||||
return Ok(());
|
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
|
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() {
|
fn main() {
|
||||||
assert_eq!(1, 2)
|
assert_eq!(1, 2) //~ ERROR: expected `;`
|
||||||
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
|
assert_eq!(3, 4) //~ ERROR: expected `;`
|
||||||
println!("hello");
|
println!("hello");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
|
error: expected `;`, found `assert_eq`
|
||||||
--> $DIR/macros-no-semicolon.rs:3:5
|
--> $DIR/macros-no-semicolon.rs:2:21
|
||||||
|
|
|
|
||||||
LL | assert_eq!(1, 2)
|
LL | assert_eq!(1, 2)
|
||||||
| - expected one of `.`, `;`, `?`, `}`, or an operator
|
| ^ help: add `;` here
|
||||||
LL | assert_eq!(3, 4)
|
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
Add a link
Reference in a new issue