diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index f62f8f1765652..0ff0cf18c6d1d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -444,14 +444,14 @@ impl<'a> Parser<'a> { /// 2. tuple type /// 3. bare trait object type where the first trait bound is parenthesized fn parse_paren_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { - let mut trailing_plus = false; - let (ts, trailing) = self.parse_paren_comma_seq(|p| { + let mut inside_has_trailing_plus = false; + let (ts, trailing_comma) = self.parse_paren_comma_seq(|p| { let ty = p.parse_ty()?; - trailing_plus = p.prev_token == TokenKind::Plus; + inside_has_trailing_plus = p.prev_token == TokenKind::Plus; Ok(ty) })?; - if ts.len() == 1 && matches!(trailing, Trailing::No) { + if ts.len() == 1 && matches!(trailing_comma, Trailing::No) { let ty = ts.into_iter().next().unwrap(); // Let's check if we actually have a bare trait object type where the first trait bound @@ -459,11 +459,12 @@ impl<'a> Parser<'a> { // what's contained between the parentheses resembles a *BareTraitBound*. // // For context, looking at bounds in general (see *Bound*), only trait bounds are - // allowed to be wrapped in parentheses, not however lifetime and use bounds. - let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); + // allowed to be wrapped in parentheses, not however outlives and use bounds. + let outside_has_eligible_trailing_plus = + allow_plus == AllowPlus::Yes && self.token.is_like_plus(); match ty.kind { // `"(" TypePath ")" "+"` - TyKind::Path(None, path) if maybe_bounds => self + TyKind::Path(None, path) if outside_has_eligible_trailing_plus => self .finish_parsing_bare_trait_object_ty( ThinVec::new(), path, @@ -471,18 +472,18 @@ impl<'a> Parser<'a> { true, ast::Parens::Yes, ), - // `"(" BareTraitBound\TypePath | UseBound ")" "+"` + // `"(" BareTraitBound\TypePath ")" "+"` // - // * FIXME: As alluded to above, only trait bounds are meant to allow parens. - // Arguably, it's an accident that we're permitting *UseBound*s and thus types - // like `(use<>)+`. Might need a T-lang FCP to change this. - // * We're checking `!trailing_plus` to prevent us from accepting code like - // `(T+)+` or `('a+)+`. - // * While we could be looking at `('a)+` which we don't want to accept, we - // know that the `parse_ty` above has already emitted an error since the - // lifetime isn't immediately followed by a `+`. + // We actually accept outlives bounds here, too, purely to reduce diagnostic output + // for ill-formed code like `('a)+`: We know for a fact that the `parse_ty` above + // has already emitted an error for the inner `'a` as it's not followed by `+`. + // This way we indirectly suppress unhelpful follow-up diagnostics like E0178 + // ("bad `+` in type") and E0224 ("no trait bound in trait object type"). TyKind::TraitObject(mut bounds, TraitObjectSyntax::None) - if maybe_bounds && bounds.len() == 1 && !trailing_plus => + if outside_has_eligible_trailing_plus + && !inside_has_trailing_plus + && let [ast::GenericBound::Trait(_) | ast::GenericBound::Outlives(_)] = + bounds.as_slice() => { self.eat_plus(); bounds.append(&mut self.parse_generic_bounds()?); diff --git a/tests/ui/impl-trait/precise-capturing/parenthesized.rs b/tests/ui/impl-trait/precise-capturing/parenthesized.rs index e3f80fc1d9f06..6ecdf5d7ddbb3 100644 --- a/tests/ui/impl-trait/precise-capturing/parenthesized.rs +++ b/tests/ui/impl-trait/precise-capturing/parenthesized.rs @@ -5,4 +5,14 @@ fn f() -> impl Sized + (use<>) {} //~^ ERROR precise capturing lists may not be parenthesized //~| HELP remove the parentheses +#[cfg(false)] +type O = Trait + (use<>); +//~^ ERROR precise capturing lists may not be parenthesized +//~| HELP remove the parentheses + +// We once used to accidentally accept this. +#[cfg(false)] +type O = (use<>) + Trait; +//~^ ERROR expected a path on the left-hand side of `+` + fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/parenthesized.stderr b/tests/ui/impl-trait/precise-capturing/parenthesized.stderr index c97fa9972ef01..b01f02bbafa89 100644 --- a/tests/ui/impl-trait/precise-capturing/parenthesized.stderr +++ b/tests/ui/impl-trait/precise-capturing/parenthesized.stderr @@ -10,5 +10,24 @@ LL - fn f() -> impl Sized + (use<>) {} LL + fn f() -> impl Sized + use<> {} | -error: aborting due to 1 previous error +error: precise capturing lists may not be parenthesized + --> $DIR/parenthesized.rs:9:18 + | +LL | type O = Trait + (use<>); + | ^^^^^^^ + | +help: remove the parentheses + | +LL - type O = Trait + (use<>); +LL + type O = Trait + use<>; + | + +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/parenthesized.rs:15:10 + | +LL | type O = (use<>) + Trait; + | ^^^^^^^ expected a path + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0178`.