]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/syntax_highlighting/highlight.rs
Merge #11774
[rust.git] / crates / ide / src / syntax_highlighting / highlight.rs
1 //! Computes color for a single element.
2
3 use hir::{AsAssocItem, HasVisibility, Semantics};
4 use ide_db::{
5     defs::{Definition, IdentClass, NameClass, NameRefClass},
6     famous_defs::FamousDefs,
7     RootDatabase, SymbolKind,
8 };
9 use rustc_hash::FxHashMap;
10 use syntax::{
11     ast, match_ast, AstNode, AstToken, NodeOrToken,
12     SyntaxKind::{self, *},
13     SyntaxNode, SyntaxToken, T,
14 };
15
16 use crate::{
17     syntax_highlighting::tags::{HlOperator, HlPunct},
18     Highlight, HlMod, HlTag,
19 };
20
21 pub(super) fn token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Highlight> {
22     if let Some(comment) = ast::Comment::cast(token.clone()) {
23         let h = HlTag::Comment;
24         return Some(match comment.kind().doc {
25             Some(_) => h | HlMod::Documentation,
26             None => h.into(),
27         });
28     }
29
30     let highlight: Highlight = match token.kind() {
31         STRING | BYTE_STRING => HlTag::StringLiteral.into(),
32         INT_NUMBER if token.ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
33             SymbolKind::Field.into()
34         }
35         INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
36         BYTE => HlTag::ByteLiteral.into(),
37         CHAR => HlTag::CharLiteral.into(),
38         IDENT if token.parent().and_then(ast::TokenTree::cast).is_some() => {
39             // from this point on we are inside a token tree, this only happens for identifiers
40             // that were not mapped down into macro invocations
41             HlTag::None.into()
42         }
43         p if p.is_punct() => punctuation(sema, token, p),
44         k if k.is_keyword() => keyword(sema, token, k)?,
45         _ => return None,
46     };
47     Some(highlight)
48 }
49
50 pub(super) fn name_like(
51     sema: &Semantics<RootDatabase>,
52     krate: Option<hir::Crate>,
53     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
54     syntactic_name_ref_highlighting: bool,
55     name_like: ast::NameLike,
56 ) -> Option<(Highlight, Option<u64>)> {
57     let mut binding_hash = None;
58     let highlight = match name_like {
59         ast::NameLike::NameRef(name_ref) => highlight_name_ref(
60             sema,
61             krate,
62             bindings_shadow_count,
63             &mut binding_hash,
64             syntactic_name_ref_highlighting,
65             name_ref,
66         ),
67         ast::NameLike::Name(name) => {
68             highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name)
69         }
70         ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) {
71             Some(IdentClass::NameClass(NameClass::Definition(def))) => {
72                 highlight_def(sema, krate, def) | HlMod::Definition
73             }
74             Some(IdentClass::NameRefClass(NameRefClass::Definition(def))) => {
75                 highlight_def(sema, krate, def)
76             }
77             // FIXME: Fallback for 'static and '_, as we do not resolve these yet
78             _ => SymbolKind::LifetimeParam.into(),
79         },
80     };
81     Some((highlight, binding_hash))
82 }
83
84 fn punctuation(sema: &Semantics<RootDatabase>, token: SyntaxToken, kind: SyntaxKind) -> Highlight {
85     let parent = token.parent();
86     let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind);
87     match (kind, parent_kind) {
88         (T![?], _) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
89         (T![&], BIN_EXPR) => HlOperator::Bitwise.into(),
90         (T![&], _) => {
91             let h = HlTag::Operator(HlOperator::Other).into();
92             let is_unsafe = parent
93                 .and_then(ast::RefExpr::cast)
94                 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
95             if let Some(true) = is_unsafe {
96                 h | HlMod::Unsafe
97             } else {
98                 h
99             }
100         }
101         (T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(),
102         (T![!], MACRO_CALL) => HlPunct::MacroBang.into(),
103         (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
104         (T![!], PREFIX_EXPR) => HlOperator::Logical.into(),
105         (T![*], PTR_TYPE) => HlTag::Keyword.into(),
106         (T![*], PREFIX_EXPR) => {
107             let is_raw_ptr = (|| {
108                 let prefix_expr = parent.and_then(ast::PrefixExpr::cast)?;
109                 let expr = prefix_expr.expr()?;
110                 sema.type_of_expr(&expr)?.original.is_raw_ptr().then(|| ())
111             })();
112             if let Some(()) = is_raw_ptr {
113                 HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
114             } else {
115                 HlOperator::Other.into()
116             }
117         }
118         (T![-], PREFIX_EXPR) => {
119             let prefix_expr = parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr());
120             match prefix_expr {
121                 Some(ast::Expr::Literal(_)) => HlTag::NumericLiteral,
122                 _ => HlTag::Operator(HlOperator::Other),
123             }
124             .into()
125         }
126         (T![+] | T![-] | T![*] | T![/] | T![%], BIN_EXPR) => HlOperator::Arithmetic.into(),
127         (T![+=] | T![-=] | T![*=] | T![/=] | T![%=], BIN_EXPR) => {
128             Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
129         }
130         (T![|] | T![&] | T![!] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(),
131         (T![|=] | T![&=] | T![^=] | T![>>=] | T![<<=], BIN_EXPR) => {
132             Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
133         }
134         (T![&&] | T![||], BIN_EXPR) => HlOperator::Logical.into(),
135         (T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=], BIN_EXPR) => {
136             HlOperator::Comparison.into()
137         }
138         (_, PREFIX_EXPR | BIN_EXPR | RANGE_EXPR | RANGE_PAT | REST_PAT) => HlOperator::Other.into(),
139         (_, ATTR) => HlTag::AttributeBracket.into(),
140         (kind, _) => match kind {
141             T!['['] | T![']'] => HlPunct::Bracket,
142             T!['{'] | T!['}'] => HlPunct::Brace,
143             T!['('] | T![')'] => HlPunct::Parenthesis,
144             T![<] | T![>] => HlPunct::Angle,
145             T![,] => HlPunct::Comma,
146             T![:] => HlPunct::Colon,
147             T![;] => HlPunct::Semi,
148             T![.] => HlPunct::Dot,
149             _ => HlPunct::Other,
150         }
151         .into(),
152     }
153 }
154
155 fn keyword(
156     sema: &Semantics<RootDatabase>,
157     token: SyntaxToken,
158     kind: SyntaxKind,
159 ) -> Option<Highlight> {
160     let h = Highlight::new(HlTag::Keyword);
161     let h = match kind {
162         T![await] => h | HlMod::Async | HlMod::ControlFlow,
163         T![async] => h | HlMod::Async,
164         T![break]
165         | T![continue]
166         | T![else]
167         | T![if]
168         | T![in]
169         | T![loop]
170         | T![match]
171         | T![return]
172         | T![while]
173         | T![yield] => h | HlMod::ControlFlow,
174         T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow,
175         T![unsafe] => h | HlMod::Unsafe,
176         T![true] | T![false] => HlTag::BoolLiteral.into(),
177         // crate is handled just as a token if it's in an `extern crate`
178         T![crate] if parent_matches::<ast::ExternCrate>(&token) => h,
179         // self, crate, super and `Self` are handled as either a Name or NameRef already, unless they
180         // are inside unmapped token trees
181         T![self] | T![crate] | T![super] | T![Self] if parent_matches::<ast::NameRef>(&token) => {
182             return None
183         }
184         T![self] if parent_matches::<ast::Name>(&token) => return None,
185         T![ref] => match token.parent().and_then(ast::IdentPat::cast) {
186             Some(ident) if sema.is_unsafe_ident_pat(&ident) => h | HlMod::Unsafe,
187             _ => h,
188         },
189         _ => h,
190     };
191     Some(h)
192 }
193
194 fn highlight_name_ref(
195     sema: &Semantics<RootDatabase>,
196     krate: Option<hir::Crate>,
197     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
198     binding_hash: &mut Option<u64>,
199     syntactic_name_ref_highlighting: bool,
200     name_ref: ast::NameRef,
201 ) -> Highlight {
202     let db = sema.db;
203     if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref) {
204         return res;
205     }
206
207     let name_class = match NameRefClass::classify(sema, &name_ref) {
208         Some(name_kind) => name_kind,
209         None if syntactic_name_ref_highlighting => {
210             return highlight_name_ref_by_syntax(name_ref, sema, krate)
211         }
212         // FIXME: This is required for helper attributes used by proc-macros, as those do not map down
213         // to anything when used.
214         // We can fix this for derive attributes since derive helpers are recorded, but not for
215         // general attributes.
216         None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) => {
217             return HlTag::Symbol(SymbolKind::Attribute).into();
218         }
219         None => return HlTag::UnresolvedReference.into(),
220     };
221     let mut h = match name_class {
222         NameRefClass::Definition(def) => {
223             if let Definition::Local(local) = &def {
224                 let name = local.name(db);
225                 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
226                 *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
227             };
228
229             let mut h = highlight_def(sema, krate, def);
230
231             match def {
232                 Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => {
233                     h |= HlMod::Consuming;
234                 }
235                 Definition::Trait(trait_) if trait_.is_unsafe(db) => {
236                     if ast::Impl::for_trait_name_ref(&name_ref)
237                         .map_or(false, |impl_| impl_.unsafe_token().is_some())
238                     {
239                         h |= HlMod::Unsafe;
240                     }
241                 }
242                 Definition::Field(field) => {
243                     if let Some(parent) = name_ref.syntax().parent() {
244                         if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
245                             if let hir::VariantDef::Union(_) = field.parent_def(db) {
246                                 h |= HlMod::Unsafe;
247                             }
248                         }
249                     }
250                 }
251                 Definition::Macro(_) => {
252                     if let Some(macro_call) =
253                         ide_db::syntax_helpers::node_ext::full_path_of_name_ref(&name_ref)
254                             .and_then(|it| it.syntax().parent().and_then(ast::MacroCall::cast))
255                     {
256                         if sema.is_unsafe_macro_call(&macro_call) {
257                             h |= HlMod::Unsafe;
258                         }
259                     }
260                 }
261                 _ => (),
262             }
263
264             h
265         }
266         NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(),
267     };
268
269     h.tag = match name_ref.token_kind() {
270         T![Self] => HlTag::Symbol(SymbolKind::SelfType),
271         T![self] => HlTag::Symbol(SymbolKind::SelfParam),
272         T![super] | T![crate] => HlTag::Keyword,
273         _ => h.tag,
274     };
275     h
276 }
277
278 fn highlight_name(
279     sema: &Semantics<RootDatabase>,
280     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
281     binding_hash: &mut Option<u64>,
282     krate: Option<hir::Crate>,
283     name: ast::Name,
284 ) -> Highlight {
285     let name_kind = NameClass::classify(sema, &name);
286     if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
287         let name = local.name(sema.db);
288         let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
289         *shadow_count += 1;
290         *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
291     };
292     match name_kind {
293         Some(NameClass::Definition(def)) => {
294             let mut h = highlight_def(sema, krate, def) | HlMod::Definition;
295             if let Definition::Trait(trait_) = &def {
296                 if trait_.is_unsafe(sema.db) {
297                     h |= HlMod::Unsafe;
298                 }
299             }
300             h
301         }
302         Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def),
303         Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
304             let mut h = HlTag::Symbol(SymbolKind::Field).into();
305             if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) {
306                 h |= HlMod::Unsafe;
307             }
308             h
309         }
310         None => highlight_name_by_syntax(name) | HlMod::Definition,
311     }
312 }
313
314 fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
315     fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
316         use std::{collections::hash_map::DefaultHasher, hash::Hasher};
317
318         let mut hasher = DefaultHasher::new();
319         x.hash(&mut hasher);
320         hasher.finish()
321     }
322
323     hash((name, shadow_count))
324 }
325
326 fn highlight_def(
327     sema: &Semantics<RootDatabase>,
328     krate: Option<hir::Crate>,
329     def: Definition,
330 ) -> Highlight {
331     let db = sema.db;
332     let mut h = match def {
333         Definition::Macro(m) => Highlight::new(HlTag::Symbol(m.kind(sema.db).into())),
334         Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)),
335         Definition::Module(module) => {
336             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
337             if module.parent(db).is_none() {
338                 h |= HlMod::CrateRoot
339             }
340             h
341         }
342         Definition::Function(func) => {
343             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
344             if let Some(item) = func.as_assoc_item(db) {
345                 h |= HlMod::Associated;
346                 match func.self_param(db) {
347                     Some(sp) => match sp.access(db) {
348                         hir::Access::Exclusive => {
349                             h |= HlMod::Mutable;
350                             h |= HlMod::Reference;
351                         }
352                         hir::Access::Shared => h |= HlMod::Reference,
353                         hir::Access::Owned => h |= HlMod::Consuming,
354                     },
355                     None => h |= HlMod::Static,
356                 }
357
358                 match item.container(db) {
359                     hir::AssocItemContainer::Impl(i) => {
360                         if i.trait_(db).is_some() {
361                             h |= HlMod::Trait;
362                         }
363                     }
364                     hir::AssocItemContainer::Trait(_t) => {
365                         h |= HlMod::Trait;
366                     }
367                 }
368             }
369
370             if func.is_unsafe(db) {
371                 h |= HlMod::Unsafe;
372             }
373             if func.is_async(db) {
374                 h |= HlMod::Async;
375             }
376
377             h
378         }
379         Definition::Adt(adt) => {
380             let h = match adt {
381                 hir::Adt::Struct(_) => HlTag::Symbol(SymbolKind::Struct),
382                 hir::Adt::Enum(_) => HlTag::Symbol(SymbolKind::Enum),
383                 hir::Adt::Union(_) => HlTag::Symbol(SymbolKind::Union),
384             };
385
386             Highlight::new(h)
387         }
388         Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)),
389         Definition::Const(konst) => {
390             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
391
392             if let Some(item) = konst.as_assoc_item(db) {
393                 h |= HlMod::Associated;
394                 match item.container(db) {
395                     hir::AssocItemContainer::Impl(i) => {
396                         if i.trait_(db).is_some() {
397                             h |= HlMod::Trait;
398                         }
399                     }
400                     hir::AssocItemContainer::Trait(_t) => {
401                         h |= HlMod::Trait;
402                     }
403                 }
404             }
405
406             h
407         }
408         Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
409         Definition::TypeAlias(type_) => {
410             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
411
412             if let Some(item) = type_.as_assoc_item(db) {
413                 h |= HlMod::Associated;
414                 match item.container(db) {
415                     hir::AssocItemContainer::Impl(i) => {
416                         if i.trait_(db).is_some() {
417                             h |= HlMod::Trait;
418                         }
419                     }
420                     hir::AssocItemContainer::Trait(_t) => {
421                         h |= HlMod::Trait;
422                     }
423                 }
424             }
425
426             h
427         }
428         Definition::BuiltinType(_) => Highlight::new(HlTag::BuiltinType),
429         Definition::Static(s) => {
430             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
431
432             if s.is_mut(db) {
433                 h |= HlMod::Mutable;
434                 h |= HlMod::Unsafe;
435             }
436
437             h
438         }
439         Definition::SelfType(_) => Highlight::new(HlTag::Symbol(SymbolKind::Impl)),
440         Definition::GenericParam(it) => match it {
441             hir::GenericParam::TypeParam(_) => Highlight::new(HlTag::Symbol(SymbolKind::TypeParam)),
442             hir::GenericParam::ConstParam(_) => {
443                 Highlight::new(HlTag::Symbol(SymbolKind::ConstParam))
444             }
445             hir::GenericParam::LifetimeParam(_) => {
446                 Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam))
447             }
448         },
449         Definition::Local(local) => {
450             let tag = if local.is_self(db) {
451                 HlTag::Symbol(SymbolKind::SelfParam)
452             } else if local.is_param(db) {
453                 HlTag::Symbol(SymbolKind::ValueParam)
454             } else {
455                 HlTag::Symbol(SymbolKind::Local)
456             };
457             let mut h = Highlight::new(tag);
458             let ty = local.ty(db);
459             if local.is_mut(db) || ty.is_mutable_reference() {
460                 h |= HlMod::Mutable;
461             }
462             if local.is_ref(db) || ty.is_reference() {
463                 h |= HlMod::Reference;
464             }
465             if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
466                 h |= HlMod::Callable;
467             }
468             h
469         }
470         Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
471         Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
472         Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
473     };
474
475     let famous_defs = FamousDefs(sema, krate);
476     let def_crate = def.module(db).map(hir::Module::krate).or_else(|| match def {
477         Definition::Module(module) => Some(module.krate()),
478         _ => None,
479     });
480     let is_from_other_crate = def_crate != krate;
481     let is_from_builtin_crate =
482         def_crate.map_or(false, |def_crate| famous_defs.builtin_crates().any(|it| def_crate == it));
483     let is_builtin_type = matches!(def, Definition::BuiltinType(_));
484     let is_public = def.visibility(db) == Some(hir::Visibility::Public);
485
486     match (is_from_other_crate, is_builtin_type, is_public) {
487         (true, false, _) => h |= HlMod::Library,
488         (false, _, true) => h |= HlMod::Public,
489         _ => {}
490     }
491
492     if is_from_builtin_crate {
493         h |= HlMod::DefaultLibrary;
494     }
495
496     h
497 }
498
499 fn highlight_method_call_by_name_ref(
500     sema: &Semantics<RootDatabase>,
501     krate: Option<hir::Crate>,
502     name_ref: &ast::NameRef,
503 ) -> Option<Highlight> {
504     let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
505     highlight_method_call(sema, krate, &mc)
506 }
507
508 fn highlight_method_call(
509     sema: &Semantics<RootDatabase>,
510     krate: Option<hir::Crate>,
511     method_call: &ast::MethodCallExpr,
512 ) -> Option<Highlight> {
513     let func = sema.resolve_method_call(method_call)?;
514
515     let mut h = SymbolKind::Function.into();
516     h |= HlMod::Associated;
517
518     if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(method_call) {
519         h |= HlMod::Unsafe;
520     }
521     if func.is_async(sema.db) {
522         h |= HlMod::Async;
523     }
524     if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
525         h |= HlMod::Trait;
526     }
527
528     let famous_defs = FamousDefs(sema, krate);
529     let def_crate = func.module(sema.db).krate();
530     let is_from_other_crate = Some(def_crate) != krate;
531     let is_from_builtin_crate = famous_defs.builtin_crates().any(|it| def_crate == it);
532     let is_public = func.visibility(sema.db) == hir::Visibility::Public;
533
534     if is_from_other_crate {
535         h |= HlMod::Library;
536     } else if is_public {
537         h |= HlMod::Public;
538     }
539
540     if is_from_builtin_crate {
541         h |= HlMod::DefaultLibrary;
542     }
543
544     if let Some(self_param) = func.self_param(sema.db) {
545         match self_param.access(sema.db) {
546             hir::Access::Shared => h |= HlMod::Reference,
547             hir::Access::Exclusive => {
548                 h |= HlMod::Mutable;
549                 h |= HlMod::Reference;
550             }
551             hir::Access::Owned => {
552                 if let Some(receiver_ty) =
553                     method_call.receiver().and_then(|it| sema.type_of_expr(&it))
554                 {
555                     if !receiver_ty.adjusted().is_copy(sema.db) {
556                         h |= HlMod::Consuming
557                     }
558                 }
559             }
560         }
561     }
562     Some(h)
563 }
564
565 fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
566     let default = HlTag::UnresolvedReference;
567
568     let parent = match name.syntax().parent() {
569         Some(it) => it,
570         _ => return default.into(),
571     };
572
573     let tag = match parent.kind() {
574         STRUCT => SymbolKind::Struct,
575         ENUM => SymbolKind::Enum,
576         VARIANT => SymbolKind::Variant,
577         UNION => SymbolKind::Union,
578         TRAIT => SymbolKind::Trait,
579         TYPE_ALIAS => SymbolKind::TypeAlias,
580         TYPE_PARAM => SymbolKind::TypeParam,
581         RECORD_FIELD => SymbolKind::Field,
582         MODULE => SymbolKind::Module,
583         FN => SymbolKind::Function,
584         CONST => SymbolKind::Const,
585         STATIC => SymbolKind::Static,
586         IDENT_PAT => SymbolKind::Local,
587         _ => return default.into(),
588     };
589
590     tag.into()
591 }
592
593 fn highlight_name_ref_by_syntax(
594     name: ast::NameRef,
595     sema: &Semantics<RootDatabase>,
596     krate: Option<hir::Crate>,
597 ) -> Highlight {
598     let default = HlTag::UnresolvedReference;
599
600     let parent = match name.syntax().parent() {
601         Some(it) => it,
602         _ => return default.into(),
603     };
604
605     match parent.kind() {
606         METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
607             .and_then(|it| highlight_method_call(sema, krate, &it))
608             .unwrap_or_else(|| SymbolKind::Function.into()),
609         FIELD_EXPR => {
610             let h = HlTag::Symbol(SymbolKind::Field);
611             let is_union = ast::FieldExpr::cast(parent)
612                 .and_then(|field_expr| sema.resolve_field(&field_expr))
613                 .map_or(false, |field| {
614                     matches!(field.parent_def(sema.db), hir::VariantDef::Union(_))
615                 });
616             if is_union {
617                 h | HlMod::Unsafe
618             } else {
619                 h.into()
620             }
621         }
622         PATH_SEGMENT => {
623             let name_based_fallback = || {
624                 if name.text().chars().next().unwrap_or_default().is_uppercase() {
625                     SymbolKind::Struct.into()
626                 } else {
627                     SymbolKind::Module.into()
628                 }
629             };
630             let path = match parent.parent().and_then(ast::Path::cast) {
631                 Some(it) => it,
632                 _ => return name_based_fallback(),
633             };
634             let expr = match path.syntax().parent() {
635                 Some(parent) => match_ast! {
636                     match parent {
637                         ast::PathExpr(path) => path,
638                         ast::MacroCall(_) => return SymbolKind::Macro.into(),
639                         _ => return name_based_fallback(),
640                     }
641                 },
642                 // within path, decide whether it is module or adt by checking for uppercase name
643                 None => return name_based_fallback(),
644             };
645             let parent = match expr.syntax().parent() {
646                 Some(it) => it,
647                 None => return default.into(),
648             };
649
650             match parent.kind() {
651                 CALL_EXPR => SymbolKind::Function.into(),
652                 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
653                     SymbolKind::Struct
654                 } else {
655                     SymbolKind::Const
656                 }
657                 .into(),
658             }
659         }
660         _ => default.into(),
661     }
662 }
663
664 fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase) -> bool {
665     // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
666     parents_match(node.clone().into(), &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST])
667         && !local.ty(db).is_copy(db)
668 }
669
670 /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
671 fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
672     while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
673         if parent.kind() != *kind {
674             return false;
675         }
676
677         // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
678         // in the same pattern is unstable: rust-lang/rust#68354.
679         node = node.parent().unwrap().into();
680         kinds = rest;
681     }
682
683     // Only true if we matched all expected kinds
684     kinds.is_empty()
685 }
686
687 fn parent_matches<N: AstNode>(token: &SyntaxToken) -> bool {
688     token.parent().map_or(false, |it| N::can_cast(it.kind()))
689 }