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