Skip to content

Commit

Permalink
Fix issue with single question mark or paren
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Apr 21, 2017
1 parent e038f58 commit 6e75def
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/doc/grammar.md
Expand Up @@ -781,10 +781,11 @@ never_type : "!" ;
### Type parameter bounds

```antlr
bound-list := bound | bound '+' bound-list '+' ?
bound := ty_bound | lt_bound
lt_bound := lifetime
ty_bound := [?] [ for<lt_param_defs> ] simple_path
bound-list := bound | bound '+' bound-list '+' ?
ty_bound := ty_bound_noparen | (ty_bound_noparen)
ty_bound_noparen := [?] [ for<lt_param_defs> ] simple_path
```

### Self types
Expand Down
51 changes: 28 additions & 23 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -4086,32 +4086,37 @@ impl<'a> Parser<'a> {
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
let mut bounds = Vec::new();
loop {
let has_parens = self.eat(&token::OpenDelim(token::Paren));
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()));
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() {
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
Expand Down
13 changes: 13 additions & 0 deletions src/test/parse-fail/bound-single-question-mark.rs
@@ -0,0 +1,13 @@
// 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: ?>() {} //~ ERROR expected identifier, found `>`

0 comments on commit 6e75def

Please sign in to comment.