about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-04-21 00:19:06 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-04-21 21:32:44 +0300
commit6e75def7dba6ee28660b8479f0ede72986c299f7 (patch)
tree3fb0cfafe35d43056786593e5593c74bb76a9fbd
parente038f58105cc1769a9c7991981822d01ebffe277 (diff)
downloadrust-6e75def7dba6ee28660b8479f0ede72986c299f7.tar.gz
rust-6e75def7dba6ee28660b8479f0ede72986c299f7.zip
Fix issue with single question mark or paren
-rw-r--r--src/doc/grammar.md5
-rw-r--r--src/libsyntax/parse/parser.rs51
-rw-r--r--src/test/parse-fail/bound-single-question-mark.rs13
3 files changed, 44 insertions, 25 deletions
diff --git a/src/doc/grammar.md b/src/doc/grammar.md
index 3fbf9f06d99..12daa24e857 100644
--- a/src/doc/grammar.md
+++ b/src/doc/grammar.md
@@ -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
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 1232411d1ec..79ae1d62915 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -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
diff --git a/src/test/parse-fail/bound-single-question-mark.rs b/src/test/parse-fail/bound-single-question-mark.rs
new file mode 100644
index 00000000000..9dde5268c08
--- /dev/null
+++ b/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 `>`