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