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