aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/defs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/defs.rs')
-rw-r--r--crates/ide_db/src/defs.rs348
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..0d0affc27
--- /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
8use hir::{
9 db::HirDatabase, Crate, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef,
10 Name, PathResolution, Semantics, TypeParam, Visibility,
11};
12use syntax::{
13 ast::{self, AstNode},
14 match_ast, SyntaxNode,
15};
16
17use crate::RootDatabase;
18
19// FIXME: a more precise name would probably be `Symbol`?
20#[derive(Debug, PartialEq, Eq, Copy, Clone)]
21pub enum Definition {
22 Macro(MacroDef),
23 Field(Field),
24 ModuleDef(ModuleDef),
25 SelfType(ImplDef),
26 Local(Local),
27 TypeParam(TypeParam),
28}
29
30impl 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)]
81pub 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
92impl 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
111pub 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)]
229pub enum NameRefClass {
230 ExternCrate(Crate),
231 Definition(Definition),
232 FieldShorthand { local: Local, field: Definition },
233}
234
235impl 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`.
247pub 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
330impl 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}