From 43be2940f72fd06b34aea802998a0bc09ee108b3 Mon Sep 17 00:00:00 2001 From: Raushan singh Date: Sat, 12 Sep 2026 13:53:31 +0000 Subject: [PATCH 1/3] test(parser): add baseline for missing turbofish with lifetime in struct expr --- .../recover/missing-turbofish-lifetime.rs | 30 ++++++++++ .../recover/missing-turbofish-lifetime.stderr | 57 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/ui/parser/recover/missing-turbofish-lifetime.rs create mode 100644 tests/ui/parser/recover/missing-turbofish-lifetime.stderr diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.rs b/tests/ui/parser/recover/missing-turbofish-lifetime.rs new file mode 100644 index 0000000000000..596b197a317da --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.rs @@ -0,0 +1,30 @@ +struct Struct<'a> { + string: &'a str, +} + +fn struct_with_reserved_lifetime() { + let _ = Struct<'_> { + //~^ ERROR labels cannot use keyword names + //~| ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR comparison operators cannot be chained + string: "", + }; +} + +fn struct_with_named_lifetime() { + let _ = Struct<'a> { + //~^ ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR comparison operators cannot be chained + string: "", + }; +} + +fn struct_with_multichar_lifetime() { + let _ = Struct<'abc> { + //~^ ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR comparison operators cannot be chained + string: "", + }; +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.stderr b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr new file mode 100644 index 0000000000000..e14d9d6aa464c --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr @@ -0,0 +1,57 @@ +error: labels cannot use keyword names + --> $DIR/missing-turbofish-lifetime.rs:6:20 + | +LL | let _ = Struct<'_> { + | ^^ + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/missing-turbofish-lifetime.rs:6:22 + | +LL | let _ = Struct<'_> { + | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: add `'` to close the char literal + | +LL | let _ = Struct<'_'> { + | + + +error: comparison operators cannot be chained + --> $DIR/missing-turbofish-lifetime.rs:6:19 + | +LL | let _ = Struct<'_> { + | ^ ^ + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/missing-turbofish-lifetime.rs:15:22 + | +LL | let _ = Struct<'a> { + | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: add `'` to close the char literal + | +LL | let _ = Struct<'a'> { + | + + +error: comparison operators cannot be chained + --> $DIR/missing-turbofish-lifetime.rs:15:19 + | +LL | let _ = Struct<'a> { + | ^ ^ + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/missing-turbofish-lifetime.rs:23:24 + | +LL | let _ = Struct<'abc> { + | ^ expected `while`, `for`, `loop` or `{` after a label + +error: comparison operators cannot be chained + --> $DIR/missing-turbofish-lifetime.rs:23:19 + | +LL | let _ = Struct<'abc> { + | ^ ^ + | + = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = help: or use `(...)` if you meant to specify fn arguments + +error: aborting due to 7 previous errors + From 0bc970244b4012f8c83aa3306206b8f31634187d Mon Sep 17 00:00:00 2001 From: Raushan singh Date: Sun, 13 Sep 2026 06:16:34 +0000 Subject: [PATCH 2/3] refactor(parser): add IS_RHS_OF_LT_AFTER_PATH restriction and delay KeywordLabel emission in eat_label --- .../rustc_parse/src/parser/diagnostics.rs | 6 ++- compiler/rustc_parse/src/parser/expr.rs | 41 +++++++++++++------ compiler/rustc_parse/src/parser/mod.rs | 2 + 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index bb8be3c9c509f..77c8e4634437a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2657,7 +2657,11 @@ impl<'a> Parser<'a> { { return false; } - let label = self.eat_label().expect("just checked if a label exists"); + let (label, err) = self.eat_label(); + if let Some(e) = err { + e.emit(); + } + let label = label.expect("just checked if a label exists"); self.bump(); // eat `:` let span = label.ident.span.to(self.prev_token.span); let mut diag = self diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a5c31672a2bc7..726f84f423057 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -192,11 +192,16 @@ impl<'a> Parser<'a> { Fixity::Right => Bound::Included(prec), Fixity::Left | Fixity::None => Bound::Excluded(prec), }; + let mut rhs_restrictions = restrictions - Restrictions::STMT_EXPR; + if matches!(op.node, AssocOp::Binary(BinOpKind::Lt)) + && matches!(lhs.kind, ExprKind::Path(..)) + { + rhs_restrictions |= Restrictions::IS_RHS_OF_LT_AFTER_PATH; + } let finish_parsing_bin_op = |this: &mut Self| { - let rhs = this.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc(min_prec) - })?; + let rhs = + this.with_res(rhs_restrictions, |this| this.parse_expr_assoc(min_prec))?; let span = this.mk_expr_sp(&lhs, lhs_span, op.span, rhs.span); Ok((rhs, span)) }; @@ -1395,7 +1400,10 @@ impl<'a> Parser<'a> { } } else if this.eat_keyword(exp!(While)) { this.parse_expr_while(None, lo) - } else if let Some(label) = this.eat_label() { + } else if let (Some(label), err) = this.eat_label() { + if let Some(e) = err { + e.emit(); + } this.parse_expr_labeled(label, true) } else if this.eat_keyword(exp!(Loop)) { this.parse_expr_loop(None, lo).map_err(|mut err| { @@ -1787,7 +1795,10 @@ impl<'a> Parser<'a> { /// with a labeled loop does not even get a warning because there is no ambiguity. fn parse_expr_break(&mut self) -> PResult<'a, Box> { let lo = self.prev_token.span; - let mut label = self.eat_label(); + let (mut label, err) = self.eat_label(); + if let Some(e) = err { + e.emit(); + } let kind = if self.token == token::Colon && let Some(label) = label.take() { @@ -1854,7 +1865,10 @@ impl<'a> Parser<'a> { /// Parse `"continue" label?`. fn parse_expr_continue(&mut self, lo: Span) -> PResult<'a, Box> { - let mut label = self.eat_label(); + let (mut label, err) = self.eat_label(); + if let Some(e) = err { + e.emit(); + } // Recover `continue label` -> `continue 'label` if self.may_recover() @@ -3004,17 +3018,18 @@ impl<'a> Parser<'a> { )) } - pub(crate) fn eat_label(&mut self) -> Option