blob: 6c05abc023878f24f7cf37259e661ba7bc367e93 [file] [log] [blame]
use crate::grammar::attributes::ATTRIBUTE_FIRST;
use super::*;
pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {
if p.at(T![<]) {
generic_param_list(p);
}
}
// test generic_param_list
// fn f<T: Clone>() {}
// test_err generic_param_list_recover
// fn f<T: Clone,, U:, V>() {}
fn generic_param_list(p: &mut Parser<'_>) {
assert!(p.at(T![<]));
let m = p.start();
delimited(
p,
T![<],
T![>],
T![,],
|| "expected generic parameter".into(),
GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST),
|p| {
// test generic_param_attribute
// fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
let m = p.start();
attributes::outer_attrs(p);
generic_param(p, m)
},
);
m.complete(p, GENERIC_PARAM_LIST);
}
const GENERIC_PARAM_FIRST: TokenSet = TokenSet::new(&[IDENT, LIFETIME_IDENT, T![const]]);
fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
match p.current() {
LIFETIME_IDENT => lifetime_param(p, m),
IDENT => type_param(p, m),
T![const] => const_param(p, m),
_ => {
m.abandon(p);
p.err_and_bump("expected generic parameter");
return false;
}
}
true
}
// test lifetime_param
// fn f<'a: 'b>() {}
fn lifetime_param(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(LIFETIME_IDENT));
lifetime(p);
if p.at(T![:]) {
lifetime_bounds(p);
}
m.complete(p, LIFETIME_PARAM);
}
// test type_param
// fn f<T: Clone>() {}
fn type_param(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(IDENT));
name(p);
if p.at(T![:]) {
bounds(p);
}
if p.at(T![=]) {
// test type_param_default
// struct S<T = i32>;
p.bump(T![=]);
types::type_(p);
}
m.complete(p, TYPE_PARAM);
}
// test const_param
// struct S<const N: u32>;
fn const_param(p: &mut Parser<'_>, m: Marker) {
p.bump(T![const]);
name(p);
if p.at(T![:]) {
types::ascription(p);
} else {
p.error("missing type for const parameter");
}
if p.eat(T![=]) {
// test const_param_default_literal
// struct A<const N: i32 = -1>;
// test const_param_default_expression
// struct A<const N: i32 = { 1 }>;
// test const_param_default_path
// struct A<const N: i32 = i32::MAX>;
generic_args::const_arg(p);
}
m.complete(p, CONST_PARAM);
}
fn lifetime_bounds(p: &mut Parser<'_>) {
assert!(p.at(T![:]));
p.bump(T![:]);
while p.at(LIFETIME_IDENT) {
lifetime(p);
if !p.eat(T![+]) {
break;
}
}
}
// test type_param_bounds
// struct S<T: 'a + ?Sized + (Copy) + ~const Drop>;
pub(super) fn bounds(p: &mut Parser<'_>) {
assert!(p.at(T![:]));
p.bump(T![:]);
bounds_without_colon(p);
}
pub(super) fn bounds_without_colon(p: &mut Parser<'_>) {
let m = p.start();
bounds_without_colon_m(p, m);
}
pub(super) fn bounds_without_colon_m(p: &mut Parser<'_>, marker: Marker) -> CompletedMarker {
while type_bound(p) {
if !p.eat(T![+]) {
break;
}
}
marker.complete(p, TYPE_BOUND_LIST)
}
fn type_bound(p: &mut Parser<'_>) -> bool {
let m = p.start();
let has_paren = p.eat(T!['(']);
match p.current() {
LIFETIME_IDENT => lifetime(p),
T![for] => types::for_type(p, false),
T![?] if p.nth_at(1, T![for]) => {
// test question_for_type_trait_bound
// fn f<T>() where T: ?for<> Sized {}
p.bump_any();
types::for_type(p, false)
}
current => {
match current {
T![?] => p.bump_any(),
T![~] => {
p.bump_any();
p.expect(T![const]);
}
// test const_trait_bound
// const fn foo(_: impl const Trait) {}
T![const] => {
p.bump_any();
}
// test async_trait_bound
// fn async_foo(_: impl async Fn(&i32)) {}
T![async] => {
p.bump_any();
}
_ => (),
}
if paths::is_use_path_start(p) {
types::path_type_bounds(p, false);
} else {
m.abandon(p);
return false;
}
}
}
if has_paren {
p.expect(T![')']);
}
m.complete(p, TYPE_BOUND);
true
}
// test where_clause
// fn foo()
// where
// 'a: 'b + 'c,
// T: Clone + Copy + 'static,
// Iterator::Item: 'a,
// <T as Iterator>::Item: 'a
// {}
pub(super) fn opt_where_clause(p: &mut Parser<'_>) {
if !p.at(T![where]) {
return;
}
let m = p.start();
p.bump(T![where]);
while is_where_predicate(p) {
where_predicate(p);
let comma = p.eat(T![,]);
match p.current() {
T!['{'] | T![;] | T![=] => break,
_ => (),
}
if !comma {
p.error("expected comma");
}
}
m.complete(p, WHERE_CLAUSE);
fn is_where_predicate(p: &mut Parser<'_>) -> bool {
match p.current() {
LIFETIME_IDENT => true,
T![impl] => false,
token => types::TYPE_FIRST.contains(token),
}
}
}
fn where_predicate(p: &mut Parser<'_>) {
let m = p.start();
match p.current() {
LIFETIME_IDENT => {
lifetime(p);
if p.at(T![:]) {
bounds(p);
} else {
p.error("expected colon");
}
}
T![impl] => {
p.error("expected lifetime or type");
}
_ => {
if p.at(T![for]) {
// test where_pred_for
// fn for_trait<F>()
// where
// for<'a> F: Fn(&'a str)
// { }
types::for_binder(p);
}
types::type_(p);
if p.at(T![:]) {
bounds(p);
} else {
p.error("expected colon");
}
}
}
m.complete(p, WHERE_PRED);
}