1 //! Computes color for a single element.
3 use hir
::{AsAssocItem, HasVisibility, Semantics}
;
5 defs
::{Definition, IdentClass, NameClass, NameRefClass}
,
6 FxHashMap
, RootDatabase
, SymbolKind
,
9 ast
, match_ast
, AstNode
, AstToken
, NodeOrToken
,
10 SyntaxKind
::{self, *}
,
11 SyntaxNode
, SyntaxToken
, T
,
15 syntax_highlighting
::tags
::{HlOperator, HlPunct}
,
16 Highlight
, HlMod
, HlTag
,
19 pub(super) fn token(sema
: &Semantics
<'_
, RootDatabase
>, token
: SyntaxToken
) -> Option
<Highlight
> {
20 if let Some(comment
) = ast
::Comment
::cast(token
.clone()) {
21 let h
= HlTag
::Comment
;
22 return Some(match comment
.kind().doc
{
23 Some(_
) => h
| HlMod
::Documentation
,
28 let highlight
: Highlight
= match token
.kind() {
29 STRING
| BYTE_STRING
=> HlTag
::StringLiteral
.into(),
30 INT_NUMBER
if token
.parent_ancestors().nth(1).map(|it
| it
.kind()) == Some(FIELD_EXPR
) => {
31 SymbolKind
::Field
.into()
33 INT_NUMBER
| FLOAT_NUMBER
=> HlTag
::NumericLiteral
.into(),
34 BYTE
=> HlTag
::ByteLiteral
.into(),
35 CHAR
=> HlTag
::CharLiteral
.into(),
36 IDENT
if token
.parent().and_then(ast
::TokenTree
::cast
).is_some() => {
37 // from this point on we are inside a token tree, this only happens for identifiers
38 // that were not mapped down into macro invocations
41 p
if p
.is_punct() => punctuation(sema
, token
, p
),
42 k
if k
.is_keyword() => keyword(sema
, token
, k
)?
,
48 pub(super) fn name_like(
49 sema
: &Semantics
<'_
, RootDatabase
>,
51 bindings_shadow_count
: &mut FxHashMap
<hir
::Name
, u32>,
52 syntactic_name_ref_highlighting
: bool
,
53 name_like
: ast
::NameLike
,
54 ) -> Option
<(Highlight
, Option
<u64>)> {
55 let mut binding_hash
= None
;
56 let highlight
= match name_like
{
57 ast
::NameLike
::NameRef(name_ref
) => highlight_name_ref(
60 bindings_shadow_count
,
62 syntactic_name_ref_highlighting
,
65 ast
::NameLike
::Name(name
) => {
66 highlight_name(sema
, bindings_shadow_count
, &mut binding_hash
, krate
, name
)
68 ast
::NameLike
::Lifetime(lifetime
) => match IdentClass
::classify_lifetime(sema
, &lifetime
) {
69 Some(IdentClass
::NameClass(NameClass
::Definition(def
))) => {
70 highlight_def(sema
, krate
, def
) | HlMod
::Definition
72 Some(IdentClass
::NameRefClass(NameRefClass
::Definition(def
))) => {
73 highlight_def(sema
, krate
, def
)
75 // FIXME: Fallback for 'static and '_, as we do not resolve these yet
76 _
=> SymbolKind
::LifetimeParam
.into(),
79 Some((highlight
, binding_hash
))
83 sema
: &Semantics
<'_
, RootDatabase
>,
87 let parent
= token
.parent();
88 let parent_kind
= parent
.as_ref().map_or(EOF
, SyntaxNode
::kind
);
89 match (kind
, parent_kind
) {
90 (T
![?
], TRY_EXPR
) => HlTag
::Operator(HlOperator
::Other
) | HlMod
::ControlFlow
,
91 (T
![&], BIN_EXPR
) => HlOperator
::Bitwise
.into(),
92 (T
![&], REF_EXPR
) => {
93 let h
= HlTag
::Operator(HlOperator
::Other
).into();
94 let is_unsafe
= parent
95 .and_then(ast
::RefExpr
::cast
)
96 .map(|ref_expr
| sema
.is_unsafe_ref_expr(&ref_expr
));
97 if let Some(true) = is_unsafe
{
103 (T
![::] | T
![->] | T
![=>] | T
![..] | T
![..=] | T
![=] | T
![@
] | T
![.], _
) => {
104 HlOperator
::Other
.into()
106 (T
![!], MACRO_CALL
| MACRO_RULES
) => HlPunct
::MacroBang
.into(),
107 (T
![!], NEVER_TYPE
) => HlTag
::BuiltinType
.into(),
108 (T
![!], PREFIX_EXPR
) => HlOperator
::Logical
.into(),
109 (T
![*], PTR_TYPE
) => HlTag
::Keyword
.into(),
110 (T
![*], PREFIX_EXPR
) => {
111 let is_raw_ptr
= (|| {
112 let prefix_expr
= parent
.and_then(ast
::PrefixExpr
::cast
)?
;
113 let expr
= prefix_expr
.expr()?
;
114 sema
.type_of_expr(&expr
)?
.original
.is_raw_ptr().then_some(())
116 if let Some(()) = is_raw_ptr
{
117 HlTag
::Operator(HlOperator
::Other
) | HlMod
::Unsafe
119 HlOperator
::Other
.into()
122 (T
![-], PREFIX_EXPR
) => {
123 let prefix_expr
= parent
.and_then(ast
::PrefixExpr
::cast
).and_then(|e
| e
.expr());
125 Some(ast
::Expr
::Literal(_
)) => HlTag
::NumericLiteral
,
126 _
=> HlTag
::Operator(HlOperator
::Other
),
130 (T
![+] | T
![-] | T
![*] | T
![/] | T
![%], BIN_EXPR
) => HlOperator
::Arithmetic
.into(),
131 (T
![+=] | T
![-=] | T
![*=] | T
![/=] | T
![%=], BIN_EXPR
) => {
132 Highlight
::from(HlOperator
::Arithmetic
) | HlMod
::Mutable
134 (T
![|] | T
![&] | T
![^
] | T
![>>] | T
![<<], BIN_EXPR
) => HlOperator
::Bitwise
.into(),
135 (T
![|=] | T
![&=] | T
![^
=] | T
![>>=] | T
![<<=], BIN_EXPR
) => {
136 Highlight
::from(HlOperator
::Bitwise
) | HlMod
::Mutable
138 (T
![&&] | T
![||], BIN_EXPR
) => HlOperator
::Logical
.into(),
139 (T
![>] | T
![<] | T
![==] | T
![>=] | T
![<=] | T
![!=], BIN_EXPR
) => {
140 HlOperator
::Comparison
.into()
142 (_
, ATTR
) => HlTag
::AttributeBracket
.into(),
143 (kind
, _
) => match kind
{
144 T
!['
['
] | T
!['
]'
] => HlPunct
::Bracket
,
145 T
!['{'] | T!['}'
] => HlPunct
::Brace
,
146 T
!['
('
] | T
!['
)'
] => HlPunct
::Parenthesis
,
147 T
![<] | T
![>] => HlPunct
::Angle
,
148 T
![,] => HlPunct
::Comma
,
149 T
![:] => HlPunct
::Colon
,
150 T
![;] => HlPunct
::Semi
,
151 T
![.] => HlPunct
::Dot
,
159 sema
: &Semantics
<'_
, RootDatabase
>,
162 ) -> Option
<Highlight
> {
163 let h
= Highlight
::new(HlTag
::Keyword
);
165 T
![await
] => h
| HlMod
::Async
| HlMod
::ControlFlow
,
166 T
![async
] => h
| HlMod
::Async
,
176 | T
![yield] => h
| HlMod
::ControlFlow
,
177 T
![do] | T
![yeet
] if parent_matches
::<ast
::YeetExpr
>(&token
) => h
| HlMod
::ControlFlow
,
178 T
![for] if parent_matches
::<ast
::ForExpr
>(&token
) => h
| HlMod
::ControlFlow
,
179 T
![unsafe] => h
| HlMod
::Unsafe
,
180 T
![true] | T
![false] => HlTag
::BoolLiteral
.into(),
181 // crate is handled just as a token if it's in an `extern crate`
182 T
![crate] if parent_matches
::<ast
::ExternCrate
>(&token
) => h
,
183 // self, crate, super and `Self` are handled as either a Name or NameRef already, unless they
184 // are inside unmapped token trees
185 T
![self] | T
![crate] | T
![super] | T
![Self] if parent_matches
::<ast
::NameRef
>(&token
) => {
188 T
![self] if parent_matches
::<ast
::Name
>(&token
) => return None
,
189 T
![ref] => match token
.parent().and_then(ast
::IdentPat
::cast
) {
190 Some(ident
) if sema
.is_unsafe_ident_pat(&ident
) => h
| HlMod
::Unsafe
,
198 fn highlight_name_ref(
199 sema
: &Semantics
<'_
, RootDatabase
>,
201 bindings_shadow_count
: &mut FxHashMap
<hir
::Name
, u32>,
202 binding_hash
: &mut Option
<u64>,
203 syntactic_name_ref_highlighting
: bool
,
204 name_ref
: ast
::NameRef
,
207 if let Some(res
) = highlight_method_call_by_name_ref(sema
, krate
, &name_ref
) {
211 let name_class
= match NameRefClass
::classify(sema
, &name_ref
) {
212 Some(name_kind
) => name_kind
,
213 None
if syntactic_name_ref_highlighting
=> {
214 return highlight_name_ref_by_syntax(name_ref
, sema
, krate
)
216 // FIXME: This is required for helper attributes used by proc-macros, as those do not map down
217 // to anything when used.
218 // We can fix this for derive attributes since derive helpers are recorded, but not for
219 // general attributes.
220 None
if name_ref
.syntax().ancestors().any(|it
| it
.kind() == ATTR
)
221 && !sema
.hir_file_for(name_ref
.syntax()).is_derive_attr_pseudo_expansion(sema
.db
) =>
223 return HlTag
::Symbol(SymbolKind
::Attribute
).into();
225 None
=> return HlTag
::UnresolvedReference
.into(),
227 let mut h
= match name_class
{
228 NameRefClass
::Definition(def
) => {
229 if let Definition
::Local(local
) = &def
{
230 let name
= local
.name(db
);
231 let shadow_count
= bindings_shadow_count
.entry(name
.clone()).or_default();
232 *binding_hash
= Some(calc_binding_hash(&name
, *shadow_count
))
235 let mut h
= highlight_def(sema
, krate
, def
);
238 Definition
::Local(local
) if is_consumed_lvalue(name_ref
.syntax(), &local
, db
) => {
239 h
|= HlMod
::Consuming
;
241 Definition
::Trait(trait_
) if trait_
.is_unsafe(db
) => {
242 if ast
::Impl
::for_trait_name_ref(&name_ref
)
243 .map_or(false, |impl_
| impl_
.unsafe_token().is_some())
248 Definition
::Field(field
) => {
249 if let Some(parent
) = name_ref
.syntax().parent() {
250 if matches
!(parent
.kind(), FIELD_EXPR
| RECORD_PAT_FIELD
) {
251 if let hir
::VariantDef
::Union(_
) = field
.parent_def(db
) {
257 Definition
::Macro(_
) => {
258 if let Some(macro_call
) =
259 ide_db
::syntax_helpers
::node_ext
::full_path_of_name_ref(&name_ref
)
260 .and_then(|it
| it
.syntax().parent().and_then(ast
::MacroCall
::cast
))
262 if sema
.is_unsafe_macro_call(¯o_call
) {
272 NameRefClass
::FieldShorthand { .. }
=> SymbolKind
::Field
.into(),
275 h
.tag
= match name_ref
.token_kind() {
276 T
![Self] => HlTag
::Symbol(SymbolKind
::SelfType
),
277 T
![self] => HlTag
::Symbol(SymbolKind
::SelfParam
),
278 T
![super] | T
![crate] => HlTag
::Keyword
,
285 sema
: &Semantics
<'_
, RootDatabase
>,
286 bindings_shadow_count
: &mut FxHashMap
<hir
::Name
, u32>,
287 binding_hash
: &mut Option
<u64>,
291 let name_kind
= NameClass
::classify(sema
, &name
);
292 if let Some(NameClass
::Definition(Definition
::Local(local
))) = &name_kind
{
293 let name
= local
.name(sema
.db
);
294 let shadow_count
= bindings_shadow_count
.entry(name
.clone()).or_default();
296 *binding_hash
= Some(calc_binding_hash(&name
, *shadow_count
))
299 Some(NameClass
::Definition(def
)) => {
300 let mut h
= highlight_def(sema
, krate
, def
) | HlMod
::Definition
;
301 if let Definition
::Trait(trait_
) = &def
{
302 if trait_
.is_unsafe(sema
.db
) {
308 Some(NameClass
::ConstReference(def
)) => highlight_def(sema
, krate
, def
),
309 Some(NameClass
::PatFieldShorthand { field_ref, .. }
) => {
310 let mut h
= HlTag
::Symbol(SymbolKind
::Field
).into();
311 if let hir
::VariantDef
::Union(_
) = field_ref
.parent_def(sema
.db
) {
316 None
=> highlight_name_by_syntax(name
) | HlMod
::Definition
,
320 fn calc_binding_hash(name
: &hir
::Name
, shadow_count
: u32) -> u64 {
321 fn hash
<T
: std
::hash
::Hash
+ std
::fmt
::Debug
>(x
: T
) -> u64 {
322 use std
::{collections::hash_map::DefaultHasher, hash::Hasher}
;
324 let mut hasher
= DefaultHasher
::new();
329 hash((name
, shadow_count
))
333 sema
: &Semantics
<'_
, RootDatabase
>,
338 let mut h
= match def
{
339 Definition
::Macro(m
) => Highlight
::new(HlTag
::Symbol(m
.kind(sema
.db
).into())),
340 Definition
::Field(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::Field
)),
341 Definition
::Module(module
) => {
342 let mut h
= Highlight
::new(HlTag
::Symbol(SymbolKind
::Module
));
343 if module
.is_crate_root(db
) {
344 h
|= HlMod
::CrateRoot
;
348 Definition
::Function(func
) => {
349 let mut h
= Highlight
::new(HlTag
::Symbol(SymbolKind
::Function
));
350 if let Some(item
) = func
.as_assoc_item(db
) {
351 h
|= HlMod
::Associated
;
352 match func
.self_param(db
) {
353 Some(sp
) => match sp
.access(db
) {
354 hir
::Access
::Exclusive
=> {
356 h
|= HlMod
::Reference
;
358 hir
::Access
::Shared
=> h
|= HlMod
::Reference
,
359 hir
::Access
::Owned
=> h
|= HlMod
::Consuming
,
361 None
=> h
|= HlMod
::Static
,
364 match item
.container(db
) {
365 hir
::AssocItemContainer
::Impl(i
) => {
366 if i
.trait_(db
).is_some() {
370 hir
::AssocItemContainer
::Trait(_t
) => {
376 if func
.is_unsafe_to_call(db
) {
379 if func
.is_async(db
) {
385 Definition
::Adt(adt
) => {
387 hir
::Adt
::Struct(_
) => HlTag
::Symbol(SymbolKind
::Struct
),
388 hir
::Adt
::Enum(_
) => HlTag
::Symbol(SymbolKind
::Enum
),
389 hir
::Adt
::Union(_
) => HlTag
::Symbol(SymbolKind
::Union
),
394 Definition
::Variant(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::Variant
)),
395 Definition
::Const(konst
) => {
396 let mut h
= Highlight
::new(HlTag
::Symbol(SymbolKind
::Const
));
398 if let Some(item
) = konst
.as_assoc_item(db
) {
399 h
|= HlMod
::Associated
;
400 match item
.container(db
) {
401 hir
::AssocItemContainer
::Impl(i
) => {
402 if i
.trait_(db
).is_some() {
406 hir
::AssocItemContainer
::Trait(_t
) => {
414 Definition
::Trait(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::Trait
)),
415 Definition
::TraitAlias(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::TraitAlias
)),
416 Definition
::TypeAlias(type_
) => {
417 let mut h
= Highlight
::new(HlTag
::Symbol(SymbolKind
::TypeAlias
));
419 if let Some(item
) = type_
.as_assoc_item(db
) {
420 h
|= HlMod
::Associated
;
421 match item
.container(db
) {
422 hir
::AssocItemContainer
::Impl(i
) => {
423 if i
.trait_(db
).is_some() {
427 hir
::AssocItemContainer
::Trait(_t
) => {
435 Definition
::BuiltinType(_
) => Highlight
::new(HlTag
::BuiltinType
),
436 Definition
::Static(s
) => {
437 let mut h
= Highlight
::new(HlTag
::Symbol(SymbolKind
::Static
));
446 Definition
::SelfType(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::Impl
)),
447 Definition
::GenericParam(it
) => match it
{
448 hir
::GenericParam
::TypeParam(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::TypeParam
)),
449 hir
::GenericParam
::ConstParam(_
) => {
450 Highlight
::new(HlTag
::Symbol(SymbolKind
::ConstParam
))
452 hir
::GenericParam
::LifetimeParam(_
) => {
453 Highlight
::new(HlTag
::Symbol(SymbolKind
::LifetimeParam
))
456 Definition
::Local(local
) => {
457 let tag
= if local
.is_self(db
) {
458 HlTag
::Symbol(SymbolKind
::SelfParam
)
459 } else if local
.is_param(db
) {
460 HlTag
::Symbol(SymbolKind
::ValueParam
)
462 HlTag
::Symbol(SymbolKind
::Local
)
464 let mut h
= Highlight
::new(tag
);
465 let ty
= local
.ty(db
);
466 if local
.is_mut(db
) || ty
.is_mutable_reference() {
469 if local
.is_ref(db
) || ty
.is_reference() {
470 h
|= HlMod
::Reference
;
472 if ty
.as_callable(db
).is_some() || ty
.impls_fnonce(db
) {
473 h
|= HlMod
::Callable
;
477 Definition
::Label(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::Label
)),
478 Definition
::BuiltinAttr(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::BuiltinAttr
)),
479 Definition
::ToolModule(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::ToolModule
)),
480 Definition
::DeriveHelper(_
) => Highlight
::new(HlTag
::Symbol(SymbolKind
::DeriveHelper
)),
483 let def_crate
= def
.krate(db
);
484 let is_from_other_crate
= def_crate
!= Some(krate
);
485 let is_from_builtin_crate
= def_crate
.map_or(false, |def_crate
| def_crate
.is_builtin(db
));
486 let is_builtin_type
= matches
!(def
, Definition
::BuiltinType(_
));
487 let is_public
= def
.visibility(db
) == Some(hir
::Visibility
::Public
);
489 match (is_from_other_crate
, is_builtin_type
, is_public
) {
490 (true, false, _
) => h
|= HlMod
::Library
,
491 (false, _
, true) => h
|= HlMod
::Public
,
495 if is_from_builtin_crate
{
496 h
|= HlMod
::DefaultLibrary
;
502 fn highlight_method_call_by_name_ref(
503 sema
: &Semantics
<'_
, RootDatabase
>,
505 name_ref
: &ast
::NameRef
,
506 ) -> Option
<Highlight
> {
507 let mc
= name_ref
.syntax().parent().and_then(ast
::MethodCallExpr
::cast
)?
;
508 highlight_method_call(sema
, krate
, &mc
)
511 fn highlight_method_call(
512 sema
: &Semantics
<'_
, RootDatabase
>,
514 method_call
: &ast
::MethodCallExpr
,
515 ) -> Option
<Highlight
> {
516 let func
= sema
.resolve_method_call(method_call
)?
;
518 let mut h
= SymbolKind
::Function
.into();
519 h
|= HlMod
::Associated
;
521 if func
.is_unsafe_to_call(sema
.db
) || sema
.is_unsafe_method_call(method_call
) {
524 if func
.is_async(sema
.db
) {
528 .as_assoc_item(sema
.db
)
529 .and_then(|it
| it
.containing_trait_or_trait_impl(sema
.db
))
535 let def_crate
= func
.module(sema
.db
).krate();
536 let is_from_other_crate
= def_crate
!= krate
;
537 let is_from_builtin_crate
= def_crate
.is_builtin(sema
.db
);
538 let is_public
= func
.visibility(sema
.db
) == hir
::Visibility
::Public
;
540 if is_from_other_crate
{
542 } else if is_public
{
546 if is_from_builtin_crate
{
547 h
|= HlMod
::DefaultLibrary
;
550 if let Some(self_param
) = func
.self_param(sema
.db
) {
551 match self_param
.access(sema
.db
) {
552 hir
::Access
::Shared
=> h
|= HlMod
::Reference
,
553 hir
::Access
::Exclusive
=> {
555 h
|= HlMod
::Reference
;
557 hir
::Access
::Owned
=> {
558 if let Some(receiver_ty
) =
559 method_call
.receiver().and_then(|it
| sema
.type_of_expr(&it
))
561 if !receiver_ty
.adjusted().is_copy(sema
.db
) {
562 h
|= HlMod
::Consuming
571 fn highlight_name_by_syntax(name
: ast
::Name
) -> Highlight
{
572 let default = HlTag
::UnresolvedReference
;
574 let parent
= match name
.syntax().parent() {
576 _
=> return default.into(),
579 let tag
= match parent
.kind() {
580 STRUCT
=> SymbolKind
::Struct
,
581 ENUM
=> SymbolKind
::Enum
,
582 VARIANT
=> SymbolKind
::Variant
,
583 UNION
=> SymbolKind
::Union
,
584 TRAIT
=> SymbolKind
::Trait
,
585 TYPE_ALIAS
=> SymbolKind
::TypeAlias
,
586 TYPE_PARAM
=> SymbolKind
::TypeParam
,
587 RECORD_FIELD
=> SymbolKind
::Field
,
588 MODULE
=> SymbolKind
::Module
,
589 FN
=> SymbolKind
::Function
,
590 CONST
=> SymbolKind
::Const
,
591 STATIC
=> SymbolKind
::Static
,
592 IDENT_PAT
=> SymbolKind
::Local
,
593 _
=> return default.into(),
599 fn highlight_name_ref_by_syntax(
601 sema
: &Semantics
<'_
, RootDatabase
>,
604 let default = HlTag
::UnresolvedReference
;
606 let parent
= match name
.syntax().parent() {
608 _
=> return default.into(),
611 match parent
.kind() {
612 METHOD_CALL_EXPR
=> ast
::MethodCallExpr
::cast(parent
)
613 .and_then(|it
| highlight_method_call(sema
, krate
, &it
))
614 .unwrap_or_else(|| SymbolKind
::Function
.into()),
616 let h
= HlTag
::Symbol(SymbolKind
::Field
);
617 let is_union
= ast
::FieldExpr
::cast(parent
)
618 .and_then(|field_expr
| sema
.resolve_field(&field_expr
))
619 .map_or(false, |field
| {
620 matches
!(field
.parent_def(sema
.db
), hir
::VariantDef
::Union(_
))
629 let name_based_fallback
= || {
630 if name
.text().chars().next().unwrap_or_default().is_uppercase() {
631 SymbolKind
::Struct
.into()
633 SymbolKind
::Module
.into()
636 let path
= match parent
.parent().and_then(ast
::Path
::cast
) {
638 _
=> return name_based_fallback(),
640 let expr
= match path
.syntax().parent() {
641 Some(parent
) => match_ast
! {
643 ast
::PathExpr(path
) => path
,
644 ast
::MacroCall(_
) => return SymbolKind
::Macro
.into(),
645 _
=> return name_based_fallback(),
648 // within path, decide whether it is module or adt by checking for uppercase name
649 None
=> return name_based_fallback(),
651 let parent
= match expr
.syntax().parent() {
653 None
=> return default.into(),
656 match parent
.kind() {
657 CALL_EXPR
=> SymbolKind
::Function
.into(),
658 _
=> if name
.text().chars().next().unwrap_or_default().is_uppercase() {
670 fn is_consumed_lvalue(node
: &SyntaxNode
, local
: &hir
::Local
, db
: &RootDatabase
) -> bool
{
671 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
672 parents_match(node
.clone().into(), &[PATH_SEGMENT
, PATH
, PATH_EXPR
, ARG_LIST
])
673 && !local
.ty(db
).is_copy(db
)
676 /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
677 fn parents_match(mut node
: NodeOrToken
<SyntaxNode
, SyntaxToken
>, mut kinds
: &[SyntaxKind
]) -> bool
{
678 while let (Some(parent
), [kind
, rest @
..]) = (&node
.parent(), kinds
) {
679 if parent
.kind() != *kind
{
683 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
684 // in the same pattern is unstable: rust-lang/rust#68354.
685 node
= node
.parent().unwrap().into();
689 // Only true if we matched all expected kinds
693 fn parent_matches
<N
: AstNode
>(token
: &SyntaxToken
) -> bool
{
694 token
.parent().map_or(false, |it
| N
::can_cast(it
.kind()))