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