From 9786ee65a51e5898b3c5718f2447ef3bbf9046a0 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 67007bef4f76cbd87b1444384db6884c6e1c5a76 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 | 40 +++++++++++++------ compiler/rustc_parse/src/parser/mod.rs | 2 + 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 40dbda2466de4..33d6251136d29 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2844,7 +2844,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 8238a6518e41d..357de350ab4dc 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -274,9 +274,13 @@ impl<'a> Parser<'a> { Fixity::Right => Bound::Included(prec), Fixity::Left | Fixity::None => Bound::Excluded(prec), }; - let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc(min_prec) - })?; + let mut rhs_restrictions = restrictions - Restrictions::STMT_EXPR; + if matches!(op, AssocOp::Binary(BinOpKind::Lt)) + && matches!(lhs.kind, ExprKind::Path(..)) + { + rhs_restrictions |= Restrictions::IS_RHS_OF_LT_AFTER_PATH; + } + let rhs = self.with_res(rhs_restrictions, |this| this.parse_expr_assoc(min_prec))?; let span = self.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span); lhs = match op { @@ -1518,7 +1522,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| { @@ -1909,7 +1916,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() { @@ -1976,7 +1986,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() @@ -3162,17 +3175,18 @@ impl<'a> Parser<'a> { )) } - pub(crate) fn eat_label(&mut self) -> Option