Skip to content

Commit 9649639

Browse files
committed
Synchronize generics-vs-qpath heuristic with rust parser
1 parent 60de331 commit 9649639

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

src/expr.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ pub(crate) mod parsing {
11891189
FieldValue, Index, Member,
11901190
};
11911191
#[cfg(feature = "full")]
1192-
use crate::generics::BoundLifetimes;
1192+
use crate::generics::{self, BoundLifetimes};
11931193
use crate::ident::Ident;
11941194
#[cfg(feature = "full")]
11951195
use crate::lifetime::Lifetime;
@@ -1251,7 +1251,7 @@ pub(crate) mod parsing {
12511251
} else if input.peek(Token![while]) {
12521252
Expr::While(input.parse()?)
12531253
} else if input.peek(Token![for])
1254-
&& !(input.peek2(Token![<]) && (input.peek3(Lifetime) || input.peek3(Token![>])))
1254+
&& !generics::parsing::choose_generics_over_qpath_after_keyword(input)
12551255
{
12561256
Expr::ForLoop(input.parse()?)
12571257
} else if input.peek(Token![loop]) {
@@ -1803,8 +1803,7 @@ pub(crate) mod parsing {
18031803
} else if input.peek(Token![|])
18041804
|| input.peek(Token![move])
18051805
|| input.peek(Token![for])
1806-
&& input.peek2(Token![<])
1807-
&& (input.peek3(Lifetime) || input.peek3(Token![>]))
1806+
&& generics::parsing::choose_generics_over_qpath_after_keyword(input)
18081807
|| input.peek(Token![const]) && !input.peek2(token::Brace)
18091808
|| input.peek(Token![static])
18101809
|| input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))

src/generics.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,8 +926,15 @@ pub(crate) mod parsing {
926926
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
927927
impl Parse for WhereClause {
928928
fn parse(input: ParseStream) -> Result<Self> {
929+
let where_token: Token![where] = input.parse()?;
930+
931+
if choose_generics_over_qpath(input) {
932+
return Err(input
933+
.error("generic parameters on `where` clauses are reserved for future use"));
934+
}
935+
929936
Ok(WhereClause {
930-
where_token: input.parse()?,
937+
where_token,
931938
predicates: {
932939
let mut predicates = Punctuated::new();
933940
loop {
@@ -1086,6 +1093,47 @@ pub(crate) mod parsing {
10861093
}
10871094
}
10881095
}
1096+
1097+
pub(crate) fn choose_generics_over_qpath(input: ParseStream) -> bool {
1098+
// Rust syntax has an ambiguity between generic parameters and qualified
1099+
// paths. In `impl <T> :: Thing<T, U> {}` this may either be a generic
1100+
// inherent impl `impl<T> ::Thing<T, U>` or a non-generic inherent impl
1101+
// for an associated type `impl <T>::Thing<T, U>`.
1102+
//
1103+
// After `<` the following continuations can only begin generics, not a
1104+
// qualified path:
1105+
//
1106+
// `<` `>` - empty generic parameters
1107+
// `<` `#` - generic parameters with attribute
1108+
// `<` LIFETIME `>` - single lifetime parameter
1109+
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
1110+
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
1111+
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
1112+
// `<` const - generic const parameter
1113+
//
1114+
// The only truly ambiguous case is:
1115+
//
1116+
// `<` IDENT `>` `::` IDENT ...
1117+
//
1118+
// which we disambiguate in favor of generics because this is almost
1119+
// always the expected one in the context of real-world code.
1120+
input.peek(Token![<])
1121+
&& (input.peek2(Token![>])
1122+
|| input.peek2(Token![#])
1123+
|| (input.peek2(Lifetime) || input.peek2(Ident))
1124+
&& (input.peek3(Token![>])
1125+
|| input.peek3(Token![,])
1126+
|| input.peek3(Token![:]) && !input.peek3(Token![::])
1127+
|| input.peek3(Token![=]))
1128+
|| input.peek2(Token![const]))
1129+
}
1130+
1131+
#[cfg(feature = "full")]
1132+
pub(crate) fn choose_generics_over_qpath_after_keyword(input: ParseStream) -> bool {
1133+
let input = input.fork();
1134+
input.call(Ident::parse_any).unwrap(); // `impl` or `for` or `where`
1135+
choose_generics_over_qpath(&input)
1136+
}
10891137
}
10901138

10911139
#[cfg(feature = "printing")]

src/item.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ pub(crate) mod parsing {
909909
use crate::error::{Error, Result};
910910
use crate::expr::Expr;
911911
use crate::ext::IdentExt as _;
912-
use crate::generics::{Generics, TypeParamBound};
912+
use crate::generics::{self, Generics, TypeParamBound};
913913
use crate::ident::Ident;
914914
use crate::item::{
915915
FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType,
@@ -2567,15 +2567,7 @@ pub(crate) mod parsing {
25672567
let unsafety: Option<Token![unsafe]> = input.parse()?;
25682568
let impl_token: Token![impl] = input.parse()?;
25692569

2570-
let has_generics = input.peek(Token![<])
2571-
&& (input.peek2(Token![>])
2572-
|| input.peek2(Token![#])
2573-
|| (input.peek2(Ident) || input.peek2(Lifetime))
2574-
&& (input.peek3(Token![:])
2575-
|| input.peek3(Token![,])
2576-
|| input.peek3(Token![>])
2577-
|| input.peek3(Token![=]))
2578-
|| input.peek2(Token![const]));
2570+
let has_generics = generics::parsing::choose_generics_over_qpath(input);
25792571
let mut generics: Generics = if has_generics {
25802572
input.parse()?
25812573
} else {

0 commit comments

Comments
 (0)