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.rs333
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
8use hir::{
9 Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
10 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 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) -> 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
109pub 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)]
222pub enum NameRefClass {
223 Definition(Definition),
224 FieldShorthand { local: Local, field: Definition },
225}
226
227impl 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`.
238pub 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(&macro_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
315impl 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}