diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index e4dff07e92cb8..0b4a59eb236d6 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -91,6 +91,10 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), None) } + pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { + self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) + } + fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { self.parse_paren_comma_seq(|p| { match p.parse_expr() { @@ -883,10 +887,7 @@ impl<'a> Parser<'a> { let first_expr = self.parse_expr()?; if self.eat(&token::Semi) { // Repeating array syntax: `[ 0; 512 ]` - let count = AnonConst { - id: DUMMY_NODE_ID, - value: self.parse_expr()?, - }; + let count = self.parse_anon_const_expr()?; self.expect(&token::CloseDelim(token::Bracket))?; ex = ExprKind::Repeat(first_expr, count); } else if self.eat(&token::Comma) { diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 0840a1551dbf4..691dbf67ca843 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -5,7 +5,7 @@ use crate::maybe_whole; use rustc_errors::{PResult, Applicability, DiagnosticBuilder, StashKey}; use rustc_error_codes::*; -use syntax::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrKind, AttrStyle, AnonConst, Item}; +use syntax::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrKind, AttrStyle, Item}; use syntax::ast::{AssocItem, AssocItemKind, ItemKind, UseTree, UseTreeKind}; use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit}; use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind}; @@ -1318,10 +1318,7 @@ impl<'a> Parser<'a> { }; let disr_expr = if self.eat(&token::Eq) { - Some(AnonConst { - id: DUMMY_NODE_ID, - value: self.parse_expr()?, - }) + Some(self.parse_anon_const_expr()?) } else { None }; diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index 6f7ab0542d5fa..9f5fd6d0a36eb 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -8,7 +8,7 @@ use rustc_error_codes::*; use syntax::ptr::P; use syntax::ast::{self, Ty, TyKind, MutTy, BareFnTy, FunctionRetTy, GenericParam, Lifetime, Ident}; use syntax::ast::{TraitBoundModifier, TraitObjectSyntax, GenericBound, GenericBounds, PolyTraitRef}; -use syntax::ast::{Mutability, AnonConst, Mac}; +use syntax::ast::{Mutability, Mac}; use syntax::token::{self, Token}; use syntax::struct_span_err; use syntax_pos::source_map::Span; @@ -73,78 +73,21 @@ impl<'a> Parser<'a> { let lo = self.token.span; let mut impl_dyn_multi = false; - let kind = if self.eat(&token::OpenDelim(token::Paren)) { - // `(TYPE)` is a parenthesized type. - // `(TYPE,)` is a tuple with a single field of type TYPE. - let mut ts = vec![]; - let mut last_comma = false; - while self.token != token::CloseDelim(token::Paren) { - ts.push(self.parse_ty()?); - if self.eat(&token::Comma) { - last_comma = true; - } else { - last_comma = false; - break; - } - } - let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus; - self.expect(&token::CloseDelim(token::Paren))?; - - if ts.len() == 1 && !last_comma { - let ty = ts.into_iter().nth(0).unwrap().into_inner(); - let maybe_bounds = allow_plus && self.token.is_like_plus(); - match ty.kind { - // `(TY_BOUND_NOPAREN) + BOUND + ...`. - TyKind::Path(None, ref path) if maybe_bounds => { - self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)? - } - TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) - if maybe_bounds && bounds.len() == 1 && !trailing_plus => { - let path = match bounds[0] { - GenericBound::Trait(ref pt, ..) => pt.trait_ref.path.clone(), - GenericBound::Outlives(..) => self.bug("unexpected lifetime bound"), - }; - self.parse_remaining_bounds(Vec::new(), path, lo, true)? - } - // `(TYPE)` - _ => TyKind::Paren(P(ty)) - } - } else { - TyKind::Tup(ts) - } + let kind = if self.check(&token::OpenDelim(token::Paren)) { + self.parse_ty_tuple_or_parens(lo, allow_plus)? } else if self.eat(&token::Not) { // Never type `!` TyKind::Never } else if self.eat(&token::BinOp(token::Star)) { - // Raw pointer - TyKind::Ptr(self.parse_ptr()?) + self.parse_ty_ptr()? } else if self.eat(&token::OpenDelim(token::Bracket)) { - // Array or slice - let t = self.parse_ty()?; - // Parse optional `; EXPR` in `[TYPE; EXPR]` - let t = match self.maybe_parse_fixed_length_of_vec()? { - None => TyKind::Slice(t), - Some(length) => TyKind::Array(t, AnonConst { - id: ast::DUMMY_NODE_ID, - value: length, - }), - }; - self.expect(&token::CloseDelim(token::Bracket))?; - t + self.parse_array_or_slice_ty()? } else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) { // Reference self.expect_and()?; self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { - // `typeof(EXPR)` - // In order to not be ambiguous, the type must be surrounded by parens. - self.expect(&token::OpenDelim(token::Paren))?; - let e = AnonConst { - id: ast::DUMMY_NODE_ID, - value: self.parse_expr()?, - }; - self.expect(&token::CloseDelim(token::Paren))?; - TyKind::Typeof(e) + self.parse_typeof_ty()? } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer @@ -155,7 +98,6 @@ impl<'a> Parser<'a> { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` - let lo = self.token.span; let lifetime_defs = self.parse_late_bound_lifetime_defs()?; if self.token_is_bare_fn_keyword() { self.parse_ty_bare_fn(lifetime_defs)? @@ -165,69 +107,33 @@ impl<'a> Parser<'a> { self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)? } } else if self.eat_keyword(kw::Impl) { - // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds(None)?; - impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; - TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) - } else if self.check_keyword(kw::Dyn) && - (self.token.span.rust_2018() || - self.look_ahead(1, |t| t.can_begin_bound() && - !can_continue_type_after_non_fn_ident(t))) { - self.bump(); // `dyn` - // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds(None)?; - impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; - TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) - } else if self.check(&token::Question) || - self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) { + self.parse_impl_ty(&mut impl_dyn_multi)? + } else if self.is_explicit_dyn_type() { + self.parse_dyn_ty(&mut impl_dyn_multi)? + } else if self.check(&token::Question) + || self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) + { // Bound list (trait object type) - TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus, None)?, - TraitObjectSyntax::None) + let bounds = self.parse_generic_bounds_common(allow_plus, None)?; + TyKind::TraitObject(bounds, TraitObjectSyntax::None) } else if self.eat_lt() { // Qualified path let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.token.is_path_start() { - // Simple path - let path = self.parse_path(PathStyle::Type)?; - if self.eat(&token::Not) { - // Macro invocation in type position - let args = self.parse_mac_args()?; - let mac = Mac { - path, - args, - prior_type_ascription: self.last_type_ascription, - }; - TyKind::Mac(mac) - } else { - // Just a type path or bound list (trait object type) starting with a trait. - // `Type` - // `Trait1 + Trait2 + 'a` - if allow_plus && self.check_plus() { - self.parse_remaining_bounds(Vec::new(), path, lo, true)? - } else { - TyKind::Path(None, path) - } - } + self.parse_path_start_ty(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { if allow_c_variadic { TyKind::CVarArgs } else { // FIXME(Centril): Should we just allow `...` syntactically // anywhere in a type and use semantic restrictions instead? - struct_span_err!( - self.sess.span_diagnostic, - lo.to(self.prev_span), - E0743, - "C-variadic type `...` may not be nested inside another type", - ) - .emit(); - + self.error_illegal_c_varadic_ty(lo); TyKind::Err } } else { let msg = format!("expected type, found {}", self.this_token_descr()); - let mut err = self.fatal(&msg); + let mut err = self.struct_span_err(self.token.span, &msg); err.span_label(self.token.span, "expected type"); self.maybe_annotate_with_ascription(&mut err, true); return Err(err); @@ -242,8 +148,48 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery) } - fn parse_remaining_bounds(&mut self, generic_params: Vec, path: ast::Path, - lo: Span, parse_plus: bool) -> PResult<'a, TyKind> { + /// Parses either: + /// - `(TYPE)`, a parenthesized type. + /// - `(TYPE,)`, a tuple with a single field of type TYPE. + fn parse_ty_tuple_or_parens(&mut self, lo: Span, allow_plus: bool) -> PResult<'a, TyKind> { + let mut trailing_plus = false; + let (ts, trailing) = self.parse_paren_comma_seq(|p| { + let ty = p.parse_ty()?; + trailing_plus = p.prev_token_kind == PrevTokenKind::Plus; + Ok(ty) + })?; + + if ts.len() == 1 && !trailing { + let ty = ts.into_iter().nth(0).unwrap().into_inner(); + let maybe_bounds = allow_plus && self.token.is_like_plus(); + match ty.kind { + // `(TY_BOUND_NOPAREN) + BOUND + ...`. + TyKind::Path(None, path) if maybe_bounds => { + self.parse_remaining_bounds(Vec::new(), path, lo, true) + } + TyKind::TraitObject(mut bounds, TraitObjectSyntax::None) + if maybe_bounds && bounds.len() == 1 && !trailing_plus => { + let path = match bounds.remove(0) { + GenericBound::Trait(pt, ..) => pt.trait_ref.path, + GenericBound::Outlives(..) => self.bug("unexpected lifetime bound"), + }; + self.parse_remaining_bounds(Vec::new(), path, lo, true) + } + // `(TYPE)` + _ => Ok(TyKind::Paren(P(ty))) + } + } else { + Ok(TyKind::Tup(ts)) + } + } + + fn parse_remaining_bounds( + &mut self, + generic_params: Vec, + path: ast::Path, + lo: Span, + parse_plus: bool, + ) -> PResult<'a, TyKind> { let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span)); let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)]; if parse_plus { @@ -253,7 +199,8 @@ impl<'a> Parser<'a> { Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } - fn parse_ptr(&mut self) -> PResult<'a, MutTy> { + /// Parses a raw pointer type: `*[const | mut] $type`. + fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> { let mutbl = self.parse_const_or_mut().unwrap_or_else(|| { let span = self.prev_span; let msg = "expected mut or const in raw pointer type"; @@ -263,23 +210,37 @@ impl<'a> Parser<'a> { .emit(); Mutability::Immutable }); - let t = self.parse_ty_no_plus()?; - Ok(MutTy { ty: t, mutbl }) + let ty = self.parse_ty_no_plus()?; + Ok(TyKind::Ptr(MutTy { ty, mutbl })) } - fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option>> { - if self.eat(&token::Semi) { - Ok(Some(self.parse_expr()?)) + /// Parses an array (`[TYPE; EXPR]`) or slice (`[TYPE]`) type. + /// The opening `[` bracket is already eaten. + fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { + let elt_ty = self.parse_ty()?; + let ty = if self.eat(&token::Semi) { + TyKind::Array(elt_ty, self.parse_anon_const_expr()?) } else { - Ok(None) - } + TyKind::Slice(elt_ty) + }; + self.expect(&token::CloseDelim(token::Bracket))?; + Ok(ty) } fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; let mutbl = self.parse_mutability(); let ty = self.parse_ty_no_plus()?; - return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl })); + Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl })) + } + + // Parses the `typeof(EXPR)`. + // To avoid ambiguity, the type is surrounded by parenthesis. + fn parse_typeof_ty(&mut self) -> PResult<'a, TyKind> { + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_anon_const_expr()?; + self.expect(&token::CloseDelim(token::Paren))?; + Ok(TyKind::Typeof(expr)) } /// Is the current token one of the keywords that signals a bare function type? @@ -289,20 +250,15 @@ impl<'a> Parser<'a> { self.check_keyword(kw::Extern) } - /// Parses a `TyKind::BareFn` type. + /// Parses a function pointer type (`TyKind::BareFn`). + /// ``` + /// [unsafe] [extern "ABI"] fn (S) -> T + /// ^~~~~^ ^~~~^ ^~^ ^ + /// | | | | + /// | | | Return type + /// Function Style ABI Parameter types + /// ``` fn parse_ty_bare_fn(&mut self, generic_params: Vec) -> PResult<'a, TyKind> { - /* - - [unsafe] [extern "ABI"] fn (S) -> T - ^~~~^ ^~~~^ ^~^ ^ - | | | | - | | | Return type - | | Argument types - | | - | ABI - Function Style - */ - let unsafety = self.parse_unsafety(); let ext = self.parse_extern()?; self.expect_keyword(kw::Fn)?; @@ -319,130 +275,241 @@ impl<'a> Parser<'a> { }))) } - pub(super) fn parse_generic_bounds(&mut self, - colon_span: Option) -> PResult<'a, GenericBounds> { + /// Parses an `impl B0 + ... + Bn` type. + fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { + // Always parse bounds greedily for better error recovery. + let bounds = self.parse_generic_bounds(None)?; + *impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; + Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) + } + + /// Is a `dyn B0 + ... + Bn` type allowed here? + fn is_explicit_dyn_type(&mut self) -> bool { + self.check_keyword(kw::Dyn) + && (self.token.span.rust_2018() + || self.look_ahead(1, |t| { + t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t) + })) + } + + /// Parses a `dyn B0 + ... + Bn` type. + /// + /// Note that this does *not* parse bare trait objects. + fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { + self.bump(); // `dyn` + // Always parse bounds greedily for better error recovery. + let bounds = self.parse_generic_bounds(None)?; + *impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; + Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)) + } + + /// Parses a type starting with a path. + /// + /// This can be: + /// 1. a type macro, `mac!(...)`, + /// 2. a bare trait object, `B0 + ... + Bn`, + /// 3. or a path, `path::to::MyType`. + fn parse_path_start_ty(&mut self, lo: Span, allow_plus: bool) -> PResult<'a, TyKind> { + // Simple path + let path = self.parse_path(PathStyle::Type)?; + if self.eat(&token::Not) { + // Macro invocation in type position + Ok(TyKind::Mac(Mac { + path, + args: self.parse_mac_args()?, + prior_type_ascription: self.last_type_ascription, + })) + } else if allow_plus && self.check_plus() { + // `Trait1 + Trait2 + 'a` + self.parse_remaining_bounds(Vec::new(), path, lo, true) + } else { + // Just a type path. + Ok(TyKind::Path(None, path)) + } + } + + fn error_illegal_c_varadic_ty(&self, lo: Span) { + struct_span_err!( + self.sess.span_diagnostic, + lo.to(self.prev_span), + E0743, + "C-variadic type `...` may not be nested inside another type", + ) + .emit(); + } + + pub(super) fn parse_generic_bounds( + &mut self, + colon_span: Option, + ) -> PResult<'a, GenericBounds> { self.parse_generic_bounds_common(true, colon_span) } /// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`. /// - /// ``` - /// BOUND = TY_BOUND | LT_BOUND - /// LT_BOUND = LIFETIME (e.g., `'a`) - /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) - /// TY_BOUND_NOPAREN = [?] [for] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`) - /// ``` - fn parse_generic_bounds_common(&mut self, - allow_plus: bool, - colon_span: Option) -> PResult<'a, GenericBounds> { + /// See `parse_generic_bound` for the `BOUND` grammar. + fn parse_generic_bounds_common( + &mut self, + allow_plus: bool, + colon_span: Option, + ) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); let mut negative_bounds = Vec::new(); - let mut last_plus_span = None; - let mut was_negative = false; - loop { - // This needs to be synchronized with `TokenKind::can_begin_bound`. - let is_bound_start = self.check_path() || self.check_lifetime() || - self.check(&token::Not) || // used for error reporting only - self.check(&token::Question) || - self.check_keyword(kw::For) || - self.check(&token::OpenDelim(token::Paren)); - if is_bound_start { - let lo = self.token.span; - let has_parens = self.eat(&token::OpenDelim(token::Paren)); - let inner_lo = self.token.span; - let is_negative = self.eat(&token::Not); - let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None }; - if self.token.is_lifetime() { - if let Some(question_span) = question { - self.span_err(question_span, - "`?` may only modify trait bounds, not lifetime bounds"); - } - bounds.push(GenericBound::Outlives(self.expect_lifetime())); - if has_parens { - let inner_span = inner_lo.to(self.prev_span); - self.expect(&token::CloseDelim(token::Paren))?; - let mut err = self.struct_span_err( - lo.to(self.prev_span), - "parenthesized lifetime bounds are not supported" - ); - if let Ok(snippet) = self.span_to_snippet(inner_span) { - err.span_suggestion_short( - lo.to(self.prev_span), - "remove the parentheses", - snippet.to_owned(), - Applicability::MachineApplicable - ); - } - err.emit(); - } - } else { - let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - let path = self.parse_path(PathStyle::Type)?; - if has_parens { - self.expect(&token::CloseDelim(token::Paren))?; - } - let poly_span = lo.to(self.prev_span); - if is_negative { - was_negative = true; - if let Some(sp) = last_plus_span.or(colon_span) { - negative_bounds.push(sp.to(poly_span)); - } - } else { - let poly_trait = PolyTraitRef::new(lifetime_defs, path, poly_span); - let modifier = if question.is_some() { - TraitBoundModifier::Maybe - } else { - TraitBoundModifier::None - }; - bounds.push(GenericBound::Trait(poly_trait, modifier)); - } - } - } else { - break + while self.can_begin_bound() { + match self.parse_generic_bound()? { + Ok(bound) => bounds.push(bound), + Err(neg_sp) => negative_bounds.push(neg_sp), } - if !allow_plus || !self.eat_plus() { break - } else { - last_plus_span = Some(self.prev_span); } } - if !negative_bounds.is_empty() || was_negative { - let negative_bounds_len = negative_bounds.len(); - let last_span = negative_bounds.last().map(|sp| *sp); - let mut err = self.struct_span_err( - negative_bounds, - "negative trait bounds are not supported", - ); - if let Some(sp) = last_span { - err.span_label(sp, "negative trait bounds are not supported"); - } - if let Some(bound_list) = colon_span { - let bound_list = bound_list.to(self.prev_span); - let mut new_bound_list = String::new(); - if !bounds.is_empty() { - let mut snippets = bounds.iter().map(|bound| bound.span()) - .map(|span| self.span_to_snippet(span)); - while let Some(Ok(snippet)) = snippets.next() { - new_bound_list.push_str(" + "); - new_bound_list.push_str(&snippet); - } - new_bound_list = new_bound_list.replacen(" +", ":", 1); + if !negative_bounds.is_empty() { + self.error_negative_bounds(colon_span, &bounds, negative_bounds); + } + + Ok(bounds) + } + + /// Can the current token begin a bound? + fn can_begin_bound(&mut self) -> bool { + // This needs to be synchronized with `TokenKind::can_begin_bound`. + self.check_path() + || self.check_lifetime() + || self.check(&token::Not) // Used for error reporting only. + || self.check(&token::Question) + || self.check_keyword(kw::For) + || self.check(&token::OpenDelim(token::Paren)) + } + + fn error_negative_bounds( + &self, + colon_span: Option, + bounds: &[GenericBound], + negative_bounds: Vec, + ) { + let negative_bounds_len = negative_bounds.len(); + let last_span = *negative_bounds.last().expect("no negative bounds, but still error?"); + let mut err = self.struct_span_err( + negative_bounds, + "negative bounds are not supported", + ); + err.span_label(last_span, "negative bounds are not supported"); + if let Some(bound_list) = colon_span { + let bound_list = bound_list.to(self.prev_span); + let mut new_bound_list = String::new(); + if !bounds.is_empty() { + let mut snippets = bounds.iter().map(|bound| self.span_to_snippet(bound.span())); + while let Some(Ok(snippet)) = snippets.next() { + new_bound_list.push_str(" + "); + new_bound_list.push_str(&snippet); } - err.span_suggestion_hidden( - bound_list, - &format!("remove the trait bound{}", pluralize!(negative_bounds_len)), - new_bound_list, - Applicability::MachineApplicable, - ); + new_bound_list = new_bound_list.replacen(" +", ":", 1); } - err.emit(); + err.tool_only_span_suggestion( + bound_list, + &format!("remove the bound{}", pluralize!(negative_bounds_len)), + new_bound_list, + Applicability::MachineApplicable, + ); } + err.emit(); + } - return Ok(bounds); + /// Parses a bound according to the grammar: + /// ``` + /// BOUND = TY_BOUND | LT_BOUND + /// ``` + fn parse_generic_bound(&mut self) -> PResult<'a, Result> { + let anchor_lo = self.prev_span; + let lo = self.token.span; + let has_parens = self.eat(&token::OpenDelim(token::Paren)); + let inner_lo = self.token.span; + let is_negative = self.eat(&token::Not); + let question = self.eat(&token::Question).then_some(self.prev_span); + let bound = if self.token.is_lifetime() { + self.parse_generic_lt_bound(lo, inner_lo, has_parens, question)? + } else { + self.parse_generic_ty_bound(lo, has_parens, question)? + }; + Ok(if is_negative { + Err(anchor_lo.to(self.prev_span)) + } else { + Ok(bound) + }) + } + + /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: + /// ``` + /// LT_BOUND = LIFETIME + /// ``` + fn parse_generic_lt_bound( + &mut self, + lo: Span, + inner_lo: Span, + has_parens: bool, + question: Option, + ) -> PResult<'a, GenericBound> { + self.error_opt_out_lifetime(question); + let bound = GenericBound::Outlives(self.expect_lifetime()); + if has_parens { + // FIXME(Centril): Consider not erroring here and accepting `('lt)` instead, + // possibly introducing `GenericBound::Paren(P)`? + self.recover_paren_lifetime(lo, inner_lo)?; + } + Ok(bound) + } + + fn error_opt_out_lifetime(&self, question: Option) { + if let Some(span) = question { + self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds") + .emit(); + } + } + + /// Recover on `('lifetime)` with `(` already eaten. + fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> { + let inner_span = inner_lo.to(self.prev_span); + self.expect(&token::CloseDelim(token::Paren))?; + let mut err = self.struct_span_err( + lo.to(self.prev_span), + "parenthesized lifetime bounds are not supported" + ); + if let Ok(snippet) = self.span_to_snippet(inner_span) { + err.span_suggestion_short( + lo.to(self.prev_span), + "remove the parentheses", + snippet.to_owned(), + Applicability::MachineApplicable + ); + } + err.emit(); + Ok(()) + } + + /// Parses a type bound according to: + /// ``` + /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) + /// TY_BOUND_NOPAREN = [?] [for] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`) + /// ``` + fn parse_generic_ty_bound( + &mut self, + lo: Span, + has_parens: bool, + question: Option, + ) -> PResult<'a, GenericBound> { + let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let path = self.parse_path(PathStyle::Type)?; + if has_parens { + self.expect(&token::CloseDelim(token::Paren))?; + } + let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span)); + let modifier = question.map_or(TraitBoundModifier::None, |_| TraitBoundModifier::Maybe); + Ok(GenericBound::Trait(poly_trait, modifier)) } + /// Optionally parses `for<$generic_params>`. pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { if self.eat_keyword(kw::For) { self.expect_lt()?; diff --git a/src/test/ui/issues/issue-58857.rs b/src/test/ui/issues/issue-58857.rs index 392e4ea0c2ecc..4350d7e5b403b 100644 --- a/src/test/ui/issues/issue-58857.rs +++ b/src/test/ui/issues/issue-58857.rs @@ -2,6 +2,6 @@ struct Conj {a : A} trait Valid {} impl Conj{} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported fn main() {} diff --git a/src/test/ui/issues/issue-58857.stderr b/src/test/ui/issues/issue-58857.stderr index ab9a0130c00b0..e2acec47e5abf 100644 --- a/src/test/ui/issues/issue-58857.stderr +++ b/src/test/ui/issues/issue-58857.stderr @@ -1,10 +1,8 @@ -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-58857.rs:4:7 | LL | impl Conj{} - | ^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bound + | ^^^^^^^^ negative bounds are not supported error: aborting due to previous error diff --git a/src/test/ui/parser/issue-33418.fixed b/src/test/ui/parser/issue-33418.fixed index 2aaa3b5b1ea50..ed885ae143566 100644 --- a/src/test/ui/parser/issue-33418.fixed +++ b/src/test/ui/parser/issue-33418.fixed @@ -1,15 +1,15 @@ // run-rustfix trait Tr {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr2: SuperA {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr3: SuperB {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr4: SuperB + SuperD {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr5 {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait SuperA {} trait SuperB {} diff --git a/src/test/ui/parser/issue-33418.rs b/src/test/ui/parser/issue-33418.rs index 5533152092719..9934284abfbbe 100644 --- a/src/test/ui/parser/issue-33418.rs +++ b/src/test/ui/parser/issue-33418.rs @@ -1,17 +1,17 @@ // run-rustfix trait Tr: !SuperA {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr2: SuperA + !SuperB {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr3: !SuperA + SuperB {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr4: !SuperA + SuperB + !SuperC + SuperD {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait Tr5: !SuperA + !SuperB {} -//~^ ERROR negative trait bounds are not supported +//~^ ERROR negative bounds are not supported trait SuperA {} trait SuperB {} diff --git a/src/test/ui/parser/issue-33418.stderr b/src/test/ui/parser/issue-33418.stderr index 479e7bed1016a..9a8733e89292e 100644 --- a/src/test/ui/parser/issue-33418.stderr +++ b/src/test/ui/parser/issue-33418.stderr @@ -1,46 +1,36 @@ -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-33418.rs:3:9 | LL | trait Tr: !SuperA {} - | ^^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bound + | ^^^^^^^^^ negative bounds are not supported -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-33418.rs:5:19 | LL | trait Tr2: SuperA + !SuperB {} - | ^^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bound + | ^^^^^^^^^ negative bounds are not supported -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-33418.rs:7:10 | LL | trait Tr3: !SuperA + SuperB {} - | ^^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bound + | ^^^^^^^^^ negative bounds are not supported -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-33418.rs:9:10 | LL | trait Tr4: !SuperA + SuperB | ^^^^^^^^^ LL | + !SuperC + SuperD {} - | ^^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bounds + | ^^^^^^^^^ negative bounds are not supported -error: negative trait bounds are not supported +error: negative bounds are not supported --> $DIR/issue-33418.rs:12:10 | LL | trait Tr5: !SuperA | ^^^^^^^^^ LL | + !SuperB {} - | ^^^^^^^^^ negative trait bounds are not supported - | - = help: remove the trait bounds + | ^^^^^^^^^ negative bounds are not supported error: aborting due to 5 previous errors diff --git a/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.rs b/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.rs new file mode 100644 index 0000000000000..5a109ba7c6894 --- /dev/null +++ b/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.rs @@ -0,0 +1,12 @@ +// In this regression test for #67146, we check that the +// negative outlives bound `!'a` is rejected by the parser. +// This regression was first introduced in PR #57364. + +fn main() {} + +fn f1() {} +//~^ ERROR negative bounds are not supported +fn f2<'a, T: Ord + !'a>() {} +//~^ ERROR negative bounds are not supported +fn f3<'a, T: !'a + Ord>() {} +//~^ ERROR negative bounds are not supported diff --git a/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.stderr b/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.stderr new file mode 100644 index 0000000000000..4dc0634730442 --- /dev/null +++ b/src/test/ui/parser/issue-67146-negative-outlives-bound-syntactic-fail.stderr @@ -0,0 +1,20 @@ +error: negative bounds are not supported + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:7:8 + | +LL | fn f1() {} + | ^^^^^^^^^^ negative bounds are not supported + +error: negative bounds are not supported + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:9:18 + | +LL | fn f2<'a, T: Ord + !'a>() {} + | ^^^^^ negative bounds are not supported + +error: negative bounds are not supported + --> $DIR/issue-67146-negative-outlives-bound-syntactic-fail.rs:11:12 + | +LL | fn f3<'a, T: !'a + Ord>() {} + | ^^^^^ negative bounds are not supported + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/type/ascription/issue-47666.stderr b/src/test/ui/type/ascription/issue-47666.stderr index 2f052341faead..648635f0c32fa 100644 --- a/src/test/ui/type/ascription/issue-47666.stderr +++ b/src/test/ui/type/ascription/issue-47666.stderr @@ -11,7 +11,7 @@ LL | let _ = Option:Some(vec![0, 1]); | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` = note: for more information, see https://github.com/rust-lang/rust/issues/23416 - = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error