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