]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/defs.rs
Merge #9353
[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 hir::{
9     db::HirDatabase, Crate, Field, GenericParam, HasAttrs, HasVisibility, Impl, Label, Local,
10     MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11 };
12 use syntax::{
13     ast::{self, AstNode, PathSegmentKind},
14     match_ast, SyntaxKind, SyntaxNode,
15 };
16
17 use crate::RootDatabase;
18
19 // FIXME: a more precise name would probably be `Symbol`?
20 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
21 pub enum Definition {
22     Macro(MacroDef),
23     Field(Field),
24     ModuleDef(ModuleDef),
25     SelfType(Impl),
26     Local(Local),
27     GenericParam(GenericParam),
28     Label(Label),
29 }
30
31 impl Definition {
32     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
33         match self {
34             Definition::Macro(it) => it.module(db),
35             Definition::Field(it) => Some(it.parent_def(db).module(db)),
36             Definition::ModuleDef(it) => it.module(db),
37             Definition::SelfType(it) => Some(it.module(db)),
38             Definition::Local(it) => Some(it.module(db)),
39             Definition::GenericParam(it) => Some(it.module(db)),
40             Definition::Label(it) => Some(it.module(db)),
41         }
42     }
43
44     pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
45         match self {
46             Definition::Field(sf) => Some(sf.visibility(db)),
47             Definition::ModuleDef(def) => match def {
48                 ModuleDef::Module(it) => {
49                     // FIXME: should work like other cases here.
50                     let parent = it.parent(db)?;
51                     parent.visibility_of(db, def)
52                 }
53                 ModuleDef::Function(it) => Some(it.visibility(db)),
54                 ModuleDef::Adt(it) => Some(it.visibility(db)),
55                 ModuleDef::Const(it) => Some(it.visibility(db)),
56                 ModuleDef::Static(it) => Some(it.visibility(db)),
57                 ModuleDef::Trait(it) => Some(it.visibility(db)),
58                 ModuleDef::TypeAlias(it) => Some(it.visibility(db)),
59                 // NB: Variants don't have their own visibility, and just inherit
60                 // one from the parent. Not sure if that's the right thing to do.
61                 ModuleDef::Variant(it) => Some(it.parent_enum(db).visibility(db)),
62                 ModuleDef::BuiltinType(_) => None,
63             },
64             Definition::Macro(_)
65             | Definition::SelfType(_)
66             | Definition::Local(_)
67             | Definition::GenericParam(_)
68             | Definition::Label(_) => None,
69         }
70     }
71
72     pub fn name(&self, db: &RootDatabase) -> Option<Name> {
73         let name = match self {
74             Definition::Macro(it) => it.name(db)?,
75             Definition::Field(it) => it.name(db),
76             Definition::ModuleDef(def) => match def {
77                 hir::ModuleDef::Module(it) => it.name(db)?,
78                 hir::ModuleDef::Function(it) => it.name(db),
79                 hir::ModuleDef::Adt(def) => match def {
80                     hir::Adt::Struct(it) => it.name(db),
81                     hir::Adt::Union(it) => it.name(db),
82                     hir::Adt::Enum(it) => it.name(db),
83                 },
84                 hir::ModuleDef::Variant(it) => it.name(db),
85                 hir::ModuleDef::Const(it) => it.name(db)?,
86                 hir::ModuleDef::Static(it) => it.name(db)?,
87                 hir::ModuleDef::Trait(it) => it.name(db),
88                 hir::ModuleDef::TypeAlias(it) => it.name(db),
89                 hir::ModuleDef::BuiltinType(it) => it.name(),
90             },
91             Definition::SelfType(_) => return None,
92             Definition::Local(it) => it.name(db)?,
93             Definition::GenericParam(it) => it.name(db),
94             Definition::Label(it) => it.name(db),
95         };
96         Some(name)
97     }
98 }
99
100 #[derive(Debug)]
101 pub enum NameClass {
102     ExternCrate(Crate),
103     Definition(Definition),
104     /// `None` in `if let None = Some(82) {}`.
105     ConstReference(Definition),
106     /// `field` in `if let Foo { field } = foo`.
107     PatFieldShorthand {
108         local_def: Local,
109         field_ref: Definition,
110     },
111 }
112
113 impl NameClass {
114     /// `Definition` defined by this name.
115     pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
116         let res = match self {
117             NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
118             NameClass::Definition(it) => it,
119             NameClass::ConstReference(_) => return None,
120             NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
121                 Definition::Local(local_def)
122             }
123         };
124         Some(res)
125     }
126
127     /// `Definition` referenced or defined by this name.
128     pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
129         match self {
130             NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
131             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
132             NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
133         }
134     }
135
136     pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
137         let _p = profile::span("classify_name");
138
139         let parent = name.syntax().parent()?;
140
141         if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
142             if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
143                 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
144             }
145         }
146
147         match_ast! {
148             match parent {
149                 ast::Rename(it) => {
150                     if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
151                         let path = use_tree.path()?;
152                         let path_segment = path.segment()?;
153                         let name_ref_class = path_segment
154                             .kind()
155                             .and_then(|kind| {
156                                 match kind {
157                                     // The rename might be from a `self` token, so fallback to the name higher
158                                     // in the use tree.
159                                     PathSegmentKind::SelfKw => {
160                                         let use_tree = use_tree
161                                             .syntax()
162                                             .parent()
163                                             .as_ref()
164                                             // Skip over UseTreeList
165                                             .and_then(SyntaxNode::parent)
166                                             .and_then(ast::UseTree::cast)?;
167                                         let path = use_tree.path()?;
168                                         let path_segment = path.segment()?;
169                                         path_segment.name_ref()
170                                     },
171                                     PathSegmentKind::Name(name_ref) => Some(name_ref),
172                                     _ => return None,
173                                 }
174                             })
175                             .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
176
177                         Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
178                     } else {
179                         let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
180                         let resolved = sema.resolve_extern_crate(&extern_crate)?;
181                         Some(NameClass::ExternCrate(resolved))
182                     }
183                 },
184                 ast::IdentPat(it) => {
185                     let local = sema.to_def(&it)?;
186
187                     if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
188                         if record_pat_field.name_ref().is_none() {
189                             if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
190                                 let field = Definition::Field(field);
191                                 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
192                             }
193                         }
194                     }
195
196                     Some(NameClass::Definition(Definition::Local(local)))
197                 },
198                 ast::SelfParam(it) => {
199                     let def = sema.to_def(&it)?;
200                     Some(NameClass::Definition(Definition::Local(def)))
201                 },
202                 ast::RecordField(it) => {
203                     let field: hir::Field = sema.to_def(&it)?;
204                     Some(NameClass::Definition(Definition::Field(field)))
205                 },
206                 ast::Module(it) => {
207                     let def = sema.to_def(&it)?;
208                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
209                 },
210                 ast::Struct(it) => {
211                     let def: hir::Struct = sema.to_def(&it)?;
212                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
213                 },
214                 ast::Union(it) => {
215                     let def: hir::Union = sema.to_def(&it)?;
216                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
217                 },
218                 ast::Enum(it) => {
219                     let def: hir::Enum = sema.to_def(&it)?;
220                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
221                 },
222                 ast::Trait(it) => {
223                     let def: hir::Trait = sema.to_def(&it)?;
224                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
225                 },
226                 ast::Static(it) => {
227                     let def: hir::Static = sema.to_def(&it)?;
228                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
229                 },
230                 ast::Variant(it) => {
231                     let def: hir::Variant = sema.to_def(&it)?;
232                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
233                 },
234                 ast::Fn(it) => {
235                     let def: hir::Function = sema.to_def(&it)?;
236                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
237                 },
238                 ast::Const(it) => {
239                     let def: hir::Const = sema.to_def(&it)?;
240                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
241                 },
242                 ast::TypeAlias(it) => {
243                     let def: hir::TypeAlias = sema.to_def(&it)?;
244                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
245                 },
246                 ast::Macro(it) => {
247                     let def = sema.to_def(&it)?;
248                     Some(NameClass::Definition(Definition::Macro(def)))
249                 },
250                 ast::TypeParam(it) => {
251                     let def = sema.to_def(&it)?;
252                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
253                 },
254                 ast::ConstParam(it) => {
255                     let def = sema.to_def(&it)?;
256                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
257                 },
258                 _ => None,
259             }
260         }
261     }
262
263     pub fn classify_lifetime(
264         sema: &Semantics<RootDatabase>,
265         lifetime: &ast::Lifetime,
266     ) -> Option<NameClass> {
267         let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
268         let parent = lifetime.syntax().parent()?;
269
270         match_ast! {
271             match parent {
272                 ast::LifetimeParam(it) => {
273                     let def = sema.to_def(&it)?;
274                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
275                 },
276                 ast::Label(it) => {
277                     let def = sema.to_def(&it)?;
278                     Some(NameClass::Definition(Definition::Label(def)))
279                 },
280                 _ => None,
281             }
282         }
283     }
284 }
285
286 #[derive(Debug)]
287 pub enum NameRefClass {
288     ExternCrate(Crate),
289     Definition(Definition),
290     FieldShorthand { local_ref: Local, field_ref: Definition },
291 }
292
293 impl NameRefClass {
294     /// `Definition`, which this name refers to.
295     pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
296         match self {
297             NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
298             NameRefClass::Definition(def) => def,
299             NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
300                 // FIXME: this is inherently ambiguous -- this name refers to
301                 // two different defs....
302                 Definition::Local(local_ref)
303             }
304         }
305     }
306
307     // Note: we don't have unit-tests for this rather important function.
308     // It is primarily exercised via goto definition tests in `ide`.
309     pub fn classify(
310         sema: &Semantics<RootDatabase>,
311         name_ref: &ast::NameRef,
312     ) -> Option<NameRefClass> {
313         let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
314
315         let parent = name_ref.syntax().parent()?;
316
317         if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
318             if let Some(func) = sema.resolve_method_call(&method_call) {
319                 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
320             }
321         }
322
323         if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
324             if let Some(field) = sema.resolve_field(&field_expr) {
325                 return Some(NameRefClass::Definition(Definition::Field(field)));
326             }
327         }
328
329         if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
330             if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
331                 let field = Definition::Field(field);
332                 let res = match local {
333                     None => NameRefClass::Definition(field),
334                     Some(local) => {
335                         NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
336                     }
337                 };
338                 return Some(res);
339             }
340         }
341
342         if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
343             if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
344                 let field = Definition::Field(field);
345                 return Some(NameRefClass::Definition(field));
346             }
347         }
348
349         if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
350             if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
351                 // `Trait<Assoc = Ty>`
352                 //        ^^^^^
353                 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
354                 let resolved = sema.resolve_path(&path)?;
355                 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
356                     // FIXME: resolve in supertraits
357                     if let Some(ty) = tr
358                         .items(sema.db)
359                         .iter()
360                         .filter_map(|assoc| match assoc {
361                             hir::AssocItem::TypeAlias(it) => Some(*it),
362                             _ => None,
363                         })
364                         .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
365                     {
366                         return Some(NameRefClass::Definition(Definition::ModuleDef(
367                             ModuleDef::TypeAlias(ty),
368                         )));
369                     }
370                 }
371
372                 return None;
373             }
374         }
375
376         if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
377             if path.qualifier().is_none() {
378                 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
379                     // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
380                     // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
381                     if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
382                         return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
383                     }
384                 }
385             }
386
387             if let Some(resolved) = sema.resolve_path(&path) {
388                 if path.syntax().ancestors().find_map(ast::Attr::cast).is_some() {
389                     if let PathResolution::Def(ModuleDef::Function(func)) = resolved {
390                         if func.attrs(sema.db).by_key("proc_macro_attribute").exists() {
391                             return Some(NameRefClass::Definition(resolved.into()));
392                         }
393                     }
394                 } else {
395                     return Some(NameRefClass::Definition(resolved.into()));
396                 }
397             }
398         }
399
400         let extern_crate = ast::ExternCrate::cast(parent)?;
401         let resolved = sema.resolve_extern_crate(&extern_crate)?;
402         Some(NameRefClass::ExternCrate(resolved))
403     }
404
405     pub fn classify_lifetime(
406         sema: &Semantics<RootDatabase>,
407         lifetime: &ast::Lifetime,
408     ) -> Option<NameRefClass> {
409         let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
410         let parent = lifetime.syntax().parent()?;
411         match parent.kind() {
412             SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
413                 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
414             }
415             SyntaxKind::LIFETIME_ARG
416             | SyntaxKind::SELF_PARAM
417             | SyntaxKind::TYPE_BOUND
418             | SyntaxKind::WHERE_PRED
419             | SyntaxKind::REF_TYPE => sema
420                 .resolve_lifetime_param(lifetime)
421                 .map(GenericParam::LifetimeParam)
422                 .map(Definition::GenericParam)
423                 .map(NameRefClass::Definition),
424             // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
425             // if our lifetime is in a LifetimeParam without being the constrained lifetime
426             _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
427                 != Some(lifetime) =>
428             {
429                 sema.resolve_lifetime_param(lifetime)
430                     .map(GenericParam::LifetimeParam)
431                     .map(Definition::GenericParam)
432                     .map(NameRefClass::Definition)
433             }
434             _ => None,
435         }
436     }
437 }
438
439 impl From<PathResolution> for Definition {
440     fn from(path_resolution: PathResolution) -> Self {
441         match path_resolution {
442             PathResolution::Def(def) => Definition::ModuleDef(def),
443             PathResolution::AssocItem(item) => {
444                 let def = match item {
445                     hir::AssocItem::Function(it) => it.into(),
446                     hir::AssocItem::Const(it) => it.into(),
447                     hir::AssocItem::TypeAlias(it) => it.into(),
448                 };
449                 Definition::ModuleDef(def)
450             }
451             PathResolution::Local(local) => Definition::Local(local),
452             PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
453             PathResolution::Macro(def) => Definition::Macro(def),
454             PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
455             PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
456         }
457     }
458 }