]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/defs.rs
internal: remove one more needless extern crate
[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     Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef, Module, ModuleDef, Name,
10     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 /// On a first blush, a single `ast::Name` defines a single definition at some
101 /// scope. That is, that, by just looking at the syntactical category, we can
102 /// unambiguously define the semantic category.
103 ///
104 /// Sadly, that's not 100% true, there are special cases. To make sure that
105 /// callers handle all the special cases correctly via exhaustive matching, we
106 /// add a [`NameClass`] enum which lists all of them!
107 ///
108 /// A model special case is `None` constant in pattern.
109 #[derive(Debug)]
110 pub enum NameClass {
111     Definition(Definition),
112     /// `None` in `if let None = Some(82) {}`.
113     /// Syntactically, it is a name, but semantically it is a reference.
114     ConstReference(Definition),
115     /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both Here the
116     /// name both introduces a definition into a local scope, and refers to an
117     /// existing definition.
118     PatFieldShorthand {
119         local_def: Local,
120         field_ref: Definition,
121     },
122 }
123
124 impl NameClass {
125     /// `Definition` defined by this name.
126     pub fn defined(self) -> Option<Definition> {
127         let res = match self {
128             NameClass::Definition(it) => it,
129             NameClass::ConstReference(_) => return None,
130             NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
131                 Definition::Local(local_def)
132             }
133         };
134         Some(res)
135     }
136
137     /// `Definition` referenced or defined by this name.
138     pub fn referenced_or_defined(self) -> Definition {
139         match self {
140             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
141             NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
142         }
143     }
144
145     pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
146         let _p = profile::span("classify_name");
147
148         let parent = name.syntax().parent()?;
149
150         if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
151             if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
152                 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
153             }
154         }
155
156         match_ast! {
157             match parent {
158                 ast::Rename(it) => {
159                     if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
160                         let path = use_tree.path()?;
161                         let path_segment = path.segment()?;
162                         let name_ref_class = path_segment
163                             .kind()
164                             .and_then(|kind| {
165                                 match kind {
166                                     // The rename might be from a `self` token, so fallback to the name higher
167                                     // in the use tree.
168                                     PathSegmentKind::SelfKw => {
169                                         let use_tree = use_tree
170                                             .syntax()
171                                             .parent()
172                                             .as_ref()
173                                             // Skip over UseTreeList
174                                             .and_then(SyntaxNode::parent)
175                                             .and_then(ast::UseTree::cast)?;
176                                         let path = use_tree.path()?;
177                                         let path_segment = path.segment()?;
178                                         path_segment.name_ref()
179                                     },
180                                     PathSegmentKind::Name(name_ref) => Some(name_ref),
181                                     _ => return None,
182                                 }
183                             })
184                             .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
185
186                         Some(NameClass::Definition(name_ref_class.referenced()))
187                     } else {
188                         let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
189                         let krate = sema.resolve_extern_crate(&extern_crate)?;
190                         let root_module = krate.root_module(sema.db);
191                         Some(NameClass::Definition(Definition::ModuleDef(root_module.into())))
192                     }
193                 },
194                 ast::IdentPat(it) => {
195                     let local = sema.to_def(&it)?;
196
197                     if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
198                         if record_pat_field.name_ref().is_none() {
199                             if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
200                                 let field = Definition::Field(field);
201                                 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
202                             }
203                         }
204                     }
205
206                     Some(NameClass::Definition(Definition::Local(local)))
207                 },
208                 ast::SelfParam(it) => {
209                     let def = sema.to_def(&it)?;
210                     Some(NameClass::Definition(Definition::Local(def)))
211                 },
212                 ast::RecordField(it) => {
213                     let field: hir::Field = sema.to_def(&it)?;
214                     Some(NameClass::Definition(Definition::Field(field)))
215                 },
216                 ast::Module(it) => {
217                     let def = sema.to_def(&it)?;
218                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
219                 },
220                 ast::Struct(it) => {
221                     let def: hir::Struct = sema.to_def(&it)?;
222                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
223                 },
224                 ast::Union(it) => {
225                     let def: hir::Union = sema.to_def(&it)?;
226                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
227                 },
228                 ast::Enum(it) => {
229                     let def: hir::Enum = sema.to_def(&it)?;
230                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
231                 },
232                 ast::Trait(it) => {
233                     let def: hir::Trait = sema.to_def(&it)?;
234                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
235                 },
236                 ast::Static(it) => {
237                     let def: hir::Static = sema.to_def(&it)?;
238                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
239                 },
240                 ast::Variant(it) => {
241                     let def: hir::Variant = sema.to_def(&it)?;
242                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
243                 },
244                 ast::Fn(it) => {
245                     let def: hir::Function = sema.to_def(&it)?;
246                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
247                 },
248                 ast::Const(it) => {
249                     let def: hir::Const = sema.to_def(&it)?;
250                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
251                 },
252                 ast::TypeAlias(it) => {
253                     let def: hir::TypeAlias = sema.to_def(&it)?;
254                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
255                 },
256                 ast::Macro(it) => {
257                     let def = sema.to_def(&it)?;
258                     Some(NameClass::Definition(Definition::Macro(def)))
259                 },
260                 ast::TypeParam(it) => {
261                     let def = sema.to_def(&it)?;
262                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
263                 },
264                 ast::ConstParam(it) => {
265                     let def = sema.to_def(&it)?;
266                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
267                 },
268                 _ => None,
269             }
270         }
271     }
272
273     pub fn classify_lifetime(
274         sema: &Semantics<RootDatabase>,
275         lifetime: &ast::Lifetime,
276     ) -> Option<NameClass> {
277         let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
278         let parent = lifetime.syntax().parent()?;
279
280         match_ast! {
281             match parent {
282                 ast::LifetimeParam(it) => {
283                     let def = sema.to_def(&it)?;
284                     Some(NameClass::Definition(Definition::GenericParam(def.into())))
285                 },
286                 ast::Label(it) => {
287                     let def = sema.to_def(&it)?;
288                     Some(NameClass::Definition(Definition::Label(def)))
289                 },
290                 _ => None,
291             }
292         }
293     }
294 }
295
296 /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
297 /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
298 /// reference most of the time, but there are a couple of annoying exceptions.
299 ///
300 /// A model special case is field shorthand syntax, which uses a single
301 /// reference to point to two different defs.
302 #[derive(Debug)]
303 pub enum NameRefClass {
304     Definition(Definition),
305     FieldShorthand { local_ref: Local, field_ref: Definition },
306 }
307
308 impl NameRefClass {
309     /// `Definition`, which this name refers to.
310     pub fn referenced(self) -> Definition {
311         match self {
312             NameRefClass::Definition(def) => def,
313             NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
314                 // FIXME: this is inherently ambiguous -- this name refers to
315                 // two different defs....
316                 Definition::Local(local_ref)
317             }
318         }
319     }
320
321     // Note: we don't have unit-tests for this rather important function.
322     // It is primarily exercised via goto definition tests in `ide`.
323     pub fn classify(
324         sema: &Semantics<RootDatabase>,
325         name_ref: &ast::NameRef,
326     ) -> Option<NameRefClass> {
327         let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
328
329         let parent = name_ref.syntax().parent()?;
330
331         if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
332             if let Some(func) = sema.resolve_method_call(&method_call) {
333                 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
334             }
335         }
336
337         if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
338             if let Some(field) = sema.resolve_field(&field_expr) {
339                 return Some(NameRefClass::Definition(Definition::Field(field)));
340             }
341         }
342
343         if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
344             if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
345                 let field = Definition::Field(field);
346                 let res = match local {
347                     None => NameRefClass::Definition(field),
348                     Some(local) => {
349                         NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
350                     }
351                 };
352                 return Some(res);
353             }
354         }
355
356         if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
357             if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
358                 let field = Definition::Field(field);
359                 return Some(NameRefClass::Definition(field));
360             }
361         }
362
363         if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
364             if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
365                 // `Trait<Assoc = Ty>`
366                 //        ^^^^^
367                 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
368                 let resolved = sema.resolve_path(&path)?;
369                 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
370                     // FIXME: resolve in supertraits
371                     if let Some(ty) = tr
372                         .items(sema.db)
373                         .iter()
374                         .filter_map(|assoc| match assoc {
375                             hir::AssocItem::TypeAlias(it) => Some(*it),
376                             _ => None,
377                         })
378                         .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
379                     {
380                         return Some(NameRefClass::Definition(Definition::ModuleDef(
381                             ModuleDef::TypeAlias(ty),
382                         )));
383                     }
384                 }
385
386                 return None;
387             }
388         }
389
390         if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
391             if path.qualifier().is_none() {
392                 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
393                     // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
394                     // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
395                     if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
396                         return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
397                     }
398                 }
399             }
400             let top_path = path.top_path();
401             let is_attribute_path = top_path
402                 .syntax()
403                 .ancestors()
404                 .find_map(ast::Attr::cast)
405                 .map(|attr| attr.path().as_ref() == Some(&top_path));
406             return match is_attribute_path {
407                 Some(true) => sema.resolve_path(&path).and_then(|resolved| {
408                     match resolved {
409                         // Don't wanna collide with builtin attributes here like `test` hence guard
410                         // so only resolve to modules that aren't the last segment
411                         PathResolution::Def(module @ ModuleDef::Module(_)) if path != top_path => {
412                             cov_mark::hit!(name_ref_classify_attr_path_qualifier);
413                             Some(NameRefClass::Definition(Definition::ModuleDef(module)))
414                         }
415                         PathResolution::Macro(mac) if mac.kind() == hir::MacroKind::Attr => {
416                             Some(NameRefClass::Definition(Definition::Macro(mac)))
417                         }
418                         _ => None,
419                     }
420                 }),
421                 Some(false) => None,
422                 None => sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition),
423             };
424         }
425
426         let extern_crate = ast::ExternCrate::cast(parent)?;
427         let krate = sema.resolve_extern_crate(&extern_crate)?;
428         let root_module = krate.root_module(sema.db);
429         Some(NameRefClass::Definition(Definition::ModuleDef(root_module.into())))
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
466 impl From<PathResolution> for Definition {
467     fn from(path_resolution: PathResolution) -> Self {
468         match path_resolution {
469             PathResolution::Def(def) => Definition::ModuleDef(def),
470             PathResolution::AssocItem(item) => {
471                 let def = match item {
472                     hir::AssocItem::Function(it) => it.into(),
473                     hir::AssocItem::Const(it) => it.into(),
474                     hir::AssocItem::TypeAlias(it) => it.into(),
475                 };
476                 Definition::ModuleDef(def)
477             }
478             PathResolution::Local(local) => Definition::Local(local),
479             PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
480             PathResolution::Macro(def) => Definition::Macro(def),
481             PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
482             PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
483         }
484     }
485 }