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