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