mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
also add recover from over parsing for let
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::{
|
||||
attr, {self as ast},
|
||||
};
|
||||
@@ -2722,8 +2721,13 @@ fn parse_fn_body(
|
||||
*sig_hi = self.prev_token.span;
|
||||
(AttrVec::new(), None)
|
||||
} else if self.check(exp!(OpenBrace)) || self.token.is_metavar_block() {
|
||||
self.parse_block_common(self.token.span, BlockCheckMode::Default, None)
|
||||
.map(|(attrs, body)| (attrs, Some(body)))?
|
||||
let prev_in_fn_body = self.in_fn_body;
|
||||
self.in_fn_body = true;
|
||||
let res = self
|
||||
.parse_block_common(self.token.span, BlockCheckMode::Default, None)
|
||||
.map(|(attrs, body)| (attrs, Some(body)));
|
||||
self.in_fn_body = prev_in_fn_body;
|
||||
res?
|
||||
} else if self.token == token::Eq {
|
||||
// Recover `fn foo() = $expr;`.
|
||||
self.bump(); // `=`
|
||||
@@ -3494,26 +3498,11 @@ fn try_recover_const_missing_semi(&mut self, rhs: &Option<ConstItemRhs>) -> Opti
|
||||
let Some(ConstItemRhs::Body(rhs)) = rhs else {
|
||||
return None;
|
||||
};
|
||||
if !self.may_recover() || rhs.span.from_expansion() {
|
||||
if !self.in_fn_body || !self.may_recover() || rhs.span.from_expansion() {
|
||||
return None;
|
||||
}
|
||||
let sm = self.psess.source_map();
|
||||
// Check if this is a binary expression that spans multiple lines
|
||||
// and the RHS looks like it could be an independent expression.
|
||||
if let ExprKind::Binary(op, lhs, rhs_inner) = &rhs.kind
|
||||
&& sm.is_multiline(lhs.span.shrink_to_hi().until(rhs_inner.span.shrink_to_lo()))
|
||||
&& matches!(op.node, BinOpKind::Mul | BinOpKind::BitAnd)
|
||||
&& classify::expr_requires_semi_to_be_stmt(rhs_inner)
|
||||
{
|
||||
let lhs_end_span = lhs.span.shrink_to_hi();
|
||||
let guar = self.dcx().emit_err(errors::ExpectedSemi {
|
||||
span: lhs_end_span,
|
||||
token: self.token,
|
||||
unexpected_token_label: Some(self.token.span),
|
||||
sugg: errors::ExpectedSemiSugg::AddSemi(lhs_end_span),
|
||||
});
|
||||
|
||||
Some(self.mk_expr(lhs.span, ExprKind::Err(guar)))
|
||||
if let Some((span, guar)) = self.missing_semi_from_binop("const", rhs) {
|
||||
Some(self.mk_expr(span, ExprKind::Err(guar)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -32,10 +32,11 @@
|
||||
ParserRange, ParserReplacement, Spacing, TokenCursor, TokenStream, TokenTree, TokenTreeCursor,
|
||||
};
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
|
||||
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation, Mutability,
|
||||
Recovered, Safety, StrLit, Visibility, VisibilityKind,
|
||||
self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, BlockCheckMode, ByRef, Const,
|
||||
CoroutineKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens,
|
||||
MgcaDisambiguation, Mutability, Recovered, Safety, StrLit, Visibility, VisibilityKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::debug_assert_matches;
|
||||
@@ -43,7 +44,7 @@
|
||||
use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult};
|
||||
use rustc_index::interval::IntervalSet;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::ThinVec;
|
||||
use token_type::TokenTypeSet;
|
||||
pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType};
|
||||
@@ -232,6 +233,8 @@ pub struct Parser<'a> {
|
||||
/// Whether the parser is allowed to do recovery.
|
||||
/// This is disabled when parsing macro arguments, see #103534
|
||||
recovery: Recovery = Recovery::Allowed,
|
||||
/// Whether we're parsing a function body.
|
||||
in_fn_body: bool = false,
|
||||
}
|
||||
|
||||
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with
|
||||
@@ -1638,6 +1641,62 @@ pub fn prev_token_uninterpolated_span(&self) -> Span {
|
||||
_ => self.prev_token.span,
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_semi_from_binop(
|
||||
&self,
|
||||
kind_desc: &str,
|
||||
expr: &Expr,
|
||||
) -> Option<(Span, ErrorGuaranteed)> {
|
||||
if self.token == TokenKind::Semi {
|
||||
return None;
|
||||
}
|
||||
if !self.may_recover() || expr.span.from_expansion() {
|
||||
return None;
|
||||
}
|
||||
let sm = self.psess.source_map();
|
||||
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind
|
||||
&& sm.is_multiline(lhs.span.shrink_to_hi().until(rhs.span.shrink_to_lo()))
|
||||
&& matches!(op.node, BinOpKind::Mul | BinOpKind::BitAnd)
|
||||
&& classify::expr_requires_semi_to_be_stmt(rhs)
|
||||
{
|
||||
let lhs_end_span = lhs.span.shrink_to_hi();
|
||||
let token_str = token_descr(&self.token);
|
||||
let mut err = self
|
||||
.dcx()
|
||||
.struct_span_err(lhs_end_span, format!("expected `;`, found {token_str}"));
|
||||
err.span_label(self.token.span, "unexpected token");
|
||||
|
||||
let continuation_span = lhs_end_span.until(rhs.span.shrink_to_hi());
|
||||
err.span_label(
|
||||
continuation_span,
|
||||
format!(
|
||||
"to finish parsing this {kind_desc}, expected this to be followed by a `;`",
|
||||
),
|
||||
);
|
||||
let op_desc = match op.node {
|
||||
BinOpKind::BitAnd => "a bit-and",
|
||||
BinOpKind::Mul => "a multiplication",
|
||||
_ => "a binary",
|
||||
};
|
||||
let mut note_spans = MultiSpan::new();
|
||||
note_spans.push_span_label(lhs.span, "parsed as the left-hand expression");
|
||||
note_spans.push_span_label(rhs.span, "parsed as the right-hand expression");
|
||||
note_spans.push_span_label(op.span, format!("this was parsed as {op_desc}"));
|
||||
err.span_note(
|
||||
note_spans,
|
||||
format!("the {kind_desc} was parsed as having {op_desc} binary expression"),
|
||||
);
|
||||
|
||||
err.span_suggestion(
|
||||
lhs_end_span,
|
||||
format!("you may have meant to write a `;` to terminate the {kind_desc} earlier"),
|
||||
";",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return Some((lhs.span, err.emit()));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Metavar captures of various kinds.
|
||||
|
||||
@@ -924,6 +924,19 @@ fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_recover_let_missing_semi(&mut self, local: &mut Local) -> Option<ErrorGuaranteed> {
|
||||
let expr = match &mut local.kind {
|
||||
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => expr,
|
||||
LocalKind::Decl => return None,
|
||||
};
|
||||
if let Some((span, guar)) = self.missing_semi_from_binop("`let` binding", expr) {
|
||||
*expr = self.mk_expr(span, ExprKind::Err(guar));
|
||||
return Some(guar);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Parses a statement, including the trailing semicolon.
|
||||
pub fn parse_full_stmt(
|
||||
&mut self,
|
||||
@@ -1066,71 +1079,74 @@ pub fn parse_full_stmt(
|
||||
}
|
||||
}
|
||||
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
|
||||
StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
|
||||
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
|
||||
match &mut local.kind {
|
||||
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
|
||||
self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
|
||||
|mut e| {
|
||||
self.recover_missing_dot(&mut e);
|
||||
self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
|
||||
e
|
||||
},
|
||||
)?;
|
||||
// We found `foo<bar, baz>`, have we fully recovered?
|
||||
self.expect_semi()?;
|
||||
}
|
||||
LocalKind::Decl => {
|
||||
if let Some(colon_sp) = local.colon_sp {
|
||||
e.span_label(
|
||||
colon_sp,
|
||||
format!(
|
||||
"while parsing the type for {}",
|
||||
local.pat.descr().map_or_else(
|
||||
|| "the binding".to_string(),
|
||||
|n| format!("`{n}`")
|
||||
)
|
||||
),
|
||||
);
|
||||
let suggest_eq = if self.token == token::Dot
|
||||
&& let _ = self.bump()
|
||||
&& let mut snapshot = self.create_snapshot_for_diagnostic()
|
||||
&& let Ok(_) = snapshot
|
||||
.parse_dot_suffix_expr(
|
||||
colon_sp,
|
||||
self.mk_expr_err(
|
||||
colon_sp,
|
||||
self.dcx()
|
||||
.delayed_bug("error during `:` -> `=` recovery"),
|
||||
),
|
||||
)
|
||||
.map_err(Diag::cancel)
|
||||
{
|
||||
true
|
||||
} else if let Some(op) = self.check_assoc_op()
|
||||
&& op.node.can_continue_expr_unambiguously()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if suggest_eq {
|
||||
e.span_suggestion_short(
|
||||
colon_sp,
|
||||
"use `=` if you meant to assign",
|
||||
"=",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
StmtKind::Let(local) => {
|
||||
if self.try_recover_let_missing_semi(local).is_some() {
|
||||
return Ok(Some(stmt));
|
||||
}
|
||||
if let Err(mut e) = self.expect_semi() {
|
||||
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
|
||||
match &mut local.kind {
|
||||
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
|
||||
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
|
||||
.map_err(|mut e| {
|
||||
self.recover_missing_dot(&mut e);
|
||||
self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
|
||||
e
|
||||
})?;
|
||||
// We found `foo<bar, baz>`, have we fully recovered?
|
||||
self.expect_semi()?;
|
||||
}
|
||||
LocalKind::Decl => {
|
||||
if let Some(colon_sp) = local.colon_sp {
|
||||
e.span_label(
|
||||
colon_sp,
|
||||
format!(
|
||||
"while parsing the type for {}",
|
||||
local.pat.descr().map_or_else(
|
||||
|| "the binding".to_string(),
|
||||
|n| format!("`{n}`")
|
||||
)
|
||||
),
|
||||
);
|
||||
let suggest_eq = if self.token == token::Dot
|
||||
&& let _ = self.bump()
|
||||
&& let mut snapshot = self.create_snapshot_for_diagnostic()
|
||||
&& let Ok(_) = snapshot
|
||||
.parse_dot_suffix_expr(
|
||||
colon_sp,
|
||||
self.mk_expr_err(
|
||||
colon_sp,
|
||||
self.dcx().delayed_bug(
|
||||
"error during `:` -> `=` recovery",
|
||||
),
|
||||
),
|
||||
)
|
||||
.map_err(Diag::cancel)
|
||||
{
|
||||
true
|
||||
} else if let Some(op) = self.check_assoc_op()
|
||||
&& op.node.can_continue_expr_unambiguously()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if suggest_eq {
|
||||
e.span_suggestion_short(
|
||||
colon_sp,
|
||||
"use `=` if you meant to assign",
|
||||
"=",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
eat_semi = false;
|
||||
}
|
||||
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
|
||||
eat_semi = false
|
||||
}
|
||||
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false,
|
||||
}
|
||||
|
||||
if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:16:38
|
||||
|
|
||||
LL | const C: u8 = u8::const_default()
|
||||
| ^ help: add `;` here
|
||||
LL | &C
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:22:9
|
||||
|
|
||||
LL | + 2
|
||||
| ^ help: add `;` here
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:27:13
|
||||
|
|
||||
LL | + val()
|
||||
| ^ help: add `;` here
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:15:19
|
||||
|
|
||||
LL | const fn foo() -> &'static u8 {
|
||||
| --- ^^^^^^^^^^^ expected `&u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:20:19
|
||||
|
|
||||
LL | const fn bar() -> u8 {
|
||||
| --- ^^ expected `u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:25:19
|
||||
|
|
||||
LL | const fn baz() -> u8 {
|
||||
| --- ^^ expected `u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
+10
-2
@@ -12,6 +12,9 @@ const fn val() -> u8 {
|
||||
42
|
||||
}
|
||||
|
||||
const C: u8 = u8::const_default()
|
||||
&1 //~ ERROR expected `;`, found keyword `const`
|
||||
|
||||
const fn foo() -> &'static u8 { //~ ERROR mismatched types
|
||||
const C: u8 = u8::const_default() //~ ERROR expected `;`
|
||||
&C
|
||||
@@ -19,12 +22,17 @@ const fn foo() -> &'static u8 { //~ ERROR mismatched types
|
||||
|
||||
const fn bar() -> u8 { //~ ERROR mismatched types
|
||||
const C: u8 = 1
|
||||
+ 2 //~ ERROR expected `;`
|
||||
+ 2 //~ ERROR expected `;`, found `}`
|
||||
}
|
||||
|
||||
const fn baz() -> u8 { //~ ERROR mismatched types
|
||||
const C: u8 = 1
|
||||
+ val() //~ ERROR expected `;`
|
||||
+ val() //~ ERROR expected `;`, found `}`
|
||||
}
|
||||
|
||||
fn buzz() {
|
||||
let r = 1 //~ ERROR expected `;`
|
||||
&r
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,100 @@
|
||||
error: expected `;`, found keyword `const`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:16:3
|
||||
|
|
||||
LL | &1
|
||||
| ^ help: add `;` here
|
||||
LL |
|
||||
LL | const fn foo() -> &'static u8 {
|
||||
| ----- unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:19:38
|
||||
|
|
||||
LL | const C: u8 = u8::const_default()
|
||||
| ______________________________________^
|
||||
LL | | &C
|
||||
| |______- to finish parsing this const, expected this to be followed by a `;`
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
|
||||
note: the const was parsed as having a bit-and binary expression
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:19:19
|
||||
|
|
||||
LL | const C: u8 = u8::const_default()
|
||||
| ------------------- parsed as the left-hand expression
|
||||
LL | &C
|
||||
| -- parsed as the right-hand expression
|
||||
| |
|
||||
| this was parsed as a bit-and
|
||||
help: you may have meant to write a `;` to terminate the const earlier
|
||||
|
|
||||
LL | const C: u8 = u8::const_default();
|
||||
| +
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:25:9
|
||||
|
|
||||
LL | + 2
|
||||
| ^ help: add `;` here
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:30:13
|
||||
|
|
||||
LL | + val()
|
||||
| ^ help: add `;` here
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:34:14
|
||||
|
|
||||
LL | let r = 1
|
||||
| ______________^
|
||||
LL | | &r
|
||||
| |______- to finish parsing this `let` binding, expected this to be followed by a `;`
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
|
||||
note: the `let` binding was parsed as having a bit-and binary expression
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:34:13
|
||||
|
|
||||
LL | let r = 1
|
||||
| - parsed as the left-hand expression
|
||||
LL | &r
|
||||
| -- parsed as the right-hand expression
|
||||
| |
|
||||
| this was parsed as a bit-and
|
||||
help: you may have meant to write a `;` to terminate the `let` binding earlier
|
||||
|
|
||||
LL | let r = 1;
|
||||
| +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:18:19
|
||||
|
|
||||
LL | const fn foo() -> &'static u8 {
|
||||
| --- ^^^^^^^^^^^ expected `&u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:23:19
|
||||
|
|
||||
LL | const fn bar() -> u8 {
|
||||
| --- ^^ expected `u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/const-recover-semi-issue-151149.rs:28:19
|
||||
|
|
||||
LL | const fn baz() -> u8 {
|
||||
| --- ^^ expected `u8`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
Reference in New Issue
Block a user