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.
6 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
9 db::HirDatabase, Crate, Field, GenericParam, HasAttrs, HasVisibility, Impl, Label, Local,
10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
13 ast::{self, AstNode, PathSegmentKind},
14 match_ast, SyntaxKind, SyntaxNode,
17 use crate::RootDatabase;
19 // FIXME: a more precise name would probably be `Symbol`?
20 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
27 GenericParam(GenericParam),
32 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
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::GenericParam(it) => Some(it.module(db)),
40 Definition::Label(it) => Some(it.module(db)),
44 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
46 Definition::Macro(_) => None,
47 Definition::Field(sf) => Some(sf.visibility(db)),
48 Definition::ModuleDef(def) => def.definition_visibility(db),
49 Definition::SelfType(_) => None,
50 Definition::Local(_) => None,
51 Definition::GenericParam(_) => None,
52 Definition::Label(_) => None,
56 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
57 let name = match self {
58 Definition::Macro(it) => it.name(db)?,
59 Definition::Field(it) => it.name(db),
60 Definition::ModuleDef(def) => match def {
61 hir::ModuleDef::Module(it) => it.name(db)?,
62 hir::ModuleDef::Function(it) => it.name(db),
63 hir::ModuleDef::Adt(def) => match def {
64 hir::Adt::Struct(it) => it.name(db),
65 hir::Adt::Union(it) => it.name(db),
66 hir::Adt::Enum(it) => it.name(db),
68 hir::ModuleDef::Variant(it) => it.name(db),
69 hir::ModuleDef::Const(it) => it.name(db)?,
70 hir::ModuleDef::Static(it) => it.name(db)?,
71 hir::ModuleDef::Trait(it) => it.name(db),
72 hir::ModuleDef::TypeAlias(it) => it.name(db),
73 hir::ModuleDef::BuiltinType(it) => it.name(),
75 Definition::SelfType(_) => return None,
76 Definition::Local(it) => it.name(db)?,
77 Definition::GenericParam(it) => it.name(db),
78 Definition::Label(it) => it.name(db),
87 Definition(Definition),
88 /// `None` in `if let None = Some(82) {}`.
89 ConstReference(Definition),
90 /// `field` in `if let Foo { field } = foo`.
93 field_ref: Definition,
98 /// `Definition` defined by this name.
99 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
100 let res = match self {
101 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
102 NameClass::Definition(it) => it,
103 NameClass::ConstReference(_) => return None,
104 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
105 Definition::Local(local_def)
111 /// `Definition` referenced or defined by this name.
112 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
114 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
115 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
116 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
120 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
121 let _p = profile::span("classify_name");
123 let parent = name.syntax().parent()?;
125 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
126 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
127 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
134 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
135 let path = use_tree.path()?;
136 let path_segment = path.segment()?;
137 let name_ref_class = path_segment
141 // The rename might be from a `self` token, so fallback to the name higher
143 PathSegmentKind::SelfKw => {
144 let use_tree = use_tree
148 // Skip over UseTreeList
149 .and_then(SyntaxNode::parent)
150 .and_then(ast::UseTree::cast)?;
151 let path = use_tree.path()?;
152 let path_segment = path.segment()?;
153 path_segment.name_ref()
155 PathSegmentKind::Name(name_ref) => Some(name_ref),
159 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
161 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
163 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
164 let resolved = sema.resolve_extern_crate(&extern_crate)?;
165 Some(NameClass::ExternCrate(resolved))
168 ast::IdentPat(it) => {
169 let local = sema.to_def(&it)?;
171 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
172 if record_pat_field.name_ref().is_none() {
173 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
174 let field = Definition::Field(field);
175 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
180 Some(NameClass::Definition(Definition::Local(local)))
182 ast::SelfParam(it) => {
183 let def = sema.to_def(&it)?;
184 Some(NameClass::Definition(Definition::Local(def)))
186 ast::RecordField(it) => {
187 let field: hir::Field = sema.to_def(&it)?;
188 Some(NameClass::Definition(Definition::Field(field)))
191 let def = sema.to_def(&it)?;
192 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
195 let def: hir::Struct = sema.to_def(&it)?;
196 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
199 let def: hir::Union = sema.to_def(&it)?;
200 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
203 let def: hir::Enum = sema.to_def(&it)?;
204 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
207 let def: hir::Trait = sema.to_def(&it)?;
208 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
211 let def: hir::Static = sema.to_def(&it)?;
212 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
214 ast::Variant(it) => {
215 let def: hir::Variant = sema.to_def(&it)?;
216 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
219 let def: hir::Function = sema.to_def(&it)?;
220 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
223 let def: hir::Const = sema.to_def(&it)?;
224 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
226 ast::TypeAlias(it) => {
227 let def: hir::TypeAlias = sema.to_def(&it)?;
228 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
231 let def = sema.to_def(&it)?;
232 Some(NameClass::Definition(Definition::Macro(def)))
234 ast::TypeParam(it) => {
235 let def = sema.to_def(&it)?;
236 Some(NameClass::Definition(Definition::GenericParam(def.into())))
238 ast::ConstParam(it) => {
239 let def = sema.to_def(&it)?;
240 Some(NameClass::Definition(Definition::GenericParam(def.into())))
247 pub fn classify_lifetime(
248 sema: &Semantics<RootDatabase>,
249 lifetime: &ast::Lifetime,
250 ) -> Option<NameClass> {
251 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
252 let parent = lifetime.syntax().parent()?;
256 ast::LifetimeParam(it) => {
257 let def = sema.to_def(&it)?;
258 Some(NameClass::Definition(Definition::GenericParam(def.into())))
261 let def = sema.to_def(&it)?;
262 Some(NameClass::Definition(Definition::Label(def)))
271 pub enum NameRefClass {
273 Definition(Definition),
274 FieldShorthand { local_ref: Local, field_ref: Definition },
278 /// `Definition`, which this name refers to.
279 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
281 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
282 NameRefClass::Definition(def) => def,
283 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
284 // FIXME: this is inherently ambiguous -- this name refers to
285 // two different defs....
286 Definition::Local(local_ref)
291 // Note: we don't have unit-tests for this rather important function.
292 // It is primarily exercised via goto definition tests in `ide`.
294 sema: &Semantics<RootDatabase>,
295 name_ref: &ast::NameRef,
296 ) -> Option<NameRefClass> {
297 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
299 let parent = name_ref.syntax().parent()?;
301 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
302 if let Some(func) = sema.resolve_method_call(&method_call) {
303 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
307 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
308 if let Some(field) = sema.resolve_field(&field_expr) {
309 return Some(NameRefClass::Definition(Definition::Field(field)));
313 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
314 if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
315 let field = Definition::Field(field);
316 let res = match local {
317 None => NameRefClass::Definition(field),
319 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
326 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
327 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
328 let field = Definition::Field(field);
329 return Some(NameRefClass::Definition(field));
333 if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
334 if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
335 // `Trait<Assoc = Ty>`
337 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
338 let resolved = sema.resolve_path(&path)?;
339 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
340 // FIXME: resolve in supertraits
344 .filter_map(|assoc| match assoc {
345 hir::AssocItem::TypeAlias(it) => Some(*it),
348 .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
350 return Some(NameRefClass::Definition(Definition::ModuleDef(
351 ModuleDef::TypeAlias(ty),
360 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
361 if path.qualifier().is_none() {
362 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
363 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
364 // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
365 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
366 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
371 if let Some(resolved) = sema.resolve_path(&path) {
372 if path.syntax().parent().and_then(ast::Attr::cast).is_some() {
373 if let PathResolution::Def(ModuleDef::Function(func)) = resolved {
374 if func.attrs(sema.db).by_key("proc_macro_attribute").exists() {
375 return Some(NameRefClass::Definition(resolved.into()));
379 return Some(NameRefClass::Definition(resolved.into()));
384 let extern_crate = ast::ExternCrate::cast(parent)?;
385 let resolved = sema.resolve_extern_crate(&extern_crate)?;
386 Some(NameRefClass::ExternCrate(resolved))
389 pub fn classify_lifetime(
390 sema: &Semantics<RootDatabase>,
391 lifetime: &ast::Lifetime,
392 ) -> Option<NameRefClass> {
393 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
394 let parent = lifetime.syntax().parent()?;
395 match parent.kind() {
396 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
397 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
399 SyntaxKind::LIFETIME_ARG
400 | SyntaxKind::SELF_PARAM
401 | SyntaxKind::TYPE_BOUND
402 | SyntaxKind::WHERE_PRED
403 | SyntaxKind::REF_TYPE => sema
404 .resolve_lifetime_param(lifetime)
405 .map(GenericParam::LifetimeParam)
406 .map(Definition::GenericParam)
407 .map(NameRefClass::Definition),
408 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
409 // if our lifetime is in a LifetimeParam without being the constrained lifetime
410 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
413 sema.resolve_lifetime_param(lifetime)
414 .map(GenericParam::LifetimeParam)
415 .map(Definition::GenericParam)
416 .map(NameRefClass::Definition)
423 impl From<PathResolution> for Definition {
424 fn from(path_resolution: PathResolution) -> Self {
425 match path_resolution {
426 PathResolution::Def(def) => Definition::ModuleDef(def),
427 PathResolution::AssocItem(item) => {
428 let def = match item {
429 hir::AssocItem::Function(it) => it.into(),
430 hir::AssocItem::Const(it) => it.into(),
431 hir::AssocItem::TypeAlias(it) => it.into(),
433 Definition::ModuleDef(def)
435 PathResolution::Local(local) => Definition::Local(local),
436 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
437 PathResolution::Macro(def) => Definition::Macro(def),
438 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
439 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),