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