Skip to content

Commit 8df670b

Browse files
committed
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 #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 #41856 (comment). Closes #41740 cc @rust-lang/lang r? @nikomatsakis
2 parents 4fdb4be + 804459b commit 8df670b

File tree

6 files changed

+49
-44
lines changed

6 files changed

+49
-44
lines changed

Diff for: src/libsyntax/ext/tt/macro_parser.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
599599
panic!(FatalError)
600600
}
601601
},
602-
"path" => {
603-
token::NtPath(panictry!(p.parse_path(PathStyle::Type)))
604-
},
602+
"path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))),
605603
"meta" => token::NtMeta(panictry!(p.parse_meta_item())),
606604
"vis" => token::NtVis(panictry!(p.parse_visibility(true))),
607605
// this is not supposed to happen, since it has been checked

Diff for: src/libsyntax/parse/parser.rs

+18-21
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub enum PathStyle {
8484
Expr,
8585
/// In other contexts, notably in types, no ambiguity exists and paths can be written
8686
/// without the disambiguator, e.g. `x<y>` - unambiguously a path.
87-
/// Paths with disambiguators are rejected for now, but may be allowed in the future.
87+
/// Paths with disambiguators are still accepted, `x::<Y>` - unambiguously a path too.
8888
Type,
8989
/// A path with generic arguments disallowed, e.g. `foo::bar::Baz`, used in imports,
9090
/// visibilities or attributes.
@@ -1755,7 +1755,7 @@ impl<'a> Parser<'a> {
17551755
self.expect(&token::ModSep)?;
17561756

17571757
let qself = QSelf { ty, position: path.segments.len() };
1758-
self.parse_path_segments(&mut path.segments, style)?;
1758+
self.parse_path_segments(&mut path.segments, style, true)?;
17591759

17601760
Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) }))
17611761
}
@@ -1770,16 +1770,20 @@ impl<'a> Parser<'a> {
17701770
/// `a::b::C::<D>` (with disambiguator)
17711771
/// `Fn(Args)` (without disambiguator)
17721772
/// `Fn::(Args)` (with disambiguator)
1773-
pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path>
1774-
{
1773+
pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path> {
1774+
self.parse_path_common(style, true)
1775+
}
1776+
1777+
pub fn parse_path_common(&mut self, style: PathStyle, enable_warning: bool)
1778+
-> PResult<'a, ast::Path> {
17751779
maybe_whole!(self, NtPath, |x| x);
17761780

17771781
let lo = self.meta_var_span.unwrap_or(self.span);
17781782
let mut segments = Vec::new();
17791783
if self.eat(&token::ModSep) {
17801784
segments.push(PathSegment::crate_root(lo));
17811785
}
1782-
self.parse_path_segments(&mut segments, style)?;
1786+
self.parse_path_segments(&mut segments, style, enable_warning)?;
17831787

17841788
Ok(ast::Path { segments, span: lo.to(self.prev_span) })
17851789
}
@@ -1804,18 +1808,19 @@ impl<'a> Parser<'a> {
18041808
self.parse_path(style)
18051809
}
18061810

1807-
fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle)
1808-
-> PResult<'a, ()> {
1811+
fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle,
1812+
enable_warning: bool) -> PResult<'a, ()> {
18091813
loop {
1810-
segments.push(self.parse_path_segment(style)?);
1814+
segments.push(self.parse_path_segment(style, enable_warning)?);
18111815

18121816
if self.is_import_coupler() || !self.eat(&token::ModSep) {
18131817
return Ok(());
18141818
}
18151819
}
18161820
}
18171821

