Fix parsing of parenthesized type args and RTN

This commit is contained in:
Lukas Wirth 2024-12-03 18:29:00 +01:00
parent edb432639b
commit 65c0b29720
44 changed files with 1022 additions and 700 deletions

View file

@ -525,7 +525,7 @@ fn method_call_expr<const FLOAT_RECOVERY: bool>(
p.bump(T![.]);
}
name_ref(p);
generic_args::opt_generic_arg_list(p, true);
generic_args::opt_generic_arg_list_expr(p);
if p.at(T!['(']) {
arg_list(p);
} else {

View file

@ -1,14 +1,13 @@
use super::*;
// test_err generic_arg_list_recover
// type T = T<0, ,T>;
pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) {
// 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 if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
m = p.start();
} else {
return;
}
@ -25,7 +24,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: boo
m.complete(p, GENERIC_ARG_LIST);
}
const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
pub(crate) const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
LIFETIME_IDENT,
IDENT,
T!['{'],
@ -47,20 +46,23 @@ const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]);
// test generic_arg
// type T = S<i32>;
fn generic_arg(p: &mut Parser<'_>) -> bool {
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 associated_type_bounds
// fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
// 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 macro_inside_generic_arg
// type A = Foo<syn::Token![_]>;
IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) && !p.nth_at(1, T![::]) => {
IDENT => {
let m = p.start();
name_ref(p);
opt_generic_arg_list(p, false);
paths::opt_path_type_args(p);
match p.current() {
T![=] => {
p.bump_any();
@ -88,45 +90,26 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
}
// 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);
m.precede(p).complete(p, PATH_TYPE).precede(p).complete(p, TYPE_ARG);
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);
}
}
}
IDENT if p.nth_at(1, T!['(']) => {
let m = p.start();
name_ref(p);
if p.nth_at(1, T![..]) {
let rtn = p.start();
p.bump(T!['(']);
p.bump(T![..]);
p.expect(T![')']);
rtn.complete(p, RETURN_TYPE_SYNTAX);
// test return_type_syntax_assoc_type_bound
// fn foo<T: Trait<method(..): Send>>() {}
generic_params::bounds(p);
m.complete(p, ASSOC_TYPE_ARG);
} else {
params::param_list_fn_trait(p);
// 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,
}
@ -190,7 +173,7 @@ pub(super) fn const_arg(p: &mut Parser<'_>) {
m.complete(p, CONST_ARG);
}
fn type_arg(p: &mut Parser<'_>) {
pub(crate) fn type_arg(p: &mut Parser<'_>) {
let m = p.start();
types::type_(p);
m.complete(p, TYPE_ARG);

View file

@ -14,12 +14,6 @@ pub(super) fn param_list_fn_def(p: &mut Parser<'_>) {
list_(p, Flavor::FnDef);
}
// test param_list_opt_patterns
// fn foo<F: FnMut(&mut Foo<'a>)>(){}
pub(super) fn param_list_fn_trait(p: &mut Parser<'_>) {
list_(p, Flavor::FnTrait);
}
pub(super) fn param_list_fn_ptr(p: &mut Parser<'_>) {
list_(p, Flavor::FnPointer);
}
@ -28,10 +22,9 @@ pub(super) fn param_list_closure(p: &mut Parser<'_>) {
list_(p, Flavor::Closure);
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Flavor {
FnDef, // Includes trait fn params; omitted param idents are not supported
FnTrait, // Params for `Fn(...)`/`FnMut(...)`/`FnOnce(...)` annotations
FnDef, // Includes trait fn params; omitted param idents are not supported
FnPointer,
Closure,
}
@ -41,7 +34,7 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) {
let (bra, ket) = match flavor {
Closure => (T![|], T![|]),
FnDef | FnTrait | FnPointer => (T!['('], T![')']),
FnDef | FnPointer => (T!['('], T![')']),
};
let list_marker = p.start();
@ -119,11 +112,6 @@ fn param(p: &mut Parser<'_>, m: Marker, flavor: Flavor) {
}
}
}
// test value_parameters_no_patterns
// type F = Box<Fn(i32, &i32, &i32, ())>;
Flavor::FnTrait => {
types::type_(p);
}
// test fn_pointer_param_ident_path
// type Foo = fn(Bar::Baz);
// type Qux = fn(baz: Bar::Baz);

View file

@ -110,7 +110,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<Completed
match p.current() {
IDENT => {
name_ref(p);
opt_path_type_args(p, mode);
opt_path_args(p, mode);
}
// test crate_path
// use crate::foo;
@ -142,38 +142,74 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<Completed
Some(m.complete(p, PATH_SEGMENT))
}
fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
match mode {
Mode::Use | Mode::Attr | Mode::Vis => {}
Mode::Type => {
// test typepathfn_with_coloncolon
// type F = Start::(Middle) -> (Middle)::End;
// type GenericArg = S<Start(Middle)::End>;
if p.at(T![::]) && p.nth_at(2, T!['(']) {
p.bump(T![::]);
}
if p.at(T!['(']) {
if p.nth_at(1, T![..]) {
// test return_type_syntax_in_path
// fn foo<T>()
// where
// T::method(..): Send,
// {}
let rtn = p.start();
p.bump(T!['(']);
p.bump(T![..]);
p.expect(T![')']);
rtn.complete(p, RETURN_TYPE_SYNTAX);
} else {
// test path_fn_trait_args
// type F = Box<Fn(i32) -> ()>;
params::param_list_fn_trait(p);
opt_ret_type(p);
}
} else {
generic_args::opt_generic_arg_list(p, false);
}
}
Mode::Expr => generic_args::opt_generic_arg_list(p, true),
pub(crate) fn opt_path_type_args(p: &mut Parser<'_>) {
// test typepathfn_with_coloncolon
// type F = Start::(Middle) -> (Middle)::End;
// type GenericArg = S<Start(Middle)::End>;
let m;
if p.at(T![::]) && matches!(p.nth(2), T![<] | T!['(']) {
m = p.start();
p.bump(T![::]);
} else if (p.current() == T![<] && p.nth(1) != T![=]) || p.current() == T!['('] {
m = p.start();
} else {
return;
}
let current = p.current();
if current == T![<] {
// test_err generic_arg_list_recover
// type T = T<0, ,T>;
// type T = T::<0, ,T>;
delimited(
p,
T![<],
T![>],
T![,],
|| "expected generic argument".into(),
generic_args::GENERIC_ARG_FIRST,
generic_args::generic_arg,
);
m.complete(p, GENERIC_ARG_LIST);
} else if p.nth_at(1, T![..]) {
// test return_type_syntax_in_path
// fn foo<T>()
// where
// T::method(..): Send,
// method(..): Send,
// method::(..): Send,
// {}
p.bump(T!['(']);
p.bump(T![..]);
p.expect(T![')']);
m.complete(p, RETURN_TYPE_SYNTAX);
} else {
// test path_fn_trait_args
// type F = Box<Fn(i32) -> ()>;
// type F = Box<::Fn(i32) -> ()>;
// type F = Box<Fn::(i32) -> ()>;
// type F = Box<::Fn::(i32) -> ()>;
delimited(
p,
T!['('],
T![')'],
T![,],
|| "expected type".into(),
types::TYPE_FIRST,
|p| {
let progress = types::TYPE_FIRST.contains(p.current());
generic_args::type_arg(p);
progress
},
);
m.complete(p, PARENTHESIZED_ARG_LIST);
opt_ret_type(p);
}
}
fn opt_path_args(p: &mut Parser<'_>, mode: Mode) {
match mode {
Mode::Use | Mode::Attr | Mode::Vis => {}
Mode::Type => opt_path_type_args(p),
Mode::Expr => generic_args::opt_generic_arg_list_expr(p),
}
}

View file

@ -50,7 +50,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
// Some path types are not allowed to have bounds (no plus)
T![<] => path_type_bounds(p, allow_bounds),
T![ident] if !p.edition().at_least_2018() && is_dyn_weak(p) => dyn_trait_type_weak(p),
_ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
_ if paths::is_path_start(p) => path_or_macro_type(p, allow_bounds),
LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
_ => {
p.err_recover("expected type", TYPE_RECOVERY_SET);
@ -337,7 +337,7 @@ pub(super) fn path_type(p: &mut Parser<'_>) {
// test macro_call_type
// type A = foo!();
// type B = crate::foo!();
fn path_or_macro_type_(p: &mut Parser<'_>, allow_bounds: bool) {
fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) {
assert!(paths::is_path_start(p));
let r = p.start();
let m = p.start();

View file

@ -256,6 +256,7 @@ pub enum SyntaxKind {
OR_PAT,
PARAM,
PARAM_LIST,
PARENTHESIZED_ARG_LIST,
PAREN_EXPR,
PAREN_PAT,
PAREN_TYPE,