Give path segment type anchors their own grammar rule

This commit is contained in:
Lukas Wirth 2025-06-04 11:30:43 +02:00
parent d2164fe08b
commit 7c3de05e3a
14 changed files with 223 additions and 158 deletions

View File

@ -931,11 +931,12 @@ pub fn new() {
// PATH_TYPE@23..26 // PATH_TYPE@23..26
// PATH@23..26 // PATH@23..26
// PATH_SEGMENT@23..26 // PATH_SEGMENT@23..26
// L_ANGLE@23..24 "<" // TYPE_ANCHOR@23..26
// PAREN_TYPE@24..26 // L_ANGLE@23..24 "<"
// L_PAREN@24..25 "(" // PAREN_TYPE@24..26
// ERROR@25..26 // L_PAREN@24..25 "("
// INT_NUMBER@25..26 "8" // ERROR@25..26
// INT_NUMBER@25..26 "8"
// PLUS@26..27 "+" // PLUS@26..27 "+"
// CONST_ARG@27..28 // CONST_ARG@27..28
// LITERAL@27..28 // LITERAL@27..28

View File

@ -89,7 +89,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<Completed
// test qual_paths // test qual_paths
// type X = <A as B>::Output; // type X = <A as B>::Output;
// fn foo() { <usize as Default>::default(); } // fn foo() { <usize as Default>::default(); }
if first && p.eat(T![<]) { if first && p.at(T![<]) {
let m = p.start();
p.bump(T![<]);
// test_err angled_path_without_qual // test_err angled_path_without_qual
// type X = <()>; // type X = <()>;
// type Y = <A as B>; // type Y = <A as B>;
@ -102,6 +104,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<Completed
} }
} }
p.expect(T![>]); p.expect(T![>]);
m.complete(p, TYPE_ANCHOR);
if !p.at(T![::]) { if !p.at(T![::]) {
p.error("expected `::`"); p.error("expected `::`");
} }

View File

