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