| use super::*; |
| |
| pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ |
| T!['('], |
| T!['['], |
| T![<], |
| T![!], |
| T![*], |
| T![&], |
| T![_], |
| T![fn], |
| T![unsafe], |
| T![extern], |
| T![for], |
| T![impl], |
| T![dyn], |
| T![Self], |
| LIFETIME_IDENT, |
| ])); |
| |
| pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[ |
| T![')'], |
| T![>], |
| T![,], |
| // test_err struct_field_recover |
| // struct S { f pub g: () } |
| T![pub], |
| ]); |
| |
| pub(crate) fn type_(p: &mut Parser<'_>) { |
| type_with_bounds_cond(p, true); |
| } |
| |
| pub(super) fn type_no_bounds(p: &mut Parser<'_>) { |
| type_with_bounds_cond(p, false); |
| } |
| |
| fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { |
| match p.current() { |
| T!['('] => paren_or_tuple_type(p), |
| T![!] => never_type(p), |
| T![*] => ptr_type(p), |
| T!['['] => array_or_slice_type(p), |
| T![&] => ref_type(p), |
| T![_] => infer_type(p), |
| T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p), |
| T![for] => for_type(p, allow_bounds), |
| T![impl] => impl_trait_type(p), |
| T![dyn] => dyn_trait_type(p), |
| // Some path types are not allowed to have bounds (no plus) |
| T![<] => path_type_bounds(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); |
| } |
| } |
| } |
| |
| pub(super) fn ascription(p: &mut Parser<'_>) { |
| assert!(p.at(T![:])); |
| p.bump(T![:]); |
| if p.at(T![=]) { |
| // recover from `let x: = expr;`, `const X: = expr;` and similar |
| // hopefully no type starts with `=` |
| p.error("missing type"); |
| return; |
| } |
| type_(p); |
| } |
| |
| fn paren_or_tuple_type(p: &mut Parser<'_>) { |
| assert!(p.at(T!['('])); |
| let m = p.start(); |
| p.bump(T!['(']); |
| let mut n_types: u32 = 0; |
| let mut trailing_comma: bool = false; |
| while !p.at(EOF) && !p.at(T![')']) { |
| n_types += 1; |
| type_(p); |
| if p.eat(T![,]) { |
| trailing_comma = true; |
| } else { |
| trailing_comma = false; |
| break; |
| } |
| } |
| p.expect(T![')']); |
| |
| let kind = if n_types == 1 && !trailing_comma { |
| // test paren_type |
| // type T = (i32); |
| PAREN_TYPE |
| } else { |
| // test unit_type |
| // type T = (); |
| |
| // test singleton_tuple_type |
| // type T = (i32,); |
| TUPLE_TYPE |
| }; |
| m.complete(p, kind); |
| } |
| |
| // test never_type |
| // type Never = !; |
| fn never_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![!])); |
| let m = p.start(); |
| p.bump(T![!]); |
| m.complete(p, NEVER_TYPE); |
| } |
| |
| fn ptr_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![*])); |
| let m = p.start(); |
| p.bump(T![*]); |
| |
| match p.current() { |
| // test pointer_type_mut |
| // type M = *mut (); |
| // type C = *mut (); |
| T![mut] | T![const] => p.bump_any(), |
| _ => { |
| // test_err pointer_type_no_mutability |
| // type T = *(); |
| p.error( |
| "expected mut or const in raw pointer type \ |
| (use `*mut T` or `*const T` as appropriate)", |
| ); |
| } |
| }; |
| |
| type_no_bounds(p); |
| m.complete(p, PTR_TYPE); |
| } |
| |
| fn array_or_slice_type(p: &mut Parser<'_>) { |
| assert!(p.at(T!['['])); |
| let m = p.start(); |
| p.bump(T!['[']); |
| |
| type_(p); |
| let kind = match p.current() { |
| // test slice_type |
| // type T = [()]; |
| T![']'] => { |
| p.bump(T![']']); |
| SLICE_TYPE |
| } |
| |
| // test array_type |
| // type T = [(); 92]; |
| T![;] => { |
| p.bump(T![;]); |
| let m = p.start(); |
| expressions::expr(p); |
| m.complete(p, CONST_ARG); |
| p.expect(T![']']); |
| ARRAY_TYPE |
| } |
| // test_err array_type_missing_semi |
| // type T = [() 92]; |
| _ => { |
| p.error("expected `;` or `]`"); |
| SLICE_TYPE |
| } |
| }; |
| m.complete(p, kind); |
| } |
| |
| // test reference_type; |
| // type A = &(); |
| // type B = &'static (); |
| // type C = &mut (); |
| fn ref_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![&])); |
| let m = p.start(); |
| p.bump(T![&]); |
| if p.at(LIFETIME_IDENT) { |
| lifetime(p); |
| } |
| p.eat(T![mut]); |
| type_no_bounds(p); |
| m.complete(p, REF_TYPE); |
| } |
| |
| // test placeholder_type |
| // type Placeholder = _; |
| fn infer_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![_])); |
| let m = p.start(); |
| p.bump(T![_]); |
| m.complete(p, INFER_TYPE); |
| } |
| |
| // test fn_pointer_type |
| // type A = fn(); |
| // type B = unsafe fn(); |
| // type C = unsafe extern "C" fn(); |
| // type D = extern "C" fn ( u8 , ... ) -> u8; |
| fn fn_ptr_type(p: &mut Parser<'_>) { |
| let m = p.start(); |
| p.eat(T![unsafe]); |
| if p.at(T![extern]) { |
| abi(p); |
| } |
| // test_err fn_pointer_type_missing_fn |
| // type F = unsafe (); |
| if !p.eat(T![fn]) { |
| m.abandon(p); |
| p.error("expected `fn`"); |
| return; |
| } |
| if p.at(T!['(']) { |
| params::param_list_fn_ptr(p); |
| } else { |
| p.error("expected parameters"); |
| } |
| // test fn_pointer_type_with_ret |
| // type F = fn() -> (); |
| opt_ret_type(p); |
| m.complete(p, FN_PTR_TYPE); |
| } |
| |
| pub(super) fn for_binder(p: &mut Parser<'_>) { |
| assert!(p.at(T![for])); |
| p.bump(T![for]); |
| if p.at(T![<]) { |
| generic_params::opt_generic_param_list(p); |
| } else { |
| p.error("expected `<`"); |
| } |
| } |
| |
| // test for_type |
| // type A = for<'a> fn() -> (); |
| // type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); |
| // type Obj = for<'a> PartialEq<&'a i32>; |
| pub(super) fn for_type(p: &mut Parser<'_>, allow_bounds: bool) { |
| assert!(p.at(T![for])); |
| let m = p.start(); |
| for_binder(p); |
| match p.current() { |
| T![fn] | T![unsafe] | T![extern] => {} |
| // OK: legacy trait object format |
| _ if paths::is_use_path_start(p) => {} |
| _ => { |
| p.error("expected a function pointer or path"); |
| } |
| } |
| type_no_bounds(p); |
| let completed = m.complete(p, FOR_TYPE); |
| |
| // test no_dyn_trait_leading_for |
| // type A = for<'a> Test<'a> + Send; |
| if allow_bounds { |
| opt_type_bounds_as_dyn_trait_type(p, completed); |
| } |
| } |
| |
| // test impl_trait_type |
| // type A = impl Iterator<Item=Foo<'a>> + 'a; |
| fn impl_trait_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![impl])); |
| let m = p.start(); |
| p.bump(T![impl]); |
| generic_params::bounds_without_colon(p); |
| m.complete(p, IMPL_TRAIT_TYPE); |
| } |
| |
| // test dyn_trait_type |
| // type A = dyn Iterator<Item=Foo<'a>> + 'a; |
| fn dyn_trait_type(p: &mut Parser<'_>) { |
| assert!(p.at(T![dyn])); |
| let m = p.start(); |
| p.bump(T![dyn]); |
| generic_params::bounds_without_colon(p); |
| m.complete(p, DYN_TRAIT_TYPE); |
| } |
| |
| // test bare_dyn_types_with_leading_lifetime |
| // type A = 'static + Trait; |
| // type B = S<'static + Trait>; |
| fn bare_dyn_trait_type(p: &mut Parser<'_>) { |
| let m = p.start(); |
| generic_params::bounds_without_colon(p); |
| m.complete(p, DYN_TRAIT_TYPE); |
| } |
| |
| // test path_type |
| // type A = Foo; |
| // type B = ::Foo; |
| // type C = self::Foo; |
| // type D = super::Foo; |
| pub(super) fn path_type(p: &mut Parser<'_>) { |
| path_type_bounds(p, true); |
| } |
| |
| // test macro_call_type |
| // type A = foo!(); |
| // type B = crate::foo!(); |
| 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(); |
| |
| paths::type_path(p); |
| |
| let kind = if p.at(T![!]) && !p.at(T![!=]) { |
| items::macro_call_after_excl(p); |
| m.complete(p, MACRO_CALL); |
| MACRO_TYPE |
| } else { |
| m.abandon(p); |
| PATH_TYPE |
| }; |
| |
| let path = r.complete(p, kind); |
| |
| if allow_bounds { |
| opt_type_bounds_as_dyn_trait_type(p, path); |
| } |
| } |
| |
| pub(super) fn path_type_bounds(p: &mut Parser<'_>, allow_bounds: bool) { |
| assert!(paths::is_path_start(p)); |
| let m = p.start(); |
| paths::type_path(p); |
| |
| // test path_type_with_bounds |
| // fn foo() -> Box<T + 'f> {} |
| // fn foo() -> Box<dyn T + 'f> {} |
| let path = m.complete(p, PATH_TYPE); |
| if allow_bounds { |
| opt_type_bounds_as_dyn_trait_type(p, path); |
| } |
| } |
| |
| /// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE |
| /// with a TYPE_BOUND_LIST |
| pub(super) fn opt_type_bounds_as_dyn_trait_type( |
| p: &mut Parser<'_>, |
| type_marker: CompletedMarker, |
| ) -> CompletedMarker { |
| assert!(matches!( |
| type_marker.kind(), |
| SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE |
| )); |
| if !p.at(T![+]) { |
| return type_marker; |
| } |
| |
| // First create a TYPE_BOUND from the completed PATH_TYPE |
| let m = type_marker.precede(p).complete(p, TYPE_BOUND); |
| |
| // Next setup a marker for the TYPE_BOUND_LIST |
| let m = m.precede(p); |
| |
| // This gets consumed here so it gets properly set |
| // in the TYPE_BOUND_LIST |
| p.eat(T![+]); |
| |
| // Parse rest of the bounds into the TYPE_BOUND_LIST |
| let m = generic_params::bounds_without_colon_m(p, m); |
| |
| // Finally precede everything with DYN_TRAIT_TYPE |
| m.precede(p).complete(p, DYN_TRAIT_TYPE) |
| } |