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