blob: 211af98e6ef0491f4791b7c7342fdf5457670137 [file] [log] [blame]
use super::*;
pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) {
let m;
if p.at(T![::]) && p.nth(2) == T![<] {
m = p.start();
p.bump(T![::]);
} else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
m = p.start();
} else {
return;
}
delimited(p, T![<], T![>], T![,], GENERIC_ARG_FIRST, generic_arg);
m.complete(p, GENERIC_ARG_LIST);
}
const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
LIFETIME_IDENT,
IDENT,
T!['{'],
T![true],
T![false],
T![-],
INT_NUMBER,
FLOAT_NUMBER,
CHAR,
BYTE,
STRING,
BYTE_STRING,
C_STRING,
])
.union(types::TYPE_FIRST);
// Despite its name, it can also be used for generic param list.
const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]);
// test generic_arg
// type T = S<i32>;
fn generic_arg(p: &mut Parser<'_>) -> bool {
match p.current() {
LIFETIME_IDENT if !p.nth_at(1, T![+]) => lifetime_arg(p),
T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
k if k.is_literal() => const_arg(p),
// test associated_type_bounds
// fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
// test macro_inside_generic_arg
// type A = Foo<syn::Token![_]>;
IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) && !p.nth_at(1, T![::]) => {
let m = p.start();
name_ref(p);
opt_generic_arg_list(p, false);
match p.current() {
T![=] => {
p.bump_any();
if types::TYPE_FIRST.contains(p.current()) {
// test assoc_type_eq
// type T = StreamingIterator<Item<'a> = &'a T>;
types::type_(p);
} else if p.at_ts(GENERIC_ARG_RECOVERY_SET) {
// Although `const_arg()` recovers as expected, we want to
// handle those here to give the following message because
// we don't know whether this associated item is a type or
// const at this point.
// test_err recover_from_missing_assoc_item_binding
// fn f() -> impl Iterator<Item = , Item = > {}
p.error("missing associated item binding");
} else {
// test assoc_const_eq
// fn foo<F: Foo<N=3>>() {}
// const TEST: usize = 3;
// fn bar<F: Foo<N={TEST}>>() {}
const_arg(p);
}
m.complete(p, ASSOC_TYPE_ARG);
}
// test assoc_type_bound
// type T = StreamingIterator<Item<'a>: Clone>;
T![:] if !p.at(T![::]) => {
generic_params::bounds(p);
m.complete(p, ASSOC_TYPE_ARG);
}
_ => {
let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
let m = paths::type_path_for_qualifier(p, m);
m.precede(p).complete(p, PATH_TYPE).precede(p).complete(p, TYPE_ARG);
}
}
}
IDENT if p.nth_at(1, T!['(']) => {
let m = p.start();
name_ref(p);
params::param_list_fn_trait(p);
if p.at(T![:]) && !p.at(T![::]) {
// test associated_return_type_bounds
// fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
generic_params::bounds(p);
m.complete(p, ASSOC_TYPE_ARG);
} else {
// test bare_dyn_types_with_paren_as_generic_args
// type A = S<Fn(i32)>;
// type A = S<Fn(i32) + Send>;
// type B = S<Fn(i32) -> i32>;
// type C = S<Fn(i32) -> i32 + Send>;
opt_ret_type(p);
let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
let m = paths::type_path_for_qualifier(p, m);
let m = m.precede(p).complete(p, PATH_TYPE);
let m = types::opt_type_bounds_as_dyn_trait_type(p, m);
m.precede(p).complete(p, TYPE_ARG);
}
}
_ if p.at_ts(types::TYPE_FIRST) => type_arg(p),
_ => return false,
}
true
}
// test lifetime_arg
// type T = S<'static>;
fn lifetime_arg(p: &mut Parser<'_>) {
let m = p.start();
lifetime(p);
m.complete(p, LIFETIME_ARG);
}
pub(super) fn const_arg_expr(p: &mut Parser<'_>) {
// The tests in here are really for `const_arg`, which wraps the content
// CONST_ARG.
match p.current() {
// test const_arg_block
// type T = S<{90 + 2}>;
T!['{'] => {
expressions::block_expr(p);
}
// test const_arg_literal
// type T = S<"hello", 0xdeadbeef>;
k if k.is_literal() => {
expressions::literal(p);
}
// test const_arg_bool_literal
// type T = S<true>;
T![true] | T![false] => {
expressions::literal(p);
}
// test const_arg_negative_number
// type T = S<-92>;
T![-] => {
let lm = p.start();
p.bump(T![-]);
expressions::literal(p);
lm.complete(p, PREFIX_EXPR);
}
_ if paths::is_use_path_start(p) => {
// This shouldn't be hit by `const_arg`
let lm = p.start();
paths::use_path(p);
lm.complete(p, PATH_EXPR);
}
_ => {
// test_err recover_from_missing_const_default
// struct A<const N: i32 = , const M: i32 =>;
p.err_recover("expected a generic const argument", GENERIC_ARG_RECOVERY_SET);
}
}
}
// test const_arg
// type T = S<92>;
pub(super) fn const_arg(p: &mut Parser<'_>) {
let m = p.start();
const_arg_expr(p);
m.complete(p, CONST_ARG);
}
fn type_arg(p: &mut Parser<'_>) {
let m = p.start();
types::type_(p);
m.complete(p, TYPE_ARG);
}