From 60c48dd16ae579e6f2e0ae74862b0420ff471da9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 14 Jan 2018 18:10:19 +0300 Subject: [PATCH] syntax: Disambiguate generics and qualified paths --- src/libsyntax/parse/parser.rs | 48 +++++++++++++------ .../private-in-public-ill-formed.rs | 4 +- src/test/parse-fail/impl-qpath.rs | 18 +++++++ src/test/parse-fail/where_with_bound.rs | 2 +- 4 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 src/test/parse-fail/impl-qpath.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a3ea659940aa0..e7565d357397c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4772,21 +4772,13 @@ impl<'a> Parser<'a> { } let lo = self.prev_span; - // 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 - // change, for now we refuse to parse `where < (ident | lifetime) (> | , | :)`. - if token::Lt == self.token { - let ident_or_lifetime = self.look_ahead(1, |t| t.is_ident() || t.is_lifetime()); - if ident_or_lifetime { - let gt_comma_or_colon = self.look_ahead(2, |t| { - *t == token::Gt || *t == token::Comma || *t == token::Colon - }); - if gt_comma_or_colon { - self.span_err(self.span, "syntax `where` is reserved for future use"); - } - } + // change we parse those generics now, but report an error. + if self.choose_generics_over_qpath() { + let generics = self.parse_generics()?; + self.span_err(generics.span, + "generic parameters on `where` clauses are reserved for future use"); } loop { @@ -5348,6 +5340,29 @@ impl<'a> Parser<'a> { } } + fn choose_generics_over_qpath(&self) -> bool { + // There's an ambiguity between generic parameters and qualified paths in impls. + // If we see `<` it may start both, so we have to inspect some following tokens. + // The following combinations can only start generics, + // but not qualified paths (with one exception): + // `<` `>` - empty generic parameters + // `<` `#` - generic parameters with attributes + // `<` (LIFETIME|IDENT) `>` - single generic parameter + // `<` (LIFETIME|IDENT) `,` - first generic parameter in a list + // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds + // `<` (LIFETIME|IDENT) `=` - generic parameter with a default + // The only truly ambiguous case is + // `<` IDENT `>` `::` IDENT ... + // we disambiguate it in favor of generics (`impl ::absolute::Path { ... }`) + // because this is what almost always expected in practice, qualified paths in impls + // (`impl ::AssocTy { ... }`) aren't even allowed by type checker at the moment. + self.token == token::Lt && + (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) || + self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) && + self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma || + t == &token::Colon || t == &token::Eq)) + } + fn parse_impl_body(&mut self) -> PResult<'a, (Vec, Vec)> { self.expect(&token::OpenDelim(token::Brace))?; let attrs = self.parse_inner_attributes()?; @@ -5378,8 +5393,11 @@ impl<'a> Parser<'a> { fn parse_item_impl(&mut self, unsafety: Unsafety, defaultness: Defaultness) -> PResult<'a, ItemInfo> { // First, parse generic parameters if necessary. - // FIXME: Disambiguate generic parameters and qualified paths (`impl ::C {}`). - let mut generics = self.parse_generics()?; + let mut generics = if self.choose_generics_over_qpath() { + self.parse_generics()? + } else { + ast::Generics::default() + }; // Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type. let polarity = if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) { diff --git a/src/test/compile-fail/private-in-public-ill-formed.rs b/src/test/compile-fail/private-in-public-ill-formed.rs index 4e10614bf62c6..480406054adb5 100644 --- a/src/test/compile-fail/private-in-public-ill-formed.rs +++ b/src/test/compile-fail/private-in-public-ill-formed.rs @@ -21,7 +21,7 @@ mod aliases_pub { type AssocAlias = m::Pub3; } - impl (::AssocAlias) { //~ ERROR no base type found for inherent implementation + impl ::AssocAlias { //~ ERROR no base type found for inherent implementation pub fn f(arg: Priv) {} // private type `aliases_pub::Priv` in public interface } } @@ -37,7 +37,7 @@ mod aliases_priv { type AssocAlias = Priv3; } - impl (::AssocAlias) { //~ ERROR no base type found for inherent implementation + impl ::AssocAlias { //~ ERROR no base type found for inherent implementation pub fn f(arg: Priv) {} // OK } } diff --git a/src/test/parse-fail/impl-qpath.rs b/src/test/parse-fail/impl-qpath.rs new file mode 100644 index 0000000000000..48dd888b2e530 --- /dev/null +++ b/src/test/parse-fail/impl-qpath.rs @@ -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 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 + +impl <*const u8>::AssocTy {} // OK +impl ::AssocTy {} // OK +impl <'a + Trait>::AssocTy {} // OK +impl <::AssocTy>::AssocTy {} // OK + +FAIL //~ ERROR diff --git a/src/test/parse-fail/where_with_bound.rs b/src/test/parse-fail/where_with_bound.rs index cb57500df797e..2948619ccd07d 100644 --- a/src/test/parse-fail/where_with_bound.rs +++ b/src/test/parse-fail/where_with_bound.rs @@ -11,6 +11,6 @@ // compile-flags: -Z parse-only fn foo() where ::Item: ToString, T: Iterator { } - //~^ syntax `where` is reserved for future use +//~^ ERROR generic parameters on `where` clauses are reserved for future use fn main() {}