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