1818-
fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> {
1822+
fn parse_path_segment(&mut self, style: PathStyle, enable_warning: bool)
1823+
-> PResult<'a, PathSegment> {
18191824
let ident_span = self.span;
18201825
let ident = self.parse_path_segment_ident()?;
18211826

@@ -1835,17 +1840,9 @@ impl<'a> Parser<'a> {
18351840
&& self.look_ahead(1, |t| is_args_start(t)) {
18361841
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
18371842
let lo = self.span;
1838-
if self.eat(&token::ModSep) {
1839-
// These errors are not strictly necessary and may be removed in the future.
1840-
if style == PathStyle::Type {
1841-
let mut err = self.diagnostic().struct_span_err(self.prev_span,
1842-
"unnecessary path disambiguator");
1843-
err.span_label(self.prev_span, "try removing `::`");
1844-
err.emit();
1845-
} else if self.token == token::OpenDelim(token::Paren) {
1846-
self.diagnostic().span_err(self.prev_span,
1847-
"`::` is not supported before parenthesized generic arguments")
1848-
}
1843+
if self.eat(&token::ModSep) && style == PathStyle::Type && enable_warning {
1844+
self.diagnostic().struct_span_warn(self.prev_span, "unnecessary path disambiguator")
1845+
.span_label(self.prev_span, "try removing `::`").emit();
18491846
}
18501847

18511848
let parameters = if self.eat_lt() {
@@ -2382,7 +2379,7 @@ impl<'a> Parser<'a> {
23822379

23832380
// Assuming we have just parsed `.`, continue parsing into an expression.
23842381
fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
2385-
let segment = self.parse_path_segment(PathStyle::Expr)?;
2382+
let segment = self.parse_path_segment(PathStyle::Expr, true)?;
23862383
Ok(match self.token {
23872384
token::OpenDelim(token::Paren) => {
23882385
// Method call `expr.f()`

Diff for: src/test/compile-fail/issue-32995.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ fn main() {
1919
//~^ ERROR parenthesized parameters may only be used with a trait
2020
//~| WARN previously accepted
2121

22-
macro_rules! pathexpr {
23-
($p:path) => { $p }
24-
}
25-
26-
let p = pathexpr!(::std::str()::from_utf8)(b"foo").unwrap();
22+
let p = ::std::str::()::from_utf8(b"foo").unwrap();
2723
//~^ ERROR parenthesized parameters may only be used with a trait
2824
//~| WARN previously accepted
2925

30-
let p = pathexpr!(::std::str::from_utf8())(b"foo").unwrap();
26+
let p = ::std::str::from_utf8::()(b"foo").unwrap();
3127
//~^ ERROR parenthesized parameters may only be used with a trait
3228
//~| WARN previously accepted
3329

Diff for: src/test/compile-fail/issue-36116.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,30 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// Unnecessary path disambiguator is ok
12+
13+
#![feature(rustc_attrs)]
14+
#![allow(unused)]
15+
16+
macro_rules! m {
17+
($p: path) => {
18+
let _ = $p(0);
19+
let _: $p;
20+
}
21+
}
22+
1123
struct Foo<T> {
1224
_a: T,
1325
}
1426

15-
fn main() {
16-
let f = Some(Foo { _a: 42 }).map(|a| a as Foo::<i32>);
17-
//~^ ERROR unnecessary path disambiguator
18-
//~| NOTE try removing `::`
27+
struct S<T>(T);
28+
29+
fn f() {
30+
let f = Some(Foo { _a: 42 }).map(|a| a as Foo::<i32>); //~ WARN unnecessary path disambiguator
31+
let g: Foo::<i32> = Foo { _a: 42 }; //~ WARN unnecessary path disambiguator
1932

20-
let g: Foo::<i32> = Foo { _a: 42 };
21-
//~^ ERROR unnecessary path disambiguator
22-
//~| NOTE try removing `::`
33+
m!(S::<u8>); // OK, no warning
2334
}
35+
36+
#[rustc_error]
37+
fn main() {} //~ ERROR compilation successful

Diff for: src/test/parse-fail/unboxed-closure-sugar-used-on-struct-3.rs renamed to src/test/compile-fail/unboxed-closure-sugar-used-on-struct-3.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
12-
13-
// Test that parentheses form doesn't work in expression paths.
11+
// Test that parentheses form parses in expression paths.
1412

1513
struct Bar<A,R> {
1614
f: A, r: R
@@ -21,10 +19,10 @@ impl<A,B> Bar<A,B> {
2119
}
2220

2321
fn bar() {
24-
let b = Box::Bar::<isize,usize>::new(); // OK
22+
let b = Bar::<isize, usize>::new(); // OK
2523

26-
let b = Box::Bar::()::new();
27-
//~^ ERROR `::` is not supported before parenthesized generic arguments
24+
let b = Bar::(isize, usize)::new(); // OK too (for the parser)
25+
//~^ ERROR parenthesized parameters may only be used with a trait
2826
}
2927

30-
fn main() { }
28+
fn main() {}

Diff for: src/test/parse-fail/type-parameters-in-field-exprs.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ fn main() {
2424
//~^ ERROR field expressions may not have generic arguments
2525
f.x::<>;
2626
//~^ ERROR field expressions may not have generic arguments
27+
f.x::();
28+
//~^ ERROR field expressions may not have generic arguments
2729
}

0 commit comments

Comments
 (0)