diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index b7b027102b2..9720bb84264 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -146,6 +146,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { TyKind::TraitObject(ref bounds) => { self.no_questions_in_bounds(bounds, "trait object types", false); } + TyKind::ImplTrait(ref bounds) => { + if !bounds.iter() + .any(|b| if let TraitTyParamBound(..) = *b { true } else { false }) { + self.err_handler().span_err(ty.span, "at least one trait must be specified"); + } + } _ => {} } @@ -284,6 +290,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_vis(self, vis) } + + fn visit_generics(&mut self, g: &'a Generics) { + let mut seen_default = None; + for ty_param in &g.ty_params { + if ty_param.default.is_some() { + seen_default = Some(ty_param.span); + } else if let Some(span) = seen_default { + self.err_handler() + .span_err(span, "type parameters with a default must be trailing"); + break + } + } + for predicate in &g.where_clause.predicates { + if let WherePredicate::EqPredicate(ref predicate) = *predicate { + self.err_handler().span_err(predicate.span, "equality constraints are not yet \ + supported in where clauses (#20041)"); + } + } + visit::walk_generics(self, g) + } } pub fn check_crate(session: &Session, krate: &Crate) { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 841b4516a18..1fd03b33a76 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1833,11 +1833,8 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } } - &hir::WherePredicate::EqPredicate(ref eq_pred) => { + &hir::WherePredicate::EqPredicate(..) => { // FIXME(#20041) - span_bug!(eq_pred.span, - "Equality constraints are not yet \ - implemented (#20041)") } } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index eedca0ffb63..080bc5072d6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -842,8 +842,11 @@ impl Clean for hir::WherePredicate { } } - hir::WherePredicate::EqPredicate(_) => { - unimplemented!() // FIXME(#20041) + hir::WherePredicate::EqPredicate(ref wrp) => { + WherePredicate::EqPredicate { + lhs: wrp.lhs_ty.clean(cx), + rhs: wrp.rhs_ty.clean(cx) + } } } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b564cc337e4..3480db8ec3b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -22,7 +22,7 @@ use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; use ast::{Ident, ImplItem, Item, ItemKind}; -use ast::{Lit, LitKind, UintTy}; +use ast::{Lifetime, LifetimeDef, Lit, LitKind, UintTy}; use ast::Local; use ast::MacStmtStyle; use ast::Mac_; @@ -193,14 +193,22 @@ pub enum TokenType { Token(token::Token), Keyword(keywords::Keyword), Operator, + Lifetime, + Ident, + Path, + Type, } impl TokenType { fn to_string(&self) -> String { match *self { TokenType::Token(ref t) => format!("`{}`", Parser::token_to_string(t)), - TokenType::Operator => "an operator".to_string(), TokenType::Keyword(kw) => format!("`{}`", kw.name()), + TokenType::Operator => "an operator".to_string(), + TokenType::Lifetime => "lifetime".to_string(), + TokenType::Ident => "identifier".to_string(), + TokenType::Path => "path".to_string(), + TokenType::Type => "type".to_string(), } } } @@ -553,6 +561,33 @@ impl<'a> Parser<'a> { } } + fn check_ident(&mut self) -> bool { + if self.token.is_ident() { + true + } else { + self.expected_tokens.push(TokenType::Ident); + false + } + } + + fn check_path(&mut self) -> bool { + if self.token.is_path_start() { + true + } else { + self.expected_tokens.push(TokenType::Path); + false + } + } + + fn check_type(&mut self) -> bool { + if self.token.can_begin_type() { + true + } else { + self.expected_tokens.push(TokenType::Type); + false + } + } + /// Expect and consume an `&`. If `&&` is seen, replace it with a single /// `&` and continue. If an `&` is not seen, signal an error. fn expect_and(&mut self) -> PResult<'a, ()> { @@ -639,13 +674,7 @@ impl<'a> Parser<'a> { let lo = span.lo + BytePos(1); Ok(self.bump_with(token::Eq, lo, span.hi)) } - _ => { - let gt_str = Parser::token_to_string(&token::Gt); - let this_token_str = self.this_token_to_string(); - Err(self.fatal(&format!("expected `{}`, found `{}`", - gt_str, - this_token_str))) - } + _ => self.unexpected() } } @@ -972,20 +1001,11 @@ impl<'a> Parser<'a> { Parses whatever can come after a `for` keyword in a type. The `for` hasn't been consumed. - Deprecated: - - - for <'lt> |S| -> T - - Eventually: - - for <'lt> [unsafe] [extern "ABI"] fn (S) -> T - - for <'lt> path::foo(a, b) - + - for <'lt> path::foo(a, b) + Trait + 'a */ - // parse <'lt> let lo = self.span.lo; - let lifetime_defs = self.parse_late_bound_lifetime_defs()?; // examine next token to decide to do @@ -994,9 +1014,9 @@ impl<'a> Parser<'a> { } else { let hi = self.span.hi; let trait_ref = self.parse_trait_ref()?; - let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs, - trait_ref: trait_ref, - span: mk_sp(lo, hi)}; + let poly_trait_ref = PolyTraitRef { bound_lifetimes: lifetime_defs, + trait_ref: trait_ref, + span: mk_sp(lo, hi)}; let other_bounds = if self.eat(&token::BinOp(token::Plus)) { self.parse_ty_param_bounds()? } else { @@ -1011,18 +1031,9 @@ impl<'a> Parser<'a> { } pub fn parse_impl_trait_type(&mut self) -> PResult<'a, TyKind> { - /* - Parses whatever can come after a `impl` keyword in a type. - The `impl` has already been consumed. - */ - - let bounds = self.parse_ty_param_bounds()?; - - if !bounds.iter().any(|b| if let TraitTyParamBound(..) = *b { true } else { false }) { - self.span_err(self.prev_span, "at least one trait must be specified"); - } - - Ok(ast::TyKind::ImplTrait(bounds)) + // Parses whatever can come after a `impl` keyword in a type. + // The `impl` has already been consumed. + Ok(ast::TyKind::ImplTrait(self.parse_ty_param_bounds()?)) } pub fn parse_ty_path(&mut self) -> PResult<'a, TyKind> { @@ -1030,7 +1041,7 @@ impl<'a> Parser<'a> { } /// parse a TyKind::BareFn type: - pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec) + pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec) -> PResult<'a, TyKind> { /* @@ -1202,13 +1213,6 @@ impl<'a> Parser<'a> { }) } - /// Parse a possibly mutable type - pub fn parse_mt(&mut self) -> PResult<'a, MutTy> { - let mutbl = self.parse_mutability()?; - let t = self.parse_ty_no_plus()?; - Ok(MutTy { ty: t, mutbl: mutbl }) - } - /// Parse optional return type [ -> TY ] in function decl pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> { if self.eat(&token::RArrow) { @@ -1260,8 +1264,8 @@ impl<'a> Parser<'a> { pprust::ty_to_string(&lhs)); err.span_label(lhs.span, &format!("expected a path")); let hi = bounds.iter().map(|x| match *x { - ast::TraitTyParamBound(ref tr, _) => tr.span.hi, - ast::RegionTyParamBound(ref r) => r.span.hi, + TraitTyParamBound(ref tr, _) => tr.span.hi, + RegionTyParamBound(ref r) => r.span.hi, }).max_by_key(|x| x.to_usize()); let full_span = hi.map(|hi| Span { lo: lhs.span.lo, @@ -1309,9 +1313,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; - let t = if self.check(&token::OpenDelim(token::Paren)) { - self.bump(); - + let t = if self.eat(&token::OpenDelim(token::Paren)) { // (t) is a parenthesized ty // (t,) is the type of a tuple with only one field, // of type t @@ -1319,9 +1321,8 @@ impl<'a> Parser<'a> { let mut last_comma = false; while self.token != token::CloseDelim(token::Paren) { ts.push(self.parse_ty()?); - if self.check(&token::Comma) { + if self.eat(&token::Comma) { last_comma = true; - self.bump(); } else { last_comma = false; break; @@ -1336,13 +1337,11 @@ impl<'a> Parser<'a> { } } else if self.eat(&token::Not) { TyKind::Never - } else if self.check(&token::BinOp(token::Star)) { + } else if self.eat(&token::BinOp(token::Star)) { // STAR POINTER (bare pointer?) - self.bump(); TyKind::Ptr(self.parse_ptr()?) - } else if self.check(&token::OpenDelim(token::Bracket)) { + } else if self.eat(&token::OpenDelim(token::Bracket)) { // VECTOR - self.expect(&token::OpenDelim(token::Bracket))?; let t = self.parse_ty()?; // Parse the `; e` in `[ i32; e ]` @@ -1354,13 +1353,15 @@ impl<'a> Parser<'a> { self.expect(&token::CloseDelim(token::Bracket))?; t } else if self.check(&token::BinOp(token::And)) || - self.token == token::AndAnd { + self.check(&token::AndAnd) { // BORROWED POINTER self.expect_and()?; self.parse_borrowed_pointee()? } else if self.check_keyword(keywords::For) { + // FIXME `+` has incorrect priority in trait object types starting with `for` (#39317). self.parse_for_in_type()? } else if self.eat_keyword(keywords::Impl) { + // FIXME figure out priority of `+` in `impl Trait1 + Trait2` (#34511). self.parse_impl_trait_type()? } else if self.token_is_bare_fn_keyword() { // BARE FUNCTION @@ -1373,10 +1374,7 @@ impl<'a> Parser<'a> { self.expect(&token::CloseDelim(token::Paren))?; TyKind::Typeof(e) } else if self.eat_lt() { - - let (qself, path) = - self.parse_qualified_path(PathStyle::Type)?; - + let (qself, path) = self.parse_qualified_path(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.token.is_path_start() { let path = self.parse_path(PathStyle::Type)?; @@ -1406,10 +1404,10 @@ impl<'a> Parser<'a> { pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { // look for `&'lt` or `&'foo ` and interpret `foo` as the region name: - let opt_lifetime = self.parse_opt_lifetime()?; - - let mt = self.parse_mt()?; - return Ok(TyKind::Rptr(opt_lifetime, mt)); + let opt_lifetime = self.eat_lifetime(); + let mutbl = self.parse_mutability()?; + let ty = self.parse_ty_no_plus()?; + return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty: ty, mutbl: mutbl })); } pub fn parse_ptr(&mut self) -> PResult<'a, MutTy> { @@ -1505,8 +1503,7 @@ impl<'a> Parser<'a> { } pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option>> { - if self.check(&token::Semi) { - self.bump(); + if self.eat(&token::Semi) { Ok(Some(self.parse_expr()?)) } else { Ok(None) @@ -1728,7 +1725,8 @@ impl<'a> Parser<'a> { // Parse types, optionally. let parameters = if self.eat_lt() { - let (lifetimes, types, bindings) = self.parse_generic_values_after_lt()?; + let (lifetimes, types, bindings) = self.parse_generic_args()?; + self.expect_gt()?; ast::AngleBracketedParameterData { lifetimes: lifetimes, types: types, @@ -1786,7 +1784,8 @@ impl<'a> Parser<'a> { // Check for a type segment. if self.eat_lt() { // Consumed `a::b::<`, go look for types - let (lifetimes, types, bindings) = self.parse_generic_values_after_lt()?; + let (lifetimes, types, bindings) = self.parse_generic_args()?; + self.expect_gt()?; segments.push(ast::PathSegment { identifier: identifier, parameters: ast::AngleBracketedParameterData { @@ -1828,124 +1827,24 @@ impl<'a> Parser<'a> { } } - /// parses 0 or 1 lifetime - pub fn parse_opt_lifetime(&mut self) -> PResult<'a, Option> { + /// Parse single lifetime 'a or nothing. + pub fn eat_lifetime(&mut self) -> Option { match self.token { - token::Lifetime(..) => { - Ok(Some(self.parse_lifetime()?)) - } - _ => { - Ok(None) - } - } - } - - /// Parses a single lifetime - /// Matches lifetime = LIFETIME - pub fn parse_lifetime(&mut self) -> PResult<'a, ast::Lifetime> { - match self.token { - token::Lifetime(i) => { - let span = self.span; + token::Lifetime(ident) => { self.bump(); - return Ok(ast::Lifetime { + Some(Lifetime { id: ast::DUMMY_NODE_ID, - span: span, - name: i.name - }); + span: self.prev_span, + name: ident.name + }) } _ => { - return Err(self.fatal("expected a lifetime name")); + self.expected_tokens.push(TokenType::Lifetime); + None } } } - /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def = - /// lifetime [':' lifetimes]` - /// - /// If `followed_by_ty_params` is None, then we are in a context - /// where only lifetime parameters are allowed, and thus we should - /// error if we encounter attributes after the bound lifetimes. - /// - /// If `followed_by_ty_params` is Some(r), then there may be type - /// parameter bindings after the lifetimes, so we should pass - /// along the parsed attributes to be attached to the first such - /// type parmeter. - pub fn parse_lifetime_defs(&mut self, - followed_by_ty_params: Option<&mut Vec>) - -> PResult<'a, Vec> - { - let mut res = Vec::new(); - loop { - let attrs = self.parse_outer_attributes()?; - match self.token { - token::Lifetime(_) => { - let lifetime = self.parse_lifetime()?; - let bounds = - if self.eat(&token::Colon) { - self.parse_lifetimes(token::BinOp(token::Plus))? - } else { - Vec::new() - }; - res.push(ast::LifetimeDef { attrs: attrs.into(), - lifetime: lifetime, - bounds: bounds }); - } - - _ => { - if let Some(recv) = followed_by_ty_params { - assert!(recv.is_empty()); - *recv = attrs; - debug!("parse_lifetime_defs ret {:?}", res); - return Ok(res); - } else if !attrs.is_empty() { - let msg = "trailing attribute after lifetime parameters"; - return Err(self.fatal(msg)); - } - } - } - - match self.token { - token::Comma => { self.bump();} - token::Gt => { return Ok(res); } - token::BinOp(token::Shr) => { return Ok(res); } - _ => { - let this_token_str = self.this_token_to_string(); - let msg = format!("expected `,` or `>` after lifetime \ - name, found `{}`", - this_token_str); - return Err(self.fatal(&msg[..])); - } - } - } - } - - /// matches lifetimes = ( lifetime ) | ( lifetime , lifetimes ) actually, it matches the empty - /// one too, but putting that in there messes up the grammar.... - /// - /// Parses zero or more comma separated lifetimes. Expects each lifetime to be followed by - /// either a comma or `>`. Used when parsing type parameter lists, where we expect something - /// like `<'a, 'b, T>`. - pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<'a, Vec> { - - let mut res = Vec::new(); - loop { - match self.token { - token::Lifetime(_) => { - res.push(self.parse_lifetime()?); - } - _ => { - return Ok(res); - } - } - - if self.token != sep { - return Ok(res); - } - - self.bump(); - } - } - /// Parse mutability (`mut` or nothing). pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> { if self.eat_keyword(keywords::Mut) { @@ -2459,7 +2358,9 @@ impl<'a> Parser<'a> { -> PResult<'a, P> { let (_, tys, bindings) = if self.eat(&token::ModSep) { self.expect_lt()?; - self.parse_generic_values_after_lt()? + let args = self.parse_generic_args()?; + self.expect_gt()?; + args } else { (Vec::new(), Vec::new(), Vec::new()) }; @@ -4094,69 +3995,68 @@ impl<'a> Parser<'a> { }).emit(); } - // Parses a sequence of bounds if a `:` is found, - // otherwise returns empty list. - fn parse_colon_then_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds> - { - if !self.eat(&token::Colon) { - Ok(Vec::new()) - } else { - self.parse_ty_param_bounds() - } - } - - // matches bounds = ( boundseq )? - // where boundseq = ( polybound + boundseq ) | polybound - // and polybound = ( 'for' '<' 'region '>' )? bound - // and bound = 'region | trait_ref + // Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`. + // BOUND = TY_BOUND | LT_BOUND + // LT_BOUND = LIFETIME (e.g. `'a`) + // TY_BOUND = [?] [for] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`) fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds> { - let mut result = vec![]; + let mut bounds = Vec::new(); loop { - let question_span = self.span; - let ate_question = self.eat(&token::Question); - match self.token { - token::Lifetime(lifetime) => { - if ate_question { - self.span_err(question_span, - "`?` may only modify trait bounds, not lifetime bounds"); - } - result.push(RegionTyParamBound(ast::Lifetime { - id: ast::DUMMY_NODE_ID, - span: self.span, - name: lifetime.name - })); - self.bump(); + let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None }; + if let Some(lifetime) = self.eat_lifetime() { + if let Some(question_span) = question { + self.span_err(question_span, + "`?` may only modify trait bounds, not lifetime bounds"); } - _ if self.token.is_path_start() || self.token.is_keyword(keywords::For) => { - let poly_trait_ref = self.parse_poly_trait_ref()?; - let modifier = if ate_question { - TraitBoundModifier::Maybe - } else { - TraitBoundModifier::None - }; - result.push(TraitTyParamBound(poly_trait_ref, modifier)) - } - _ => break, - } + bounds.push(RegionTyParamBound(lifetime)); + } else {if self.check_keyword(keywords::For) || self.check_path() { + let poly_trait_ref = self.parse_poly_trait_ref()?; + let modifier = if question.is_some() { + TraitBoundModifier::Maybe + } else { + TraitBoundModifier::None + }; + bounds.push(TraitTyParamBound(poly_trait_ref, modifier)); + } else { + break + }} if !self.eat(&token::BinOp(token::Plus)) { - break; + break } } - return Ok(result); + return Ok(bounds); + } + + // Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`. + // BOUND = LT_BOUND (e.g. `'a`) + fn parse_lt_param_bounds(&mut self) -> Vec { + let mut lifetimes = Vec::new(); + while let Some(lifetime) = self.eat_lifetime() { + lifetimes.push(lifetime); + + if !self.eat(&token::BinOp(token::Plus)) { + break + } + } + lifetimes } /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )? - fn parse_ty_param(&mut self, preceding_attrs: Vec) -> PResult<'a, TyParam> { + fn parse_ty_param(&mut self, preceding_attrs: Vec) -> PResult<'a, TyParam> { let span = self.span; let ident = self.parse_ident()?; - let bounds = self.parse_colon_then_ty_param_bounds()?; + // Parse optional colon and param bounds. + let bounds = if self.eat(&token::Colon) { + self.parse_ty_param_bounds()? + } else { + Vec::new() + }; - let default = if self.check(&token::Eq) { - self.bump(); + let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None @@ -4172,6 +4072,51 @@ impl<'a> Parser<'a> { }) } + /// Parses (possibly empty) list of lifetime and type parameters, possibly including + /// trailing comma and erroneous trailing attributes. + pub fn parse_generic_params(&mut self) -> PResult<'a, (Vec, Vec)> { + let mut lifetime_defs = Vec::new(); + let mut ty_params = Vec::new(); + let mut seen_ty_param = false; + loop { + let attrs = self.parse_outer_attributes()?; + if let Some(lifetime) = self.eat_lifetime() { + // Parse lifetime parameter. + let bounds = if self.eat(&token::Colon) { + self.parse_lt_param_bounds() + } else { + Vec::new() + }; + lifetime_defs.push(LifetimeDef { + attrs: attrs.into(), + lifetime: lifetime, + bounds: bounds, + }); + if seen_ty_param { + self.span_err(self.prev_span, + "lifetime parameters must be declared prior to type parameters"); + } + } else {if self.check_ident() { + // Parse type parameter. + ty_params.push(self.parse_ty_param(attrs)?); + seen_ty_param = true; + } else { + // Check for trailing attributes and stop parsing. + if !attrs.is_empty() { + let param_kind = if seen_ty_param { "type" } else { "lifetime" }; + self.span_err(attrs[0].span, + &format!("trailing attribute after {} parameters", param_kind)); + } + break + }} + + if !self.eat(&token::Comma) { + break + } + } + Ok((lifetime_defs, ty_params)) + } + /// Parse a set of optional generic type parameter declarations. Where /// clauses are not parsed here, and must be added later via /// `parse_where_clause()`. @@ -4181,45 +4126,11 @@ impl<'a> Parser<'a> { /// where typaramseq = ( typaram ) | ( typaram , typaramseq ) pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { maybe_whole!(self, NtGenerics, |x| x); - let span_lo = self.span.lo; - if self.eat(&token::Lt) { - // Upon encountering attribute in generics list, we do not - // know if it is attached to lifetime or to type param. - // - // Solution: 1. eagerly parse attributes in tandem with - // lifetime defs, 2. store last set of parsed (and unused) - // attributes in `attrs`, and 3. pass in those attributes - // when parsing formal type param after lifetime defs. - let mut attrs = vec![]; - let lifetime_defs = self.parse_lifetime_defs(Some(&mut attrs))?; - let mut seen_default = false; - let mut post_lifetime_attrs = Some(attrs); - let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| { - p.forbid_lifetime()?; - // Move out of `post_lifetime_attrs` if present. O/w - // not first type param: parse attributes anew. - let attrs = match post_lifetime_attrs.as_mut() { - None => p.parse_outer_attributes()?, - Some(attrs) => mem::replace(attrs, vec![]), - }; - post_lifetime_attrs = None; - let ty_param = p.parse_ty_param(attrs)?; - if ty_param.default.is_some() { - seen_default = true; - } else if seen_default { - let prev_span = p.prev_span; - p.span_err(prev_span, - "type parameters with a default must be trailing"); - } - Ok(ty_param) - })?; - if let Some(attrs) = post_lifetime_attrs { - if !attrs.is_empty() { - self.span_err(attrs[0].span, - "trailing attribute after lifetime parameters"); - } - } + let span_lo = self.span.lo; + if self.eat_lt() { + let (lifetime_defs, ty_params) = self.parse_generic_params()?; + self.expect_gt()?; Ok(ast::Generics { lifetimes: lifetime_defs, ty_params: ty_params, @@ -4234,85 +4145,52 @@ impl<'a> Parser<'a> { } } - fn parse_generic_values_after_lt(&mut self) -> PResult<'a, (Vec, - Vec>, - Vec)> { - let span_lo = self.span.lo; - let lifetimes = self.parse_lifetimes(token::Comma)?; - - let missing_comma = !lifetimes.is_empty() && - !self.token.is_like_gt() && - self.prev_token_kind != PrevTokenKind::Comma; - - if missing_comma { - - let msg = format!("expected `,` or `>` after lifetime \ - name, found `{}`", - self.this_token_to_string()); - let mut err = self.diagnostic().struct_span_err(self.span, &msg); - - let span_hi = self.span.hi; - let span_hi = match self.parse_ty_no_plus() { - Ok(..) => self.span.hi, - Err(ref mut err) => { - self.cancel(err); - span_hi + /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, + /// possibly including trailing comma. + fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec>, Vec)> { + let mut lifetimes = Vec::new(); + let mut types = Vec::new(); + let mut bindings = Vec::new(); + let mut seen_type = false; + let mut seen_binding = false; + loop { + if let Some(lifetime) = self.eat_lifetime() { + // Parse lifetime argument. + lifetimes.push(lifetime); + if seen_type || seen_binding { + self.span_err(self.prev_span, + "lifetime parameters must be declared prior to type parameters"); } - }; - - let msg = format!("did you mean a single argument type &'a Type, \ - or did you mean the comma-separated arguments \ - 'a, Type?"); - err.span_note(mk_sp(span_lo, span_hi), &msg); - return Err(err); - } - - // First parse types. - let (types, returned) = self.parse_seq_to_gt_or_return( - Some(token::Comma), - |p| { - p.forbid_lifetime()?; - if p.look_ahead(1, |t| t == &token::Eq) { - Ok(None) - } else { - Ok(Some(p.parse_ty()?)) - } - } - )?; - - // If we found the `>`, don't continue. - if !returned { - return Ok((lifetimes, types, Vec::new())); - } - - // Then parse type bindings. - let bindings = self.parse_seq_to_gt( - Some(token::Comma), - |p| { - p.forbid_lifetime()?; - let lo = p.span.lo; - let ident = p.parse_ident()?; - p.expect(&token::Eq)?; - let ty = p.parse_ty_no_plus()?; - let hi = ty.span.hi; - let span = mk_sp(lo, hi); - return Ok(TypeBinding{id: ast::DUMMY_NODE_ID, + } else {if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) { + // Parse associated type binding. + let lo = self.span.lo; + let ident = self.parse_ident()?; + self.bump(); + let ty = self.parse_ty()?; + bindings.push(TypeBinding { + id: ast::DUMMY_NODE_ID, ident: ident, ty: ty, - span: span, + span: mk_sp(lo, self.prev_span.hi), }); - } - )?; - Ok((lifetimes, types, bindings)) - } + seen_binding = true; + } else if self.check_type() { + // Parse type argument. + types.push(self.parse_ty()?); + if seen_binding { + self.span_err(types[types.len() - 1].span, + "type parameters must be declared prior to associated type bindings"); + } + seen_type = true; + } else { + break + }} - fn forbid_lifetime(&mut self) -> PResult<'a, ()> { - if self.token.is_lifetime() { - let span = self.span; - return Err(self.diagnostic().struct_span_err(span, "lifetime parameters must be \ - declared prior to type parameters")) + if !self.eat(&token::Comma) { + break + } } - Ok(()) + Ok((lifetimes, types, bindings)) } /// Parses an optional `where` clause and places it in `generics`. @@ -4320,7 +4198,7 @@ impl<'a> Parser<'a> { /// ```ignore /// where T : Trait + 'b, 'a : 'b /// ``` - pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> { + pub fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { maybe_whole!(self, NtWhereClause, |x| x); let mut where_clause = WhereClause { @@ -4332,7 +4210,7 @@ impl<'a> Parser<'a> { return Ok(where_clause); } - // This is a temporary hack. + // This is a temporary future proofing. // // We are considering adding generics to the `where` keyword as an alternative higher-rank // parameter syntax (as in `where<'a>` or `where`. To avoid that being a breaking @@ -4349,106 +4227,64 @@ impl<'a> Parser<'a> { } } - let mut parsed_something = false; loop { let lo = self.span.lo; - match self.token { - token::OpenDelim(token::Brace) => { - break - } + if let Some(lifetime) = self.eat_lifetime() { + // Bounds starting with a colon are mandatory, but possibly empty. + self.expect(&token::Colon)?; + let bounds = self.parse_lt_param_bounds(); + where_clause.predicates.push(ast::WherePredicate::RegionPredicate( + ast::WhereRegionPredicate { + span: mk_sp(lo, self.prev_span.hi), + lifetime: lifetime, + bounds: bounds, + } + )); + } else {if self.check_type() { + // Parse optional `for<'a, 'b>`. + // This `for` is parsed greedily and applies to the whole predicate, + // the bounded type can have its own `for` applying only to it. + // Example 1: for<'a> Trait1<'a>: Trait2<'a /*ok*/> + // Example 2: (for<'a> Trait1<'a>): Trait2<'a /*not ok*/> + // Example 3: for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /*ok*/, 'b /*not ok*/> + let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - token::Lifetime(..) => { - let bounded_lifetime = - self.parse_lifetime()?; - - self.expect(&token::Colon)?; - - let bounds = - self.parse_lifetimes(token::BinOp(token::Plus))?; - - let hi = self.prev_span.hi; - let span = mk_sp(lo, hi); - - where_clause.predicates.push(ast::WherePredicate::RegionPredicate( - ast::WhereRegionPredicate { - span: span, - lifetime: bounded_lifetime, - bounds: bounds + // Parse type with mandatory colon and (possibly empty) bounds, + // or with mandatory equality sign and the second type. + let ty = self.parse_ty()?; + if self.eat(&token::Colon) { + let bounds = self.parse_ty_param_bounds()?; + where_clause.predicates.push(ast::WherePredicate::BoundPredicate( + ast::WhereBoundPredicate { + span: mk_sp(lo, self.prev_span.hi), + bound_lifetimes: lifetime_defs, + bounded_ty: ty, + bounds: bounds, } )); - - parsed_something = true; - } - - _ => { - let bound_lifetimes = if self.eat_keyword(keywords::For) { - // Higher ranked constraint. - self.expect(&token::Lt)?; - let lifetime_defs = self.parse_lifetime_defs(None)?; - self.expect_gt()?; - lifetime_defs - } else { - vec![] - }; - - let bounded_ty = self.parse_ty_no_plus()?; - - if self.eat(&token::Colon) { - let bounds = self.parse_ty_param_bounds()?; - let hi = self.prev_span.hi; - let span = mk_sp(lo, hi); - - if bounds.is_empty() { - self.span_err(span, - "each predicate in a `where` clause must have \ - at least one bound in it"); + // FIXME: Decide what should be used here, `=` or `==`. + } else if self.eat(&token::Eq) || self.eat(&token::EqEq) { + let rhs_ty = self.parse_ty()?; + where_clause.predicates.push(ast::WherePredicate::EqPredicate( + ast::WhereEqPredicate { + span: mk_sp(lo, self.prev_span.hi), + lhs_ty: ty, + rhs_ty: rhs_ty, + id: ast::DUMMY_NODE_ID, } - - where_clause.predicates.push(ast::WherePredicate::BoundPredicate( - ast::WhereBoundPredicate { - span: span, - bound_lifetimes: bound_lifetimes, - bounded_ty: bounded_ty, - bounds: bounds, - })); - - parsed_something = true; - } else if self.eat(&token::Eq) { - // let ty = try!(self.parse_ty_no_plus()); - let hi = self.prev_span.hi; - let span = mk_sp(lo, hi); - // where_clause.predicates.push( - // ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { - // id: ast::DUMMY_NODE_ID, - // span: span, - // path: panic!("NYI"), //bounded_ty, - // ty: ty, - // })); - // parsed_something = true; - // // FIXME(#18433) - self.span_err(span, - "equality constraints are not yet supported \ - in where clauses (#20041)"); - } else { - let prev_span = self.prev_span; - self.span_err(prev_span, - "unexpected token in `where` clause"); - } + )); + } else { + return self.unexpected(); } - }; + } else { + break + }} if !self.eat(&token::Comma) { break } } - if !parsed_something { - let prev_span = self.prev_span; - self.span_err(prev_span, - "a `where` clause must have at least one predicate \ - in it"); - } - Ok(where_clause) } @@ -4547,13 +4383,13 @@ impl<'a> Parser<'a> { } else if self.look_ahead(1, |t| t.is_lifetime()) && isolated_self(self, 2) { self.bump(); - let lt = self.parse_lifetime()?; + let lt = self.eat_lifetime().expect("not a lifetime"); (SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self)) } else if self.look_ahead(1, |t| t.is_lifetime()) && self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) && isolated_self(self, 3) { self.bump(); - let lt = self.parse_lifetime()?; + let lt = self.eat_lifetime().expect("not a lifetime"); self.bump(); (SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self)) } else { @@ -4855,8 +4691,12 @@ impl<'a> Parser<'a> { let ident = self.parse_ident()?; let mut tps = self.parse_generics()?; - // Parse supertrait bounds. - let bounds = self.parse_colon_then_ty_param_bounds()?; + // Parse optional colon and supertrait bounds. + let bounds = if self.eat(&token::Colon) { + self.parse_ty_param_bounds()? + } else { + Vec::new() + }; tps.where_clause = self.parse_where_clause()?; @@ -4947,17 +4787,21 @@ impl<'a> Parser<'a> { /// Parse a::B fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> { - Ok(ast::TraitRef { + Ok(TraitRef { path: self.parse_path(PathStyle::Type)?, ref_id: ast::DUMMY_NODE_ID, }) } - fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { + fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { if self.eat_keyword(keywords::For) { - self.expect(&token::Lt)?; - let lifetime_defs = self.parse_lifetime_defs(None)?; + self.expect_lt()?; + let (lifetime_defs, ty_params) = self.parse_generic_params()?; self.expect_gt()?; + if !ty_params.is_empty() { + self.span_err(ty_params[0].span, + "only lifetime parameters can be used in this context"); + } Ok(lifetime_defs) } else { Ok(Vec::new()) @@ -4969,7 +4813,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - Ok(ast::PolyTraitRef { + Ok(PolyTraitRef { bound_lifetimes: lifetime_defs, trait_ref: self.parse_trait_ref()?, span: mk_sp(lo, self.prev_span.hi), diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index bf790b96e37..d9e47a6b56e 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -187,6 +187,29 @@ impl Token { } } + /// Returns `true` if the token can appear at the start of a type. + pub fn can_begin_type(&self) -> bool { + match *self { + OpenDelim(Paren) => true, // tuple + OpenDelim(Bracket) => true, // array + Ident(..) => true, // type name or keyword + Underscore => true, // placeholder + Not => true, // never + BinOp(Star) => true, // raw pointer + BinOp(And) => true, // reference + AndAnd => true, // double reference + Lt | BinOp(Shl) => true, // associated path + ModSep => true, // global path + Interpolated(ref nt) => match **nt { + NtTy(..) => true, + NtIdent(..) => true, + NtPath(..) => true, + _ => false, + }, + _ => false, + } + } + /// Returns `true` if the token is any literal pub fn is_lit(&self) -> bool { match *self { diff --git a/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs b/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs index a38a7bfb937..5e09473ab77 100644 --- a/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs +++ b/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs @@ -17,10 +17,7 @@ struct RefAny<'a, T>(&'a T); -impl<#[rustc_1] 'a, #[rustc_2] T, #[oops]> RefAny<'a, T> { - //~^ ERROR expected identifier, found `>` -} +impl<#[rustc_1] 'a, #[rustc_2] T, #[oops]> RefAny<'a, T> {} +//~^ ERROR trailing attribute after type parameters -fn main() { - -} +fn main() {} diff --git a/src/test/compile-fail/generic-non-trailing-defaults.rs b/src/test/compile-fail/generic-non-trailing-defaults.rs new file mode 100644 index 00000000000..77e55203263 --- /dev/null +++ b/src/test/compile-fail/generic-non-trailing-defaults.rs @@ -0,0 +1,20 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Heap; + +struct Vec; +//~^ ERROR type parameters with a default must be trailing + +struct Foo, C>; +//~^ ERROR type parameters with a default must be trailing +//~| ERROR type parameters with a default cannot use forward declared identifiers + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/no-trait.rs b/src/test/compile-fail/impl-trait/no-trait.rs new file mode 100644 index 00000000000..ce61c5bf63d --- /dev/null +++ b/src/test/compile-fail/impl-trait/no-trait.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +fn f() -> impl 'static {} //~ ERROR at least one trait must be specified + +fn main() {} diff --git a/src/test/compile-fail/issue-20616-1.rs b/src/test/compile-fail/issue-20616-1.rs index 5b4f9942d28..a1949df661a 100644 --- a/src/test/compile-fail/issue-20616-1.rs +++ b/src/test/compile-fail/issue-20616-1.rs @@ -16,7 +16,7 @@ type Type_1_<'a, T> = &'a T; -type Type_1<'a T> = &'a T; //~ error: expected `,` or `>` after lifetime name, found `T` +type Type_1<'a T> = &'a T; //~ error: expected one of `,`, `:`, or `>`, found `T` //type Type_2 = Type_1_<'static ()>; // error: expected `,` or `>` after lifetime name, found `(` diff --git a/src/test/compile-fail/issue-20616-2.rs b/src/test/compile-fail/issue-20616-2.rs index 65305ff3ac8..87b836d6872 100644 --- a/src/test/compile-fail/issue-20616-2.rs +++ b/src/test/compile-fail/issue-20616-2.rs @@ -19,7 +19,7 @@ type Type_1_<'a, T> = &'a T; //type Type_1<'a T> = &'a T; // error: expected `,` or `>` after lifetime name, found `T` -type Type_2 = Type_1_<'static ()>; //~ error: expected `,` or `>` after lifetime name, found `(` +type Type_2 = Type_1_<'static ()>; //~ error: expected one of `,` or `>`, found `(` //type Type_3 = Box; // error: expected type, found `,` diff --git a/src/test/compile-fail/issue-20616-3.rs b/src/test/compile-fail/issue-20616-3.rs index 101f81019d9..e5ed46d2cb3 100644 --- a/src/test/compile-fail/issue-20616-3.rs +++ b/src/test/compile-fail/issue-20616-3.rs @@ -22,7 +22,7 @@ type Type_1_<'a, T> = &'a T; //type Type_2 = Type_1_<'static ()>; // error: expected `,` or `>` after lifetime name, found `(` -type Type_3 = Box; //~ error: expected type, found `,` +type Type_3 = Box; //~ error: expected one of `>`, identifier, lifetime, or type, found `,` //type Type_4 = Type_1_<'static,, T>; // error: expected type, found `,` diff --git a/src/test/compile-fail/issue-20616-4.rs b/src/test/compile-fail/issue-20616-4.rs index 6450e9ed68c..9b731289e13 100644 --- a/src/test/compile-fail/issue-20616-4.rs +++ b/src/test/compile-fail/issue-20616-4.rs @@ -25,7 +25,8 @@ type Type_1_<'a, T> = &'a T; //type Type_3 = Box; // error: expected type, found `,` -type Type_4 = Type_1_<'static,, T>; //~ error: expected type, found `,` +type Type_4 = Type_1_<'static,, T>; +//~^ error: expected one of `>`, identifier, lifetime, or type, found `,` type Type_5_<'a> = Type_1_<'a, ()>; diff --git a/src/test/compile-fail/issue-20616-5.rs b/src/test/compile-fail/issue-20616-5.rs index d1840427ad8..5e3b024da9a 100644 --- a/src/test/compile-fail/issue-20616-5.rs +++ b/src/test/compile-fail/issue-20616-5.rs @@ -31,7 +31,8 @@ type Type_1_<'a, T> = &'a T; type Type_5_<'a> = Type_1_<'a, ()>; -type Type_5<'a> = Type_1_<'a, (),,>; //~ error: expected type, found `,` +type Type_5<'a> = Type_1_<'a, (),,>; +//~^ error: expected one of `>`, identifier, lifetime, or type, found `,` //type Type_6 = Type_5_<'a,,>; // error: expected type, found `,` diff --git a/src/test/compile-fail/issue-20616-6.rs b/src/test/compile-fail/issue-20616-6.rs index b0b5bc653f6..b6ee26f9f62 100644 --- a/src/test/compile-fail/issue-20616-6.rs +++ b/src/test/compile-fail/issue-20616-6.rs @@ -34,7 +34,8 @@ type Type_5_<'a> = Type_1_<'a, ()>; //type Type_5<'a> = Type_1_<'a, (),,>; // error: expected type, found `,` -type Type_6 = Type_5_<'a,,>; //~ error: expected type, found `,` +type Type_6 = Type_5_<'a,,>; +//~^ error: expected one of `>`, identifier, lifetime, or type, found `,` //type Type_7 = Box<(),,>; // error: expected type, found `,` diff --git a/src/test/compile-fail/issue-20616-7.rs b/src/test/compile-fail/issue-20616-7.rs index 0958f8b4ed2..fef3dd4e31d 100644 --- a/src/test/compile-fail/issue-20616-7.rs +++ b/src/test/compile-fail/issue-20616-7.rs @@ -37,7 +37,7 @@ type Type_5_<'a> = Type_1_<'a, ()>; //type Type_6 = Type_5_<'a,,>; // error: expected type, found `,` -type Type_7 = Box<(),,>; //~ error: expected type, found `,` +type Type_7 = Box<(),,>; //~ error: expected one of `>`, identifier, lifetime, or type, found `,` //type Type_8<'a,,> = &'a (); // error: expected ident, found `,` diff --git a/src/test/compile-fail/issue-20616-8.rs b/src/test/compile-fail/issue-20616-8.rs index d6cf9acae9b..b7bef47c4f4 100644 --- a/src/test/compile-fail/issue-20616-8.rs +++ b/src/test/compile-fail/issue-20616-8.rs @@ -40,7 +40,7 @@ type Type_5_<'a> = Type_1_<'a, ()>; //type Type_7 = Box<(),,>; // error: expected type, found `,` -type Type_8<'a,,> = &'a (); //~ error: expected identifier, found `,` +type Type_8<'a,,> = &'a (); //~ error: expected one of `>`, identifier, or lifetime, found `,` //type Type_9 = Box; // error: expected identifier, found `,` diff --git a/src/test/compile-fail/issue-20616-9.rs b/src/test/compile-fail/issue-20616-9.rs index d64cec446ef..5c16d24cef8 100644 --- a/src/test/compile-fail/issue-20616-9.rs +++ b/src/test/compile-fail/issue-20616-9.rs @@ -43,4 +43,4 @@ type Type_5_<'a> = Type_1_<'a, ()>; //type Type_8<'a,,> = &'a (); // error: expected identifier, found `,` -type Type_9 = Box; //~ error: expected identifier, found `,` +type Type_9 = Box; //~ error: expected one of `>`, identifier, or lifetime, found `,` diff --git a/src/test/compile-fail/where-equality-constraints.rs b/src/test/compile-fail/where-equality-constraints.rs new file mode 100644 index 00000000000..5b2fe2901c4 --- /dev/null +++ b/src/test/compile-fail/where-equality-constraints.rs @@ -0,0 +1,16 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f() where u8 = u16 {} +//~^ ERROR equality constraints are not yet supported in where clauses +fn g() where for<'a> &(u8,) == u16, {} +//~^ ERROR equality constraints are not yet supported in where clauses + +fn main() {} diff --git a/src/test/compile-fail/where-lifetime-resolution.rs b/src/test/compile-fail/where-lifetime-resolution.rs new file mode 100644 index 00000000000..f4c6842206d --- /dev/null +++ b/src/test/compile-fail/where-lifetime-resolution.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Trait1 {} +trait Trait2 {} + +fn f() where + for<'a> Trait1<'a>: Trait1<'a>, // OK + (for<'a> Trait1<'a>): Trait1<'a>, + //~^ ERROR use of undeclared lifetime name `'a` + for<'a> for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>, + //~^ ERROR use of undeclared lifetime name `'b` + //~| ERROR nested quantification of lifetimes +{} + +fn main() {} diff --git a/src/test/parse-fail/bounds-lifetime-1.rs b/src/test/parse-fail/bounds-lifetime-1.rs new file mode 100644 index 00000000000..824d243d5f8 --- /dev/null +++ b/src/test/parse-fail/bounds-lifetime-1.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +type A = for<'a 'b> fn(); //~ ERROR expected one of `,`, `:`, or `>`, found `'b` + +fn main() {} diff --git a/src/test/parse-fail/bounds-lifetime-2.rs b/src/test/parse-fail/bounds-lifetime-2.rs new file mode 100644 index 00000000000..3c67dda70f5 --- /dev/null +++ b/src/test/parse-fail/bounds-lifetime-2.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +type A = for<'a + 'b> fn(); //~ ERROR expected one of `,`, `:`, or `>`, found `+` + +fn main() {} diff --git a/src/test/parse-fail/bounds-lifetime-where-1.rs b/src/test/parse-fail/bounds-lifetime-where-1.rs new file mode 100644 index 00000000000..aae9d729987 --- /dev/null +++ b/src/test/parse-fail/bounds-lifetime-where-1.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +type A where 'a; //~ ERROR expected `:`, found `;` + +fn main() {} diff --git a/src/test/parse-fail/bounds-lifetime-where.rs b/src/test/parse-fail/bounds-lifetime-where.rs new file mode 100644 index 00000000000..0a30818bc96 --- /dev/null +++ b/src/test/parse-fail/bounds-lifetime-where.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +type A where 'a: 'b + 'c = u8; // OK +type A where 'a: 'b, = u8; // OK +type A where 'a: = u8; // OK +type A where 'a:, = u8; // OK +type A where 'a: 'b + 'c = u8; // OK +type A where = u8; // OK +type A where 'a: 'b + = u8; // OK +type A where , = u8; //~ ERROR expected one of `=`, lifetime, or type, found `,` + +fn main() {} diff --git a/src/test/parse-fail/bounds-lifetime.rs b/src/test/parse-fail/bounds-lifetime.rs new file mode 100644 index 00000000000..5113a6b4803 --- /dev/null +++ b/src/test/parse-fail/bounds-lifetime.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , 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 + +type A = for<'a: 'b + 'c> fn(); // OK +type A = for<'a: 'b,> fn(); // OK +type A = for<'a:> fn(); // OK +type A = for<'a:,> fn(); // OK +type A = for<'a> fn(); // OK +type A = for<> fn(); // OK +type A = for<'a: 'b +> fn(); // OK + +type A = for<'a, T> fn(); //~ ERROR only lifetime parameters can be used in this context +type A = for<,> fn(); //~ ERROR expected one of `>`, identifier, or lifetime, found `,` + +fn main() {} diff --git a/src/test/parse-fail/bounds-type-where.rs b/src/test/parse-fail/bounds-type-where.rs new file mode 100644 index 00000000000..9dc5d827744 --- /dev/null +++ b/src/test/parse-fail/bounds-type-where.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +type A where for<'a> for<'b> Trait1 + ?Trait2: 'a + Trait = u8; // OK +type A where T: Trait, = u8; // OK +type A where T: = u8; // OK +type A where T:, = u8; // OK +type A where T: Trait + Trait = u8; // OK +type A where = u8; // OK +type A where T: Trait + = u8; // OK +type A where T, = u8; +//~^ ERROR expected one of `!`, `(`, `+`, `::`, `:`, `<`, `==`, or `=`, found `,` + +fn main() {} diff --git a/src/test/parse-fail/bounds-type.rs b/src/test/parse-fail/bounds-type.rs new file mode 100644 index 00000000000..c224b44a14b --- /dev/null +++ b/src/test/parse-fail/bounds-type.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , 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 + +struct S< + T: 'a + Tr, // OK + T: Tr + 'a, // OK + T: 'a, // OK + T:, // OK + T: ?for<'a: 'b + 'c> Trait, // OK + T: Tr +, // OK + T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds +>; + +fn main() {} diff --git a/src/test/parse-fail/issue-14303-path.rs b/src/test/parse-fail/issue-14303-path.rs index 431a917c2d9..f0d1feffec8 100644 --- a/src/test/parse-fail/issue-14303-path.rs +++ b/src/test/parse-fail/issue-14303-path.rs @@ -12,4 +12,3 @@ fn bar<'a, T>(x: mymodule::X<'a, T, 'b, 'c>) {} //~^ ERROR lifetime parameters must be declared prior to type parameters -//~^^ ERROR expected pattern, found `'c` diff --git a/src/test/parse-fail/generic-non-trailing-defaults.rs b/src/test/parse-fail/issue-17904-2.rs similarity index 74% rename from src/test/parse-fail/generic-non-trailing-defaults.rs rename to src/test/parse-fail/issue-17904-2.rs index 2bb593258ae..3f41c0edd2e 100644 --- a/src/test/parse-fail/generic-non-trailing-defaults.rs +++ b/src/test/parse-fail/issue-17904-2.rs @@ -10,10 +10,6 @@ // compile-flags: -Z parse-only -Z continue-parse-after-error -struct Heap; - -struct Vec; //~ ERROR type parameters with a default must be trailing - -struct Foo, C>; //~ ERROR type parameters with a default must be trailing +struct Bar { x: T } where T: Copy //~ ERROR expected item, found `where` fn main() {} diff --git a/src/test/parse-fail/issue-17904.rs b/src/test/parse-fail/issue-17904.rs index de5aeb02ab7..ae28ac76acb 100644 --- a/src/test/parse-fail/issue-17904.rs +++ b/src/test/parse-fail/issue-17904.rs @@ -13,7 +13,6 @@ struct Baz where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax. struct Baz where U: Eq(U) -> R; // Notice this parses as well. struct Baz(U) where U: Eq; // This rightfully signals no error as well. -struct Foo where T: Copy, (T); //~ ERROR unexpected token in `where` clause -struct Bar { x: T } where T: Copy //~ ERROR expected item, found `where` +struct Foo where T: Copy, (T); //~ ERROR expected one of `+`, `:`, `==`, or `=`, found `;` fn main() {} diff --git a/src/test/parse-fail/issue-32214.rs b/src/test/parse-fail/issue-32214.rs index 3ba59c8ee94..9e200094093 100644 --- a/src/test/parse-fail/issue-32214.rs +++ b/src/test/parse-fail/issue-32214.rs @@ -10,8 +10,7 @@ // compile-flags: -Z parse-only -Z continue-parse-after-error -pub fn test >() { - //~^ ERROR expected `=`, found `>` -} +pub fn test >() {} +//~^ ERROR type parameters must be declared prior to associated type bindings fn main() { } diff --git a/src/test/parse-fail/lifetime-semicolon.rs b/src/test/parse-fail/lifetime-semicolon.rs index 7010d0e7deb..e1975952fca 100644 --- a/src/test/parse-fail/lifetime-semicolon.rs +++ b/src/test/parse-fail/lifetime-semicolon.rs @@ -15,5 +15,4 @@ struct Foo<'a, 'b> { } fn foo<'a, 'b>(x: &mut Foo<'a; 'b>) {} -//~^ ERROR expected `,` or `>` after lifetime name, found `;` -//~^^ NOTE did you mean a single argument type &'a Type, or did you mean the comma-separated +//~^ ERROR expected one of `,` or `>`, found `;` diff --git a/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs b/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs index 78d97454087..cf67b518fff 100644 --- a/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs +++ b/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs @@ -10,13 +10,13 @@ // compile-flags: -Z parse-only -Z continue-parse-after-error +// Empty predicate list is OK fn equal1(_: &T, _: &T) -> bool where { -//~^ ERROR a `where` clause must have at least one predicate in it true } +// Empty bound list is OK fn equal2(_: &T, _: &T) -> bool where T: { -//~^ ERROR each predicate in a `where` clause must have at least one bound true }