Skip to content

Commit 0851ad6

Browse files
committed
Try to recover from over-parsing in const item when a semicolon is missing
1 parent 9f6cd6d commit 0851ad6

4 files changed

Lines changed: 128 additions & 1 deletion

File tree

compiler/rustc_parse/src/errors.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,6 @@ pub(crate) struct HelpIdentifierStartsWithNumber {
12841284
pub(crate) struct ExpectedSemi {
12851285
pub span: Span,
12861286
pub token: Token,
1287-
12881287
pub unexpected_token_label: Option<Span>,
12891288
pub sugg: ExpectedSemiSugg,
12901289
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_ast::ast::*;
66
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
77
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
88
use rustc_ast::util::case::Case;
9+
use rustc_ast::util::classify;
910
use rustc_ast::{
1011
attr, {self as ast},
1112
};
@@ -1571,6 +1572,9 @@ impl<'a> Parser<'a> {
15711572

15721573
generics.where_clause = where_clause;
15731574

1575+
if let Some(recovered_rhs) = self.try_recover_const_missing_semi(&rhs) {
1576+
return Ok((ident, generics, ty, Some(ConstItemRhs::Body(recovered_rhs))));
1577+
}
15741578
self.expect_semi()?;
15751579

15761580
Ok((ident, generics, ty, rhs))
@@ -3407,6 +3411,48 @@ impl<'a> Parser<'a> {
34073411
Ok(Some(_))
34083412
)
34093413
}
3414+
3415+
/// Try to recover from over-parsing in const item when a semicolon is missing.
3416+
///
3417+
/// This detects cases where we parsed too much because a semicolon was missing
3418+
/// and the next line started an expression that the parser treated as a continuation
3419+
/// (e.g., `foo() \n &bar` was parsed as `foo() & bar`).
3420+
///
3421+
/// Returns a corrected expression if recovery is successful.
3422+
fn try_recover_const_missing_semi(&mut self, rhs: &Option<ConstItemRhs>) -> Option<Box<Expr>> {
3423+
if self.token == TokenKind::Semi {
3424+
return None;
3425+
}
3426+
let Some(ConstItemRhs::Body(rhs)) = rhs else {
3427+
return None;
3428+
};
3429+
if !self.may_recover() || rhs.span.from_expansion() {
3430+
return None;
3431+
}
3432+
let sm = self.psess.source_map();
3433+
let is_multiline =
3434+
|sp1: Span, sp2: Span| sm.is_multiline(sp1.shrink_to_hi().until(sp2.shrink_to_lo()));
3435+
3436+
// Check if this is a binary expression that spans multiple lines
3437+
// and the RHS looks like it could be an independent expression.
3438+
if let ExprKind::Binary(op, lhs, rhs_inner) = &rhs.kind
3439+
&& is_multiline(lhs.span, rhs_inner.span)
3440+
&& matches!(op.node, |BinOpKind::Mul| BinOpKind::BitAnd)
3441+
&& classify::expr_requires_semi_to_be_stmt(rhs_inner)
3442+
{
3443+
let lhs_end_span = lhs.span.shrink_to_hi();
3444+
let guar = self.dcx().emit_err(errors::ExpectedSemi {
3445+
span: lhs_end_span,
3446+
token: self.token,
3447+
unexpected_token_label: Some(self.token.span),
3448+
sugg: errors::ExpectedSemiSugg::AddSemi(lhs_end_span),
3449+
});
3450+
3451+
Some(self.mk_expr(lhs.span, ExprKind::Err(guar)))
3452+
} else {
3453+
None
3454+
}
3455+
}
34103456
}
34113457

34123458
enum IsMacroRulesItem {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![feature(const_trait_impl)]
2+
3+
const trait ConstDefault {
4+
fn const_default() -> Self;
5+
}
6+
7+
impl const ConstDefault for u8 {
8+
fn const_default() -> Self { 0 }
9+
}
10+
11+
const fn val() -> u8 {
12+
42
13+
}
14+
15+
const fn foo() -> &'static u8 { //~ ERROR mismatched types
16+
const C: u8 = u8::const_default() //~ ERROR expected `;`
17+
&C
18+
}
19+
20+
const fn bar() -> u8 { //~ ERROR mismatched types
21+
const C: u8 = 1
22+
+ 2 //~ ERROR expected `;`
23+
}
24+
25+
const fn baz() -> u8 { //~ ERROR mismatched types
26+
const C: u8 = 1
27+
+ val() //~ ERROR expected `;`
28+
}
29+
30+
fn main() {}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: expected `;`, found `}`
2+
--> $DIR/const-recover-semi-issue-151149.rs:16:38
3+
|
4+
LL | const C: u8 = u8::const_default()
5+
| ^ help: add `;` here
6+
LL | &C
7+
LL | }
8+
| - unexpected token
9+
10+
error: expected `;`, found `}`
11+
--> $DIR/const-recover-semi-issue-151149.rs:22:9
12+
|
13+
LL | + 2
14+
| ^ help: add `;` here
15+
LL | }
16+
| - unexpected token
17+
18+
error: expected `;`, found `}`
19+
--> $DIR/const-recover-semi-issue-151149.rs:27:13
20+
|
21+
LL | + val()
22+
| ^ help: add `;` here
23+
LL | }
24+
| - unexpected token
25+
26+
error[E0308]: mismatched types
27+
--> $DIR/const-recover-semi-issue-151149.rs:15:19
28+
|
29+
LL | const fn foo() -> &'static u8 {
30+
| --- ^^^^^^^^^^^ expected `&u8`, found `()`
31+
| |
32+
| implicitly returns `()` as its body has no tail or `return` expression
33+
34+
error[E0308]: mismatched types
35+
--> $DIR/const-recover-semi-issue-151149.rs:20:19
36+
|
37+
LL | const fn bar() -> u8 {
38+
| --- ^^ expected `u8`, found `()`
39+
| |
40+
| implicitly returns `()` as its body has no tail or `return` expression
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/const-recover-semi-issue-151149.rs:25:19
44+
|
45+
LL | const fn baz() -> u8 {
46+
| --- ^^ expected `u8`, found `()`
47+
| |
48+
| implicitly returns `()` as its body has no tail or `return` expression
49+
50+
error: aborting due to 6 previous errors
51+
52+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)