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