Skip to content
Merged
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
7 changes: 3 additions & 4 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@ pub(crate) mod parsing {
FieldValue, Index, Member,
};
#[cfg(feature = "full")]
use crate::generics::BoundLifetimes;
use crate::generics::{self, BoundLifetimes};
use crate::ident::Ident;
#[cfg(feature = "full")]
use crate::lifetime::Lifetime;
Expand Down Expand Up @@ -1251,7 +1251,7 @@ pub(crate) mod parsing {
} else if input.peek(Token![while]) {
Expr::While(input.parse()?)
} else if input.peek(Token![for])
&& !(input.peek2(Token![<]) && (input.peek3(Lifetime) || input.peek3(Token![>])))
&& !generics::parsing::choose_generics_over_qpath_after_keyword(input)
{
Expr::ForLoop(input.parse()?)
} else if input.peek(Token![loop]) {
Expand Down Expand Up @@ -1803,8 +1803,7 @@ pub(crate) mod parsing {
} else if input.peek(Token![|])
|| input.peek(Token![move])
|| input.peek(Token![for])
&& input.peek2(Token![<])
&& (input.peek3(Lifetime) || input.peek3(Token![>]))
&& generics::parsing::choose_generics_over_qpath_after_keyword(input)
|| input.peek(Token![const]) && !input.peek2(token::Brace)
|| input.peek(Token![static])
|| input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))
Expand Down
50 changes: 49 additions & 1 deletion src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,8 +926,15 @@ pub(crate) mod parsing {
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
impl Parse for WhereClause {
fn parse(input: ParseStream) -> Result<Self> {
let where_token: Token![where] = input.parse()?;

if choose_generics_over_qpath(input) {
return Err(input
.error("generic parameters on `where` clauses are reserved for future use"));
}

Ok(WhereClause {
where_token: input.parse()?,
where_token,
predicates: {
let mut predicates = Punctuated::new();
loop {
Expand Down Expand Up @@ -1086,6 +1093,47 @@ pub(crate) mod parsing {
}
}
}

pub(crate) fn choose_generics_over_qpath(input: ParseStream) -> bool {
// Rust syntax has an ambiguity between generic parameters and qualified
// paths. In `impl <T> :: Thing<T, U> {}` this may either be a generic
// inherent impl `impl<T> ::Thing<T, U>` or a non-generic inherent impl
// for an associated type `impl <T>::Thing<T, U>`.
//
// After `<` the following continuations can only begin generics, not a
// qualified path:
//
// `<` `>` - empty generic parameters
// `<` `#` - generic parameters with attribute
// `<` LIFETIME `>` - single lifetime parameter
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
// `<` const - generic const parameter
//
// The only truly ambiguous case is:
//
// `<` IDENT `>` `::` IDENT ...
//
// which we disambiguate in favor of generics because this is almost
// always the expected one in the context of real-world code.
input.peek(Token![<])
&& (input.peek2(Token![>])
|| input.peek2(Token![#])
|| (input.peek2(Lifetime) || input.peek2(Ident))
&& (input.peek3(Token![>])
|| input.peek3(Token![,])
|| input.peek3(Token![:]) && !input.peek3(Token![::])
|| input.peek3(Token![=]))
|| input.peek2(Token![const]))
}

#[cfg(feature = "full")]
pub(crate) fn choose_generics_over_qpath_after_keyword(input: ParseStream) -> bool {
let input = input.fork();
input.call(Ident::parse_any).unwrap(); // `impl` or `for` or `where`
choose_generics_over_qpath(&input)
}
}

#[cfg(feature = "printing")]
Expand Down
12 changes: 2 additions & 10 deletions src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ pub(crate) mod parsing {
use crate::error::{Error, Result};
use crate::expr::Expr;
use crate::ext::IdentExt as _;
use crate::generics::{Generics, TypeParamBound};
use crate::generics::{self, Generics, TypeParamBound};
use crate::ident::Ident;
use crate::item::{
FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType,
Expand Down Expand Up @@ -2567,15 +2567,7 @@ pub(crate) mod parsing {
let unsafety: Option<Token![unsafe]> = input.parse()?;
let impl_token: Token![impl] = input.parse()?;

let has_generics = input.peek(Token![<])
&& (input.peek2(Token![>])
|| input.peek2(Token![#])
|| (input.peek2(Ident) || input.peek2(Lifetime))
&& (input.peek3(Token![:])
|| input.peek3(Token![,])
|| input.peek3(Token![>])
|| input.peek3(Token![=]))
|| input.peek2(Token![const]));
let has_generics = generics::parsing::choose_generics_over_qpath(input);
let mut generics: Generics = if has_generics {
input.parse()?
} else {
Expand Down
Loading