diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-01-09 20:08:41 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-01-09 20:08:41 +0000 |
commit | bcb9ae18de1fa405eb2add56409ae264267607bb (patch) | |
tree | 36a2142b0898a347e052bf5721896c90ef0f9fee /crates/ide/src/syntax_highlighting.rs | |
parent | 927657432e77bc971f2b853dec7b98b909351541 (diff) | |
parent | eeceff317964af160fb785578d2d5666d9c3efe7 (diff) |
Merge #7223
7223: Refactor highlighting r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 560 |
1 files changed, 33 insertions, 527 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 079248511..b82e3775e 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -3,30 +3,29 @@ pub(crate) mod tags; | |||
3 | mod highlights; | 3 | mod highlights; |
4 | mod injector; | 4 | mod injector; |
5 | 5 | ||
6 | mod highlight; | ||
6 | mod format; | 7 | mod format; |
7 | mod inject; | ||
8 | mod macro_rules; | 8 | mod macro_rules; |
9 | mod inject; | ||
9 | 10 | ||
10 | mod html; | 11 | mod html; |
11 | #[cfg(test)] | 12 | #[cfg(test)] |
12 | mod tests; | 13 | mod tests; |
13 | 14 | ||
14 | use hir::{AsAssocItem, Local, Name, Semantics, VariantDef}; | 15 | use hir::{Name, Semantics}; |
15 | use ide_db::{ | 16 | use ide_db::RootDatabase; |
16 | defs::{Definition, NameClass, NameRefClass}, | ||
17 | RootDatabase, | ||
18 | }; | ||
19 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
20 | use syntax::{ | 18 | use syntax::{ |
21 | ast::{self, HasFormatSpecifier}, | 19 | ast::{self, HasFormatSpecifier}, |
22 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, | 20 | AstNode, AstToken, Direction, NodeOrToken, |
23 | SyntaxKind::{self, *}, | 21 | SyntaxKind::*, |
24 | SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, | 22 | SyntaxNode, TextRange, WalkEvent, T, |
25 | }; | 23 | }; |
26 | 24 | ||
27 | use crate::{ | 25 | use crate::{ |
28 | syntax_highlighting::{ | 26 | syntax_highlighting::{ |
29 | format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter, tags::Highlight, | 27 | format::FormatStringHighlighter, highlights::Highlights, |
28 | macro_rules::MacroRulesHighlighter, tags::Highlight, | ||
30 | }, | 29 | }, |
31 | FileId, HlMod, HlTag, SymbolKind, | 30 | FileId, HlMod, HlTag, SymbolKind, |
32 | }; | 31 | }; |
@@ -73,8 +72,19 @@ pub(crate) fn highlight( | |||
73 | } | 72 | } |
74 | }; | 73 | }; |
75 | 74 | ||
76 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | ||
77 | let mut hl = highlights::Highlights::new(range_to_highlight); | 75 | let mut hl = highlights::Highlights::new(range_to_highlight); |
76 | traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting); | ||
77 | hl.to_vec() | ||
78 | } | ||
79 | |||
80 | fn traverse( | ||
81 | hl: &mut Highlights, | ||
82 | sema: &Semantics<RootDatabase>, | ||
83 | root: &SyntaxNode, | ||
84 | range_to_highlight: TextRange, | ||
85 | syntactic_name_ref_highlighting: bool, | ||
86 | ) { | ||
87 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | ||
78 | 88 | ||
79 | let mut current_macro_call: Option<ast::MacroCall> = None; | 89 | let mut current_macro_call: Option<ast::MacroCall> = None; |
80 | let mut current_macro_rules: Option<ast::MacroRules> = None; | 90 | let mut current_macro_rules: Option<ast::MacroRules> = None; |
@@ -128,24 +138,24 @@ pub(crate) fn highlight( | |||
128 | } | 138 | } |
129 | _ => (), | 139 | _ => (), |
130 | } | 140 | } |
131 | |||
132 | match &event { | 141 | match &event { |
133 | // Check for Rust code in documentation | ||
134 | WalkEvent::Leave(NodeOrToken::Node(node)) => { | ||
135 | if ast::Attr::can_cast(node.kind()) { | ||
136 | inside_attribute = false | ||
137 | } | ||
138 | inject::doc_comment(&mut hl, node); | ||
139 | } | ||
140 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | 142 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { |
141 | inside_attribute = true | 143 | inside_attribute = true |
142 | } | 144 | } |
145 | WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | ||
146 | inside_attribute = false | ||
147 | } | ||
143 | _ => (), | 148 | _ => (), |
144 | } | 149 | } |
145 | 150 | ||
146 | let element = match event { | 151 | let element = match event { |
147 | WalkEvent::Enter(it) => it, | 152 | WalkEvent::Enter(it) => it, |
148 | WalkEvent::Leave(_) => continue, | 153 | WalkEvent::Leave(it) => { |
154 | if let Some(node) = it.as_node() { | ||
155 | inject::doc_comment(hl, node); | ||
156 | } | ||
157 | continue; | ||
158 | } | ||
149 | }; | 159 | }; |
150 | 160 | ||
151 | let range = element.text_range(); | 161 | let range = element.text_range(); |
@@ -179,13 +189,13 @@ pub(crate) fn highlight( | |||
179 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { | 189 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { |
180 | if token.is_raw() { | 190 | if token.is_raw() { |
181 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 191 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
182 | if inject::ra_fixture(&mut hl, &sema, token, expanded).is_some() { | 192 | if inject::ra_fixture(hl, &sema, token, expanded).is_some() { |
183 | continue; | 193 | continue; |
184 | } | 194 | } |
185 | } | 195 | } |
186 | } | 196 | } |
187 | 197 | ||
188 | if let Some((mut highlight, binding_hash)) = highlight_element( | 198 | if let Some((mut highlight, binding_hash)) = highlight::element( |
189 | &sema, | 199 | &sema, |
190 | &mut bindings_shadow_count, | 200 | &mut bindings_shadow_count, |
191 | syntactic_name_ref_highlighting, | 201 | syntactic_name_ref_highlighting, |
@@ -202,7 +212,7 @@ pub(crate) fn highlight( | |||
202 | if let Some(string) = | 212 | if let Some(string) = |
203 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) | 213 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) |
204 | { | 214 | { |
205 | format_string_highlighter.highlight_format_string(&mut hl, &string, range); | 215 | format_string_highlighter.highlight_format_string(hl, &string, range); |
206 | // Highlight escape sequences | 216 | // Highlight escape sequences |
207 | if let Some(char_ranges) = string.char_ranges() { | 217 | if let Some(char_ranges) = string.char_ranges() { |
208 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { | 218 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { |
@@ -218,8 +228,6 @@ pub(crate) fn highlight( | |||
218 | } | 228 | } |
219 | } | 229 | } |
220 | } | 230 | } |
221 | |||
222 | hl.to_vec() | ||
223 | } | 231 | } |
224 | 232 | ||
225 | fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | 233 | fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { |
@@ -237,505 +245,3 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | |||
237 | 245 | ||
238 | Some(TextRange::new(range_start, range_end)) | 246 | Some(TextRange::new(range_start, range_end)) |
239 | } | 247 | } |
240 | |||
241 | /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. | ||
242 | fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool { | ||
243 | while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { | ||
244 | if parent.kind() != *kind { | ||
245 | return false; | ||
246 | } | ||
247 | |||
248 | // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value | ||
249 | // in the same pattern is unstable: rust-lang/rust#68354. | ||
250 | node = node.parent().unwrap().into(); | ||
251 | kinds = rest; | ||
252 | } | ||
253 | |||
254 | // Only true if we matched all expected kinds | ||
255 | kinds.len() == 0 | ||
256 | } | ||
257 | |||
258 | fn is_consumed_lvalue( | ||
259 | node: NodeOrToken<SyntaxNode, SyntaxToken>, | ||
260 | local: &Local, | ||
261 | db: &RootDatabase, | ||
262 | ) -> bool { | ||
263 | // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. | ||
264 | parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) | ||
265 | } | ||
266 | |||
267 | fn highlight_element( | ||
268 | sema: &Semantics<RootDatabase>, | ||
269 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | ||
270 | syntactic_name_ref_highlighting: bool, | ||
271 | element: SyntaxElement, | ||
272 | ) -> Option<(Highlight, Option<u64>)> { | ||
273 | let db = sema.db; | ||
274 | let mut binding_hash = None; | ||
275 | let highlight: Highlight = match element.kind() { | ||
276 | FN => { | ||
277 | bindings_shadow_count.clear(); | ||
278 | return None; | ||
279 | } | ||
280 | |||
281 | // Highlight definitions depending on the "type" of the definition. | ||
282 | NAME => { | ||
283 | let name = element.into_node().and_then(ast::Name::cast).unwrap(); | ||
284 | let name_kind = NameClass::classify(sema, &name); | ||
285 | |||
286 | if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { | ||
287 | if let Some(name) = local.name(db) { | ||
288 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | ||
289 | *shadow_count += 1; | ||
290 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
291 | } | ||
292 | }; | ||
293 | |||
294 | match name_kind { | ||
295 | Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(), | ||
296 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
297 | Some(NameClass::ConstReference(def)) => highlight_def(db, def), | ||
298 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { | ||
299 | let mut h = HlTag::Symbol(SymbolKind::Field).into(); | ||
300 | if let Definition::Field(field) = field_ref { | ||
301 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
302 | h |= HlMod::Unsafe; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | h | ||
307 | } | ||
308 | None => highlight_name_by_syntax(name) | HlMod::Definition, | ||
309 | } | ||
310 | } | ||
311 | |||
312 | // Highlight references like the definitions they resolve to | ||
313 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { | ||
314 | // even though we track whether we are in an attribute or not we still need this special case | ||
315 | // as otherwise we would emit unresolved references for name refs inside attributes | ||
316 | Highlight::from(HlTag::Symbol(SymbolKind::Function)) | ||
317 | } | ||
318 | NAME_REF => { | ||
319 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | ||
320 | highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { | ||
321 | match NameRefClass::classify(sema, &name_ref) { | ||
322 | Some(name_kind) => match name_kind { | ||
323 | NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), | ||
324 | NameRefClass::Definition(def) => { | ||
325 | if let Definition::Local(local) = &def { | ||
326 | if let Some(name) = local.name(db) { | ||
327 | let shadow_count = | ||
328 | bindings_shadow_count.entry(name.clone()).or_default(); | ||
329 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
330 | } | ||
331 | }; | ||
332 | |||
333 | let mut h = highlight_def(db, def); | ||
334 | |||
335 | if let Definition::Local(local) = &def { | ||
336 | if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { | ||
337 | h |= HlMod::Consuming; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | if let Some(parent) = name_ref.syntax().parent() { | ||
342 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | ||
343 | if let Definition::Field(field) = def { | ||
344 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
345 | h |= HlMod::Unsafe; | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | h | ||
352 | } | ||
353 | NameRefClass::FieldShorthand { .. } => { | ||
354 | HlTag::Symbol(SymbolKind::Field).into() | ||
355 | } | ||
356 | }, | ||
357 | None if syntactic_name_ref_highlighting => { | ||
358 | highlight_name_ref_by_syntax(name_ref, sema) | ||
359 | } | ||
360 | None => HlTag::UnresolvedReference.into(), | ||
361 | } | ||
362 | }) | ||
363 | } | ||
364 | |||
365 | // Simple token-based highlighting | ||
366 | COMMENT => { | ||
367 | let comment = element.into_token().and_then(ast::Comment::cast)?; | ||
368 | let h = HlTag::Comment; | ||
369 | match comment.kind().doc { | ||
370 | Some(_) => h | HlMod::Documentation, | ||
371 | None => h.into(), | ||
372 | } | ||
373 | } | ||
374 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), | ||
375 | ATTR => HlTag::Attribute.into(), | ||
376 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | ||
377 | BYTE => HlTag::ByteLiteral.into(), | ||
378 | CHAR => HlTag::CharLiteral.into(), | ||
379 | QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, | ||
380 | LIFETIME => { | ||
381 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); | ||
382 | |||
383 | match NameClass::classify_lifetime(sema, &lifetime) { | ||
384 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
385 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
386 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
387 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), | ||
388 | }, | ||
389 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition, | ||
390 | } | ||
391 | } | ||
392 | p if p.is_punct() => match p { | ||
393 | T![&] => { | ||
394 | let h = HlTag::Operator.into(); | ||
395 | let is_unsafe = element | ||
396 | .parent() | ||
397 | .and_then(ast::RefExpr::cast) | ||
398 | .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) | ||
399 | .unwrap_or(false); | ||
400 | if is_unsafe { | ||
401 | h | HlMod::Unsafe | ||
402 | } else { | ||
403 | h | ||
404 | } | ||
405 | } | ||
406 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), | ||
407 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
408 | HlTag::Symbol(SymbolKind::Macro).into() | ||
409 | } | ||
410 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { | ||
411 | HlTag::BuiltinType.into() | ||
412 | } | ||
413 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { | ||
414 | HlTag::Keyword.into() | ||
415 | } | ||
416 | T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
417 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
418 | |||
419 | let expr = prefix_expr.expr()?; | ||
420 | let ty = sema.type_of_expr(&expr)?; | ||
421 | if ty.is_raw_ptr() { | ||
422 | HlTag::Operator | HlMod::Unsafe | ||
423 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { | ||
424 | HlTag::Operator.into() | ||
425 | } else { | ||
426 | HlTag::Punctuation.into() | ||
427 | } | ||
428 | } | ||
429 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
430 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
431 | |||
432 | let expr = prefix_expr.expr()?; | ||
433 | match expr { | ||
434 | ast::Expr::Literal(_) => HlTag::NumericLiteral, | ||
435 | _ => HlTag::Operator, | ||
436 | } | ||
437 | .into() | ||
438 | } | ||
439 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
440 | HlTag::Operator.into() | ||
441 | } | ||
442 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(), | ||
443 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { | ||
444 | HlTag::Operator.into() | ||
445 | } | ||
446 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(), | ||
447 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(), | ||
448 | _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), | ||
449 | _ => HlTag::Punctuation.into(), | ||
450 | }, | ||
451 | |||
452 | k if k.is_keyword() => { | ||
453 | let h = Highlight::new(HlTag::Keyword); | ||
454 | match k { | ||
455 | T![break] | ||
456 | | T![continue] | ||
457 | | T![else] | ||
458 | | T![if] | ||
459 | | T![loop] | ||
460 | | T![match] | ||
461 | | T![return] | ||
462 | | T![while] | ||
463 | | T![in] => h | HlMod::ControlFlow, | ||
464 | T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, | ||
465 | T![unsafe] => h | HlMod::Unsafe, | ||
466 | T![true] | T![false] => HlTag::BoolLiteral.into(), | ||
467 | T![self] => { | ||
468 | let self_param_is_mut = element | ||
469 | .parent() | ||
470 | .and_then(ast::SelfParam::cast) | ||
471 | .and_then(|p| p.mut_token()) | ||
472 | .is_some(); | ||
473 | let self_path = &element | ||
474 | .parent() | ||
475 | .as_ref() | ||
476 | .and_then(SyntaxNode::parent) | ||
477 | .and_then(ast::Path::cast) | ||
478 | .and_then(|p| sema.resolve_path(&p)); | ||
479 | let mut h = HlTag::Symbol(SymbolKind::SelfParam).into(); | ||
480 | if self_param_is_mut | ||
481 | || matches!(self_path, | ||
482 | Some(hir::PathResolution::Local(local)) | ||
483 | if local.is_self(db) | ||
484 | && (local.is_mut(db) || local.ty(db).is_mutable_reference()) | ||
485 | ) | ||
486 | { | ||
487 | h |= HlMod::Mutable | ||
488 | } | ||
489 | |||
490 | if let Some(hir::PathResolution::Local(local)) = self_path { | ||
491 | if is_consumed_lvalue(element, &local, db) { | ||
492 | h |= HlMod::Consuming; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | h | ||
497 | } | ||
498 | T![ref] => element | ||
499 | .parent() | ||
500 | .and_then(ast::IdentPat::cast) | ||
501 | .and_then(|ident_pat| { | ||
502 | if sema.is_unsafe_ident_pat(&ident_pat) { | ||
503 | Some(HlMod::Unsafe) | ||
504 | } else { | ||
505 | None | ||
506 | } | ||
507 | }) | ||
508 | .map(|modifier| h | modifier) | ||
509 | .unwrap_or(h), | ||
510 | _ => h, | ||
511 | } | ||
512 | } | ||
513 | |||
514 | _ => return None, | ||
515 | }; | ||
516 | |||
517 | return Some((highlight, binding_hash)); | ||
518 | |||
519 | fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { | ||
520 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | ||
521 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | ||
522 | |||
523 | let mut hasher = DefaultHasher::new(); | ||
524 | x.hash(&mut hasher); | ||
525 | hasher.finish() | ||
526 | } | ||
527 | |||
528 | hash((name, shadow_count)) | ||
529 | } | ||
530 | } | ||
531 | |||
532 | fn is_child_of_impl(element: &SyntaxElement) -> bool { | ||
533 | match element.parent() { | ||
534 | Some(e) => e.kind() == IMPL, | ||
535 | _ => false, | ||
536 | } | ||
537 | } | ||
538 | |||
539 | fn highlight_func_by_name_ref( | ||
540 | sema: &Semantics<RootDatabase>, | ||
541 | name_ref: &ast::NameRef, | ||
542 | ) -> Option<Highlight> { | ||
543 | let method_call = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; | ||
544 | highlight_method_call(sema, &method_call) | ||
545 | } | ||
546 | |||
547 | fn highlight_method_call( | ||
548 | sema: &Semantics<RootDatabase>, | ||
549 | method_call: &ast::MethodCallExpr, | ||
550 | ) -> Option<Highlight> { | ||
551 | let func = sema.resolve_method_call(&method_call)?; | ||
552 | let mut h = HlTag::Symbol(SymbolKind::Function).into(); | ||
553 | h |= HlMod::Associated; | ||
554 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | ||
555 | h |= HlMod::Unsafe; | ||
556 | } | ||
557 | if let Some(self_param) = func.self_param(sema.db) { | ||
558 | match self_param.access(sema.db) { | ||
559 | hir::Access::Shared => (), | ||
560 | hir::Access::Exclusive => h |= HlMod::Mutable, | ||
561 | hir::Access::Owned => { | ||
562 | if let Some(receiver_ty) = | ||
563 | method_call.receiver().and_then(|it| sema.type_of_expr(&it)) | ||
564 | { | ||
565 | if !receiver_ty.is_copy(sema.db) { | ||
566 | h |= HlMod::Consuming | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | } | ||
571 | } | ||
572 | Some(h) | ||
573 | } | ||
574 | |||
575 | fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | ||
576 | match def { | ||
577 | Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), | ||
578 | Definition::Field(_) => HlTag::Symbol(SymbolKind::Field), | ||
579 | Definition::ModuleDef(def) => match def { | ||
580 | hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module), | ||
581 | hir::ModuleDef::Function(func) => { | ||
582 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); | ||
583 | if func.as_assoc_item(db).is_some() { | ||
584 | h |= HlMod::Associated; | ||
585 | if func.self_param(db).is_none() { | ||
586 | h |= HlMod::Static | ||
587 | } | ||
588 | } | ||
589 | if func.is_unsafe(db) { | ||
590 | h |= HlMod::Unsafe; | ||
591 | } | ||
592 | return h; | ||
593 | } | ||
594 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), | ||
595 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum), | ||
596 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union), | ||
597 | hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant), | ||
598 | hir::ModuleDef::Const(konst) => { | ||
599 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); | ||
600 | if konst.as_assoc_item(db).is_some() { | ||
601 | h |= HlMod::Associated | ||
602 | } | ||
603 | return h; | ||
604 | } | ||
605 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), | ||
606 | hir::ModuleDef::TypeAlias(type_) => { | ||
607 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | ||
608 | if type_.as_assoc_item(db).is_some() { | ||
609 | h |= HlMod::Associated | ||
610 | } | ||
611 | return h; | ||
612 | } | ||
613 | hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType, | ||
614 | hir::ModuleDef::Static(s) => { | ||
615 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); | ||
616 | if s.is_mut(db) { | ||
617 | h |= HlMod::Mutable; | ||
618 | h |= HlMod::Unsafe; | ||
619 | } | ||
620 | return h; | ||
621 | } | ||
622 | }, | ||
623 | Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), | ||
624 | Definition::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), | ||
625 | Definition::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), | ||
626 | Definition::Local(local) => { | ||
627 | let tag = if local.is_param(db) { | ||
628 | HlTag::Symbol(SymbolKind::ValueParam) | ||
629 | } else { | ||
630 | HlTag::Symbol(SymbolKind::Local) | ||
631 | }; | ||
632 | let mut h = Highlight::new(tag); | ||
633 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | ||
634 | h |= HlMod::Mutable; | ||
635 | } | ||
636 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | ||
637 | h |= HlMod::Callable; | ||
638 | } | ||
639 | return h; | ||
640 | } | ||
641 | Definition::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), | ||
642 | Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), | ||
643 | } | ||
644 | .into() | ||
645 | } | ||
646 | |||
647 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | ||
648 | let default = HlTag::UnresolvedReference; | ||
649 | |||
650 | let parent = match name.syntax().parent() { | ||
651 | Some(it) => it, | ||
652 | _ => return default.into(), | ||
653 | }; | ||
654 | |||
655 | let tag = match parent.kind() { | ||
656 | STRUCT => HlTag::Symbol(SymbolKind::Struct), | ||
657 | ENUM => HlTag::Symbol(SymbolKind::Enum), | ||
658 | VARIANT => HlTag::Symbol(SymbolKind::Variant), | ||
659 | UNION => HlTag::Symbol(SymbolKind::Union), | ||
660 | TRAIT => HlTag::Symbol(SymbolKind::Trait), | ||
661 | TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias), | ||
662 | TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam), | ||
663 | RECORD_FIELD => HlTag::Symbol(SymbolKind::Field), | ||
664 | MODULE => HlTag::Symbol(SymbolKind::Module), | ||
665 | FN => HlTag::Symbol(SymbolKind::Function), | ||
666 | CONST => HlTag::Symbol(SymbolKind::Const), | ||
667 | STATIC => HlTag::Symbol(SymbolKind::Static), | ||
668 | IDENT_PAT => HlTag::Symbol(SymbolKind::Local), | ||
669 | _ => default, | ||
670 | }; | ||
671 | |||
672 | tag.into() | ||
673 | } | ||
674 | |||
675 | fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { | ||
676 | let default = HlTag::UnresolvedReference; | ||
677 | |||
678 | let parent = match name.syntax().parent() { | ||
679 | Some(it) => it, | ||
680 | _ => return default.into(), | ||
681 | }; | ||
682 | |||
683 | match parent.kind() { | ||
684 | METHOD_CALL_EXPR => { | ||
685 | return ast::MethodCallExpr::cast(parent) | ||
686 | .and_then(|method_call| highlight_method_call(sema, &method_call)) | ||
687 | .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into()); | ||
688 | } | ||
689 | FIELD_EXPR => { | ||
690 | let h = HlTag::Symbol(SymbolKind::Field); | ||
691 | let is_union = ast::FieldExpr::cast(parent) | ||
692 | .and_then(|field_expr| { | ||
693 | let field = sema.resolve_field(&field_expr)?; | ||
694 | Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { | ||
695 | true | ||
696 | } else { | ||
697 | false | ||
698 | }) | ||
699 | }) | ||
700 | .unwrap_or(false); | ||
701 | if is_union { | ||
702 | h | HlMod::Unsafe | ||
703 | } else { | ||
704 | h.into() | ||
705 | } | ||
706 | } | ||
707 | PATH_SEGMENT => { | ||
708 | let path = match parent.parent().and_then(ast::Path::cast) { | ||
709 | Some(it) => it, | ||
710 | _ => return default.into(), | ||
711 | }; | ||
712 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
713 | Some(it) => it, | ||
714 | _ => { | ||
715 | // within path, decide whether it is module or adt by checking for uppercase name | ||
716 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
717 | HlTag::Symbol(SymbolKind::Struct) | ||
718 | } else { | ||
719 | HlTag::Symbol(SymbolKind::Module) | ||
720 | } | ||
721 | .into(); | ||
722 | } | ||
723 | }; | ||
724 | let parent = match expr.syntax().parent() { | ||
725 | Some(it) => it, | ||
726 | None => return default.into(), | ||
727 | }; | ||
728 | |||
729 | match parent.kind() { | ||
730 | CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(), | ||
731 | _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
732 | HlTag::Symbol(SymbolKind::Struct) | ||
733 | } else { | ||
734 | HlTag::Symbol(SymbolKind::Const) | ||
735 | } | ||
736 | .into(), | ||
737 | } | ||
738 | } | ||
739 | _ => default.into(), | ||
740 | } | ||
741 | } | ||