]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/defs.rs
5a4cfe6e941fcda904ca1fc2ec3ada6192004dd7
[rust.git] / crates / ide_db / src / defs.rs
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::{
10     Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Field, Function, GenericParam,
11     HasVisibility, Impl, ItemInNs, Label, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
12     Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
13 };
14 use stdx::impl_from;
15 use syntax::{
16     ast::{self, AstNode},
17     match_ast, AstToken, SyntaxKind, SyntaxNode, SyntaxToken,
18 };
19
20 use crate::RootDatabase;
21
22 // FIXME: a more precise name would probably be `Symbol`?
23 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
24 pub enum Definition {
25     Macro(MacroDef),
26     Field(Field),
27     Module(Module),
28     Function(Function),
29     Adt(Adt),
30     Variant(Variant),
31     Const(Const),
32     Static(Static),
33     Trait(Trait),
34     TypeAlias(TypeAlias),
35     BuiltinType(BuiltinType),
36     SelfType(Impl),
37     Local(Local),
38     GenericParam(GenericParam),
39     Label(Label),
40     BuiltinAttr(BuiltinAttr),
41     ToolModule(ToolModule),
42 }
43
44 impl Definition {
45     pub fn canonical_module_path(&self, db: &RootDatabase) -> Option<impl Iterator<Item = Module>> {
46         self.module(db).map(|it| it.path_to_root(db).into_iter().rev())
47     }
48
49     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
50         let module = match self {
51             Definition::Macro(it) => it.module(db)?,
52             Definition::Module(it) => it.parent(db)?,
53             Definition::Field(it) => it.parent_def(db).module(db),
54             Definition::Function(it) => it.module(db),
55             Definition::Adt(it) => it.module(db),
56             Definition::Const(it) => it.module(db),
57             Definition::Static(it) => it.module(db),
58             Definition::Trait(it) => it.module(db),
59             Definition::TypeAlias(it) => it.module(db),
60             Definition::Variant(it) => it.module(db),
61             Definition::SelfType(it) => it.module(db),
62             Definition::Local(it) => it.module(db),
63             Definition::GenericParam(it) => it.module(db),
64             Definition::Label(it) => it.module(db),
65             Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
66                 return None
67             }
68         };
69         Some(module)
70     }
71
72     pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
73         let vis = match self {
74             Definition::Field(sf) => sf.visibility(db),
75             Definition::Module(it) => it.visibility(db),
76             Definition::Function(it) => it.visibility(db),
77             Definition::Adt(it) => it.visibility(db),
78             Definition::Const(it) => it.visibility(db),
79             Definition::Static(it) => it.visibility(db),
80             Definition::Trait(it) => it.visibility(db),
81             Definition::TypeAlias(it) => it.visibility(db),
82             Definition::Variant(it) => it.visibility(db),
83             Definition::BuiltinType(_) => Visibility::Public,
84             Definition::Macro(_) => return None,
85             Definition::BuiltinAttr(_)
86             | Definition::ToolModule(_)
87             | Definition::SelfType(_)
88             | Definition::Local(_)
89             | Definition::GenericParam(_)
90             | Definition::Label(_) => return None,
91         };
92         Some(vis)
93     }
94
95     pub fn name(&self, db: &RootDatabase) -> Option<Name> {
96         let name = match self {
97             Definition::Macro(it) => it.name(db)?,
98             Definition::Field(it) => it.name(db),
99             Definition::Module(it) => it.name(db)?,
100             Definition::Function(it) => it.name(db),
101             Definition::Adt(it) => it.name(db),
102             Definition::Variant(it) => it.name(db),
103             Definition::Const(it) => it.name(db)?,
104             Definition::Static(it) => it.name(db),
105             Definition::Trait(it) => it.name(db),
106             Definition::TypeAlias(it) => it.name(db),
107             Definition::BuiltinType(it) => it.name(),
108             Definition::SelfType(_) => return None,
109             Definition::Local(it) => it.name(db)?,
110             Definition::GenericParam(it) => it.name(db),
111             Definition::Label(it) => it.name(db),
112             Definition::BuiltinAttr(_) => return None, // FIXME
113             Definition::ToolModule(_) => return None,  // FIXME
114         };
115         Some(name)
116     }
117 }
118
119 pub enum IdentClass {
120     NameClass(NameClass),
121     NameRefClass(NameRefClass),
122 }
123
124 impl IdentClass {
125     pub fn classify_node(sema: &Semantics<RootDatabase>, node: &SyntaxNode) -> Option<IdentClass> {
126         match_ast! {
127             match node {
128                 ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass),
129                 ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(IdentClass::NameRefClass),
130                 ast::Lifetime(lifetime) => {
131                     NameClass::classify_lifetime(sema, &lifetime)
132                         .map(IdentClass::NameClass)
133                         .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
134                 },
135                 _ => None,
136             }
137         }
138     }
139
140     pub fn classify_token(
141         sema: &Semantics<RootDatabase>,
142         token: &SyntaxToken,
143     ) -> Option<IdentClass> {
144         let parent = token.parent()?;
145         // resolve derives if possible
146         if let Some(ident) = ast::Ident::cast(token.clone()) {
147             let attr = ast::TokenTree::cast(parent.clone())
148                 .and_then(|tt| tt.parent_meta())
149                 .and_then(|meta| meta.parent_attr());
150             if let Some(attr) = attr {
151                 return NameRefClass::classify_derive(sema, &attr, &ident)
152                     .map(IdentClass::NameRefClass);
153             }
154         }
155         Self::classify_node(sema, &parent)
156     }
157
158     pub fn definitions(self) -> ArrayVec<Definition, 2> {
159         let mut res = ArrayVec::new();
160         match self {
161             IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
162                 res.push(it)
163             }
164             IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
165                 res.push(Definition::Local(local_def));
166                 res.push(Definition::Field(field_ref));
167             }
168             IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
169             IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
170                 res.push(Definition::Local(local_ref));
171                 res.push(Definition::Field(field_ref));
172             }
173         }
174         res
175     }
176 }
177
178 /// On a first blush, a single `ast::Name` defines a single definition at some
179 /// scope. That is, that, by just looking at the syntactical category, we can
180 /// unambiguously define the semantic category.
181 ///
182 /// Sadly, that's not 100% true, there are special cases. To make sure that
183 /// callers handle all the special cases correctly via exhaustive matching, we
184 /// add a [`NameClass`] enum which lists all of them!
185 ///
186 /// A model special case is `None` constant in pattern.
187 #[derive(Debug)]
188 pub enum NameClass {
189     Definition(Definition),
190     /// `None` in `if let None = Some(82) {}`.
191     /// Syntactically, it is a name, but semantically it is a reference.
192     ConstReference(Definition),
193     /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces
194     /// a definition into a local scope, and refers to an existing definition.
195     PatFieldShorthand {
196         local_def: Local,
197         field_ref: Field,
198     },
199 }
200
201 impl NameClass {
202     /// `Definition` defined by this name.
203     pub fn defined(self) -> Option<Definition> {
204         let res = match self {
205             NameClass::Definition(it) => it,
206             NameClass::ConstReference(_) => return None,
207             NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
208                 Definition::Local(local_def)
209             }
210         };
211         Some(res)
212     }
213
214     pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
215         let _p = profile::span("classify_name");
216
217         let parent = name.syntax().parent()?;
218
219         let def = if let Some(item) = ast::Item::cast(parent.clone()) {
220             match item {
221                 ast::Item::MacroRules(it) => {
222                     Definition::Macro(sema.to_def(&ast::Macro::MacroRules(it))?)
223                 }
224                 ast::Item::MacroDef(it) => {
225                     Definition::Macro(sema.to_def(&ast::Macro::MacroDef(it))?)
226                 }
227                 ast::Item::Const(it) => Definition::Const(sema.to_def(&it)?),
228                 ast::Item::Fn(it) => {
229                     let def = sema.to_def(&it)?;
230                     def.as_proc_macro(sema.db)
231                         .map(Definition::Macro)
232                         .unwrap_or(Definition::Function(def))
233                 }
234                 ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
235                 ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
236                 ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
237                 ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
238                 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
239                 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
240                 ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
241                 _ => return None,
242             }
243         } else if let Some(it) = ast::IdentPat::cast(parent.clone()) {
244             if let Some(def) = sema.resolve_bind_pat_to_const(&it) {
245                 return Some(NameClass::ConstReference(Definition::from(def)));
246             }
247
248             let local = sema.to_def(&it)?;
249             let pat_parent = it.syntax().parent();
250             if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
251                 if record_pat_field.name_ref().is_none() {
252                     if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
253                         return Some(NameClass::PatFieldShorthand {
254                             local_def: local,
255                             field_ref: field,
256                         });
257                     }
258                 }
259             }
260
261             Definition::Local(local)
262         } else if let Some(it) = ast::Rename::cast(parent.clone()) {
263             if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
264                 let path = use_tree.path()?;
265                 let path_segment = path.segment()?;
266                 let name_ref = path_segment.name_ref()?;
267                 let name_ref = if name_ref.self_token().is_some() {
268                     use_tree
269                         .syntax()
270                         .parent()
271                         .as_ref()
272                         // Skip over UseTreeList
273                         .and_then(|it| {
274                             let use_tree = it.parent().and_then(ast::UseTree::cast)?;
275                             let path = use_tree.path()?;
276                             let path_segment = path.segment()?;
277                             path_segment.name_ref()
278                         })
279                         .unwrap_or(name_ref)
280                 } else {
281                     name_ref
282                 };
283                 let name_ref_class = NameRefClass::classify(sema, &name_ref)?;
284
285                 match name_ref_class {
286                     NameRefClass::Definition(def) => def,
287                     NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
288                         Definition::Field(field_ref)
289                     }
290                 }
291             } else {
292                 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
293                 let krate = sema.resolve_extern_crate(&extern_crate)?;
294                 let root_module = krate.root_module(sema.db);
295                 Definition::Module(root_module)
296             }
297         } else {
298             match_ast! {
299                 match parent {
300                     ast::SelfParam(it) => Definition::Local(sema.to_def(&it)?),
301                     ast::RecordField(it) => Definition::Field(sema.to_def(&it)?),
302                     ast::Variant(it) => Definition::Variant(sema.to_def(&it)?),
303                     ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
304                     ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
305                     _ => return None,
306                 }
307             }
308         };
309
310         Some(NameClass::Definition(def))
311     }
312
313     pub fn classify_lifetime(
314         sema: &Semantics<RootDatabase>,
315         lifetime: &ast::Lifetime,
316     ) -> Option<NameClass> {
317         let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
318         let parent = lifetime.syntax().parent()?;
319
320         match_ast! {
321             match parent {
322                 ast::LifetimeParam(it) => {
323                     let def = sema.to_def(&it)?;
324                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
325                 },
326                 ast::Label(it) => {
327                     let def = sema.to_def(&it)?;
328                     Some(NameClass::Definition(Definition::Label(def)))
329                 },
330                 _ => None,
331             }
332         }
333     }
334 }
335
336 /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
337 /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
338 /// reference most of the time, but there are a couple of annoying exceptions.
339 ///
340 /// A model special case is field shorthand syntax, which uses a single
341 /// reference to point to two different defs.
342 #[derive(Debug)]
343 pub enum NameRefClass {
344     Definition(Definition),
345     FieldShorthand { local_ref: Local, field_ref: Field },
346 }
347
348 impl NameRefClass {
349     // Note: we don't have unit-tests for this rather important function.
350     // It is primarily exercised via goto definition tests in `ide`.
351     pub fn classify(
352         sema: &Semantics<RootDatabase>,
353         name_ref: &ast::NameRef,
354     ) -> Option<NameRefClass> {
355         let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
356
357         let parent = name_ref.syntax().parent()?;
358
359         if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
360             if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
361                 let res = match local {
362                     None => NameRefClass::Definition(Definition::Field(field)),
363                     Some(local) => {
364                         NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
365                     }
366                 };
367                 return Some(res);
368             }
369         }
370
371         if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) {
372             if path.parent_path().is_none() {
373                 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
374                     // Only use this to resolve to macro calls for last segments as qualifiers resolve
375                     // to modules below.
376                     if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
377                         return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
378                     }
379                 }
380             }
381             return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
382         }
383
384         match_ast! {
385             match parent {
386                 ast::MethodCallExpr(method_call) => {
387                     sema.resolve_method_call(&method_call)
388                         .map(Definition::Function)
389                         .map(NameRefClass::Definition)
390                 },
391                 ast::FieldExpr(field_expr) => {
392                     sema.resolve_field(&field_expr)
393                         .map(Definition::Field)
394                         .map(NameRefClass::Definition)
395                 },
396                 ast::RecordPatField(record_pat_field) => {
397                     sema.resolve_record_pat_field(&record_pat_field)
398                         .map(Definition::Field)
399                         .map(NameRefClass::Definition)
400                 },
401                 ast::AssocTypeArg(_) => {
402                     // `Trait<Assoc = Ty>`
403                     //        ^^^^^
404                     let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
405                     let resolved = sema.resolve_path(&containing_path)?;
406                     if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
407                         // FIXME: resolve in supertraits
408                         if let Some(ty) = tr
409                             .items(sema.db)
410                             .iter()
411                             .filter_map(|&assoc| match assoc {
412                                 hir::AssocItem::TypeAlias(it) => Some(it),
413                                 _ => None,
414                             })
415                             .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str())
416                         {
417                             return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
418                         }
419                     }
420                     None
421                 },
422                 ast::ExternCrate(extern_crate) => {
423                     let krate = sema.resolve_extern_crate(&extern_crate)?;
424                     let root_module = krate.root_module(sema.db);
425                     Some(NameRefClass::Definition(Definition::Module(root_module)))
426                 },
427                 _ => None
428             }
429         }
430     }
431
432     pub fn classify_lifetime(
433         sema: &Semantics<RootDatabase>,
434         lifetime: &ast::Lifetime,
435     ) -> Option<NameRefClass> {
436         let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
437         let parent = lifetime.syntax().parent()?;
438         match parent.kind() {
439             SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
440                 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
441             }
442             SyntaxKind::LIFETIME_ARG
443             | SyntaxKind::SELF_PARAM
444             | SyntaxKind::TYPE_BOUND
445             | SyntaxKind::WHERE_PRED
446             | SyntaxKind::REF_TYPE => sema
447                 .resolve_lifetime_param(lifetime)
448                 .map(GenericParam::LifetimeParam)
449                 .map(Definition::GenericParam)
450                 .map(NameRefClass::Definition),
451             // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
452             // if our lifetime is in a LifetimeParam without being the constrained lifetime
453             _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
454                 != Some(lifetime) =>
455             {
456                 sema.resolve_lifetime_param(lifetime)
457                     .map(GenericParam::LifetimeParam)
458                     .map(Definition::GenericParam)
459                     .map(NameRefClass::Definition)
460             }
461             _ => None,
462         }
463     }
464
465     pub fn classify_derive(
466         sema: &Semantics<RootDatabase>,
467         attr: &ast::Attr,
468         ident: &ast::Ident,
469     ) -> Option<NameRefClass> {
470         sema.resolve_derive_ident(&attr, &ident).map(Definition::from).map(NameRefClass::Definition)
471     }
472 }
473
474 impl_from!(
475     Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
476     GenericParam, Label
477     for Definition
478 );
479
480 impl From<Impl> for Definition {
481     fn from(impl_: Impl) -> Self {
482         Definition::SelfType(impl_)
483     }
484 }
485
486 impl AsAssocItem for Definition {
487     fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
488         match self {
489             Definition::Function(it) => it.as_assoc_item(db),
490             Definition::Const(it) => it.as_assoc_item(db),
491             Definition::TypeAlias(it) => it.as_assoc_item(db),
492             _ => None,
493         }
494     }
495 }
496
497 impl From<AssocItem> for Definition {
498     fn from(assoc_item: AssocItem) -> Self {
499         match assoc_item {
500             AssocItem::Function(it) => Definition::Function(it),
501             AssocItem::Const(it) => Definition::Const(it),
502             AssocItem::TypeAlias(it) => Definition::TypeAlias(it),
503         }
504     }
505 }
506
507 impl From<PathResolution> for Definition {
508     fn from(path_resolution: PathResolution) -> Self {
509         match path_resolution {
510             PathResolution::Def(def) => def.into(),
511             PathResolution::AssocItem(item) => {
512                 let def: ModuleDef = match item {
513                     hir::AssocItem::Function(it) => it.into(),
514                     hir::AssocItem::Const(it) => it.into(),
515                     hir::AssocItem::TypeAlias(it) => it.into(),
516                 };
517                 def.into()
518             }
519             PathResolution::Local(local) => Definition::Local(local),
520             PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
521             PathResolution::Macro(def) => Definition::Macro(def),
522             PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
523             PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
524             PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
525             PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
526         }
527     }
528 }
529
530 impl From<ModuleDef> for Definition {
531     fn from(def: ModuleDef) -> Self {
532         match def {
533             ModuleDef::Module(it) => Definition::Module(it),
534             ModuleDef::Function(it) => Definition::Function(it),
535             ModuleDef::Adt(it) => Definition::Adt(it),
536             ModuleDef::Variant(it) => Definition::Variant(it),
537             ModuleDef::Const(it) => Definition::Const(it),
538             ModuleDef::Static(it) => Definition::Static(it),
539             ModuleDef::Trait(it) => Definition::Trait(it),
540             ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
541             ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
542         }
543     }
544 }
545
546 impl From<Definition> for Option<ItemInNs> {
547     fn from(def: Definition) -> Self {
548         let item = match def {
549             Definition::Module(it) => ModuleDef::Module(it),
550             Definition::Function(it) => ModuleDef::Function(it),
551             Definition::Adt(it) => ModuleDef::Adt(it),
552             Definition::Variant(it) => ModuleDef::Variant(it),
553             Definition::Const(it) => ModuleDef::Const(it),
554             Definition::Static(it) => ModuleDef::Static(it),
555             Definition::Trait(it) => ModuleDef::Trait(it),
556             Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
557             Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
558             _ => return None,
559         };
560         Some(ItemInNs::from(item))
561     }
562 }