enum PrevTokenKind {
DocComment,
Comma,
+ Plus,
Interpolated,
Eof,
Other,
self.prev_token_kind = match self.token {
token::DocComment(..) => PrevTokenKind::DocComment,
token::Comma => PrevTokenKind::Comma,
+ token::BinOp(token::Plus) => PrevTokenKind::Plus,
token::Interpolated(..) => PrevTokenKind::Interpolated,
token::Eof => PrevTokenKind::Eof,
_ => PrevTokenKind::Other,
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().unwrap();
+ let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
match ty.node {
- // Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318).
- TyKind::Path(None, ref path)
- if allow_plus && self.token == token::BinOp(token::Plus) => {
- self.bump(); // `+`
- let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo.to(self.prev_span));
- let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
- bounds.append(&mut self.parse_ty_param_bounds()?);
- TyKind::TraitObject(bounds)
+ // `(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)
+ if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
+ let path = match bounds[0] {
+ TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
+ _ => self.bug("unexpected lifetime bound"),
+ };
+ self.parse_remaining_bounds(Vec::new(), path, lo, true)?
+ }
+ // `(TYPE)`
_ => TyKind::Paren(P(ty))
}
} else {
// Just a type path or bound list (trait object type) starting with a trait.
// `Type`
// `Trait1 + Trait2 + 'a`
- if allow_plus && self.eat(&token::BinOp(token::Plus)) {
- let poly_trait = PolyTraitRef::new(Vec::new(), path, lo.to(self.prev_span));
- let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
- bounds.append(&mut self.parse_ty_param_bounds()?);
- TyKind::TraitObject(bounds)
+ if allow_plus && self.check(&token::BinOp(token::Plus)) {
+ self.parse_remaining_bounds(Vec::new(), path, lo, true)?
} else {
TyKind::Path(None, path)
}
self.parse_ty_bare_fn(lifetime_defs)?
} else {
let path = self.parse_path(PathStyle::Type)?;
- let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
- let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
- if allow_plus && self.eat(&token::BinOp(token::Plus)) {
- bounds.append(&mut self.parse_ty_param_bounds()?)
- }
- TyKind::TraitObject(bounds)
+ let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
+ self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
}
} else if self.eat_keyword(keywords::Impl) {
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
Ok(P(ty))
}
+ fn parse_remaining_bounds(&mut self, lifetime_defs: Vec<LifetimeDef>, path: ast::Path,
+ lo: Span, parse_plus: bool) -> PResult<'a, TyKind> {
+ let poly_trait_ref = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
+ let mut bounds = vec![TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)];
+ if parse_plus {
+ self.bump(); // `+`
+ bounds.append(&mut self.parse_ty_param_bounds()?);
+ }
+ Ok(TyKind::TraitObject(bounds))
+ }
+
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
// Do not add `+` to expected tokens.
if !allow_plus || self.token != token::BinOp(token::Plus) {
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
// BOUND = TY_BOUND | LT_BOUND
// LT_BOUND = LIFETIME (e.g. `'a`)
- // TY_BOUND = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
+ // TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
+ // TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
let mut bounds = Vec::new();
loop {
- let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
- if self.check_lifetime() {
- if let Some(question_span) = question {
- self.span_err(question_span,
- "`?` may only modify trait bounds, not lifetime bounds");
- }
- bounds.push(RegionTyParamBound(self.expect_lifetime()));
- } else if self.check_keyword(keywords::For) || self.check_path() {
- let lo = self.span;
- let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
- let path = self.parse_path(PathStyle::Type)?;
- let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
- let modifier = if question.is_some() {
- TraitBoundModifier::Maybe
+ let is_bound_start = self.check_path() || self.check_lifetime() ||
+ self.check(&token::Question) ||
+ self.check_keyword(keywords::For) ||
+ self.check(&token::OpenDelim(token::Paren));
+ if is_bound_start {
+ let has_parens = self.eat(&token::OpenDelim(token::Paren));
+ 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(RegionTyParamBound(self.expect_lifetime()));
} else {
- TraitBoundModifier::None
- };
- bounds.push(TraitTyParamBound(poly_trait, modifier));
+ let lo = self.span;
+ let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+ let path = self.parse_path(PathStyle::Type)?;
+ let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
+ let modifier = if question.is_some() {
+ TraitBoundModifier::Maybe
+ } else {
+ TraitBoundModifier::None
+ };
+ bounds.push(TraitTyParamBound(poly_trait, modifier));
+ }
+ if has_parens {
+ self.expect(&token::CloseDelim(token::Paren))?;
+ if let Some(&RegionTyParamBound(..)) = bounds.last() {
+ self.span_err(self.prev_span,
+ "parenthesized lifetime bounds are not supported");
+ }
+ }
} else {
break
}