mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-18 03:13:31 +00:00
183 lines
6.2 KiB
Rust
183 lines
6.2 KiB
Rust
use super::*;
|
|
|
|
// test_err generic_arg_list_recover_expr
|
|
// const _: () = T::<0, ,T>;
|
|
// const _: () = T::<0, ,T>();
|
|
pub(super) fn opt_generic_arg_list_expr(p: &mut Parser<'_>) {
|
|
let m;
|
|
if p.at(T![::]) && p.nth(2) == T![<] {
|
|
m = p.start();
|
|
p.bump(T![::]);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
delimited(
|
|
p,
|
|
T![<],
|
|
T![>],
|
|
T![,],
|
|
|| "expected generic argument".into(),
|
|
GENERIC_ARG_FIRST,
|
|
generic_arg,
|
|
);
|
|
m.complete(p, GENERIC_ARG_LIST);
|
|
}
|
|
|
|
pub(crate) 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, dyn T, fn()>;
|
|
pub(crate) 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 generic_arg_bounds
|
|
// type Plain = Foo<Item, Item::Item, Item: Bound, Item = Item>;
|
|
// type GenericArgs = Foo<Item<T>, Item::<T>, Item<T>: Bound, Item::<T>: Bound, Item<T> = Item, Item::<T> = Item>;
|
|
// type ParenthesizedArgs = Foo<Item(T), Item::(T), Item(T): Bound, Item::(T): Bound, Item(T) = Item, Item::(T) = Item>;
|
|
// type RTN = Foo<Item(..), Item(..), Item(..): Bound, Item(..): Bound, Item(..) = Item, Item(..) = Item>;
|
|
|
|
// test edition_2015_dyn_prefix_inside_generic_arg 2015
|
|
// type A = Foo<dyn T>;
|
|
T![ident] if !p.edition().at_least_2018() && types::is_dyn_weak(p) => type_arg(p),
|
|
// test macro_inside_generic_arg
|
|
// type A = Foo<syn::Token![_]>;
|
|
k if PATH_NAME_REF_KINDS.contains(k) => {
|
|
let m = p.start();
|
|
name_ref_mod_path(p);
|
|
paths::opt_path_type_args(p);
|
|
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>;
|
|
// type T = StreamingIterator<Item(T): Clone>;
|
|
T![:] if !p.at(T![::]) => {
|
|
generic_params::bounds(p);
|
|
m.complete(p, ASSOC_TYPE_ARG);
|
|
}
|
|
// Turned out to be just a normal path type (mirror `path_or_macro_type`)
|
|
_ => {
|
|
let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
|
|
let m = paths::type_path_for_qualifier(p, m);
|
|
let m = if p.at(T![!]) && !p.at(T![!=]) {
|
|
let m = m.precede(p);
|
|
items::macro_call_after_excl(p);
|
|
m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_TYPE)
|
|
} else {
|
|
m.precede(p).complete(p, PATH_TYPE)
|
|
};
|
|
types::opt_type_bounds_as_dyn_trait_type(p, 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_path_start(p) => {
|
|
// This shouldn't be hit by `const_arg`
|
|
let lm = p.start();
|
|
paths::expr_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);
|
|
}
|
|
|
|
pub(crate) fn type_arg(p: &mut Parser<'_>) {
|
|
let m = p.start();
|
|
types::type_(p);
|
|
m.complete(p, TYPE_ARG);
|
|
}
|