about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-08-21 23:03:57 +0000
committerbors <bors@rust-lang.org>2017-08-21 23:03:57 +0000
commit8df670b6a60f245d266dbd0b650a549b7b806bac (patch)
treee9310dc78c8bea36acc9ee43bb7d48eda654895d /src
parent4fdb4bedfd2395e1e1ebb923b10165b850e6064c (diff)
parent804459bdca28010137990220e617a6b6cbab18d0 (diff)
downloadrust-8df670b6a60f245d266dbd0b650a549b7b806bac.tar.gz
rust-8df670b6a60f245d266dbd0b650a549b7b806bac.zip
Auto merge of #43540 - petrochenkov:pathrelax, r=nikomatsakis
syntax: Relax path grammar

TLDR: Accept the disambiguator `::` in "type" paths (`Type::<Args>`), accept the disambiguator `::` before parenthesized generic arguments (`Fn::(Args)`).

The "turbofish" disambiguator `::<>` in expression paths is a necessary evil required for path parsing to be both simple and to give reasonable results.
Since paths in expressions usually refer to values (but not necessarily, e.g. `Struct::<u8> { field: 0 }` is disambiguated, but refers to a type), people often consider `::<>` to be inherent to *values*, and not *expressions* and want to write disambiguated paths for values even in contexts where disambiguation is not strictly necessary, for example when a path is passed to a macro `m!(Vec::<i32>::new)`.
The problem is that currently, if the disambiguator is not *required*, then it's *prohibited*. This results in confusion - see https://github.com/rust-lang/rust/issues/41740, https://internals.rust-lang.org/t/macro-path-uses-novel-syntax/5561.

This PR makes the disambiguator *optional* instead of prohibited in contexts where it's not strictly required, so people can pass paths to macros in whatever form they consider natural (e.g. disambiguated form for value paths).
This PR also accepts the disambiguator in paths with parenthesized arguments (`Fn::(Args)`) for consistency and to simplify testing of stuff like https://github.com/rust-lang/rust/pull/41856#issuecomment-301219194.

Closes https://github.com/rust-lang/rust/issues/41740

cc @rust-lang/lang
r? @nikomatsakis
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/ext/tt/macro_parser.rs4
-rw-r--r--src/libsyntax/parse/parser.rs39
-rw-r--r--src/test/compile-fail/issue-32995.rs8
-rw-r--r--src/test/compile-fail/issue-36116.rs28
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs (renamed from src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs)12
-rw-r--r--src/test/parse-fail/type-parameters-in-field-exprs.rs2
6 files changed, 49 insertions, 44 deletions
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index aea48632d5d..405d06dafbf 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -599,9 +599,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
                 panic!(FatalError)
             }
         },
-        "path" => {
-            token::NtPath(panictry!(p.parse_path(PathStyle::Type)))
-        },
+        "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))),
         "meta" => token::NtMeta(panictry!(p.parse_meta_item())),
         "vis" => token::NtVis(panictry!(p.parse_visibility(true))),
         // this is not supposed to happen, since it has been checked
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index e251d136f23..84e3d8ddf70 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -84,7 +84,7 @@ pub enum PathStyle {
     Expr,
     /// In other contexts, notably in types, no ambiguity exists and paths can be written
     /// without the disambiguator, e.g. `x<y>` - unambiguously a path.
-    /// Paths with disambiguators are rejected for now, but may be allowed in the future.
+    /// Paths with disambiguators are still accepted, `x::<Y>` - unambiguously a path too.
     Type,
     /// A path with generic arguments disallowed, e.g. `foo::bar::Baz`, used in imports,
     /// visibilities or attributes.
@@ -1755,7 +1755,7 @@ impl<'a> Parser<'a> {
         self.expect(&token::ModSep)?;
 
         let qself = QSelf { ty, position: path.segments.len() };
-        self.parse_path_segments(&mut path.segments, style)?;
+        self.parse_path_segments(&mut path.segments, style, true)?;
 
         Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) }))
     }
