aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_db/src/defs.rs
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-08-24 10:19:53 +0100
committerZac Pullar-Strecker <[email protected]>2020-08-24 10:20:13 +0100
commit7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch)
treebdb47765991cb973b2cd5481a088fac636bd326c /crates/ra_ide_db/src/defs.rs
parentca464650eeaca6195891199a93f4f76cf3e7e697 (diff)
parente65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff)
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/ra_ide_db/src/defs.rs')
-rw-r--r--crates/ra_ide_db/src/defs.rs314
1 files changed, 0 insertions, 314 deletions
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
deleted file mode 100644
index df56f2d9e..000000000
--- a/crates/ra_ide_db/src/defs.rs
+++ /dev/null
@@ -1,314 +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,
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::BindPat::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 = path_segment.name_ref()?;
127 let name_ref_class = classify_name_ref(sema, &name_ref)?;
128
129 Some(NameClass::Definition(name_ref_class.definition()))
130 },
131 ast::BindPat(it) => {
132 let local = sema.to_def(&it)?;
133
134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
135 if record_field_pat.name_ref().is_none() {
136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
140 }
141 }
142
143 Some(NameClass::Definition(Definition::Local(local)))
144 },
145 ast::RecordField(it) => {
146 let field: hir::Field = sema.to_def(&it)?;
147 Some(NameClass::Definition(Definition::Field(field)))
148 },
149 ast::Module(it) => {
150 let def = sema.to_def(&it)?;
151 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
152 },
153 ast::Struct(it) => {
154 let def: hir::Struct = sema.to_def(&it)?;
155 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
156 },
157 ast::Union(it) => {
158 let def: hir::Union = sema.to_def(&it)?;
159 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
160 },
161 ast::Enum(it) => {
162 let def: hir::Enum = sema.to_def(&it)?;
163 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
164 },
165 ast::Trait(it) => {
166 let def: hir::Trait = sema.to_def(&it)?;
167 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
168 },
169 ast::Static(it) => {
170 let def: hir::Static = sema.to_def(&it)?;
171 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
172 },
173 ast::Variant(it) => {
174 let def: hir::EnumVariant = sema.to_def(&it)?;
175 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
176 },
177 ast::Fn(it) => {
178 let def: hir::Function = sema.to_def(&it)?;
179 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
180 },
181 ast::Const(it) => {
182 let def: hir::Const = sema.to_def(&it)?;
183 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
184 },
185 ast::TypeAlias(it) => {
186 let def: hir::TypeAlias = sema.to_def(&it)?;
187 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
188 },
189 ast::MacroCall(it) => {
190 let def = sema.to_def(&it)?;
191 Some(NameClass::Definition(Definition::Macro(def)))
192 },
193 ast::TypeParam(it) => {
194 let def = sema.to_def(&it)?;
195 Some(NameClass::Definition(Definition::TypeParam(def)))
196 },
197 _ => None,
198 }
199 }
200}
201
202#[derive(Debug)]
203pub enum NameRefClass {
204 Definition(Definition),
205 FieldShorthand { local: Local, field: Definition },
206}
207
208impl NameRefClass {
209 pub fn definition(self) -> Definition {
210 match self {
211 NameRefClass::Definition(def) => def,
212 NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local),
213 }
214 }
215}
216
217// Note: we don't have unit-tests for this rather important function.
218// It is primarily exercised via goto definition tests in `ra_ide`.
219pub fn classify_name_ref(
220 sema: &Semantics<RootDatabase>,
221 name_ref: &ast::NameRef,
222) -> Option<NameRefClass> {
223 let _p = profile("classify_name_ref");
224
225 let parent = name_ref.syntax().parent()?;
226
227 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
228 if let Some(func) = sema.resolve_method_call(&method_call) {
229 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
230 }
231 }
232
233 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
234 if let Some(field) = sema.resolve_field(&field_expr) {
235 return Some(NameRefClass::Definition(Definition::Field(field)));
236 }
237 }
238
239 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
240 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
241 let field = Definition::Field(field);
242 let res = match local {
243 None => NameRefClass::Definition(field),
244 Some(local) => NameRefClass::FieldShorthand { field, local },
245 };
246 return Some(res);
247 }
248 }
249
250 if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
251 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
252 let field = Definition::Field(field);
253 return Some(NameRefClass::Definition(field));
254 }
255 }
256
257 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
258 // `Trait<Assoc = Ty>`
259 // ^^^^^
260 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
261 let resolved = sema.resolve_path(&path)?;
262 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
263 if let Some(ty) = tr
264 .items(sema.db)
265 .iter()
266 .filter_map(|assoc| match assoc {
267 hir::AssocItem::TypeAlias(it) => Some(*it),
268 _ => None,
269 })
270 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
271 {
272 return Some(NameRefClass::Definition(Definition::ModuleDef(
273 ModuleDef::TypeAlias(ty),
274 )));
275 }
276 }
277 }
278
279 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
280 if let Some(path) = macro_call.path() {
281 if path.qualifier().is_none() {
282 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
283 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
284 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
285 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
286 }
287 }
288 }
289 }
290
291 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
292 let resolved = sema.resolve_path(&path)?;
293 Some(NameRefClass::Definition(resolved.into()))
294}
295
296impl From<PathResolution> for Definition {
297 fn from(path_resolution: PathResolution) -> Self {
298 match path_resolution {
299 PathResolution::Def(def) => Definition::ModuleDef(def),
300 PathResolution::AssocItem(item) => {
301 let def = match item {
302 hir::AssocItem::Function(it) => it.into(),
303 hir::AssocItem::Const(it) => it.into(),
304 hir::AssocItem::TypeAlias(it) => it.into(),
305 };
306 Definition::ModuleDef(def)
307 }
308 PathResolution::Local(local) => Definition::Local(local),
309 PathResolution::TypeParam(par) => Definition::TypeParam(par),
310 PathResolution::Macro(def) => Definition::Macro(def),
311 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
312 }
313 }
314}