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