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::Module).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 |= HlMod::CrateRoot;
421 h
422 }
423 };
424
425 h.tag = match name_ref.token_kind() {
426 T![Self] => HlTag::Symbol(SymbolKind::SelfType),
427 T![self] => HlTag::Symbol(SymbolKind::SelfParam),
428 T![super] | T![crate] => HlTag::Keyword,
429 _ => h.tag,
430 };
431 h
432}
433
434fn highlight_name(
435 sema: &Semantics<'_, RootDatabase>,
436 bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
437 binding_hash: &mut Option<u64>,
438 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
439 krate: Option<hir::Crate>,
440 name: ast::Name,
441 edition: Edition,
442) -> Highlight {
443 let name_kind = NameClass::classify(sema, &name);
444 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind
445 && let Some(bindings_shadow_count) = bindings_shadow_count
446 {
447 let name = local.name(sema.db);
448 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
449 *shadow_count += 1;
450 *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
451 };
452 match name_kind {
453 Some(NameClass::Definition(def)) => {
454 let mut h = highlight_def(sema, krate, def, edition, false) | HlMod::Definition;
455 if let Definition::Trait(trait_) = &def
456 && trait_.is_unsafe(sema.db)
457 {
458 h |= HlMod::Unsafe;
459 }
460 h
461 }
462 Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition, true),
463 Some(NameClass::PatFieldShorthand { .. }) => {
464 let mut h = HlTag::Symbol(SymbolKind::Field).into();
465 let is_unsafe =
466 name.syntax().parent().and_then(ast::IdentPat::cast).is_some_and(|it| {
467 is_unsafe_node(AstPtr::new(&ast::Pat::IdentPat(it)).wrap_right())
468 });
469 if is_unsafe {
470 h |= HlMod::Unsafe;
471 }
472 h
473 }
474 None => highlight_name_by_syntax(name) | HlMod::Definition,
475 }
476}
477
478fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
479 hash_once::<ide_db::FxHasher>((name.as_str(), shadow_count))
480}
481
482pub(super) fn highlight_def(
483 sema: &Semantics<'_, RootDatabase>,
484 krate: Option<hir::Crate>,
485 def: Definition,
486 edition: Edition,
487 is_ref: bool,
488) -> Highlight {
489 let db = sema.db;
490 let (mut h, attrs) = match def {
491 Definition::Macro(m) => {
492 (Highlight::new(HlTag::Symbol(m.kind(sema.db).into())), Some(m.attrs(sema.db)))
493 }
494 Definition::Field(field) => {
495 (Highlight::new(HlTag::Symbol(SymbolKind::Field)), Some(field.attrs(sema.db)))
496 }
497 Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None),
498 Definition::Crate(krate) => (
499 Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot,
500 Some(krate.attrs(sema.db)),
501 ),
502 Definition::Module(module) => {
503 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
504 if module.is_crate_root(db) {
505 h |= HlMod::CrateRoot;
506 }
507
508 (h, Some(module.attrs(sema.db)))
509 }
510 Definition::Function(func) => {
511 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
512 if let Some(item) = func.as_assoc_item(db) {
513 h |= HlMod::Associated;
514 match func.self_param(db) {
515 Some(sp) => {
516 h.tag = HlTag::Symbol(SymbolKind::Method);
517 match sp.access(db) {
518 hir::Access::Exclusive => {
519 h |= HlMod::Mutable;
520 h |= HlMod::Reference;
521 }
522 hir::Access::Shared => h |= HlMod::Reference,
523 hir::Access::Owned => h |= HlMod::Consuming,
524 }
525 }
526 None => h |= HlMod::Static,
527 }
528
529 match item.container(db) {
530 hir::AssocItemContainer::Impl(i) => {
531 if i.trait_(db).is_some() {
532 h |= HlMod::Trait;
533 }
534 }
535 hir::AssocItemContainer::Trait(_t) => {
536 h |= HlMod::Trait;
537 }
538 }
539 }
540
541 if !is_ref && func.is_unsafe_to_call(db, None, edition) {
547 h |= HlMod::Unsafe;
548 }
549 if func.is_async(db) {
550 h |= HlMod::Async;
551 }
552 if func.is_const(db) {
553 h |= HlMod::Const;
554 }
555
556 (h, Some(func.attrs(sema.db)))
557 }
558 Definition::Adt(adt) => {
559 let h = match adt {
560 hir::Adt::Struct(_) => HlTag::Symbol(SymbolKind::Struct),
561 hir::Adt::Enum(_) => HlTag::Symbol(SymbolKind::Enum),
562 hir::Adt::Union(_) => HlTag::Symbol(SymbolKind::Union),
563 };
564
565 (Highlight::new(h), Some(adt.attrs(sema.db)))
566 }
567 Definition::Variant(variant) => {
568 (Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Some(variant.attrs(sema.db)))
569 }
570 Definition::Const(konst) => {
571 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const;
572 if let Some(item) = konst.as_assoc_item(db) {
573 h |= HlMod::Associated;
574 h |= HlMod::Static;
575 match item.container(db) {
576 hir::AssocItemContainer::Impl(i) => {
577 if i.trait_(db).is_some() {
578 h |= HlMod::Trait;
579 }
580 }
581 hir::AssocItemContainer::Trait(_t) => {
582 h |= HlMod::Trait;
583 }
584 }
585 }
586
587 (h, Some(konst.attrs(sema.db)))
588 }
589 Definition::Trait(trait_) => {
590 (Highlight::new(HlTag::Symbol(SymbolKind::Trait)), Some(trait_.attrs(sema.db)))
591 }
592 Definition::TypeAlias(type_) => {
593 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
594
595 if let Some(item) = type_.as_assoc_item(db) {
596 h |= HlMod::Associated;
597 h |= HlMod::Static;
598 match item.container(db) {
599 hir::AssocItemContainer::Impl(i) => {
600 if i.trait_(db).is_some() {
601 h |= HlMod::Trait;
602 }
603 }
604 hir::AssocItemContainer::Trait(_t) => {
605 h |= HlMod::Trait;
606 }
607 }
608 }
609
610 (h, Some(type_.attrs(sema.db)))
611 }
612 Definition::BuiltinType(_) => (Highlight::new(HlTag::BuiltinType), None),
613 Definition::BuiltinLifetime(_) => {
614 (Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), None)
615 }
616 Definition::Static(s) => {
617 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
618
619 if s.is_mut(db) {
620 h |= HlMod::Mutable;
621 if !is_ref {
622 h |= HlMod::Unsafe;
623 }
624 }
625
626 (h, Some(s.attrs(sema.db)))
627 }
628 Definition::SelfType(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Impl)), None),
629 Definition::GenericParam(it) => (
630 match it {
631 hir::GenericParam::TypeParam(_) => {
632 Highlight::new(HlTag::Symbol(SymbolKind::TypeParam))
633 }
634 hir::GenericParam::ConstParam(_) => {
635 Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) | HlMod::Const
636 }
637 hir::GenericParam::LifetimeParam(_) => {
638 Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam))
639 }
640 },
641 None,
642 ),
643 Definition::Local(local) => {
644 let tag = if local.is_self(db) {
645 HlTag::Symbol(SymbolKind::SelfParam)
646 } else if local.is_param(db) {
647 HlTag::Symbol(SymbolKind::ValueParam)
648 } else {
649 HlTag::Symbol(SymbolKind::Local)
650 };
651 let mut h = Highlight::new(tag);
652 let ty = local.ty(db);
653 if local.is_mut(db) || ty.is_mutable_reference() {
654 h |= HlMod::Mutable;
655 }
656 if local.is_ref(db) || ty.is_reference() {
657 h |= HlMod::Reference;
658 }
659 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
660 h |= HlMod::Callable;
661 }
662 (h, None)
663 }
664 Definition::ExternCrateDecl(extern_crate) => {
665 let mut highlight =
666 Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot;
667 if extern_crate.alias(db).is_none() {
668 highlight |= HlMod::Library;
669 }
670 (highlight, Some(extern_crate.attrs(sema.db)))
671 }
672 Definition::Label(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Label)), None),
673 Definition::BuiltinAttr(_) => {
674 (Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), None)
675 }
676 Definition::ToolModule(_) => (Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), None),
677 Definition::DeriveHelper(_) => {
678 (Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), None)
679 }
680 Definition::InlineAsmRegOrRegClass(_) => {
681 (Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)), None)
682 }
683 Definition::InlineAsmOperand(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Local)), None),
684 };
685
686 let def_crate = def.krate(db);
687 let is_from_other_crate = def_crate != krate;
688 let is_from_builtin_crate = def_crate.is_some_and(|def_crate| def_crate.is_builtin(db));
689 let is_builtin = matches!(
690 def,
691 Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_)
692 );
693 match is_from_other_crate {
694 true if !is_builtin => h |= HlMod::Library,
695 false if def.visibility(db) == Some(hir::Visibility::Public) => h |= HlMod::Public,
696 _ => (),
697 }
698
699 if is_from_builtin_crate {
700 h |= HlMod::DefaultLibrary;
701 }
702
703 if let Some(attrs) = attrs
704 && attrs.is_deprecated()
705 {
706 h |= HlMod::Deprecated;
707 }
708
709 h
710}
711
712fn highlight_method_call_by_name_ref(
713 sema: &Semantics<'_, RootDatabase>,
714 krate: Option<hir::Crate>,
715 name_ref: &ast::NameRef,
716 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
717) -> Option<Highlight> {
718 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
719 highlight_method_call(sema, krate, &mc, is_unsafe_node)
720}
721
722fn highlight_method_call(
723 sema: &Semantics<'_, RootDatabase>,
724 krate: Option<hir::Crate>,
725 method_call: &ast::MethodCallExpr,
726 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
727) -> Option<Highlight> {
728 let func = sema.resolve_method_call(method_call)?;
729
730 let mut h = SymbolKind::Method.into();
731
732 let is_unsafe = is_unsafe_node(AstPtr::new(method_call).upcast::<ast::Expr>().wrap_left());
733 if is_unsafe {
734 h |= HlMod::Unsafe;
735 }
736 if func.is_async(sema.db) {
737 h |= HlMod::Async;
738 }
739 if func.is_const(sema.db) {
740 h |= HlMod::Const;
741 }
742 if func
743 .as_assoc_item(sema.db)
744 .and_then(|it| it.container_or_implemented_trait(sema.db))
745 .is_some()
746 {
747 h |= HlMod::Trait;
748 }
749
750 let def_crate = func.module(sema.db).krate(sema.db);
751 let is_from_other_crate = krate.as_ref().map_or(false, |krate| def_crate != *krate);
752 let is_from_builtin_crate = def_crate.is_builtin(sema.db);
753 let is_public = func.visibility(sema.db) == hir::Visibility::Public;
754 let is_deprecated = func.attrs(sema.db).is_deprecated();
755
756 if is_from_other_crate {
757 h |= HlMod::Library;
758 } else if is_public {
759 h |= HlMod::Public;
760 }
761
762 if is_from_builtin_crate {
763 h |= HlMod::DefaultLibrary;
764 }
765
766 if is_deprecated {
767 h |= HlMod::Deprecated;
768 }
769
770 if let Some(self_param) = func.self_param(sema.db) {
771 match self_param.access(sema.db) {
772 hir::Access::Shared => h |= HlMod::Reference,
773 hir::Access::Exclusive => {
774 h |= HlMod::Mutable;
775 h |= HlMod::Reference;
776 }
777 hir::Access::Owned => {
778 if let Some(receiver_ty) =
779 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
780 && !receiver_ty.adjusted().is_copy(sema.db)
781 {
782 h |= HlMod::Consuming
783 }
784 }
785 }
786 }
787 Some(h)
788}
789
790fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
791 let default = HlTag::UnresolvedReference;
792
793 let parent = match name.syntax().parent() {
794 Some(it) => it,
795 _ => return default.into(),
796 };
797
798 let tag = match parent.kind() {
799 STRUCT => SymbolKind::Struct,
800 ENUM => SymbolKind::Enum,
801 VARIANT => SymbolKind::Variant,
802 UNION => SymbolKind::Union,
803 TRAIT => SymbolKind::Trait,
804 TYPE_ALIAS => SymbolKind::TypeAlias,
805 TYPE_PARAM => SymbolKind::TypeParam,
806 RECORD_FIELD => SymbolKind::Field,
807 MODULE => SymbolKind::Module,
808 FN => SymbolKind::Function,
809 CONST => SymbolKind::Const,
810 STATIC => SymbolKind::Static,
811 IDENT_PAT => SymbolKind::Local,
812 FORMAT_ARGS_ARG => SymbolKind::Local,
813 RENAME => SymbolKind::Local,
814 MACRO_RULES => SymbolKind::Macro,
815 CONST_PARAM => SymbolKind::ConstParam,
816 SELF_PARAM => SymbolKind::SelfParam,
817 ASM_OPERAND_NAMED => SymbolKind::Local,
818 _ => return default.into(),
819 };
820
821 tag.into()
822}
823
824fn highlight_name_ref_by_syntax(
825 name: ast::NameRef,
826 sema: &Semantics<'_, RootDatabase>,
827 krate: Option<hir::Crate>,
828 is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
829) -> Highlight {
830 let default = HlTag::UnresolvedReference;
831
832 let parent = match name.syntax().parent() {
833 Some(it) => it,
834 _ => return default.into(),
835 };
836
837 match parent.kind() {
838 EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot,
839 METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
840 .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node))
841 .unwrap_or_else(|| SymbolKind::Method.into()),
842 FIELD_EXPR => {
843 let h = HlTag::Symbol(SymbolKind::Field);
844 let is_unsafe = ast::Expr::cast(parent)
845 .is_some_and(|it| is_unsafe_node(AstPtr::new(&it).wrap_left()));
846 if is_unsafe { h | HlMod::Unsafe } else { h.into() }
847 }
848 RECORD_EXPR_FIELD | RECORD_PAT_FIELD => HlTag::Symbol(SymbolKind::Field).into(),
849 PATH_SEGMENT => {
850 let name_based_fallback = || {
851 if name.text().chars().next().unwrap_or_default().is_uppercase() {
852 SymbolKind::Struct.into()
853 } else {
854 SymbolKind::Module.into()
855 }
856 };
857 let path = match parent.parent().and_then(ast::Path::cast) {
858 Some(it) => it,
859 _ => return name_based_fallback(),
860 };
861 let expr = match path.syntax().parent() {
862 Some(parent) => match_ast! {
863 match parent {
864 ast::PathExpr(path) => path,
865 ast::MacroCall(_) => return SymbolKind::Macro.into(),
866 _ => return name_based_fallback(),
867 }
868 },
869 None => return name_based_fallback(),
871 };
872 let parent = match expr.syntax().parent() {
873 Some(it) => it,
874 None => return default.into(),
875 };
876
877 match parent.kind() {
878 CALL_EXPR => SymbolKind::Function.into(),
879 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
880 SymbolKind::Struct
881 } else {
882 SymbolKind::Const
883 }
884 .into(),
885 }
886 }
887 ASSOC_TYPE_ARG => SymbolKind::TypeAlias.into(),
888 USE_BOUND_GENERIC_ARGS => SymbolKind::TypeParam.into(),
889 _ => default.into(),
890 }
891}
892
893fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase) -> bool {
894 parents_match(node.clone().into(), &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST])
896 && !local.ty(db).is_copy(db)
897}
898
899fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
901 while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
902 if parent.kind() != *kind {
903 return false;
904 }
905
906 node = parent.into();
907 kinds = rest;
908 }
909
910 kinds.is_empty()
912}
913
914fn parent_matches<N: AstNode>(token: &SyntaxToken) -> bool {
915 token.parent().is_some_and(|it| N::can_cast(it.kind()))
916}