@@ -1770,8 +1770,12 @@ impl<'a> Parser<'a> {
     /// `a::b::C::<D>` (with disambiguator)
     /// `Fn(Args)` (without disambiguator)
     /// `Fn::(Args)` (with disambiguator)
-    pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path>
-    {
+    pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path> {
+        self.parse_path_common(style, true)
+    }
+
+    pub fn parse_path_common(&mut self, style: PathStyle, enable_warning: bool)
+                             -> PResult<'a, ast::Path> {
         maybe_whole!(self, NtPath, |x| x);
 
         let lo = self.meta_var_span.unwrap_or(self.span);
@@ -1779,7 +1783,7 @@ impl<'a> Parser<'a> {
         if self.eat(&token::ModSep) {
             segments.push(PathSegment::crate_root(lo));
         }
-        self.parse_path_segments(&mut segments, style)?;
+        self.parse_path_segments(&mut segments, style, enable_warning)?;
 
         Ok(ast::Path { segments, span: lo.to(self.prev_span) })
     }
@@ -1804,10 +1808,10 @@ impl<'a> Parser<'a> {
         self.parse_path(style)
     }
 
-    fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle)
-                           -> PResult<'a, ()> {
+    fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle,
+                           enable_warning: bool) -> PResult<'a, ()> {
         loop {
-            segments.push(self.parse_path_segment(style)?);
+            segments.push(self.parse_path_segment(style, enable_warning)?);
 
             if self.is_import_coupler() || !self.eat(&token::ModSep) {
                 return Ok(());
@@ -1815,7 +1819,8 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> {
+    fn parse_path_segment(&mut self, style: PathStyle, enable_warning: bool)
+                          -> PResult<'a, PathSegment> {
         let ident_span = self.span;
         let ident = self.parse_path_segment_ident()?;
 
@@ -1835,17 +1840,9 @@ impl<'a> Parser<'a> {
                                       && self.look_ahead(1, |t| is_args_start(t)) {
             // Generic arguments are found - `<`, `(`, `::<` or `::(`.
             let lo = self.span;
-            if self.eat(&token::ModSep) {
-                // These errors are not strictly necessary and may be removed in the future.
-                if style == PathStyle::Type {
-                    let mut err = self.diagnostic().struct_span_err(self.prev_span,
-                        "unnecessary path disambiguator");
-                    err.span_label(self.prev_span, "try removing `::`");
-                    err.emit();
-                } else if self.token == token::OpenDelim(token::Paren) {
-                    self.diagnostic().span_err(self.prev_span,
-                        "`::` is not supported before parenthesized generic arguments")
-                }
+            if self.eat(&token::ModSep) && style == PathStyle::Type && enable_warning {
+                self.diagnostic().struct_span_warn(self.prev_span, "unnecessary path disambiguator")
+                                 .span_label(self.prev_span, "try removing `::`").emit();
             }
 
             let parameters = if self.eat_lt() {
@@ -2382,7 +2379,7 @@ impl<'a> Parser<'a> {
 
     // Assuming we have just parsed `.`, continue parsing into an expression.
     fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
-        let segment = self.parse_path_segment(PathStyle::Expr)?;
+        let segment = self.parse_path_segment(PathStyle::Expr, true)?;
         Ok(match self.token {
             token::OpenDelim(token::Paren) => {
                 // Method call `expr.f()`
diff --git a/src/test/compile-fail/issue-32995.rs b/src/test/compile-fail/issue-32995.rs
index 4b7f82943ba..ffbd0c0c22a 100644
--- a/src/test/compile-fail/issue-32995.rs
+++ b/src/test/compile-fail/issue-32995.rs
@@ -19,15 +19,11 @@ fn main() {
     //~^ ERROR parenthesized parameters may only be used with a trait
     //~| WARN previously accepted
 
-    macro_rules! pathexpr {
-        ($p:path) => { $p }
-    }
-
-    let p = pathexpr!(::std::str()::from_utf8)(b"foo").unwrap();
+    let p = ::std::str::()::from_utf8(b"foo").unwrap();
     //~^ ERROR parenthesized parameters may only be used with a trait
     //~| WARN previously accepted
 
-    let p = pathexpr!(::std::str::from_utf8())(b"foo").unwrap();
+    let p = ::std::str::from_utf8::()(b"foo").unwrap();
     //~^ ERROR parenthesized parameters may only be used with a trait
     //~| WARN previously accepted
 
diff --git a/src/test/compile-fail/issue-36116.rs b/src/test/compile-fail/issue-36116.rs
index 737955b2ff3..3afbfa61984 100644
--- a/src/test/compile-fail/issue-36116.rs
+++ b/src/test/compile-fail/issue-36116.rs
@@ -8,16 +8,30 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// Unnecessary path disambiguator is ok
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+macro_rules! m {
+    ($p: path) => {
+        let _ = $p(0);
+        let _: $p;
+    }
+}
+
 struct Foo<T> {
     _a: T,
 }
 
-fn main() {
-    let f = Some(Foo { _a: 42 }).map(|a| a as Foo::<i32>);
-    //~^ ERROR unnecessary path disambiguator
-    //~| NOTE try removing `::`
+struct S<T>(T);
+
+fn f() {
+    let f = Some(Foo { _a: 42 }).map(|a| a as Foo::<i32>); //~ WARN unnecessary path disambiguator
+    let g: Foo::<i32> = Foo { _a: 42 }; //~ WARN unnecessary path disambiguator
 
-    let g: Foo::<i32> = Foo { _a: 42 };
-    //~^ ERROR unnecessary path disambiguator
-    //~| NOTE try removing `::`
+    m!(S::<u8>); // OK, no warning
 }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs b/src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs
index 548a5078a74..42fffe546c2 100644
--- a/src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs
@@ -8,9 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-flags: -Z parse-only
-
-// Test that parentheses form doesn't work in expression paths.
+// Test that parentheses form parses in expression paths.
 
 struct Bar<A,R> {
     f: A, r: R
@@ -21,10 +19,10 @@ impl<A,B> Bar<A,B> {
 }
 
 fn bar() {
-    let b = Box::Bar::<isize,usize>::new(); // OK
+    let b = Bar::<isize, usize>::new(); // OK
 
-    let b = Box::Bar::()::new();
-    //~^ ERROR `::` is not supported before parenthesized generic arguments
+    let b = Bar::(isize, usize)::new(); // OK too (for the parser)
+    //~^ ERROR parenthesized parameters may only be used with a trait
 }
 
-fn main() { }
+fn main() {}
diff --git a/src/test/parse-fail/type-parameters-in-field-exprs.rs b/src/test/parse-fail/type-parameters-in-field-exprs.rs
index 95c307c5670..cb018ff1bfa 100644
--- a/src/test/parse-fail/type-parameters-in-field-exprs.rs
+++ b/src/test/parse-fail/type-parameters-in-field-exprs.rs
@@ -24,4 +24,6 @@ fn main() {
     //~^ ERROR field expressions may not have generic arguments
     f.x::<>;
     //~^ ERROR field expressions may not have generic arguments
+    f.x::();
+    //~^ ERROR field expressions may not have generic arguments
 }