From 7c3de05e3ac33a1b965bbdd3703243780143b9d2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 4 Jun 2025 11:30:43 +0200 Subject: [PATCH] Give path segment type anchors their own grammar rule --- .../macro_expansion_tests/mbe/regression.rs | 11 ++-- crates/parser/src/grammar/paths.rs | 5 +- crates/parser/src/syntax_kind/generated.rs | 2 + .../inline/err/angled_path_without_qual.rast | 42 ++++++------ .../test_data/parser/inline/ok/call_expr.rast | 46 ++++++------- .../parser/inline/ok/qual_paths.rast | 62 ++++++++--------- .../inline/ok/type_path_in_pattern.rast | 9 +-- .../parser/inline/ok/where_clause.rast | 31 ++++----- .../parser/ok/0036_fully_qualified.rast | 31 ++++----- .../parser/ok/0042_ufcs_call_list.rast | 15 +++-- .../parser/ok/0067_where_for_pred.rast | 31 ++++----- crates/syntax/rust.ungram | 7 +- crates/syntax/src/ast/generated/nodes.rs | 66 ++++++++++++++++--- crates/syntax/src/ast/node_ext.rs | 23 +++---- 14 files changed, 223 insertions(+), 158 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index cb4fcd887d..2cc3ca8c75 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -931,11 +931,12 @@ pub fn new() { // PATH_TYPE@23..26 // PATH@23..26 // PATH_SEGMENT@23..26 -// L_ANGLE@23..24 "<" -// PAREN_TYPE@24..26 -// L_PAREN@24..25 "(" -// ERROR@25..26 -// INT_NUMBER@25..26 "8" +// TYPE_ANCHOR@23..26 +// L_ANGLE@23..24 "<" +// PAREN_TYPE@24..26 +// L_PAREN@24..25 "(" +// ERROR@25..26 +// INT_NUMBER@25..26 "8" // PLUS@26..27 "+" // CONST_ARG@27..28 // LITERAL@27..28 diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index 770827c6b0..e628bcc056 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -89,7 +89,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option::Output; // fn foo() { ::default(); } - if first && p.eat(T![<]) { + if first && p.at(T![<]) { + let m = p.start(); + p.bump(T![<]); // test_err angled_path_without_qual // type X = <()>; // type Y = ; @@ -102,6 +104,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option]); + m.complete(p, TYPE_ANCHOR); if !p.at(T![::]) { p.error("expected `::`"); } diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index b1727509b1..f534546ea0 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -291,6 +291,7 @@ pub enum SyntaxKind { TUPLE_STRUCT_PAT, TUPLE_TYPE, TYPE_ALIAS, + TYPE_ANCHOR, TYPE_ARG, TYPE_BOUND, TYPE_BOUND_LIST, @@ -463,6 +464,7 @@ impl SyntaxKind { | TUPLE_STRUCT_PAT | TUPLE_TYPE | TYPE_ALIAS + | TYPE_ANCHOR | TYPE_ARG | TYPE_BOUND | TYPE_BOUND_LIST diff --git a/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast b/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast index 0529e9750e..53fbe0b615 100644 --- a/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast +++ b/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast @@ -10,11 +10,12 @@ SOURCE_FILE PATH_TYPE PATH PATH_SEGMENT - L_ANGLE "<" - TUPLE_TYPE - L_PAREN "(" - R_PAREN ")" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" TYPE_ALIAS @@ -28,21 +29,22 @@ SOURCE_FILE PATH_TYPE PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "A" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "B" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "A" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "B" + R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" error 13: expected `::` diff --git a/crates/parser/test_data/parser/inline/ok/call_expr.rast b/crates/parser/test_data/parser/inline/ok/call_expr.rast index 19cc8d5ac7..7c1d894f7e 100644 --- a/crates/parser/test_data/parser/inline/ok/call_expr.rast +++ b/crates/parser/test_data/parser/inline/ok/call_expr.rast @@ -88,13 +88,14 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF @@ -119,21 +120,22 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Trait" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/qual_paths.rast b/crates/parser/test_data/parser/inline/ok/qual_paths.rast index 8c66cfe599..10f8a6a751 100644 --- a/crates/parser/test_data/parser/inline/ok/qual_paths.rast +++ b/crates/parser/test_data/parser/inline/ok/qual_paths.rast @@ -11,21 +11,22 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "A" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "B" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "A" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "B" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF @@ -51,21 +52,22 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "usize" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Default" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "usize" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Default" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast b/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast index 297f7575ca..3d27afa5ec 100644 --- a/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast +++ b/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast @@ -19,10 +19,11 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - INFER_TYPE - UNDERSCORE "_" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + INFER_TYPE + UNDERSCORE "_" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/where_clause.rast b/crates/parser/test_data/parser/inline/ok/where_clause.rast index a3cbe457e1..9adfe2caa7 100644 --- a/crates/parser/test_data/parser/inline/ok/where_clause.rast +++ b/crates/parser/test_data/parser/inline/ok/where_clause.rast @@ -84,21 +84,22 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "T" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Iterator" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0036_fully_qualified.rast b/crates/parser/test_data/parser/ok/0036_fully_qualified.rast index 9382020e2f..2fecb1cc47 100644 --- a/crates/parser/test_data/parser/ok/0036_fully_qualified.rast +++ b/crates/parser/test_data/parser/ok/0036_fully_qualified.rast @@ -45,21 +45,22 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Iterator" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast b/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast index a536b0e881..d1d1ffacf0 100644 --- a/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast +++ b/crates/parser/test_data/parser/ok/0042_ufcs_call_list.rast @@ -107,13 +107,14 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - R_ANGLE ">" + TYPE_ANCHOR + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0067_where_for_pred.rast b/crates/parser/test_data/parser/ok/0067_where_for_pred.rast index cd3b21ae94..8bf1090f9c 100644 --- a/crates/parser/test_data/parser/ok/0067_where_for_pred.rast +++ b/crates/parser/test_data/parser/ok/0067_where_for_pred.rast @@ -288,26 +288,27 @@ SOURCE_FILE PATH PATH PATH_SEGMENT - L_ANGLE "<" - REF_TYPE - AMP "&" - LIFETIME - LIFETIME_IDENT "'a" + TYPE_ANCHOR + L_ANGLE "<" + REF_TYPE + AMP "&" + LIFETIME + LIFETIME_IDENT "'a" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + WHITESPACE " " + AS_KW "as" WHITESPACE " " PATH_TYPE PATH PATH_SEGMENT NAME_REF - IDENT "T" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Baz" - R_ANGLE ">" + IDENT "Baz" + R_ANGLE ">" COLON2 "::" PATH_SEGMENT NAME_REF diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 10abca7d35..c81da06682 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -39,7 +39,10 @@ PathSegment = | NameRef GenericArgList? | NameRef ParenthesizedArgList RetType? | NameRef ReturnTypeSyntax -| '<' Type ('as' PathType)? '>' +| TypeAnchor + +TypeAnchor = + '<' Type ('as' PathType)? '>' ReturnTypeSyntax = '(' '..' ')' @@ -98,7 +101,7 @@ WhereClause = 'where' predicates:(WherePred (',' WherePred)* ','?) WherePred = - ('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList? + ('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList? //*************************// diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index cd9f4dba89..04c7e8a578 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1232,21 +1232,13 @@ impl PathSegment { support::child(&self.syntax) } #[inline] - pub fn path_type(&self) -> Option { support::child(&self.syntax) } - #[inline] pub fn ret_type(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn return_type_syntax(&self) -> Option { support::child(&self.syntax) } #[inline] - pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn type_anchor(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } - #[inline] - pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } - #[inline] - pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } - #[inline] - pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } } pub struct PathType { pub(crate) syntax: SyntaxNode, @@ -1739,6 +1731,21 @@ impl TypeAlias { #[inline] pub fn type_token(&self) -> Option { support::token(&self.syntax, T![type]) } } +pub struct TypeAnchor { + pub(crate) syntax: SyntaxNode, +} +impl TypeAnchor { + #[inline] + pub fn path_type(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn ty(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } + #[inline] + pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } + #[inline] + pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } +} pub struct TypeArg { pub(crate) syntax: SyntaxNode, } @@ -7108,6 +7115,42 @@ impl fmt::Debug for TypeAlias { f.debug_struct("TypeAlias").field("syntax", &self.syntax).finish() } } +impl AstNode for TypeAnchor { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_ANCHOR + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ANCHOR } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for TypeAnchor { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for TypeAnchor {} +impl PartialEq for TypeAnchor { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for TypeAnchor { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for TypeAnchor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TypeAnchor").field("syntax", &self.syntax).finish() + } +} impl AstNode for TypeArg { #[inline] fn kind() -> SyntaxKind @@ -10624,6 +10667,11 @@ impl std::fmt::Display for TypeAlias { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for TypeAnchor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for TypeArg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index b9ccd34cff..dcf853427e 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -276,18 +276,15 @@ impl ast::PathSegment { _ => PathSegmentKind::Name(name_ref), } } else { - match self.syntax().first_child_or_token()?.kind() { - T![<] => { - // or - // T is any TypeRef, Trait has to be a PathType - let mut type_refs = - self.syntax().children().filter(|node| ast::Type::can_cast(node.kind())); - let type_ref = type_refs.next().and_then(ast::Type::cast); - let trait_ref = type_refs.next().and_then(ast::PathType::cast); - PathSegmentKind::Type { type_ref, trait_ref } - } - _ => return None, - } + let anchor = self.type_anchor()?; + // FIXME: Move this over to `ast::TypeAnchor` + // or + // T is any TypeRef, Trait has to be a PathType + let mut type_refs = + anchor.syntax().children().filter(|node| ast::Type::can_cast(node.kind())); + let type_ref = type_refs.next().and_then(ast::Type::cast); + let trait_ref = type_refs.next().and_then(ast::PathType::cast); + PathSegmentKind::Type { type_ref, trait_ref } }; Some(res) } @@ -473,7 +470,7 @@ impl ast::Impl { // [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778) impl ast::PathSegment { pub fn qualifying_trait(&self) -> Option { - let mut path_types = support::children(self.syntax()); + let mut path_types = support::children(self.type_anchor()?.syntax()); let first = path_types.next()?; path_types.next().or(Some(first)) }