diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index 407f38daad4f..c35f915b00e6 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -218,6 +218,9 @@ pub(super) fn lower_generic_args( let arg = ConstRefOrPath::from_expr_opt(arg.expr()); args.push(GenericArg::Const(arg)) } + ast::GenericArg::ReturnTypeArg(_) => { + // FIXME: return type notation is experimental, we don't do anything with it yet. + } } } diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs index 919d9b91ebab..55794954a829 100644 --- a/crates/parser/src/grammar/generic_args.rs +++ b/crates/parser/src/grammar/generic_args.rs @@ -76,6 +76,7 @@ fn generic_arg(p: &mut Parser<'_>) -> bool { } } } + IDENT if p.nth(1) == T!['('] && p.nth_at(2, T![..]) => return_type_arg(p), _ if p.at_ts(types::TYPE_FIRST) => type_arg(p), _ => return false, } @@ -139,3 +140,20 @@ fn type_arg(p: &mut Parser<'_>) { types::type_(p); m.complete(p, TYPE_ARG); } + +// test return_type_arg +// type T = S; +pub(super) fn return_type_arg(p: &mut Parser<'_>) { + let m = p.start(); + p.expect(IDENT); + p.expect(T!['(']); + p.expect(T![..]); + p.expect(T![')']); + if !p.at(T![:]) { + p.error("expected :"); + m.abandon(p); + return; + } + generic_params::bounds(p); + m.complete(p, RETURN_TYPE_ARG); +} diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index cd87b304a2fb..2af6e1b9867c 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -245,6 +245,7 @@ pub enum SyntaxKind { GENERIC_PARAM, LIFETIME_PARAM, TYPE_PARAM, + RETURN_TYPE_ARG, CONST_PARAM, GENERIC_ARG_LIST, LIFETIME, diff --git a/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rast b/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rast new file mode 100644 index 000000000000..26d474f54f47 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rast @@ -0,0 +1,33 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "T" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + RETURN_TYPE_ARG + IDENT "foo" + L_PAREN "(" + DOT2 ".." + R_PAREN ")" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rs b/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rs new file mode 100644 index 000000000000..2a9ff270839f --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0206_return_type_arg.rs @@ -0,0 +1 @@ +type T = S; diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 1c15a606f957..08c8749e36c7 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -46,6 +46,7 @@ GenericArg = | AssocTypeArg | LifetimeArg | ConstArg +| ReturnTypeArg TypeArg = Type @@ -59,6 +60,9 @@ LifetimeArg = ConstArg = Expr +ReturnTypeArg = + NameRef '(' '..' ')' ':' TypeBoundList + MacroCall = Attr* Path '!' TokenTree ';'? diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 0e84aca5c7d9..6aa7efd83246 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -142,6 +142,18 @@ impl ConstArg { pub fn expr(&self) -> Option { support::child(&self.syntax) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ReturnTypeArg { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasTypeBounds for ReturnTypeArg {} +impl ReturnTypeArg { + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn dotdot_token(&self) -> Option { support::token(&self.syntax, T![..]) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TypeBoundList { pub(crate) syntax: SyntaxNode, @@ -1516,6 +1528,7 @@ pub enum GenericArg { AssocTypeArg(AssocTypeArg), LifetimeArg(LifetimeArg), ConstArg(ConstArg), + ReturnTypeArg(ReturnTypeArg), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1865,6 +1878,17 @@ impl AstNode for ConstArg { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for ReturnTypeArg { + fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_ARG } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for TypeBoundList { fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST } fn cast(syntax: SyntaxNode) -> Option { @@ -3219,9 +3243,12 @@ impl From for GenericArg { impl From for GenericArg { fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) } } +impl From for GenericArg { + fn from(node: ReturnTypeArg) -> GenericArg { GenericArg::ReturnTypeArg(node) } +} impl AstNode for GenericArg { fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG) + matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG | RETURN_TYPE_ARG) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { @@ -3229,6 +3256,7 @@ impl AstNode for GenericArg { ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }), LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }), CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }), + RETURN_TYPE_ARG => GenericArg::ReturnTypeArg(ReturnTypeArg { syntax }), _ => return None, }; Some(res) @@ -3239,6 +3267,7 @@ impl AstNode for GenericArg { GenericArg::AssocTypeArg(it) => &it.syntax, GenericArg::LifetimeArg(it) => &it.syntax, GenericArg::ConstArg(it) => &it.syntax, + GenericArg::ReturnTypeArg(it) => &it.syntax, } } } @@ -4170,7 +4199,13 @@ impl AstNode for AnyHasTypeBounds { fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - ASSOC_TYPE_ARG | TRAIT | TYPE_ALIAS | LIFETIME_PARAM | TYPE_PARAM | WHERE_PRED + ASSOC_TYPE_ARG + | RETURN_TYPE_ARG + | TRAIT + | TYPE_ALIAS + | LIFETIME_PARAM + | TYPE_PARAM + | WHERE_PRED ) } fn cast(syntax: SyntaxNode) -> Option { @@ -4333,6 +4368,11 @@ impl std::fmt::Display for ConstArg { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ReturnTypeArg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for TypeBoundList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs index ccce71966ff8..caef6a795397 100644 --- a/crates/syntax/src/tests/ast_src.rs +++ b/crates/syntax/src/tests/ast_src.rs @@ -199,6 +199,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "GENERIC_PARAM", "LIFETIME_PARAM", "TYPE_PARAM", + "RETURN_TYPE_ARG", "CONST_PARAM", "GENERIC_ARG_LIST", "LIFETIME",