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