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