diff options
Diffstat (limited to 'crates/ide_db/src/defs.rs')
-rw-r--r-- | crates/ide_db/src/defs.rs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs new file mode 100644 index 000000000..7b5d6ac49 --- /dev/null +++ b/crates/ide_db/src/defs.rs | |||
@@ -0,0 +1,348 @@ | |||
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 `ra_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(¯o_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 | } | ||