diff options
Diffstat (limited to 'crates/ra_ide_db/src/defs.rs')
-rw-r--r-- | crates/ra_ide_db/src/defs.rs | 333 |
1 files changed, 0 insertions, 333 deletions
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs deleted file mode 100644 index b51000b03..000000000 --- a/crates/ra_ide_db/src/defs.rs +++ /dev/null | |||
@@ -1,333 +0,0 @@ | |||
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 | Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, | ||
10 | Semantics, TypeParam, Visibility, | ||
11 | }; | ||
12 | use ra_prof::profile; | ||
13 | use ra_syntax::{ | ||
14 | ast::{self, AstNode}, | ||
15 | match_ast, SyntaxNode, | ||
16 | }; | ||
17 | |||
18 | use crate::RootDatabase; | ||
19 | |||
20 | // FIXME: a more precise name would probably be `Symbol`? | ||
21 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
22 | pub enum Definition { | ||
23 | Macro(MacroDef), | ||
24 | Field(Field), | ||
25 | ModuleDef(ModuleDef), | ||
26 | SelfType(ImplDef), | ||
27 | Local(Local), | ||
28 | TypeParam(TypeParam), | ||
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::TypeParam(it) => Some(it.module(db)), | ||
40 | } | ||
41 | } | ||
42 | |||
43 | pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> { | ||
44 | match self { | ||
45 | Definition::Macro(_) => None, | ||
46 | Definition::Field(sf) => Some(sf.visibility(db)), | ||
47 | Definition::ModuleDef(def) => def.definition_visibility(db), | ||
48 | Definition::SelfType(_) => None, | ||
49 | Definition::Local(_) => None, | ||
50 | Definition::TypeParam(_) => None, | ||
51 | } | ||
52 | } | ||
53 | |||
54 | pub fn name(&self, db: &RootDatabase) -> Option<Name> { | ||
55 | let name = match self { | ||
56 | Definition::Macro(it) => it.name(db)?, | ||
57 | Definition::Field(it) => it.name(db), | ||
58 | Definition::ModuleDef(def) => match def { | ||
59 | hir::ModuleDef::Module(it) => it.name(db)?, | ||
60 | hir::ModuleDef::Function(it) => it.name(db), | ||
61 | hir::ModuleDef::Adt(def) => match def { | ||
62 | hir::Adt::Struct(it) => it.name(db), | ||
63 | hir::Adt::Union(it) => it.name(db), | ||
64 | hir::Adt::Enum(it) => it.name(db), | ||
65 | }, | ||
66 | hir::ModuleDef::EnumVariant(it) => it.name(db), | ||
67 | hir::ModuleDef::Const(it) => it.name(db)?, | ||
68 | hir::ModuleDef::Static(it) => it.name(db)?, | ||
69 | hir::ModuleDef::Trait(it) => it.name(db), | ||
70 | hir::ModuleDef::TypeAlias(it) => it.name(db), | ||
71 | hir::ModuleDef::BuiltinType(_) => return None, | ||
72 | }, | ||
73 | Definition::SelfType(_) => return None, | ||
74 | Definition::Local(it) => it.name(db)?, | ||
75 | Definition::TypeParam(it) => it.name(db), | ||
76 | }; | ||
77 | Some(name) | ||
78 | } | ||
79 | } | ||
80 | |||
81 | #[derive(Debug)] | ||
82 | pub enum NameClass { | ||
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) -> Option<Definition> { | ||
94 | match self { | ||
95 | NameClass::Definition(it) => Some(it), | ||
96 | NameClass::ConstReference(_) => None, | ||
97 | NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)), | ||
98 | } | ||
99 | } | ||
100 | |||
101 | pub fn definition(self) -> Definition { | ||
102 | match self { | ||
103 | NameClass::Definition(it) | NameClass::ConstReference(it) => it, | ||
104 | NameClass::FieldShorthand { local: _, field } => field, | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { | ||
110 | let _p = profile("classify_name"); | ||
111 | |||
112 | let parent = name.syntax().parent()?; | ||
113 | |||
114 | if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) { | ||
115 | if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { | ||
116 | return Some(NameClass::ConstReference(Definition::ModuleDef(def))); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | match_ast! { | ||
121 | match parent { | ||
122 | ast::Rename(it) => { | ||
123 | let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; | ||
124 | let path = use_tree.path()?; | ||
125 | let path_segment = path.segment()?; | ||
126 | let name_ref_class = path_segment | ||
127 | .name_ref() | ||
128 | // The rename might be from a `self` token, so fallback to the name higher | ||
129 | // in the use tree. | ||
130 | .or_else(||{ | ||
131 | if path_segment.self_token().is_none() { | ||
132 | return None; | ||
133 | } | ||
134 | |||
135 | let use_tree = use_tree | ||
136 | .syntax() | ||
137 | .parent() | ||
138 | .as_ref() | ||
139 | // Skip over UseTreeList | ||
140 | .and_then(SyntaxNode::parent) | ||
141 | .and_then(ast::UseTree::cast)?; | ||
142 | let path = use_tree.path()?; | ||
143 | let path_segment = path.segment()?; | ||
144 | path_segment.name_ref() | ||
145 | }) | ||
146 | .and_then(|name_ref| classify_name_ref(sema, &name_ref))?; | ||
147 | |||
148 | Some(NameClass::Definition(name_ref_class.definition())) | ||
149 | }, | ||
150 | ast::IdentPat(it) => { | ||
151 | let local = sema.to_def(&it)?; | ||
152 | |||
153 | if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordPatField::cast) { | ||
154 | if record_field_pat.name_ref().is_none() { | ||
155 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { | ||
156 | let field = Definition::Field(field); | ||
157 | return Some(NameClass::FieldShorthand { local, field }); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | Some(NameClass::Definition(Definition::Local(local))) | ||
163 | }, | ||
164 | ast::RecordField(it) => { | ||
165 | let field: hir::Field = sema.to_def(&it)?; | ||
166 | Some(NameClass::Definition(Definition::Field(field))) | ||
167 | }, | ||
168 | ast::Module(it) => { | ||
169 | let def = sema.to_def(&it)?; | ||
170 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
171 | }, | ||
172 | ast::Struct(it) => { | ||
173 | let def: hir::Struct = sema.to_def(&it)?; | ||
174 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
175 | }, | ||
176 | ast::Union(it) => { | ||
177 | let def: hir::Union = sema.to_def(&it)?; | ||
178 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
179 | }, | ||
180 | ast::Enum(it) => { | ||
181 | let def: hir::Enum = sema.to_def(&it)?; | ||
182 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
183 | }, | ||
184 | ast::Trait(it) => { | ||
185 | let def: hir::Trait = sema.to_def(&it)?; | ||
186 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
187 | }, | ||
188 | ast::Static(it) => { | ||
189 | let def: hir::Static = sema.to_def(&it)?; | ||
190 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
191 | }, | ||
192 | ast::Variant(it) => { | ||
193 | let def: hir::EnumVariant = sema.to_def(&it)?; | ||
194 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
195 | }, | ||
196 | ast::Fn(it) => { | ||
197 | let def: hir::Function = sema.to_def(&it)?; | ||
198 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
199 | }, | ||
200 | ast::Const(it) => { | ||
201 | let def: hir::Const = sema.to_def(&it)?; | ||
202 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
203 | }, | ||
204 | ast::TypeAlias(it) => { | ||
205 | let def: hir::TypeAlias = sema.to_def(&it)?; | ||
206 | Some(NameClass::Definition(Definition::ModuleDef(def.into()))) | ||
207 | }, | ||
208 | ast::MacroCall(it) => { | ||
209 | let def = sema.to_def(&it)?; | ||
210 | Some(NameClass::Definition(Definition::Macro(def))) | ||
211 | }, | ||
212 | ast::TypeParam(it) => { | ||
213 | let def = sema.to_def(&it)?; | ||
214 | Some(NameClass::Definition(Definition::TypeParam(def))) | ||
215 | }, | ||
216 | _ => None, | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | #[derive(Debug)] | ||
222 | pub enum NameRefClass { | ||
223 | Definition(Definition), | ||
224 | FieldShorthand { local: Local, field: Definition }, | ||
225 | } | ||
226 | |||
227 | impl NameRefClass { | ||
228 | pub fn definition(self) -> Definition { | ||
229 | match self { | ||
230 | NameRefClass::Definition(def) => def, | ||
231 | NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local), | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // Note: we don't have unit-tests for this rather important function. | ||
237 | // It is primarily exercised via goto definition tests in `ra_ide`. | ||
238 | pub fn classify_name_ref( | ||
239 | sema: &Semantics<RootDatabase>, | ||
240 | name_ref: &ast::NameRef, | ||
241 | ) -> Option<NameRefClass> { | ||
242 | let _p = profile("classify_name_ref"); | ||
243 | |||
244 | let parent = name_ref.syntax().parent()?; | ||
245 | |||
246 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | ||
247 | if let Some(func) = sema.resolve_method_call(&method_call) { | ||
248 | return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | ||
253 | if let Some(field) = sema.resolve_field(&field_expr) { | ||
254 | return Some(NameRefClass::Definition(Definition::Field(field))); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { | ||
259 | if let Some((field, local)) = sema.resolve_record_field(&record_field) { | ||
260 | let field = Definition::Field(field); | ||
261 | let res = match local { | ||
262 | None => NameRefClass::Definition(field), | ||
263 | Some(local) => NameRefClass::FieldShorthand { field, local }, | ||
264 | }; | ||
265 | return Some(res); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | if let Some(record_field_pat) = ast::RecordPatField::cast(parent.clone()) { | ||
270 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { | ||
271 | let field = Definition::Field(field); | ||
272 | return Some(NameRefClass::Definition(field)); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | if ast::AssocTypeArg::cast(parent.clone()).is_some() { | ||
277 | // `Trait<Assoc = Ty>` | ||
278 | // ^^^^^ | ||
279 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; | ||
280 | let resolved = sema.resolve_path(&path)?; | ||
281 | if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved { | ||
282 | if let Some(ty) = tr | ||
283 | .items(sema.db) | ||
284 | .iter() | ||
285 | .filter_map(|assoc| match assoc { | ||
286 | hir::AssocItem::TypeAlias(it) => Some(*it), | ||
287 | _ => None, | ||
288 | }) | ||
289 | .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) | ||
290 | { | ||
291 | return Some(NameRefClass::Definition(Definition::ModuleDef( | ||
292 | ModuleDef::TypeAlias(ty), | ||
293 | ))); | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | ||
299 | if let Some(path) = macro_call.path() { | ||
300 | if path.qualifier().is_none() { | ||
301 | // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment | ||
302 | // paths are handled below (allowing `log<|>::info!` to resolve to the log crate). | ||
303 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | ||
304 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; | ||
311 | let resolved = sema.resolve_path(&path)?; | ||
312 | Some(NameRefClass::Definition(resolved.into())) | ||
313 | } | ||
314 | |||
315 | impl From<PathResolution> for Definition { | ||
316 | fn from(path_resolution: PathResolution) -> Self { | ||
317 | match path_resolution { | ||
318 | PathResolution::Def(def) => Definition::ModuleDef(def), | ||
319 | PathResolution::AssocItem(item) => { | ||
320 | let def = match item { | ||
321 | hir::AssocItem::Function(it) => it.into(), | ||
322 | hir::AssocItem::Const(it) => it.into(), | ||
323 | hir::AssocItem::TypeAlias(it) => it.into(), | ||
324 | }; | ||
325 | Definition::ModuleDef(def) | ||
326 | } | ||
327 | PathResolution::Local(local) => Definition::Local(local), | ||
328 | PathResolution::TypeParam(par) => Definition::TypeParam(par), | ||
329 | PathResolution::Macro(def) => Definition::Macro(def), | ||
330 | PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), | ||
331 | } | ||
332 | } | ||
333 | } | ||