diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 0a8a0203013bf..b43c2981ea0fc 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -774,24 +774,29 @@ impl<'a> Parser<'a> { } } - /// Eats `+` possibly breaking tokens like `+=` in process. + /// Eats `+` possibly breaking tokens like `+=` in the process. fn eat_plus(&mut self) -> bool { self.break_and_eat(exp!(Plus)) } - /// Eats `&` possibly breaking tokens like `&&` in process. + /// Eats `!` possibly breaking tokens like `!=` in the process. + fn eat_bang(&mut self) -> bool { + self.break_and_eat(exp!(Bang)) + } + + /// Eats `&` possibly breaking tokens like `&&` in the process. /// Signals an error if `&` is not eaten. fn expect_and(&mut self) -> PResult<'a, ()> { if self.break_and_eat(exp!(And)) { Ok(()) } else { self.unexpected() } } - /// Eats `|` possibly breaking tokens like `||` in process. + /// Eats `|` possibly breaking tokens like `||` in the process. /// Signals an error if `|` was not eaten. fn expect_or(&mut self) -> PResult<'a, ()> { if self.break_and_eat(exp!(Or)) { Ok(()) } else { self.unexpected() } } - /// Eats `<` possibly breaking tokens like `<<` in process. + /// Eats `<` possibly breaking tokens like `<<` in the process. fn eat_lt(&mut self) -> bool { let ate = self.break_and_eat(exp!(Lt)); if ate { @@ -802,13 +807,13 @@ impl<'a> Parser<'a> { ate } - /// Eats `<` possibly breaking tokens like `<<` in process. + /// Eats `<` possibly breaking tokens like `<<` in the process. /// Signals an error if `<` was not eaten. fn expect_lt(&mut self) -> PResult<'a, ()> { if self.eat_lt() { Ok(()) } else { self.unexpected() } } - /// Eats `>` possibly breaking tokens like `>>` in process. + /// Eats `>` possibly breaking tokens like `>>` in the process. /// Signals an error if `>` was not eaten. fn expect_gt(&mut self) -> PResult<'a, ()> { if self.break_and_eat(exp!(Gt)) { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index a415849b91517..05b4488778bdd 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -758,7 +758,9 @@ impl<'a> Parser<'a> { } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. } else if self.eat(exp!(Bang)) { - // Parse `!` + // Ideally we'd use `eat_bang` here to allow us to parse `!=>` as `! =>`. However, + // `break_and_eat` doesn't "reglue" the split-off `=` with any following `>` (since + // that would likely be super fragile and complex). self.psess.gated_spans.gate(sym::never_patterns, self.prev_token.span); PatKind::Never } else if self.eat_keyword(exp!(Underscore)) { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 0d479731e73e3..af118ff533e48 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -280,12 +280,12 @@ impl<'a> Parser<'a> { return Ok(ty); } - let lo = self.token.span; let mut impl_dyn_multi = false; + let mut lo = self.token.span; let kind = if self.check(exp!(OpenParen)) { self.parse_ty_tuple_or_parens(lo, allow_plus)? - } else if self.eat(exp!(Bang)) { - // Never type `!` + } else if self.eat_bang() { + lo = self.prev_token.span; TyKind::Never } else if self.eat(exp!(Star)) { self.parse_ty_ptr()? diff --git a/tests/ui/parser/token-splitting.rs b/tests/ui/parser/token-splitting.rs new file mode 100644 index 0000000000000..3c2ab6e54d41f --- /dev/null +++ b/tests/ui/parser/token-splitting.rs @@ -0,0 +1,35 @@ +// Check some places in which we want to split multi-character punctuation. +//@ edition: 2015 +//@ check-pass +#![feature(never_type)] // only used inside `bang_eq_never_ty`! +#![expect(bare_trait_objects)] + +use std::fmt::Debug; + +fn plus_eq_bound() { + // issue: + struct W { t: T } + struct S { t: T } + + // Bare & `dyn`-prefixed trait object types take different paths in the parser. + // Therefore, test both branches. + + let _: Debug + = *(&() as &dyn Debug); + let _: Debug += *(&() as &dyn Debug); + + let _: dyn Debug + = *(&() as &dyn Debug); + let _: dyn Debug += *(&() as &dyn Debug); + + #[cfg(false)] fn w() where Trait + = () {} + #[cfg(false)] fn s() where Trait += () {} +} + +fn bang_eq_never_ty(x: !) { + let _: ! = x; + let _: != x; + + #[cfg(false)] struct W; + #[cfg(false)] struct S; +} + +fn main() {} diff --git a/tests/ui/parser/trait-plusequal-splitting.rs b/tests/ui/parser/trait-plusequal-splitting.rs deleted file mode 100644 index 2824da50d0f8e..0000000000000 --- a/tests/ui/parser/trait-plusequal-splitting.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Fixes issue where `+` in generics weren't parsed if they were part of a `+=`. - -//@ check-pass - -struct Whitespace { t: T } -struct TokenSplit { t: T } - -fn main() {}