]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! `NameDefinition` keeps information about the element we want to search references for. |
2 | //! The element is represented by `NameKind`. It's located inside some `container` and | |
3 | //! has a `visibility`, which defines a search scope. | |
4 | //! Note that the reference search is possible for not all of the classified items. | |
5 | ||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | |
7 | ||
8 | use arrayvec::ArrayVec; | |
9 | use hir::{ | |
781aab86 | 10 | Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef, |
add651ee FG |
11 | ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, |
12 | Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, | |
13 | TypeAlias, Variant, Visibility, | |
064997fb FG |
14 | }; |
15 | use stdx::impl_from; | |
16 | use syntax::{ | |
17 | ast::{self, AstNode}, | |
18 | match_ast, SyntaxKind, SyntaxNode, SyntaxToken, | |
19 | }; | |
20 | ||
21 | use crate::RootDatabase; | |
22 | ||
23 | // FIXME: a more precise name would probably be `Symbol`? | |
24 | #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] | |
25 | pub enum Definition { | |
26 | Macro(Macro), | |
27 | Field(Field), | |
28 | Module(Module), | |
29 | Function(Function), | |
30 | Adt(Adt), | |
31 | Variant(Variant), | |
32 | Const(Const), | |
33 | Static(Static), | |
34 | Trait(Trait), | |
353b0b11 | 35 | TraitAlias(TraitAlias), |
064997fb FG |
36 | TypeAlias(TypeAlias), |
37 | BuiltinType(BuiltinType), | |
38 | SelfType(Impl), | |
064997fb | 39 | GenericParam(GenericParam), |
9ffffee4 | 40 | Local(Local), |
064997fb FG |
41 | Label(Label), |
42 | DeriveHelper(DeriveHelper), | |
43 | BuiltinAttr(BuiltinAttr), | |
44 | ToolModule(ToolModule), | |
add651ee | 45 | ExternCrateDecl(ExternCrateDecl), |
064997fb FG |
46 | } |
47 | ||
48 | impl Definition { | |
49 | pub fn canonical_module_path(&self, db: &RootDatabase) -> Option<impl Iterator<Item = Module>> { | |
50 | self.module(db).map(|it| it.path_to_root(db).into_iter().rev()) | |
51 | } | |
52 | ||
53 | pub fn krate(&self, db: &RootDatabase) -> Option<Crate> { | |
54 | Some(match self { | |
55 | Definition::Module(m) => m.krate(), | |
56 | _ => self.module(db)?.krate(), | |
57 | }) | |
58 | } | |
59 | ||
60 | pub fn module(&self, db: &RootDatabase) -> Option<Module> { | |
61 | let module = match self { | |
62 | Definition::Macro(it) => it.module(db), | |
63 | Definition::Module(it) => it.parent(db)?, | |
64 | Definition::Field(it) => it.parent_def(db).module(db), | |
65 | Definition::Function(it) => it.module(db), | |
66 | Definition::Adt(it) => it.module(db), | |
67 | Definition::Const(it) => it.module(db), | |
68 | Definition::Static(it) => it.module(db), | |
69 | Definition::Trait(it) => it.module(db), | |
353b0b11 | 70 | Definition::TraitAlias(it) => it.module(db), |
064997fb FG |
71 | Definition::TypeAlias(it) => it.module(db), |
72 | Definition::Variant(it) => it.module(db), | |
73 | Definition::SelfType(it) => it.module(db), | |
74 | Definition::Local(it) => it.module(db), | |
75 | Definition::GenericParam(it) => it.module(db), | |
76 | Definition::Label(it) => it.module(db), | |
add651ee | 77 | Definition::ExternCrateDecl(it) => it.module(db), |
064997fb FG |
78 | Definition::DeriveHelper(it) => it.derive().module(db), |
79 | Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => { | |
80 | return None | |
81 | } | |
82 | }; | |
83 | Some(module) | |
84 | } | |
85 | ||
86 | pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> { | |
87 | let vis = match self { | |
88 | Definition::Field(sf) => sf.visibility(db), | |
89 | Definition::Module(it) => it.visibility(db), | |
90 | Definition::Function(it) => it.visibility(db), | |
91 | Definition::Adt(it) => it.visibility(db), | |
92 | Definition::Const(it) => it.visibility(db), | |
93 | Definition::Static(it) => it.visibility(db), | |
94 | Definition::Trait(it) => it.visibility(db), | |
353b0b11 | 95 | Definition::TraitAlias(it) => it.visibility(db), |
064997fb FG |
96 | Definition::TypeAlias(it) => it.visibility(db), |
97 | Definition::Variant(it) => it.visibility(db), | |
add651ee | 98 | Definition::ExternCrateDecl(it) => it.visibility(db), |
064997fb FG |
99 | Definition::BuiltinType(_) => Visibility::Public, |
100 | Definition::Macro(_) => return None, | |
101 | Definition::BuiltinAttr(_) | |
102 | | Definition::ToolModule(_) | |
103 | | Definition::SelfType(_) | |
104 | | Definition::Local(_) | |
105 | | Definition::GenericParam(_) | |
106 | | Definition::Label(_) | |
107 | | Definition::DeriveHelper(_) => return None, | |
108 | }; | |
109 | Some(vis) | |
110 | } | |
111 | ||
112 | pub fn name(&self, db: &RootDatabase) -> Option<Name> { | |
113 | let name = match self { | |
114 | Definition::Macro(it) => it.name(db), | |
115 | Definition::Field(it) => it.name(db), | |
116 | Definition::Module(it) => it.name(db)?, | |
117 | Definition::Function(it) => it.name(db), | |
118 | Definition::Adt(it) => it.name(db), | |
119 | Definition::Variant(it) => it.name(db), | |
120 | Definition::Const(it) => it.name(db)?, | |
121 | Definition::Static(it) => it.name(db), | |
122 | Definition::Trait(it) => it.name(db), | |
353b0b11 | 123 | Definition::TraitAlias(it) => it.name(db), |
064997fb FG |
124 | Definition::TypeAlias(it) => it.name(db), |
125 | Definition::BuiltinType(it) => it.name(), | |
126 | Definition::SelfType(_) => return None, | |
127 | Definition::Local(it) => it.name(db), | |
128 | Definition::GenericParam(it) => it.name(db), | |
129 | Definition::Label(it) => it.name(db), | |
130 | Definition::BuiltinAttr(_) => return None, // FIXME | |
131 | Definition::ToolModule(_) => return None, // FIXME | |
132 | Definition::DeriveHelper(it) => it.name(db), | |
add651ee | 133 | Definition::ExternCrateDecl(it) => return it.alias_or_name(db), |
064997fb FG |
134 | }; |
135 | Some(name) | |
136 | } | |
137 | } | |
138 | ||
f2b60f7d | 139 | // FIXME: IdentClass as a name no longer fits |
064997fb FG |
140 | #[derive(Debug)] |
141 | pub enum IdentClass { | |
142 | NameClass(NameClass), | |
143 | NameRefClass(NameRefClass), | |
f2b60f7d | 144 | Operator(OperatorClass), |
064997fb FG |
145 | } |
146 | ||
147 | impl IdentClass { | |
148 | pub fn classify_node( | |
149 | sema: &Semantics<'_, RootDatabase>, | |
150 | node: &SyntaxNode, | |
151 | ) -> Option<IdentClass> { | |
152 | match_ast! { | |
153 | match node { | |
154 | ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass), | |
155 | ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(IdentClass::NameRefClass), | |
156 | ast::Lifetime(lifetime) => { | |
157 | NameClass::classify_lifetime(sema, &lifetime) | |
158 | .map(IdentClass::NameClass) | |
159 | .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass)) | |
160 | }, | |
f2b60f7d FG |
161 | ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator), |
162 | ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator), | |
163 | ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator), | |
164 | ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator), | |
165 | ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator), | |
064997fb FG |
166 | _ => None, |
167 | } | |
168 | } | |
169 | } | |
170 | ||
171 | pub fn classify_token( | |
172 | sema: &Semantics<'_, RootDatabase>, | |
173 | token: &SyntaxToken, | |
174 | ) -> Option<IdentClass> { | |
175 | let parent = token.parent()?; | |
176 | Self::classify_node(sema, &parent) | |
177 | } | |
178 | ||
179 | pub fn classify_lifetime( | |
180 | sema: &Semantics<'_, RootDatabase>, | |
181 | lifetime: &ast::Lifetime, | |
182 | ) -> Option<IdentClass> { | |
183 | NameRefClass::classify_lifetime(sema, lifetime) | |
184 | .map(IdentClass::NameRefClass) | |
185 | .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass)) | |
186 | } | |
187 | ||
188 | pub fn definitions(self) -> ArrayVec<Definition, 2> { | |
189 | let mut res = ArrayVec::new(); | |
190 | match self { | |
191 | IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { | |
192 | res.push(it) | |
193 | } | |
194 | IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { | |
195 | res.push(Definition::Local(local_def)); | |
196 | res.push(Definition::Field(field_ref)); | |
197 | } | |
198 | IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), | |
199 | IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { | |
200 | res.push(Definition::Local(local_ref)); | |
201 | res.push(Definition::Field(field_ref)); | |
202 | } | |
add651ee FG |
203 | IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => { |
204 | res.push(Definition::ExternCrateDecl(decl)); | |
205 | res.push(Definition::Module(krate.root_module())); | |
206 | } | |
f2b60f7d FG |
207 | IdentClass::Operator( |
208 | OperatorClass::Await(func) | |
209 | | OperatorClass::Prefix(func) | |
210 | | OperatorClass::Bin(func) | |
211 | | OperatorClass::Index(func) | |
212 | | OperatorClass::Try(func), | |
213 | ) => res.push(Definition::Function(func)), | |
214 | } | |
215 | res | |
216 | } | |
217 | ||
218 | pub fn definitions_no_ops(self) -> ArrayVec<Definition, 2> { | |
219 | let mut res = ArrayVec::new(); | |
220 | match self { | |
221 | IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { | |
222 | res.push(it) | |
223 | } | |
224 | IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { | |
225 | res.push(Definition::Local(local_def)); | |
226 | res.push(Definition::Field(field_ref)); | |
227 | } | |
228 | IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), | |
229 | IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { | |
230 | res.push(Definition::Local(local_ref)); | |
231 | res.push(Definition::Field(field_ref)); | |
232 | } | |
add651ee FG |
233 | IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => { |
234 | res.push(Definition::ExternCrateDecl(decl)); | |
235 | res.push(Definition::Module(krate.root_module())); | |
236 | } | |
f2b60f7d | 237 | IdentClass::Operator(_) => (), |
064997fb FG |
238 | } |
239 | res | |
240 | } | |
241 | } | |
242 | ||
243 | /// On a first blush, a single `ast::Name` defines a single definition at some | |
244 | /// scope. That is, that, by just looking at the syntactical category, we can | |
245 | /// unambiguously define the semantic category. | |
246 | /// | |
247 | /// Sadly, that's not 100% true, there are special cases. To make sure that | |
248 | /// callers handle all the special cases correctly via exhaustive matching, we | |
249 | /// add a [`NameClass`] enum which lists all of them! | |
250 | /// | |
251 | /// A model special case is `None` constant in pattern. | |
252 | #[derive(Debug)] | |
253 | pub enum NameClass { | |
254 | Definition(Definition), | |
255 | /// `None` in `if let None = Some(82) {}`. | |
256 | /// Syntactically, it is a name, but semantically it is a reference. | |
257 | ConstReference(Definition), | |
258 | /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces | |
259 | /// a definition into a local scope, and refers to an existing definition. | |
260 | PatFieldShorthand { | |
261 | local_def: Local, | |
262 | field_ref: Field, | |
263 | }, | |
264 | } | |
265 | ||
266 | impl NameClass { | |
267 | /// `Definition` defined by this name. | |
268 | pub fn defined(self) -> Option<Definition> { | |
269 | let res = match self { | |
270 | NameClass::Definition(it) => it, | |
271 | NameClass::ConstReference(_) => return None, | |
272 | NameClass::PatFieldShorthand { local_def, field_ref: _ } => { | |
273 | Definition::Local(local_def) | |
274 | } | |
275 | }; | |
276 | Some(res) | |
277 | } | |
278 | ||
279 | pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<NameClass> { | |
280 | let _p = profile::span("classify_name"); | |
281 | ||
282 | let parent = name.syntax().parent()?; | |
283 | ||
284 | let definition = match_ast! { | |
285 | match parent { | |
286 | ast::Item(it) => classify_item(sema, it)?, | |
287 | ast::IdentPat(it) => return classify_ident_pat(sema, it), | |
288 | ast::Rename(it) => classify_rename(sema, it)?, | |
289 | ast::SelfParam(it) => Definition::Local(sema.to_def(&it)?), | |
290 | ast::RecordField(it) => Definition::Field(sema.to_def(&it)?), | |
291 | ast::Variant(it) => Definition::Variant(sema.to_def(&it)?), | |
292 | ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), | |
293 | ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), | |
294 | _ => return None, | |
295 | } | |
296 | }; | |
297 | return Some(NameClass::Definition(definition)); | |
298 | ||
299 | fn classify_item( | |
300 | sema: &Semantics<'_, RootDatabase>, | |
301 | item: ast::Item, | |
302 | ) -> Option<Definition> { | |
303 | let definition = match item { | |
304 | ast::Item::MacroRules(it) => { | |
305 | Definition::Macro(sema.to_def(&ast::Macro::MacroRules(it))?) | |
306 | } | |
307 | ast::Item::MacroDef(it) => { | |
308 | Definition::Macro(sema.to_def(&ast::Macro::MacroDef(it))?) | |
309 | } | |
310 | ast::Item::Const(it) => Definition::Const(sema.to_def(&it)?), | |
311 | ast::Item::Fn(it) => { | |
312 | let def = sema.to_def(&it)?; | |
313 | def.as_proc_macro(sema.db) | |
314 | .map(Definition::Macro) | |
315 | .unwrap_or(Definition::Function(def)) | |
316 | } | |
317 | ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?), | |
318 | ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?), | |
319 | ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?), | |
353b0b11 | 320 | ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?), |
064997fb FG |
321 | ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?), |
322 | ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)), | |
323 | ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)), | |
324 | ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)), | |
add651ee | 325 | ast::Item::ExternCrate(it) => Definition::ExternCrateDecl(sema.to_def(&it)?), |
064997fb FG |
326 | _ => return None, |
327 | }; | |
328 | Some(definition) | |
329 | } | |
330 | ||
331 | fn classify_ident_pat( | |
332 | sema: &Semantics<'_, RootDatabase>, | |
333 | ident_pat: ast::IdentPat, | |
334 | ) -> Option<NameClass> { | |
335 | if let Some(def) = sema.resolve_bind_pat_to_const(&ident_pat) { | |
336 | return Some(NameClass::ConstReference(Definition::from(def))); | |
337 | } | |
338 | ||
339 | let local = sema.to_def(&ident_pat)?; | |
340 | let pat_parent = ident_pat.syntax().parent(); | |
341 | if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) { | |
342 | if record_pat_field.name_ref().is_none() { | |
353b0b11 | 343 | if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) { |
064997fb FG |
344 | return Some(NameClass::PatFieldShorthand { |
345 | local_def: local, | |
346 | field_ref: field, | |
347 | }); | |
348 | } | |
349 | } | |
350 | } | |
351 | Some(NameClass::Definition(Definition::Local(local))) | |
352 | } | |
353 | ||
354 | fn classify_rename( | |
355 | sema: &Semantics<'_, RootDatabase>, | |
356 | rename: ast::Rename, | |
357 | ) -> Option<Definition> { | |
358 | if let Some(use_tree) = rename.syntax().parent().and_then(ast::UseTree::cast) { | |
359 | let path = use_tree.path()?; | |
360 | sema.resolve_path(&path).map(Definition::from) | |
361 | } else { | |
add651ee FG |
362 | sema.to_def(&rename.syntax().parent().and_then(ast::ExternCrate::cast)?) |
363 | .map(Definition::ExternCrateDecl) | |
064997fb FG |
364 | } |
365 | } | |
366 | } | |
367 | ||
368 | pub fn classify_lifetime( | |
369 | sema: &Semantics<'_, RootDatabase>, | |
370 | lifetime: &ast::Lifetime, | |
371 | ) -> Option<NameClass> { | |
372 | let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string()); | |
373 | let parent = lifetime.syntax().parent()?; | |
374 | ||
375 | if let Some(it) = ast::LifetimeParam::cast(parent.clone()) { | |
376 | sema.to_def(&it).map(Into::into).map(Definition::GenericParam) | |
377 | } else if let Some(it) = ast::Label::cast(parent) { | |
378 | sema.to_def(&it).map(Definition::Label) | |
379 | } else { | |
380 | None | |
381 | } | |
382 | .map(NameClass::Definition) | |
383 | } | |
384 | } | |
385 | ||
f2b60f7d FG |
386 | #[derive(Debug)] |
387 | pub enum OperatorClass { | |
388 | Await(Function), | |
389 | Prefix(Function), | |
390 | Index(Function), | |
391 | Try(Function), | |
392 | Bin(Function), | |
393 | } | |
394 | ||
395 | impl OperatorClass { | |
396 | pub fn classify_await( | |
397 | sema: &Semantics<'_, RootDatabase>, | |
398 | await_expr: &ast::AwaitExpr, | |
399 | ) -> Option<OperatorClass> { | |
400 | sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await) | |
401 | } | |
402 | ||
403 | pub fn classify_prefix( | |
404 | sema: &Semantics<'_, RootDatabase>, | |
405 | prefix_expr: &ast::PrefixExpr, | |
406 | ) -> Option<OperatorClass> { | |
407 | sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix) | |
408 | } | |
409 | ||
410 | pub fn classify_try( | |
411 | sema: &Semantics<'_, RootDatabase>, | |
412 | try_expr: &ast::TryExpr, | |
413 | ) -> Option<OperatorClass> { | |
414 | sema.resolve_try_expr(try_expr).map(OperatorClass::Try) | |
415 | } | |
416 | ||
417 | pub fn classify_index( | |
418 | sema: &Semantics<'_, RootDatabase>, | |
419 | index_expr: &ast::IndexExpr, | |
420 | ) -> Option<OperatorClass> { | |
421 | sema.resolve_index_expr(index_expr).map(OperatorClass::Index) | |
422 | } | |
423 | ||
424 | pub fn classify_bin( | |
425 | sema: &Semantics<'_, RootDatabase>, | |
426 | bin_expr: &ast::BinExpr, | |
427 | ) -> Option<OperatorClass> { | |
428 | sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin) | |
429 | } | |
430 | } | |
431 | ||
064997fb FG |
432 | /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than |
433 | /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a | |
434 | /// reference most of the time, but there are a couple of annoying exceptions. | |
435 | /// | |
436 | /// A model special case is field shorthand syntax, which uses a single | |
437 | /// reference to point to two different defs. | |
438 | #[derive(Debug)] | |
439 | pub enum NameRefClass { | |
440 | Definition(Definition), | |
add651ee FG |
441 | FieldShorthand { |
442 | local_ref: Local, | |
443 | field_ref: Field, | |
444 | }, | |
445 | /// The specific situation where we have an extern crate decl without a rename | |
446 | /// Here we have both a declaration and a reference. | |
447 | /// ```rs | |
448 | /// extern crate foo; | |
449 | /// ``` | |
450 | ExternCrateShorthand { | |
451 | decl: ExternCrateDecl, | |
452 | krate: Crate, | |
453 | }, | |
064997fb FG |
454 | } |
455 | ||
456 | impl NameRefClass { | |
457 | // Note: we don't have unit-tests for this rather important function. | |
458 | // It is primarily exercised via goto definition tests in `ide`. | |
459 | pub fn classify( | |
460 | sema: &Semantics<'_, RootDatabase>, | |
461 | name_ref: &ast::NameRef, | |
462 | ) -> Option<NameRefClass> { | |
463 | let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string()); | |
464 | ||
465 | let parent = name_ref.syntax().parent()?; | |
466 | ||
467 | if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { | |
468 | if let Some((field, local, _)) = sema.resolve_record_field(&record_field) { | |
469 | let res = match local { | |
470 | None => NameRefClass::Definition(Definition::Field(field)), | |
471 | Some(local) => { | |
472 | NameRefClass::FieldShorthand { field_ref: field, local_ref: local } | |
473 | } | |
474 | }; | |
475 | return Some(res); | |
476 | } | |
477 | } | |
478 | ||
479 | if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) { | |
480 | if path.parent_path().is_none() { | |
481 | if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { | |
482 | // Only use this to resolve to macro calls for last segments as qualifiers resolve | |
483 | // to modules below. | |
484 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | |
485 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); | |
486 | } | |
487 | } | |
488 | } | |
489 | return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition); | |
490 | } | |
491 | ||
492 | match_ast! { | |
493 | match parent { | |
494 | ast::MethodCallExpr(method_call) => { | |
353b0b11 FG |
495 | sema.resolve_method_call_field_fallback(&method_call) |
496 | .map(|it| { | |
497 | it.map_left(Definition::Function) | |
498 | .map_right(Definition::Field) | |
499 | .either(NameRefClass::Definition, NameRefClass::Definition) | |
500 | }) | |
064997fb FG |
501 | }, |
502 | ast::FieldExpr(field_expr) => { | |
503 | sema.resolve_field(&field_expr) | |
504 | .map(Definition::Field) | |
505 | .map(NameRefClass::Definition) | |
506 | }, | |
507 | ast::RecordPatField(record_pat_field) => { | |
508 | sema.resolve_record_pat_field(&record_pat_field) | |
353b0b11 FG |
509 | .map(|(field, ..)|field) |
510 | .map(Definition::Field) | |
511 | .map(NameRefClass::Definition) | |
512 | }, | |
513 | ast::RecordExprField(record_expr_field) => { | |
514 | sema.resolve_record_field(&record_expr_field) | |
515 | .map(|(field, ..)|field) | |
064997fb FG |
516 | .map(Definition::Field) |
517 | .map(NameRefClass::Definition) | |
518 | }, | |
519 | ast::AssocTypeArg(_) => { | |
520 | // `Trait<Assoc = Ty>` | |
521 | // ^^^^^ | |
522 | let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; | |
523 | let resolved = sema.resolve_path(&containing_path)?; | |
524 | if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved { | |
525 | if let Some(ty) = tr | |
526 | .items_with_supertraits(sema.db) | |
527 | .iter() | |
528 | .filter_map(|&assoc| match assoc { | |
529 | hir::AssocItem::TypeAlias(it) => Some(it), | |
530 | _ => None, | |
531 | }) | |
532 | .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str()) | |
533 | { | |
534 | return Some(NameRefClass::Definition(Definition::TypeAlias(ty))); | |
535 | } | |
536 | } | |
537 | None | |
538 | }, | |
add651ee FG |
539 | ast::ExternCrate(extern_crate_ast) => { |
540 | let extern_crate = sema.to_def(&extern_crate_ast)?; | |
541 | let krate = extern_crate.resolved_crate(sema.db)?; | |
542 | Some(if extern_crate_ast.rename().is_some() { | |
543 | NameRefClass::Definition(Definition::Module(krate.root_module())) | |
544 | } else { | |
545 | NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } | |
546 | }) | |
064997fb FG |
547 | }, |
548 | _ => None | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | pub fn classify_lifetime( | |
554 | sema: &Semantics<'_, RootDatabase>, | |
555 | lifetime: &ast::Lifetime, | |
556 | ) -> Option<NameRefClass> { | |
557 | let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string()); | |
558 | let parent = lifetime.syntax().parent()?; | |
559 | match parent.kind() { | |
560 | SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { | |
561 | sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition) | |
562 | } | |
563 | SyntaxKind::LIFETIME_ARG | |
564 | | SyntaxKind::SELF_PARAM | |
565 | | SyntaxKind::TYPE_BOUND | |
566 | | SyntaxKind::WHERE_PRED | |
567 | | SyntaxKind::REF_TYPE => sema | |
568 | .resolve_lifetime_param(lifetime) | |
569 | .map(GenericParam::LifetimeParam) | |
570 | .map(Definition::GenericParam) | |
571 | .map(NameRefClass::Definition), | |
572 | // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check | |
573 | // if our lifetime is in a LifetimeParam without being the constrained lifetime | |
574 | _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref() | |
575 | != Some(lifetime) => | |
576 | { | |
577 | sema.resolve_lifetime_param(lifetime) | |
578 | .map(GenericParam::LifetimeParam) | |
579 | .map(Definition::GenericParam) | |
580 | .map(NameRefClass::Definition) | |
581 | } | |
582 | _ => None, | |
583 | } | |
584 | } | |
585 | } | |
586 | ||
587 | impl_from!( | |
353b0b11 | 588 | Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local, |
064997fb FG |
589 | GenericParam, Label, Macro |
590 | for Definition | |
591 | ); | |
592 | ||
593 | impl From<Impl> for Definition { | |
594 | fn from(impl_: Impl) -> Self { | |
595 | Definition::SelfType(impl_) | |
596 | } | |
597 | } | |
598 | ||
599 | impl AsAssocItem for Definition { | |
600 | fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> { | |
601 | match self { | |
602 | Definition::Function(it) => it.as_assoc_item(db), | |
603 | Definition::Const(it) => it.as_assoc_item(db), | |
604 | Definition::TypeAlias(it) => it.as_assoc_item(db), | |
605 | _ => None, | |
606 | } | |
607 | } | |
608 | } | |
609 | ||
610 | impl From<AssocItem> for Definition { | |
611 | fn from(assoc_item: AssocItem) -> Self { | |
612 | match assoc_item { | |
613 | AssocItem::Function(it) => Definition::Function(it), | |
614 | AssocItem::Const(it) => Definition::Const(it), | |
615 | AssocItem::TypeAlias(it) => Definition::TypeAlias(it), | |
616 | } | |
617 | } | |
618 | } | |
619 | ||
620 | impl From<PathResolution> for Definition { | |
621 | fn from(path_resolution: PathResolution) -> Self { | |
622 | match path_resolution { | |
623 | PathResolution::Def(def) => def.into(), | |
624 | PathResolution::Local(local) => Definition::Local(local), | |
625 | PathResolution::TypeParam(par) => Definition::GenericParam(par.into()), | |
626 | PathResolution::ConstParam(par) => Definition::GenericParam(par.into()), | |
627 | PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), | |
628 | PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr), | |
629 | PathResolution::ToolModule(tool) => Definition::ToolModule(tool), | |
630 | PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper), | |
631 | } | |
632 | } | |
633 | } | |
634 | ||
635 | impl From<ModuleDef> for Definition { | |
636 | fn from(def: ModuleDef) -> Self { | |
637 | match def { | |
638 | ModuleDef::Module(it) => Definition::Module(it), | |
639 | ModuleDef::Function(it) => Definition::Function(it), | |
640 | ModuleDef::Adt(it) => Definition::Adt(it), | |
641 | ModuleDef::Variant(it) => Definition::Variant(it), | |
642 | ModuleDef::Const(it) => Definition::Const(it), | |
643 | ModuleDef::Static(it) => Definition::Static(it), | |
644 | ModuleDef::Trait(it) => Definition::Trait(it), | |
353b0b11 | 645 | ModuleDef::TraitAlias(it) => Definition::TraitAlias(it), |
064997fb FG |
646 | ModuleDef::TypeAlias(it) => Definition::TypeAlias(it), |
647 | ModuleDef::Macro(it) => Definition::Macro(it), | |
648 | ModuleDef::BuiltinType(it) => Definition::BuiltinType(it), | |
649 | } | |
650 | } | |
651 | } | |
781aab86 FG |
652 | |
653 | impl From<DocLinkDef> for Definition { | |
654 | fn from(def: DocLinkDef) -> Self { | |
655 | match def { | |
656 | DocLinkDef::ModuleDef(it) => it.into(), | |
657 | DocLinkDef::Field(it) => it.into(), | |
658 | DocLinkDef::SelfType(it) => it.into(), | |
659 | } | |
660 | } | |
661 | } |