1use std::ops::ControlFlow;
4
5use either::Either;
6use hir::{AsAssocItem, HasAttrs, HasVisibility, Semantics};
7use ide_db::{
8 FxHashMap, RootDatabase, SymbolKind,
9 defs::{Definition, IdentClass, NameClass, NameRefClass},
10 syntax_helpers::node_ext::walk_pat,
11};
12use span::Edition;
13use stdx::hash_once;
14use syntax::{
15 AstNode, AstPtr, AstToken, NodeOrToken,
16 SyntaxKind::{self, *},
17 SyntaxNode, SyntaxNodePtr, SyntaxToken, T, ast, match_ast,
18};
19
20use crate::{
21 Highlight, HlMod, HlTag,
22 syntax_highlighting::tags::{HlOperator, HlPunct},
23};
24
25pub(super) fn token(
26 sema: &Semantics<'_, RootDatabase>,
27 token: SyntaxToken,
28 edition: Edition,
29 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
30 in_tt: bool,
31) -> Option<Highlight> {
32 if let Some(comment) = ast::Comment::cast(token.clone()) {
33 let h = HlTag::Comment;
34 return Some(match comment.kind().doc {
35 Some(_) => h | HlMod::Documentation,
36 None => h.into(),
37 });
38 }
39
40 let h = match token.kind() {
41 STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(),
42 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
43 BYTE => HlTag::ByteLiteral.into(),
44 CHAR => HlTag::CharLiteral.into(),
45 IDENT if in_tt => {
46 HlTag::None.into()
49 }
50 p if p.is_punct() => punctuation(sema, token, p, is_unsafe_node),
51 k if k.is_keyword(edition) => {
52 if in_tt && token.prev_token().is_some_and(|t| t.kind() == T![$]) {
53 HlTag::None.into()
55 } else {
56 keyword(token, k)
57 }
58 }
59 _ => return None,
60 };
61 Some(h)
62}
63
64pub(super) fn name_like(
65 sema: &Semantics<'_, RootDatabase>,
66 krate: Option<hir::Crate>,
67 bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
68 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
69 syntactic_name_ref_highlighting: bool,
70 name_like: ast::NameLike,
71 edition: Edition,
72) -> Option<(Highlight, Option<u64>)> {
73 let mut binding_hash = None;
74 let highlight = match name_like {
75 ast::NameLike::NameRef(name_ref) => highlight_name_ref(
76 sema,
77 krate,
78 bindings_shadow_count,
79 &mut binding_hash,
80 is_unsafe_node,
81 syntactic_name_ref_highlighting,
82 name_ref,
83 edition,
84 ),
85 ast::NameLike::Name(name) => highlight_name(
86 sema,
87 bindings_shadow_count,
88 &mut binding_hash,
89 is_unsafe_node,
90 krate,
91 name,
92 edition,
93 ),
94 ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) {
95 Some(IdentClass::NameClass(NameClass::Definition(def))) => {
96 highlight_def(sema, krate, def, edition, false) | HlMod::Definition
97 }
98 Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => {
99 highlight_def(sema, krate, def, edition, true)
100 }
101 _ => SymbolKind::LifetimeParam.into(),
103 },
104 };
105 Some((highlight, binding_hash))
106}
107
108fn punctuation(
109 sema: &Semantics<'_, RootDatabase>,
110 token: SyntaxToken,
111 kind: SyntaxKind,
112 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
113) -> Highlight {
114 let operator_parent = token.parent();
115 let parent_kind = operator_parent.as_ref().map_or(EOF, SyntaxNode::kind);
116
117 match (kind, parent_kind) {
118 (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
119 (T![&], BIN_EXPR) => HlOperator::Bitwise.into(),
120 (T![&], REF_EXPR | REF_PAT) => HlTag::Operator(HlOperator::Other).into(),
121 (T![..] | T![..=], _) => match token.parent().and_then(ast::Pat::cast) {
122 Some(pat) if is_unsafe_node(AstPtr::new(&pat).wrap_right()) => {
123 Highlight::from(HlOperator::Other) | HlMod::Unsafe
124 }
125 _ => HlOperator::Other.into(),
126 },
127 (T![::] | T![->] | T![=>] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(),
128 (T![!], MACRO_CALL) => {
129 if operator_parent
130 .and_then(ast::MacroCall::cast)
131 .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call))
132 {
133 Highlight::from(HlPunct::MacroBang) | HlMod::Unsafe
134 } else {
135 HlPunct::MacroBang.into()
136 }
137 }
138 (T![!], MACRO_RULES) => HlPunct::MacroBang.into(),
139 (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
140 (T![!], PREFIX_EXPR) => HlOperator::Negation.into(),
141 (T![*], PTR_TYPE) => HlTag::Keyword.into(),
142 (T![*], PREFIX_EXPR) => {
143 let h = HlTag::Operator(HlOperator::Other).into();
144 let ptr = operator_parent
145 .as_ref()
146 .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)));
147 if ptr.is_some_and(is_unsafe_node) { h | HlMod::Unsafe } else { h }
148 }
149 (T![-], PREFIX_EXPR) => {
150 let prefix_expr =
151 operator_parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr());
152 match prefix_expr {
153 Some(ast::Expr::Literal(_)) => HlTag::NumericLiteral,
154 _ => HlTag::Operator(HlOperator::Other),
155 }
156 .into()
157 }
158 (T![+] | T![-] | T![*] | T![/] | T![%], BIN_EXPR) => HlOperator::Arithmetic.into(),
159 (T![+=] | T![-=] | T![*=] | T![/=] | T![%=], BIN_EXPR) => {
160 Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
161 }
162 (T![|] | T![&] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(),
163 (T![|=] | T![&=] | T![^=] | T![>>=] | T![<<=], BIN_EXPR) => {
164 Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
165 }
166 (T![&&] | T![||], BIN_EXPR) => HlOperator::Logical.into(),
167 (T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=], BIN_EXPR) => {
168 HlOperator::Comparison.into()
169 }
170 (_, ATTR) => HlTag::AttributeBracket.into(),
171 (T![>], _)
172 if operator_parent
173 .as_ref()
174 .and_then(SyntaxNode::parent)
175 .is_some_and(|it| it.kind() == MACRO_RULES) =>
176 {
177 HlOperator::Other.into()
178 }
179 (kind, _) => match kind {
180 T!['['] | T![']'] => {
181 let is_unsafe_macro = operator_parent
182 .as_ref()
183 .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
184 .and_then(ast::MacroCall::cast)
185 .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call));
186 let is_unsafe = is_unsafe_macro
187 || operator_parent
188 .as_ref()
189 .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)))
190 .is_some_and(is_unsafe_node);
191 if is_unsafe {
192 return Highlight::from(HlPunct::Bracket) | HlMod::Unsafe;
193 } else {
194 HlPunct::Bracket
195 }
196 }
197 T!['{'] | T!['}'] => {
198 let is_unsafe_macro = operator_parent
199 .as_ref()
200 .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
201 .and_then(ast::MacroCall::cast)
202 .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call));
203 let is_unsafe = is_unsafe_macro
204 || operator_parent
205 .as_ref()
206 .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it)))
207 .is_some_and(is_unsafe_node);
208 if is_unsafe {
209 return Highlight::from(HlPunct::Brace) | HlMod::Unsafe;
210 } else {
211 HlPunct::Brace
212 }
213 }
214 T!['('] | T![')'] => {
215 let is_unsafe_macro = operator_parent
216 .as_ref()
217 .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent())
218 .and_then(ast::MacroCall::cast)
219 .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call));
220 let is_unsafe = is_unsafe_macro
221 || operator_parent
222 .and_then(|it| {
223 if ast::ArgList::can_cast(it.kind()) { it.parent() } else { Some(it) }
224 })
225 .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(&it)))
226 .is_some_and(is_unsafe_node);
227
228 if is_unsafe {
229 return Highlight::from(HlPunct::Parenthesis) | HlMod::Unsafe;
230 } else {
231 HlPunct::Parenthesis
232 }
233 }
234 T![<] | T![>] => HlPunct::Angle,
235 T![,] => return HlPunct::Comma.into(),
238 T![:] => HlPunct::Colon,
239 T![;] => HlPunct::Semi,
240 T![.] => HlPunct::Dot,
241 _ => HlPunct::Other,
242 }
243 .into(),
244 }
245}
246
247fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight {
248 let h = Highlight::new(HlTag::Keyword);
249 match kind {
250 T![await] => h | HlMod::Async | HlMod::ControlFlow,
251 T![async] => h | HlMod::Async,
252 T![break]
253 | T![continue]
254 | T![else]
255 | T![if]
256 | T![in]
257 | T![loop]
258 | T![match]
259 | T![return]
260 | T![while]
261 | T![yield] => h | HlMod::ControlFlow,
262 T![do] | T![yeet] if parent_matches::<ast::YeetExpr>(&token) => h | HlMod::ControlFlow,
263 T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow,
264 T![unsafe] => h | HlMod::Unsafe,
265 T![const] => h | HlMod::Const,
266 T![true] | T![false] => HlTag::BoolLiteral.into(),
267 T![crate] if parent_matches::<ast::ExternCrate>(&token) => h,
269 _ => h,
270 }
271}
272
273fn highlight_name_ref(
274 sema: &Semantics<'_, RootDatabase>,
275 krate: Option<hir::Crate>,
276 bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
277 binding_hash: &mut Option<u64>,
278 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
279 syntactic_name_ref_highlighting: bool,
280 name_ref: ast::NameRef,
281 edition: Edition,
282) -> Highlight {
283 let db = sema.db;
284 if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, is_unsafe_node) {
285 return res;
286 }
287
288 let name_class = match NameRefClass::classify(sema, &name_ref) {
289 Some(name_kind) => name_kind,
290 None if syntactic_name_ref_highlighting => {
291 return highlight_name_ref_by_syntax(name_ref, sema, krate, is_unsafe_node);
292 }
293 None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR)
298 && !sema
299 .hir_file_for(name_ref.syntax())
300 .macro_file()
301 .is_some_and(|it| it.is_derive_attr_pseudo_expansion(sema.db)) =>
302 {
303 return HlTag::Symbol(SymbolKind::Attribute).into();
304 }
305 None => return HlTag::UnresolvedReference.into(),
306 };
307 let mut h = match name_class {
308 NameRefClass::Definition(def, _) => {
309 if let Definition::Local(local) = &def
310 && let Some(bindings_shadow_count) = bindings_shadow_count
311 {
312 let name = local.name(sema.db);
313 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
314 *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
315 };
316
317 let mut h = highlight_def(sema, krate, def, edition, true);
318
319 match def {
320 Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => {
321 h |= HlMod::Consuming;
322 }
323 Definition::Trait(trait_) if trait_.is_unsafe(db) => {
325 if ast::Impl::for_trait_name_ref(&name_ref)
326 .is_some_and(|impl_| impl_.unsafe_token().is_some())
327 {
328 h |= HlMod::Unsafe;
329 }
330 }
331 Definition::Function(_) => {
332 let is_unsafe = name_ref
333 .syntax()
334 .parent()
335 .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
336 .and_then(ast::PathExpr::cast)
337 .and_then(|it| it.syntax().parent())
338 .and_then(ast::CallExpr::cast)
339 .is_some_and(|it| {
340 is_unsafe_node(AstPtr::new(&ast::Expr::CallExpr(it)).wrap_left())
341 });
342 if is_unsafe {
343 h |= HlMod::Unsafe;
344 }
345 }
346 Definition::Macro(_) => {
347 let is_unsafe = name_ref
348 .syntax()
349 .parent()
350 .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
351 .and_then(ast::MacroCall::cast)
352 .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call));
353 if is_unsafe {
354 h |= HlMod::Unsafe;
355 }
356 }
357 Definition::Field(_) => {
358 let is_unsafe = name_ref
359 .syntax()
360 .parent()
361 .and_then(|it| {
362 match_ast! { match it {
363 ast::FieldExpr(expr) => Some(is_unsafe_node(AstPtr::new(&Either::Left(expr.into())))),
364 ast::RecordPatField(pat) => {
365 walk_pat(&pat.pat()?, &mut |pat| {
366 if is_unsafe_node(AstPtr::new(&Either::Right(pat))) {
367 ControlFlow::Break(true)
368 }
369 else {ControlFlow::Continue(())}
370 }).break_value()
371 },
372 _ => None,
373 }}
374 })
375 .unwrap_or(false);
376 if is_unsafe {
377 h |= HlMod::Unsafe;
378 }
379 }
380 Definition::Static(_) => {
381 let is_unsafe = name_ref
382 .syntax()
383 .parent()
384 .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent())
385 .and_then(ast::PathExpr::cast)
386 .is_some_and(|it| {
387 is_unsafe_node(AstPtr::new(&ast::Expr::PathExpr(it)).wrap_left())
388 });
389 if is_unsafe {
390 h |= HlMod::Unsafe;
391 }
392 }
393 _ => (),
394 }
395
396 h
397 }
398 NameRefClass::FieldShorthand { field_ref, .. } => {
399 highlight_def(sema, krate, field_ref.into(), edition, true)
400 }
401 NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => {
402 let mut h = HlTag::Symbol(SymbolKind::CrateRoot).into();
403
404 if krate.as_ref().is_some_and(|krate| resolved_krate != *krate) {
405 h |= HlMod::Library;
406 }
407
408 let is_public = decl.visibility(db) == hir::Visibility::Public;
409 if is_public {
410 h |= HlMod::Public
411 }
412 let is_from_builtin_crate = resolved_krate.is_builtin(db);
413 if is_from_builtin_crate {
414 h |= HlMod::DefaultLibrary;
415 }
416 let is_deprecated = resolved_krate.attrs(sema.db).is_deprecated();
417 if is_deprecated {
418 h |= HlMod::Deprecated;
419 }
420 h
421 }
422 };
423
424 h.tag = match name_ref.token_kind() {
425 T![Self] => HlTag::Symbol(SymbolKind::SelfType),
426 T![self] => HlTag::Symbol(SymbolKind::SelfParam),
427 T![super] | T![crate] => HlTag::Keyword,
428 _ => h.tag,
429 };
430 h
431}
432
433fn highlight_name(
434 sema: &Semantics<'_, RootDatabase>,
435 bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
436 binding_hash: &mut Option<u64>,
437 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
438 krate: Option<hir::Crate>,
439 name: ast::Name,
440 edition: Edition,
441) -> Highlight {
442 let name_kind = NameClass::classify(sema, &name);
443 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind
444 && let Some(bindings_shadow_count) = bindings_shadow_count
445 {
446 let name = local.name(sema.db);
447 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
448 *shadow_count += 1;
449 *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
450 };
451 match name_kind {
452 Some(NameClass::Definition(def)) => {
453 let mut h = highlight_def(sema, krate, def, edition, false) | HlMod::Definition;
454 if let Definition::Trait(trait_) = &def
455 && trait_.is_unsafe(sema.db)
456 {
457 h |= HlMod::Unsafe;
458 }
459 h
460 }
461 Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition, true),
462 Some(NameClass::PatFieldShorthand { .. }) => {
463 let mut h = HlTag::Symbol(SymbolKind::Field).into();
464 let is_unsafe =
465 name.syntax().parent().and_then(ast::IdentPat::cast).is_some_and(|it| {
466 is_unsafe_node(AstPtr::new(&ast::Pat::IdentPat(it)).wrap_right())
467 });
468 if is_unsafe {
469 h |= HlMod::Unsafe;
470 }
471 h
472 }
473 None => highlight_name_by_syntax(name) | HlMod::Definition,
474 }
475}
476
477fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
478 hash_once::<ide_db::FxHasher>((name.as_str(), shadow_count))
479}
480
481pub(super) fn highlight_def(
482 sema: &Semantics<'_, RootDatabase>,
483 krate: Option<hir::Crate>,
484 def: Definition,
485 edition: Edition,
486 is_ref: bool,
487) -> Highlight {
488 let db = sema.db;
489 let (mut h, attrs) = match def {
490 Definition::Macro(m) => {
491 (Highlight::new(HlTag::Symbol(m.kind(sema.db).into())), Some(m.attrs(sema.db)))
492 }
493 Definition::Field(field) => {
494 (Highlight::new(HlTag::Symbol(SymbolKind::Field)), Some(field.attrs(sema.db)))
495 }
496 Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None),
497 Definition::Crate(krate) => {
498 (Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)), Some(krate.attrs(sema.db)))
499 }
500 Definition::Module(module) => {
501 let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) {
502 SymbolKind::CrateRoot
503 } else {
504 SymbolKind::Module
505 }));
506 (h, Some(module.attrs(sema.db)))
507 }
508 Definition::Function(func) => {
509 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
510 if let Some(item) = func.as_assoc_item(db) {
511 h |= HlMod::Associated;
512 match func.self_param(db) {
513 Some(sp) => {
514 h.tag = HlTag::Symbol(SymbolKind::Method);
515 match sp.access(db) {
516 hir::Access::Exclusive => {
517 h |= HlMod::Mutable;
518 h |= HlMod::Reference;
519 }
520 hir::Access::Shared => h |= HlMod::Reference,
521 hir::Access::Owned => h |= HlMod::Consuming,
522 }
523 }
524 None => h |= HlMod::Static,
525 }
526
527 match item.container(db) {
528 hir::AssocItemContainer::Impl(i) => {
529 if i.trait_(db).is_some() {
530 h |= HlMod::Trait;
531 }
532 }
533 hir::AssocItemContainer::Trait(_t) => {
534 h |= HlMod::Trait;
535 }
536 }
537 }
538
539 if !is_ref && func.is_unsafe_to_call(db, None, edition) {
545 h |= HlMod::Unsafe;
546 }
547 if func.is_async(db) {
548 h |= HlMod::Async;
549 }
550 if func.is_const(db) {
551 h |= HlMod::Const;
552 }
553
554 (h, Some(func.attrs(sema.db)))
555 }
556 Definition::Adt(adt) => {
557 let h = match adt {
558 hir::Adt::Struct(_) => HlTag::Symbol(SymbolKind::Struct),
559 hir::Adt::Enum(_) => HlTag::Symbol(SymbolKind::Enum),
560 hir::Adt::Union(_) => HlTag::Symbol(SymbolKind::Union),
561 };
562
563 (Highlight::new(h), Some(adt.attrs(sema.db)))
564 }
565 Definition::Variant(variant) => {
566 (Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Some(variant.attrs(sema.db)))
567 }
568 Definition::Const(konst) => {
569 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const;
570 if let Some(item) = konst.as_assoc_item(db) {
571 h |= HlMod::Associated;
572 h |= HlMod::Static;
573 match item.container(db) {
574 hir::AssocItemContainer::Impl(i) => {
575 if i.trait_(db).is_some() {
576 h |= HlMod::Trait;
577 }
578 }
579 hir::AssocItemContainer::Trait(_t) => {
580 h |= HlMod::Trait;
581 }
582 }
583 }
584
585 (h, Some(konst.attrs(sema.db)))
586 }
587 Definition::Trait(trait_) => {
588 (Highlight::new(HlTag::Symbol(SymbolKind::Trait)), Some(trait_.attrs(sema.db)))
589 }
590 Definition::TypeAlias(type_) => {
591 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
592
593 if let Some(item) = type_.as_assoc_item(db) {
594 h |= HlMod::Associated;
595 h |= HlMod::Static;
596 match item.container(db) {
597 hir::AssocItemContainer::Impl(i) => {
598 if i.trait_(db).is_some() {
599 h |= HlMod::Trait;
600 }
601 }
602 hir::AssocItemContainer::Trait(_t) => {
603 h |= HlMod::Trait;
604 }
605 }
606 }
607
608 (h, Some(type_.attrs(sema.db)))
609 }
610 Definition::BuiltinType(_) => (Highlight::new(HlTag::BuiltinType), None),
611 Definition::BuiltinLifetime(_) => {
612 (Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), None)
613 }
614 Definition::Static(s) => {
615 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
616
617 if s.is_mut(db) {
618 h |= HlMod::Mutable;
619 if !is_ref {
620 h |= HlMod::Unsafe;
621 }
622 }
623
624 (h, Some(s.attrs(sema.db)))
625 }
626 Definition::SelfType(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Impl)), None),
627 Definition::GenericParam(it) => (
628 match it {
629 hir::GenericParam::TypeParam(_) => {
630 Highlight::new(HlTag::Symbol(SymbolKind::TypeParam))
631 }
632 hir::GenericParam::ConstParam(_) => {
633 Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) | HlMod::Const
634 }
635 hir::GenericParam::LifetimeParam(_) => {
636 Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam))
637 }
638 },
639 None,
640 ),
641 Definition::Local(local) => {
642 let tag = if local.is_self(db) {
643 HlTag::Symbol(SymbolKind::SelfParam)
644 } else if local.is_param(db) {
645 HlTag::Symbol(SymbolKind::ValueParam)
646 } else {
647 HlTag::Symbol(SymbolKind::Local)
648 };
649 let mut h = Highlight::new(tag);
650 let ty = local.ty(db);
651 if local.is_mut(db) || ty.is_mutable_reference() {
652 h |= HlMod::Mutable;
653 }
654 if local.is_ref(db) || ty.is_reference() {
655 h |= HlMod::Reference;
656 }
657 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
658 h |= HlMod::Callable;
659 }
660 (h, None)
661 }
662 Definition::ExternCrateDecl(extern_crate) => {
663 let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot));
664 if extern_crate.alias(db).is_none() {
665 highlight |= HlMod::Library;
666 }
667 (highlight, Some(extern_crate.attrs(sema.db)))
668 }
669 Definition::Label(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Label)), None),
670 Definition::BuiltinAttr(_) => {
671 (Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), None)
672 }
673 Definition::ToolModule(_) => (Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), None),
674 Definition::DeriveHelper(_) => {
675 (Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), None)
676 }
677 Definition::InlineAsmRegOrRegClass(_) => {
678 (Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)), None)
679 }
680 Definition::InlineAsmOperand(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Local)), None),
681 };
682
683 let def_crate = def.krate(db);
684 let is_from_other_crate = def_crate != krate;
685 let is_from_builtin_crate = def_crate.is_some_and(|def_crate| def_crate.is_builtin(db));
686 let is_builtin = matches!(
687 def,
688 Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_)
689 );
690 match is_from_other_crate {
691 true if !is_builtin => h |= HlMod::Library,
692 false if def.visibility(db) == Some(hir::Visibility::Public) => h |= HlMod::Public,
693 _ => (),
694 }
695
696 if is_from_builtin_crate {
697 h |= HlMod::DefaultLibrary;
698 }
699
700 if let Some(attrs) = attrs
701 && attrs.is_deprecated()
702 {
703 h |= HlMod::Deprecated;
704 }
705
706 h
707}
708
709fn highlight_method_call_by_name_ref(
710 sema: &Semantics<'_, RootDatabase>,
711 krate: Option<hir::Crate>,
712 name_ref: &ast::NameRef,
713 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
714) -> Option<Highlight> {
715 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
716 highlight_method_call(sema, krate, &mc, is_unsafe_node)
717}
718
719fn highlight_method_call(
720 sema: &Semantics<'_, RootDatabase>,
721 krate: Option<hir::Crate>,
722 method_call: &ast::MethodCallExpr,
723 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
724) -> Option<Highlight> {
725 let func = sema.resolve_method_call(method_call)?;
726
727 let mut h = SymbolKind::Method.into();
728
729 let is_unsafe = is_unsafe_node(AstPtr::new(method_call).upcast::<ast::Expr>().wrap_left());
730 if is_unsafe {
731 h |= HlMod::Unsafe;
732 }
733 if func.is_async(sema.db) {
734 h |= HlMod::Async;
735 }
736 if func.is_const(sema.db) {
737 h |= HlMod::Const;
738 }
739 if func
740 .as_assoc_item(sema.db)
741 .and_then(|it| it.container_or_implemented_trait(sema.db))
742 .is_some()
743 {
744 h |= HlMod::Trait;
745 }
746
747 let def_crate = func.module(sema.db).krate(sema.db);
748 let is_from_other_crate = krate.as_ref().map_or(false, |krate| def_crate != *krate);
749 let is_from_builtin_crate = def_crate.is_builtin(sema.db);
750 let is_public = func.visibility(sema.db) == hir::Visibility::Public;
751 let is_deprecated = func.attrs(sema.db).is_deprecated();
752
753 if is_from_other_crate {
754 h |= HlMod::Library;
755 } else if is_public {
756 h |= HlMod::Public;
757 }
758
759 if is_from_builtin_crate {
760 h |= HlMod::DefaultLibrary;
761 }
762
763 if is_deprecated {
764 h |= HlMod::Deprecated;
765 }
766
767 if let Some(self_param) = func.self_param(sema.db) {
768 match self_param.access(sema.db) {
769 hir::Access::Shared => h |= HlMod::Reference,
770 hir::Access::Exclusive => {
771 h |= HlMod::Mutable;
772 h |= HlMod::Reference;
773 }
774 hir::Access::Owned => {
775 if let Some(receiver_ty) =
776 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
777 && !receiver_ty.adjusted().is_copy(sema.db)
778 {
779 h |= HlMod::Consuming
780 }
781 }
782 }
783 }
784 Some(h)
785}
786
787fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
788 let default = HlTag::UnresolvedReference;
789
790 let parent = match name.syntax().parent() {
791 Some(it) => it,
792 _ => return default.into(),
793 };
794
795 let tag = match parent.kind() {
796 STRUCT => SymbolKind::Struct,
797 ENUM => SymbolKind::Enum,
798 VARIANT => SymbolKind::Variant,
799 UNION => SymbolKind::Union,
800 TRAIT => SymbolKind::Trait,
801 TYPE_ALIAS => SymbolKind::TypeAlias,
802 TYPE_PARAM => SymbolKind::TypeParam,
803 RECORD_FIELD => SymbolKind::Field,
804 MODULE => SymbolKind::Module,
805 EXTERN_CRATE => SymbolKind::CrateRoot,
806 FN => SymbolKind::Function,
807 CONST => SymbolKind::Const,
808 STATIC => SymbolKind::Static,
809 IDENT_PAT => SymbolKind::Local,
810 FORMAT_ARGS_ARG => SymbolKind::Local,
811 RENAME => SymbolKind::Local,
812 MACRO_RULES => SymbolKind::Macro,
813 CONST_PARAM => SymbolKind::ConstParam,
814 SELF_PARAM => SymbolKind::SelfParam,
815 ASM_OPERAND_NAMED => SymbolKind::Local,
816 _ => return default.into(),
817 };
818
819 tag.into()
820}
821
822fn highlight_name_ref_by_syntax(
823 name: ast::NameRef,
824 sema: &Semantics<'_, RootDatabase>,
825 krate: Option<hir::Crate>,
826 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
827) -> Highlight {
828 let default = HlTag::UnresolvedReference;
829
830 let parent = match name.syntax().parent() {
831 Some(it) => it,
832 _ => return default.into(),
833 };
834
835 match parent.kind() {
836 EXTERN_CRATE => HlTag::Symbol(SymbolKind::CrateRoot).into(),
837 METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
838 .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node))
839 .unwrap_or_else(|| SymbolKind::Method.into()),
840 FIELD_EXPR => {
841 let h = HlTag::Symbol(SymbolKind::Field);
842 let is_unsafe = ast::Expr::cast(parent)
843 .is_some_and(|it| is_unsafe_node(AstPtr::new(&it).wrap_left()));
844 if is_unsafe { h | HlMod::Unsafe } else { h.into() }
845 }
846 RECORD_EXPR_FIELD | RECORD_PAT_FIELD => HlTag::Symbol(SymbolKind::Field).into(),
847 PATH_SEGMENT => {
848 let name_based_fallback = || {
849 if name.text().chars().next().unwrap_or_default().is_uppercase() {
850 SymbolKind::Struct.into()
851 } else {
852 SymbolKind::Module.into()
853 }
854 };
855 let path = match parent.parent().and_then(ast::Path::cast) {
856 Some(it) => it,
857 _ => return name_based_fallback(),
858 };
859 let expr = match path.syntax().parent() {
860 Some(parent) => match_ast! {
861 match parent {
862 ast::PathExpr(path) => path,
863 ast::MacroCall(_) => return SymbolKind::Macro.into(),
864 _ => return name_based_fallback(),
865 }
866 },
867 None => return name_based_fallback(),
869 };
870 let parent = match expr.syntax().parent() {
871 Some(it) => it,
872 None => return default.into(),
873 };
874
875 match parent.kind() {
876 CALL_EXPR => SymbolKind::Function.into(),
877 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
878 SymbolKind::Struct
879 } else {
880 SymbolKind::Const
881 }
882 .into(),
883 }
884 }
885 ASSOC_TYPE_ARG => SymbolKind::TypeAlias.into(),
886 USE_BOUND_GENERIC_ARGS => SymbolKind::TypeParam.into(),
887 _ => default.into(),
888 }
889}
890
891fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase) -> bool {
892 parents_match(node.clone().into(), &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST])
894 && !local.ty(db).is_copy(db)
895}
896
897fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
899 while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
900 if parent.kind() != *kind {
901 return false;
902 }
903
904 node = parent.into();
905 kinds = rest;
906 }
907
908 kinds.is_empty()
910}
911
912fn parent_matches<N: AstNode>(token: &SyntaxToken) -> bool {
913 token.parent().is_some_and(|it| N::can_cast(it.kind()))
914}