Skip to content

[Do not merge] Allow generic parameters to be specified in expressions without :: #53074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#![feature(slice_sort_by_cached_key)]
#![feature(str_escape)]
#![feature(unicode_internals)]
#![feature(catch_expr)]

#![recursion_limit="256"]

Expand Down
115 changes: 82 additions & 33 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ impl<'a> Parser<'a> {
}
}

#[inline]
fn check_ident(&mut self) -> bool {
if self.token.is_ident() {
true
Expand All @@ -893,6 +894,7 @@ impl<'a> Parser<'a> {
}
}

#[inline]
fn check_type(&mut self) -> bool {
if self.token.can_begin_type() {
true
Expand Down Expand Up @@ -1714,11 +1716,10 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(keywords::Const) {
Mutability::Immutable
} else {
let span = self.prev_span;
self.span_err(span,
"expected mut or const in raw pointer type (use \
`*mut T` or `*const T` as appropriate)");
Mutability::Immutable
let mut err = self.fatal("expected mut or const in raw pointer type (use \
`*mut T` or `*const T` as appropriate)");
err.span_label(self.prev_span, "expected mut or const");
return Err(err);
};
let t = self.parse_ty_no_plus()?;
Ok(MutTy { ty: t, mutbl: mutbl })
Expand Down Expand Up @@ -1984,20 +1985,26 @@ impl<'a> Parser<'a> {
-> PResult<'a, PathSegment> {
let ident = self.parse_path_segment_ident()?;

let is_args_start = |token: &token::Token| match *token {
token::Lt | token::BinOp(token::Shl) | token::OpenDelim(token::Paren) => true,
let is_args_start = |token: &token::Token, include_paren: bool| match *token {
token::Lt | token::BinOp(token::Shl) => true,
token::OpenDelim(token::Paren) => include_paren,
_ => false,
};
let check_args_start = |this: &mut Self| {
this.expected_tokens.extend_from_slice(
&[TokenType::Token(token::Lt), TokenType::Token(token::OpenDelim(token::Paren))]
);
is_args_start(&this.token)
let check_args_start = |this: &mut Self, include_paren: bool| {
this.expected_tokens.push(TokenType::Token(token::Lt));
if include_paren {
this.expected_tokens.push(TokenType::Token(token::OpenDelim(token::Paren)));
}
is_args_start(&this.token, include_paren)
};

Ok(if style == PathStyle::Type && check_args_start(self) ||
let expr_without_disambig = style == PathStyle::Expr && check_args_start(self, false);
let parser_snapshot_before_generics = self.clone();

Ok(if style == PathStyle::Type && check_args_start(self, true) ||
style != PathStyle::Mod && self.check(&token::ModSep)
&& self.look_ahead(1, |t| is_args_start(t)) {
&& self.look_ahead(1, |t| is_args_start(t, true))
|| expr_without_disambig {
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
let lo = self.span;
if self.eat(&token::ModSep) && style == PathStyle::Type && enable_warning {
Expand All @@ -2007,10 +2014,26 @@ impl<'a> Parser<'a> {

let args = if self.eat_lt() {
// `<'a, T, A = U>`
let (args, bindings) = self.parse_generic_args()?;
self.expect_gt()?;
let span = lo.to(self.prev_span);
AngleBracketedArgs { args, bindings, span }.into()
let args: PResult<_> = do catch {
let (args, bindings) = self.parse_generic_args()?;
self.expect_gt()?;
let span = lo.to(self.prev_span);
AngleBracketedArgs { args, bindings, span }
};

match args {
Err(mut err) => {
if expr_without_disambig {
err.cancel();
mem::replace(self, parser_snapshot_before_generics);
return Ok(PathSegment::from_ident(ident));
}
return Err(err);
}
_ => {
args?.into()
}
}
} else {
// `(T, U) -> R`
self.bump(); // `(`
Expand All @@ -2036,6 +2059,7 @@ impl<'a> Parser<'a> {
})
}

#[inline]
crate fn check_lifetime(&mut self) -> bool {
self.expected_tokens.push(TokenType::Lifetime);
self.token.is_lifetime()
Expand Down Expand Up @@ -5039,51 +5063,76 @@ impl<'a> Parser<'a> {

/// 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<GenericArg>, Vec<TypeBinding>)> {
let mut args = Vec::new();
let mut bindings = Vec::new();
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
let mut args: Option<Vec<GenericArg>> = None;
let mut bindings: Option<Vec<TypeBinding>> = None;
let mut seen_type = false;
let mut seen_binding = false;
loop {
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
let mut arg = None;
let mut binding = None;

let (is_not_like_plus, is_eq) = if let Some(TokenTree::Token(_, tok)) =
self.token_cursor.frame.tree_cursor.look_ahead(0) {
(!tok.is_like_plus(), tok == token::Eq)
} else {
(false, false)
};

if is_not_like_plus && self.check_lifetime() {
// Parse lifetime argument.
args.push(GenericArg::Lifetime(self.expect_lifetime()));
if seen_type || seen_binding {
self.span_err(self.prev_span,
"lifetime parameters must be declared prior to type parameters");
}
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
arg = Some(GenericArg::Lifetime(self.expect_lifetime()));
} else if is_eq && self.check_ident() {
// Parse associated type binding.
let lo = self.span;
let ident = self.parse_ident()?;
self.bump();
let ty = self.parse_ty()?;
bindings.push(TypeBinding {
seen_binding = true;
binding = Some(TypeBinding {
id: ast::DUMMY_NODE_ID,
ident,
ty,
span: lo.to(self.prev_span),
});
seen_binding = true;
} else if self.check_type() {
// Parse type argument.
let ty_param = self.parse_ty()?;
let ty = self.parse_ty()?;
if seen_binding {
self.span_err(ty_param.span,
self.span_err(ty.span,
"type parameters must be declared prior to associated type bindings");
}
args.push(GenericArg::Type(ty_param));
seen_type = true;
arg = Some(GenericArg::Type(ty));
} else {
break
break;
}

if let Some(arg) = arg {
if let Some(ref mut args) = args {
args.push(arg);
} else {
args = Some(vec![arg]);
}
}

if let Some(binding) = binding {
if let Some(ref mut bindings) = bindings {
bindings.push(binding);
} else {
bindings = Some(vec![binding]);
}
}

if !self.eat(&token::Comma) {
break
break;
}
}
Ok((args, bindings))
Ok((args.unwrap_or_default(), bindings.unwrap_or_default()))
}

/// Parses an optional `where` clause and places it in `generics`.
Expand Down
10 changes: 9 additions & 1 deletion src/libsyntax/parse/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ pub(crate) fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool {
].contains(&ident.name)
}

#[inline]
fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool {
let ident_token: Token = Ident(ident, is_raw);

Expand Down Expand Up @@ -167,7 +168,7 @@ pub enum Token {
Comma,
Semi,
Colon,
ModSep,
ModSep, // `::`
RArrow,
LArrow,
FatArrow,
Expand Down Expand Up @@ -218,6 +219,7 @@ impl Token {
Ident(ident, ident.is_raw_guess())
}

#[inline]
crate fn is_like_plus(&self) -> bool {
match *self {
BinOp(Plus) | BinOpEq(Plus) => true,
Expand Down Expand Up @@ -307,6 +309,7 @@ impl Token {
}

/// Returns an identifier if this token is an identifier.
#[inline]
pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> {
match *self {
Ident(ident, is_raw) => Some((ident, is_raw)),
Expand All @@ -318,6 +321,7 @@ impl Token {
}
}
/// Returns a lifetime identifier if this token is a lifetime.
#[inline]
pub fn lifetime(&self) -> Option<ast::Ident> {
match *self {
Lifetime(ident) => Some(ident),
Expand All @@ -329,10 +333,12 @@ impl Token {
}
}
/// Returns `true` if the token is an identifier.
#[inline]
pub fn is_ident(&self) -> bool {
self.ident().is_some()
}
/// Returns `true` if the token is a lifetime.
#[inline]
crate fn is_lifetime(&self) -> bool {
self.lifetime().is_some()
}
Expand Down Expand Up @@ -376,6 +382,7 @@ impl Token {
self.ident().map(|(ident, is_raw)| ident.name == kw.name() && !is_raw).unwrap_or(false)
}

#[inline]
pub fn is_path_segment_keyword(&self) -> bool {
match self.ident() {
Some((id, false)) => id.is_path_segment_keyword(),
Expand Down Expand Up @@ -409,6 +416,7 @@ impl Token {
}

/// Returns `true` if the token is either a special identifier or a keyword.
#[inline]
pub fn is_reserved_ident(&self) -> bool {
match self.ident() {
Some((id, false)) => id.is_reserved(),
Expand Down
2 changes: 1 addition & 1 deletion src/test/parse-fail/pat-ranges-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
// Parsing of range patterns

fn main() {
let 10 ..= makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, or `=`, found `!`
let 10 ..= makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, `<`, or `=`, found `!`
}
5 changes: 0 additions & 5 deletions src/test/parse-fail/require-parens-for-chained-comparison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,4 @@ fn main() {

false == 0 < 2;
//~^ ERROR: chained comparison operators require parentheses

f<X>();
//~^ ERROR: chained comparison operators require parentheses
//~| HELP: use `::<...>` instead of `<...>`
//~| HELP: or use `(...)`
}
8 changes: 4 additions & 4 deletions src/test/ui/did_you_mean/issue-40396.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
// except according to those terms.

fn foo() {
println!("{:?}", (0..13).collect<Vec<i32>>()); //~ ERROR chained comparison
println!("{:?}", (0..13).collect<Vec<i32>>()); // ok
}

fn bar() {
println!("{:?}", Vec<i32>::new()); //~ ERROR chained comparison
println!("{:?}", Vec<i32>::new()); // ok
}

fn qux() {
println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR chained comparison
//~^ ERROR chained comparison
println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR expected function, found struct `Vec`
//~^ ERROR attempted to take value of method `collect`
}

fn main() {}
44 changes: 12 additions & 32 deletions src/test/ui/did_you_mean/issue-40396.stderr
Original file line number Diff line number Diff line change
@@ -1,38 +1,18 @@
error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:12:37
error[E0423]: expected function, found struct `Vec`
--> $DIR/issue-40396.rs:20:38
|
LL | println!("{:?}", (0..13).collect<Vec<i32>>()); //~ ERROR chained comparison
| ^^^^^^^^
|
= help: use `::<...>` instead of `<...>` if you meant to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments

error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:16:25
|
LL | println!("{:?}", Vec<i32>::new()); //~ ERROR chained comparison
| ^^^^^^^
|
= help: use `::<...>` instead of `<...>` if you meant to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments

error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:20:37
|
LL | println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR chained comparison
| ^^^^^^^^
|
= help: use `::<...>` instead of `<...>` if you meant to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments
LL | println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR expected function, found struct `Vec`
| ^^^^^^^^ did you mean `Vec { /* fields */ }`?

error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:20:41
error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>`
--> $DIR/issue-40396.rs:20:30
|
LL | println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR chained comparison
| ^^^^^^
LL | println!("{:?}", (0..13).collect<Vec<i32>()); //~ ERROR expected function, found struct `Vec`
| ^^^^^^^
|
= help: use `::<...>` instead of `<...>` if you meant to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments
= help: maybe a `()` to call it is missing?

error: aborting due to 4 previous errors
error: aborting due to 2 previous errors

Some errors occurred: E0423, E0615.
For more information about an error, try `rustc --explain E0423`.
Loading