@ -291,6 +291,7 @@ pub enum SyntaxKind {
TUPLE_STRUCT_PAT, TUPLE_STRUCT_PAT,
TUPLE_TYPE, TUPLE_TYPE,
TYPE_ALIAS, TYPE_ALIAS,
TYPE_ANCHOR,
TYPE_ARG, TYPE_ARG,
TYPE_BOUND, TYPE_BOUND,
TYPE_BOUND_LIST, TYPE_BOUND_LIST,
@ -463,6 +464,7 @@ impl SyntaxKind {
| TUPLE_STRUCT_PAT | TUPLE_STRUCT_PAT
| TUPLE_TYPE | TUPLE_TYPE
| TYPE_ALIAS | TYPE_ALIAS
| TYPE_ANCHOR
| TYPE_ARG | TYPE_ARG
| TYPE_BOUND | TYPE_BOUND
| TYPE_BOUND_LIST | TYPE_BOUND_LIST

View File

@ -10,11 +10,12 @@ SOURCE_FILE
PATH_TYPE PATH_TYPE
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
TUPLE_TYPE L_ANGLE "<"
L_PAREN "(" TUPLE_TYPE
R_PAREN ")" L_PAREN "("
R_ANGLE ">" R_PAREN ")"
R_ANGLE ">"
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n" WHITESPACE "\n"
TYPE_ALIAS TYPE_ALIAS
@ -28,21 +29,22 @@ SOURCE_FILE
PATH_TYPE PATH_TYPE
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "A" NAME_REF
WHITESPACE " " IDENT "A"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "B" NAME_REF
R_ANGLE ">" IDENT "B"
R_ANGLE ">"
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n" WHITESPACE "\n"
error 13: expected `::` error 13: expected `::`

View File

@ -88,13 +88,14 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Foo" NAME_REF
R_ANGLE ">" IDENT "Foo"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -119,21 +120,22 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Foo" NAME_REF
WHITESPACE " " IDENT "Foo"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Trait" NAME_REF
R_ANGLE ">" IDENT "Trait"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -11,21 +11,22 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "A" NAME_REF
WHITESPACE " " IDENT "A"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "B" NAME_REF
R_ANGLE ">" IDENT "B"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -51,21 +52,22 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "usize" NAME_REF
WHITESPACE " " IDENT "usize"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Default" NAME_REF
R_ANGLE ">" IDENT "Default"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -19,10 +19,11 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
INFER_TYPE L_ANGLE "<"
UNDERSCORE "_" INFER_TYPE
R_ANGLE ">" UNDERSCORE "_"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -84,21 +84,22 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "T" NAME_REF
WHITESPACE " " IDENT "T"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Iterator" NAME_REF
R_ANGLE ">" IDENT "Iterator"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -45,21 +45,22 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "S" NAME_REF
WHITESPACE " " IDENT "S"
AS_KW "as" WHITESPACE " "
WHITESPACE " " AS_KW "as"
PATH_TYPE WHITESPACE " "
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Iterator" NAME_REF
R_ANGLE ">" IDENT "Iterator"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -107,13 +107,14 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
PATH_TYPE L_ANGLE "<"
PATH PATH_TYPE
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "Foo" NAME_REF
R_ANGLE ">" IDENT "Foo"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -288,26 +288,27 @@ SOURCE_FILE
PATH PATH
PATH PATH
PATH_SEGMENT PATH_SEGMENT
L_ANGLE "<" TYPE_ANCHOR
REF_TYPE L_ANGLE "<"
AMP "&" REF_TYPE
LIFETIME AMP "&"
LIFETIME_IDENT "'a" LIFETIME
LIFETIME_IDENT "'a"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "T"
WHITESPACE " "
AS_KW "as"
WHITESPACE " " WHITESPACE " "
PATH_TYPE PATH_TYPE
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "T" IDENT "Baz"
WHITESPACE " " R_ANGLE ">"
AS_KW "as"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "Baz"
R_ANGLE ">"
COLON2 "::" COLON2 "::"
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF

View File

@ -39,7 +39,10 @@ PathSegment =
| NameRef GenericArgList? | NameRef GenericArgList?
| NameRef ParenthesizedArgList RetType? | NameRef ParenthesizedArgList RetType?
| NameRef ReturnTypeSyntax | NameRef ReturnTypeSyntax
| '<' Type ('as' PathType)? '>' | TypeAnchor
TypeAnchor =
'<' Type ('as' PathType)? '>'
ReturnTypeSyntax = ReturnTypeSyntax =
'(' '..' ')' '(' '..' ')'
@ -98,7 +101,7 @@ WhereClause =
'where' predicates:(WherePred (',' WherePred)* ','?) 'where' predicates:(WherePred (',' WherePred)* ','?)
WherePred = WherePred =
('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList? ('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList?
//*************************// //*************************//

View File

@ -1232,21 +1232,13 @@ impl PathSegment {
support::child(&self.syntax) support::child(&self.syntax)
} }
#[inline] #[inline]
pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
#[inline]
pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) } pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
#[inline] #[inline]
pub fn return_type_syntax(&self) -> Option<ReturnTypeSyntax> { support::child(&self.syntax) } pub fn return_type_syntax(&self) -> Option<ReturnTypeSyntax> { support::child(&self.syntax) }
#[inline] #[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } pub fn type_anchor(&self) -> Option<TypeAnchor> { support::child(&self.syntax) }
#[inline] #[inline]
pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
#[inline]
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
#[inline]
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
#[inline]
pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
} }
pub struct PathType { pub struct PathType {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
@ -1739,6 +1731,21 @@ impl TypeAlias {
#[inline] #[inline]
pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![type]) } pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![type]) }
} }
pub struct TypeAnchor {
pub(crate) syntax: SyntaxNode,
}
impl TypeAnchor {
#[inline]
pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
#[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
#[inline]
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
#[inline]
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
#[inline]
pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
}
pub struct TypeArg { pub struct TypeArg {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
} }
@ -7108,6 +7115,42 @@ impl fmt::Debug for TypeAlias {
f.debug_struct("TypeAlias").field("syntax", &self.syntax).finish() 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<Self> {
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<H: hash::Hasher>(&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 { impl AstNode for TypeArg {
#[inline] #[inline]
fn kind() -> SyntaxKind fn kind() -> SyntaxKind
@ -10624,6 +10667,11 @@ impl std::fmt::Display for TypeAlias {
std::fmt::Display::fmt(self.syntax(), f) 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 { impl std::fmt::Display for TypeArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)

View File

@ -276,18 +276,15 @@ impl ast::PathSegment {
_ => PathSegmentKind::Name(name_ref), _ => PathSegmentKind::Name(name_ref),
} }
} else { } else {
match self.syntax().first_child_or_token()?.kind() { let anchor = self.type_anchor()?;
T![<] => { // FIXME: Move this over to `ast::TypeAnchor`
// <T> or <T as Trait> // <T> or <T as Trait>
// T is any TypeRef, Trait has to be a PathType // T is any TypeRef, Trait has to be a PathType
let mut type_refs = let mut type_refs =
self.syntax().children().filter(|node| ast::Type::can_cast(node.kind())); anchor.syntax().children().filter(|node| ast::Type::can_cast(node.kind()));
let type_ref = type_refs.next().and_then(ast::Type::cast); let type_ref = type_refs.next().and_then(ast::Type::cast);
let trait_ref = type_refs.next().and_then(ast::PathType::cast); let trait_ref = type_refs.next().and_then(ast::PathType::cast);
PathSegmentKind::Type { type_ref, trait_ref } PathSegmentKind::Type { type_ref, trait_ref }
}
_ => return None,
}
}; };
Some(res) Some(res)
} }
@ -473,7 +470,7 @@ impl ast::Impl {
// [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778) // [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778)
impl ast::PathSegment { impl ast::PathSegment {
pub fn qualifying_trait(&self) -> Option<ast::PathType> { pub fn qualifying_trait(&self) -> Option<ast::PathType> {
let mut path_types = support::children(self.syntax()); let mut path_types = support::children(self.type_anchor()?.syntax());
let first = path_types.next()?; let first = path_types.next()?;
path_types.next().or(Some(first)) path_types.next().or(Some(first))
} }