Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,6 @@ pub(crate) struct HelpIdentifierStartsWithNumber {
pub(crate) struct ExpectedSemi {
pub span: Span,
pub token: Token,

pub unexpected_token_label: Option<Span>,
pub sugg: ExpectedSemiSugg,
}
Expand Down
43 changes: 43 additions & 0 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_ast::ast::*;
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},
};
Expand Down Expand Up @@ -1571,6 +1572,9 @@ impl<'a> Parser<'a> {

generics.where_clause = where_clause;

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

Ok((ident, generics, ty, rhs))
Expand Down Expand Up @@ -3407,6 +3411,45 @@ impl<'a> Parser<'a> {
Ok(Some(_))
)
}

/// Try to recover from over-parsing in const item when a semicolon is missing.
///
/// This detects cases where we parsed too much because a semicolon was missing
/// and the next line started an expression that the parser treated as a continuation
/// (e.g., `foo() \n &bar` was parsed as `foo() & bar`).
///
/// Returns a corrected expression if recovery is successful.
fn try_recover_const_missing_semi(&mut self, rhs: &Option<ConstItemRhs>) -> Option<Box<Expr>> {
if self.token == TokenKind::Semi {
return None;
}
let Some(ConstItemRhs::Body(rhs)) = rhs else {
return None;
};
if !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)))
} else {
None
}
}
}

enum IsMacroRulesItem {
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/issues/const-recover-semi-issue-151149.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![feature(const_trait_impl)]

const trait ConstDefault {
fn const_default() -> Self;
}

impl const ConstDefault for u8 {
fn const_default() -> Self { 0 }
}

const fn val() -> u8 {
42
}

const fn foo() -> &'static u8 { //~ ERROR mismatched types
const C: u8 = u8::const_default() //~ ERROR expected `;`
&C
}

const fn bar() -> u8 { //~ ERROR mismatched types
const C: u8 = 1
+ 2 //~ ERROR expected `;`
}

const fn baz() -> u8 { //~ ERROR mismatched types
const C: u8 = 1
+ val() //~ ERROR expected `;`
}

fn main() {}
52 changes: 52 additions & 0 deletions tests/ui/issues/const-recover-semi-issue-151149.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be lovely if we could mark the function's tail expr as TyKind::Err so that we didn't emit a second error...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any flag from parser we can set it directly?

Copy link
Member Author

@chenyukang chenyukang Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit 471b929 will remove the later mismatched error, not sure there is any simpler solution, seems a little over engineering for a corner case.


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`.
Loading