syntax: Support parentheses around trait bounds
This commit is contained in:
parent
5695c3e943
commit
e038f58105
@ -152,6 +152,7 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
|
|||||||
enum PrevTokenKind {
|
enum PrevTokenKind {
|
||||||
DocComment,
|
DocComment,
|
||||||
Comma,
|
Comma,
|
||||||
|
Plus,
|
||||||
Interpolated,
|
Interpolated,
|
||||||
Eof,
|
Eof,
|
||||||
Other,
|
Other,
|
||||||
@ -1061,6 +1062,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.prev_token_kind = match self.token {
|
self.prev_token_kind = match self.token {
|
||||||
token::DocComment(..) => PrevTokenKind::DocComment,
|
token::DocComment(..) => PrevTokenKind::DocComment,
|
||||||
token::Comma => PrevTokenKind::Comma,
|
token::Comma => PrevTokenKind::Comma,
|
||||||
|
token::BinOp(token::Plus) => PrevTokenKind::Plus,
|
||||||
token::Interpolated(..) => PrevTokenKind::Interpolated,
|
token::Interpolated(..) => PrevTokenKind::Interpolated,
|
||||||
token::Eof => PrevTokenKind::Eof,
|
token::Eof => PrevTokenKind::Eof,
|
||||||
_ => PrevTokenKind::Other,
|
_ => PrevTokenKind::Other,
|
||||||
@ -1354,20 +1356,29 @@ impl<'a> Parser<'a> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus;
|
||||||
self.expect(&token::CloseDelim(token::Paren))?;
|
self.expect(&token::CloseDelim(token::Paren))?;
|
||||||
|
|
||||||
if ts.len() == 1 && !last_comma {
|
if ts.len() == 1 && !last_comma {
|
||||||
let ty = ts.into_iter().nth(0).unwrap().unwrap();
|
let ty = ts.into_iter().nth(0).unwrap().unwrap();
|
||||||
|
let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
|
||||||
match ty.node {
|
match ty.node {
|
||||||
// Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318).
|
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
|
||||||
TyKind::Path(None, ref path)
|
TyKind::Path(None, ref path) if maybe_bounds => {
|
||||||
if allow_plus && self.token == token::BinOp(token::Plus) => {
|
|
||||||
self.bump(); // `+`
|
self.bump(); // `+`
|
||||||
let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo.to(self.prev_span));
|
let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo.to(self.prev_span));
|
||||||
let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
|
let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
|
||||||
bounds.append(&mut self.parse_ty_param_bounds()?);
|
bounds.append(&mut self.parse_ty_param_bounds()?);
|
||||||
TyKind::TraitObject(bounds)
|
TyKind::TraitObject(bounds)
|
||||||
}
|
}
|
||||||
|
TyKind::TraitObject(ref bounds)
|
||||||
|
if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
|
||||||
|
self.bump(); // `+`
|
||||||
|
let mut bounds = bounds.clone();
|
||||||
|
bounds.append(&mut self.parse_ty_param_bounds()?);
|
||||||
|
TyKind::TraitObject(bounds)
|
||||||
|
}
|
||||||
|
// `(TYPE)`
|
||||||
_ => TyKind::Paren(P(ty))
|
_ => TyKind::Paren(P(ty))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -4070,10 +4081,12 @@ impl<'a> Parser<'a> {
|
|||||||
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
|
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
|
||||||
// BOUND = TY_BOUND | LT_BOUND
|
// BOUND = TY_BOUND | LT_BOUND
|
||||||
// LT_BOUND = LIFETIME (e.g. `'a`)
|
// 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> {
|
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
|
||||||
let mut bounds = Vec::new();
|
let mut bounds = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
|
let has_parens = self.eat(&token::OpenDelim(token::Paren));
|
||||||
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
|
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
|
||||||
if self.check_lifetime() {
|
if self.check_lifetime() {
|
||||||
if let Some(question_span) = question {
|
if let Some(question_span) = question {
|
||||||
@ -4081,6 +4094,11 @@ impl<'a> Parser<'a> {
|
|||||||
"`?` may only modify trait bounds, not lifetime bounds");
|
"`?` may only modify trait bounds, not lifetime bounds");
|
||||||
}
|
}
|
||||||
bounds.push(RegionTyParamBound(self.expect_lifetime()));
|
bounds.push(RegionTyParamBound(self.expect_lifetime()));
|
||||||
|
if has_parens {
|
||||||
|
self.expect(&token::CloseDelim(token::Paren))?;
|
||||||
|
self.span_err(self.prev_span,
|
||||||
|
"parenthesized lifetime bounds are not supported");
|
||||||
|
}
|
||||||
} else if self.check_keyword(keywords::For) || self.check_path() {
|
} else if self.check_keyword(keywords::For) || self.check_path() {
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||||
@ -4092,6 +4110,9 @@ impl<'a> Parser<'a> {
|
|||||||
TraitBoundModifier::None
|
TraitBoundModifier::None
|
||||||
};
|
};
|
||||||
bounds.push(TraitTyParamBound(poly_trait, modifier));
|
bounds.push(TraitTyParamBound(poly_trait, modifier));
|
||||||
|
if has_parens {
|
||||||
|
self.expect(&token::CloseDelim(token::Paren))?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
20
src/test/parse-fail/trait-object-bad-parens.rs
Normal file
20
src/test/parse-fail/trait-object-bad-parens.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only -Z continue-parse-after-error
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _: Box<((Copy)) + Copy>;
|
||||||
|
//~^ ERROR expected a path on the left-hand side of `+`, not `((Copy))`
|
||||||
|
let _: Box<(Copy + Copy) + Copy>;
|
||||||
|
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
|
||||||
|
let _: Box<(Copy +) + Copy>;
|
||||||
|
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)`
|
||||||
|
}
|
18
src/test/parse-fail/trait-object-lifetime-parens.rs
Normal file
18
src/test/parse-fail/trait-object-lifetime-parens.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only -Z continue-parse-after-error
|
||||||
|
|
||||||
|
fn f<T: Copy + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not supported
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _: Box<Copy + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
|
||||||
|
let _: Box<('a) + Copy>; //~ ERROR expected type, found `'a`
|
||||||
|
}
|
21
src/test/parse-fail/trait-object-trait-parens.rs
Normal file
21
src/test/parse-fail/trait-object-trait-parens.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: -Z parse-only
|
||||||
|
|
||||||
|
fn f<T: (Copy) + (?Sized) + (for<'a> Trait<'a>)>() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
|
||||||
|
let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>;
|
||||||
|
let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAIL //~ ERROR
|
Loading…
x
Reference in New Issue
Block a user