diff options
author | Aleksey Kladov <[email protected]> | 2021-01-09 20:07:32 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-01-09 20:07:32 +0000 |
commit | eeceff317964af160fb785578d2d5666d9c3efe7 (patch) | |
tree | 36a2142b0898a347e052bf5721896c90ef0f9fee /crates/ide/src/syntax_highlighting | |
parent | 927657432e77bc971f2b853dec7b98b909351541 (diff) |
Refactor highlighting
Diffstat (limited to 'crates/ide/src/syntax_highlighting')
-rw-r--r-- | crates/ide/src/syntax_highlighting/highlight.rs | 517 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 2 |
2 files changed, 518 insertions, 1 deletions
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs new file mode 100644 index 000000000..1a88975d2 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -0,0 +1,517 @@ | |||
1 | //! Computes color for a single element. | ||
2 | |||
3 | use hir::{AsAssocItem, Semantics, VariantDef}; | ||
4 | use ide_db::{ | ||
5 | defs::{Definition, NameClass, NameRefClass}, | ||
6 | RootDatabase, | ||
7 | }; | ||
8 | use rustc_hash::FxHashMap; | ||
9 | use syntax::{ | ||
10 | ast, AstNode, AstToken, NodeOrToken, SyntaxElement, | ||
11 | SyntaxKind::{self, *}, | ||
12 | SyntaxNode, SyntaxToken, T, | ||
13 | }; | ||
14 | |||
15 | use crate::{Highlight, HlMod, HlTag, SymbolKind}; | ||
16 | |||
17 | pub(super) fn element( | ||
18 | sema: &Semantics<RootDatabase>, | ||
19 | bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, | ||
20 | syntactic_name_ref_highlighting: bool, | ||
21 | element: SyntaxElement, | ||
22 | ) -> Option<(Highlight, Option<u64>)> { | ||
23 | let db = sema.db; | ||
24 | let mut binding_hash = None; | ||
25 | let highlight: Highlight = match element.kind() { | ||
26 | FN => { | ||
27 | bindings_shadow_count.clear(); | ||
28 | return None; | ||
29 | } | ||
30 | |||
31 | // Highlight definitions depending on the "type" of the definition. | ||
32 | NAME => { | ||
33 | let name = element.into_node().and_then(ast::Name::cast).unwrap(); | ||
34 | let name_kind = NameClass::classify(sema, &name); | ||
35 | |||
36 | if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { | ||
37 | if let Some(name) = local.name(db) { | ||
38 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | ||
39 | *shadow_count += 1; | ||
40 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
41 | } | ||
42 | }; | ||
43 | |||
44 | match name_kind { | ||
45 | Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(), | ||
46 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
47 | Some(NameClass::ConstReference(def)) => highlight_def(db, def), | ||
48 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { | ||
49 | let mut h = HlTag::Symbol(SymbolKind::Field).into(); | ||
50 | if let Definition::Field(field) = field_ref { | ||
51 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
52 | h |= HlMod::Unsafe; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | h | ||
57 | } | ||
58 | None => highlight_name_by_syntax(name) | HlMod::Definition, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | // Highlight references like the definitions they resolve to | ||
63 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { | ||
64 | // even though we track whether we are in an attribute or not we still need this special case | ||
65 | // as otherwise we would emit unresolved references for name refs inside attributes | ||
66 | Highlight::from(HlTag::Symbol(SymbolKind::Function)) | ||
67 | } | ||
68 | NAME_REF => { | ||
69 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | ||
70 | highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { | ||
71 | match NameRefClass::classify(sema, &name_ref) { | ||
72 | Some(name_kind) => match name_kind { | ||
73 | NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), | ||
74 | NameRefClass::Definition(def) => { | ||
75 | if let Definition::Local(local) = &def { | ||
76 | if let Some(name) = local.name(db) { | ||
77 | let shadow_count = | ||
78 | bindings_shadow_count.entry(name.clone()).or_default(); | ||
79 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
80 | } | ||
81 | }; | ||
82 | |||
83 | let mut h = highlight_def(db, def); | ||
84 | |||
85 | if let Definition::Local(local) = &def { | ||
86 | if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { | ||
87 | h |= HlMod::Consuming; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if let Some(parent) = name_ref.syntax().parent() { | ||
92 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | ||
93 | if let Definition::Field(field) = def { | ||
94 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
95 | h |= HlMod::Unsafe; | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | h | ||
102 | } | ||
103 | NameRefClass::FieldShorthand { .. } => { | ||
104 | HlTag::Symbol(SymbolKind::Field).into() | ||
105 | } | ||
106 | }, | ||
107 | None if syntactic_name_ref_highlighting => { | ||
108 | highlight_name_ref_by_syntax(name_ref, sema) | ||
109 | } | ||
110 | None => HlTag::UnresolvedReference.into(), | ||
111 | } | ||
112 | }) | ||
113 | } | ||
114 | |||
115 | // Simple token-based highlighting | ||
116 | COMMENT => { | ||
117 | let comment = element.into_token().and_then(ast::Comment::cast)?; | ||
118 | let h = HlTag::Comment; | ||
119 | match comment.kind().doc { | ||
120 | Some(_) => h | HlMod::Documentation, | ||
121 | None => h.into(), | ||
122 | } | ||
123 | } | ||
124 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), | ||
125 | ATTR => HlTag::Attribute.into(), | ||
126 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | ||
127 | BYTE => HlTag::ByteLiteral.into(), | ||
128 | CHAR => HlTag::CharLiteral.into(), | ||
129 | QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, | ||
130 | LIFETIME => { | ||
131 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); | ||
132 | |||
133 | match NameClass::classify_lifetime(sema, &lifetime) { | ||
134 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
135 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
136 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
137 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), | ||
138 | }, | ||
139 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition, | ||
140 | } | ||
141 | } | ||
142 | p if p.is_punct() => match p { | ||
143 | T![&] => { | ||
144 | let h = HlTag::Operator.into(); | ||
145 | let is_unsafe = element | ||
146 | .parent() | ||
147 | .and_then(ast::RefExpr::cast) | ||
148 | .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) | ||
149 | .unwrap_or(false); | ||
150 | if is_unsafe { | ||
151 | h | HlMod::Unsafe | ||
152 | } else { | ||
153 | h | ||
154 | } | ||
155 | } | ||
156 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), | ||
157 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
158 | HlTag::Symbol(SymbolKind::Macro).into() | ||
159 | } | ||
160 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { | ||
161 | HlTag::BuiltinType.into() | ||
162 | } | ||
163 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { | ||
164 | HlTag::Keyword.into() | ||
165 | } | ||
166 | T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
167 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
168 | |||
169 | let expr = prefix_expr.expr()?; | ||
170 | let ty = sema.type_of_expr(&expr)?; | ||
171 | if ty.is_raw_ptr() { | ||
172 | HlTag::Operator | HlMod::Unsafe | ||
173 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { | ||
174 | HlTag::Operator.into() | ||
175 | } else { | ||
176 | HlTag::Punctuation.into() | ||
177 | } | ||
178 | } | ||
179 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
180 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
181 | |||
182 | let expr = prefix_expr.expr()?; | ||
183 | match expr { | ||
184 | ast::Expr::Literal(_) => HlTag::NumericLiteral, | ||
185 | _ => HlTag::Operator, | ||
186 | } | ||
187 | .into() | ||
188 | } | ||
189 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
190 | HlTag::Operator.into() | ||
191 | } | ||
192 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(), | ||
193 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { | ||
194 | HlTag::Operator.into() | ||
195 | } | ||
196 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(), | ||
197 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(), | ||
198 | _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), | ||
199 | _ => HlTag::Punctuation.into(), | ||
200 | }, | ||
201 | |||
202 | k if k.is_keyword() => { | ||
203 | let h = Highlight::new(HlTag::Keyword); | ||
204 | match k { | ||
205 | T![break] | ||
206 | | T![continue] | ||
207 | | T![else] | ||
208 | | T![if] | ||
209 | | T![loop] | ||
210 | | T![match] | ||
211 | | T![return] | ||
212 | | T![while] | ||
213 | | T![in] => h | HlMod::ControlFlow, | ||
214 | T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, | ||
215 | T![unsafe] => h | HlMod::Unsafe, | ||
216 | T![true] | T![false] => HlTag::BoolLiteral.into(), | ||
217 | T![self] => { | ||
218 | let self_param_is_mut = element | ||
219 | .parent() | ||
220 | .and_then(ast::SelfParam::cast) | ||
221 | .and_then(|p| p.mut_token()) | ||
222 | .is_some(); | ||
223 | let self_path = &element | ||
224 | .parent() | ||
225 | .as_ref() | ||
226 | .and_then(SyntaxNode::parent) | ||
227 | .and_then(ast::Path::cast) | ||
228 | .and_then(|p| sema.resolve_path(&p)); | ||
229 | let mut h = HlTag::Symbol(SymbolKind::SelfParam).into(); | ||
230 | if self_param_is_mut | ||
231 | || matches!(self_path, | ||
232 | Some(hir::PathResolution::Local(local)) | ||
233 | if local.is_self(db) | ||
234 | && (local.is_mut(db) || local.ty(db).is_mutable_reference()) | ||
235 | ) | ||
236 | { | ||
237 | h |= HlMod::Mutable | ||
238 | } | ||
239 | |||
240 | if let Some(hir::PathResolution::Local(local)) = self_path { | ||
241 | if is_consumed_lvalue(element, &local, db) { | ||
242 | h |= HlMod::Consuming; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | h | ||
247 | } | ||
248 | T![ref] => element | ||
249 | .parent() | ||
250 | .and_then(ast::IdentPat::cast) | ||
251 | .and_then(|ident_pat| { | ||
252 | if sema.is_unsafe_ident_pat(&ident_pat) { | ||
253 | Some(HlMod::Unsafe) | ||
254 | } else { | ||
255 | None | ||
256 | } | ||
257 | }) | ||
258 | .map(|modifier| h | modifier) | ||
259 | .unwrap_or(h), | ||
260 | _ => h, | ||
261 | } | ||
262 | } | ||
263 | |||
264 | _ => return None, | ||
265 | }; | ||
266 | |||
267 | return Some((highlight, binding_hash)); | ||
268 | |||
269 | fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { | ||
270 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | ||
271 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | ||
272 | |||
273 | let mut hasher = DefaultHasher::new(); | ||
274 | x.hash(&mut hasher); | ||
275 | hasher.finish() | ||
276 | } | ||
277 | |||
278 | hash((name, shadow_count)) | ||
279 | } | ||
280 | } | ||
281 | |||
282 | fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | ||
283 | match def { | ||
284 | Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), | ||
285 | Definition::Field(_) => HlTag::Symbol(SymbolKind::Field), | ||
286 | Definition::ModuleDef(def) => match def { | ||
287 | hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module), | ||
288 | hir::ModuleDef::Function(func) => { | ||
289 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); | ||
290 | if func.as_assoc_item(db).is_some() { | ||
291 | h |= HlMod::Associated; | ||
292 | if func.self_param(db).is_none() { | ||
293 | h |= HlMod::Static | ||
294 | } | ||
295 | } | ||
296 | if func.is_unsafe(db) { | ||
297 | h |= HlMod::Unsafe; | ||
298 | } | ||
299 | return h; | ||
300 | } | ||
301 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), | ||
302 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum), | ||
303 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union), | ||
304 | hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant), | ||
305 | hir::ModuleDef::Const(konst) => { | ||
306 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); | ||
307 | if konst.as_assoc_item(db).is_some() { | ||
308 | h |= HlMod::Associated | ||
309 | } | ||
310 | return h; | ||
311 | } | ||
312 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), | ||
313 | hir::ModuleDef::TypeAlias(type_) => { | ||
314 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | ||
315 | if type_.as_assoc_item(db).is_some() { | ||
316 | h |= HlMod::Associated | ||
317 | } | ||
318 | return h; | ||
319 | } | ||
320 | hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType, | ||
321 | hir::ModuleDef::Static(s) => { | ||
322 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); | ||
323 | if s.is_mut(db) { | ||
324 | h |= HlMod::Mutable; | ||
325 | h |= HlMod::Unsafe; | ||
326 | } | ||
327 | return h; | ||
328 | } | ||
329 | }, | ||
330 | Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), | ||
331 | Definition::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), | ||
332 | Definition::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), | ||
333 | Definition::Local(local) => { | ||
334 | let tag = if local.is_param(db) { | ||
335 | HlTag::Symbol(SymbolKind::ValueParam) | ||
336 | } else { | ||
337 | HlTag::Symbol(SymbolKind::Local) | ||
338 | }; | ||
339 | let mut h = Highlight::new(tag); | ||
340 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | ||
341 | h |= HlMod::Mutable; | ||
342 | } | ||
343 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | ||
344 | h |= HlMod::Callable; | ||
345 | } | ||
346 | return h; | ||
347 | } | ||
348 | Definition::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), | ||
349 | Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), | ||
350 | } | ||
351 | .into() | ||
352 | } | ||
353 | |||
354 | fn highlight_func_by_name_ref( | ||
355 | sema: &Semantics<RootDatabase>, | ||
356 | name_ref: &ast::NameRef, | ||
357 | ) -> Option<Highlight> { | ||
358 | let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; | ||
359 | highlight_method_call(sema, &mc) | ||
360 | } | ||
361 | |||
362 | fn highlight_method_call( | ||
363 | sema: &Semantics<RootDatabase>, | ||
364 | method_call: &ast::MethodCallExpr, | ||
365 | ) -> Option<Highlight> { | ||
366 | let func = sema.resolve_method_call(&method_call)?; | ||
367 | let mut h = HlTag::Symbol(SymbolKind::Function).into(); | ||
368 | h |= HlMod::Associated; | ||
369 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | ||
370 | h |= HlMod::Unsafe; | ||
371 | } | ||
372 | if let Some(self_param) = func.self_param(sema.db) { | ||
373 | match self_param.access(sema.db) { | ||
374 | hir::Access::Shared => (), | ||
375 | hir::Access::Exclusive => h |= HlMod::Mutable, | ||
376 | hir::Access::Owned => { | ||
377 | if let Some(receiver_ty) = | ||
378 | method_call.receiver().and_then(|it| sema.type_of_expr(&it)) | ||
379 | { | ||
380 | if !receiver_ty.is_copy(sema.db) { | ||
381 | h |= HlMod::Consuming | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | Some(h) | ||
388 | } | ||
389 | |||
390 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | ||
391 | let default = HlTag::UnresolvedReference; | ||
392 | |||
393 | let parent = match name.syntax().parent() { | ||
394 | Some(it) => it, | ||
395 | _ => return default.into(), | ||
396 | }; | ||
397 | |||
398 | let tag = match parent.kind() { | ||
399 | STRUCT => HlTag::Symbol(SymbolKind::Struct), | ||
400 | ENUM => HlTag::Symbol(SymbolKind::Enum), | ||
401 | VARIANT => HlTag::Symbol(SymbolKind::Variant), | ||
402 | UNION => HlTag::Symbol(SymbolKind::Union), | ||
403 | TRAIT => HlTag::Symbol(SymbolKind::Trait), | ||
404 | TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias), | ||
405 | TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam), | ||
406 | RECORD_FIELD => HlTag::Symbol(SymbolKind::Field), | ||
407 | MODULE => HlTag::Symbol(SymbolKind::Module), | ||
408 | FN => HlTag::Symbol(SymbolKind::Function), | ||
409 | CONST => HlTag::Symbol(SymbolKind::Const), | ||
410 | STATIC => HlTag::Symbol(SymbolKind::Static), | ||
411 | IDENT_PAT => HlTag::Symbol(SymbolKind::Local), | ||
412 | _ => default, | ||
413 | }; | ||
414 | |||
415 | tag.into() | ||
416 | } | ||
417 | |||
418 | fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { | ||
419 | let default = HlTag::UnresolvedReference; | ||
420 | |||
421 | let parent = match name.syntax().parent() { | ||
422 | Some(it) => it, | ||
423 | _ => return default.into(), | ||
424 | }; | ||
425 | |||
426 | match parent.kind() { | ||
427 | METHOD_CALL_EXPR => { | ||
428 | return ast::MethodCallExpr::cast(parent) | ||
429 | .and_then(|it| highlight_method_call(sema, &it)) | ||
430 | .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into()); | ||
431 | } | ||
432 | FIELD_EXPR => { | ||
433 | let h = HlTag::Symbol(SymbolKind::Field); | ||
434 | let is_union = ast::FieldExpr::cast(parent) | ||
435 | .and_then(|field_expr| { | ||
436 | let field = sema.resolve_field(&field_expr)?; | ||
437 | Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { | ||
438 | true | ||
439 | } else { | ||
440 | false | ||
441 | }) | ||
442 | }) | ||
443 | .unwrap_or(false); | ||
444 | if is_union { | ||
445 | h | HlMod::Unsafe | ||
446 | } else { | ||
447 | h.into() | ||
448 | } | ||
449 | } | ||
450 | PATH_SEGMENT => { | ||
451 | let path = match parent.parent().and_then(ast::Path::cast) { | ||
452 | Some(it) => it, | ||
453 | _ => return default.into(), | ||
454 | }; | ||
455 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
456 | Some(it) => it, | ||
457 | _ => { | ||
458 | // within path, decide whether it is module or adt by checking for uppercase name | ||
459 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
460 | HlTag::Symbol(SymbolKind::Struct) | ||
461 | } else { | ||
462 | HlTag::Symbol(SymbolKind::Module) | ||
463 | } | ||
464 | .into(); | ||
465 | } | ||
466 | }; | ||
467 | let parent = match expr.syntax().parent() { | ||
468 | Some(it) => it, | ||
469 | None => return default.into(), | ||
470 | }; | ||
471 | |||
472 | match parent.kind() { | ||
473 | CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(), | ||
474 | _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
475 | HlTag::Symbol(SymbolKind::Struct) | ||
476 | } else { | ||
477 | HlTag::Symbol(SymbolKind::Const) | ||
478 | } | ||
479 | .into(), | ||
480 | } | ||
481 | } | ||
482 | _ => default.into(), | ||
483 | } | ||
484 | } | ||
485 | |||
486 | fn is_consumed_lvalue( | ||
487 | node: NodeOrToken<SyntaxNode, SyntaxToken>, | ||
488 | local: &hir::Local, | ||
489 | db: &RootDatabase, | ||
490 | ) -> bool { | ||
491 | // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. | ||
492 | parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) | ||
493 | } | ||
494 | |||
495 | /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. | ||
496 | fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool { | ||
497 | while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { | ||
498 | if parent.kind() != *kind { | ||
499 | return false; | ||
500 | } | ||
501 | |||
502 | // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value | ||
503 | // in the same pattern is unstable: rust-lang/rust#68354. | ||
504 | node = node.parent().unwrap().into(); | ||
505 | kinds = rest; | ||
506 | } | ||
507 | |||
508 | // Only true if we matched all expected kinds | ||
509 | kinds.len() == 0 | ||
510 | } | ||
511 | |||
512 | fn is_child_of_impl(element: &SyntaxElement) -> bool { | ||
513 | match element.parent() { | ||
514 | Some(e) => e.kind() == IMPL, | ||
515 | _ => false, | ||
516 | } | ||
517 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 4647a72c2..281461493 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Syntax highlighting injections such as highlighting of documentation tests. | 1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. |
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::call_info::ActiveParameter; | 4 | use ide_db::call_info::ActiveParameter; |