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).
8 use arrayvec::ArrayVec;
10 Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Field, Function, GenericParam,
11 HasVisibility, Impl, ItemInNs, Label, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
12 Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
17 match_ast, AstToken, SyntaxKind, SyntaxNode, SyntaxToken,
20 use crate::RootDatabase;
22 // FIXME: a more precise name would probably be `Symbol`?
23 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
35 BuiltinType(BuiltinType),
38 GenericParam(GenericParam),
40 BuiltinAttr(BuiltinAttr),
41 ToolModule(ToolModule),
45 pub fn canonical_module_path(&self, db: &RootDatabase) -> Option<impl Iterator<Item = Module>> {
46 self.module(db).map(|it| it.path_to_root(db).into_iter().rev())
49 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
50 let module = match self {
51 Definition::Macro(it) => it.module(db)?,
52 Definition::Module(it) => it.parent(db)?,
53 Definition::Field(it) => it.parent_def(db).module(db),
54 Definition::Function(it) => it.module(db),
55 Definition::Adt(it) => it.module(db),
56 Definition::Const(it) => it.module(db),
57 Definition::Static(it) => it.module(db),
58 Definition::Trait(it) => it.module(db),
59 Definition::TypeAlias(it) => it.module(db),
60 Definition::Variant(it) => it.module(db),
61 Definition::SelfType(it) => it.module(db),
62 Definition::Local(it) => it.module(db),
63 Definition::GenericParam(it) => it.module(db),
64 Definition::Label(it) => it.module(db),
65 Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
72 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
73 let vis = match self {
74 Definition::Field(sf) => sf.visibility(db),
75 Definition::Module(it) => it.visibility(db),
76 Definition::Function(it) => it.visibility(db),
77 Definition::Adt(it) => it.visibility(db),
78 Definition::Const(it) => it.visibility(db),
79 Definition::Static(it) => it.visibility(db),
80 Definition::Trait(it) => it.visibility(db),
81 Definition::TypeAlias(it) => it.visibility(db),
82 Definition::Variant(it) => it.visibility(db),
83 Definition::BuiltinType(_) => Visibility::Public,
84 Definition::Macro(_) => return None,
85 Definition::BuiltinAttr(_)
86 | Definition::ToolModule(_)
87 | Definition::SelfType(_)
88 | Definition::Local(_)
89 | Definition::GenericParam(_)
90 | Definition::Label(_) => return None,
95 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
96 let name = match self {
97 Definition::Macro(it) => it.name(db)?,
98 Definition::Field(it) => it.name(db),
99 Definition::Module(it) => it.name(db)?,
100 Definition::Function(it) => it.name(db),
101 Definition::Adt(it) => it.name(db),
102 Definition::Variant(it) => it.name(db),
103 Definition::Const(it) => it.name(db)?,
104 Definition::Static(it) => it.name(db),
105 Definition::Trait(it) => it.name(db),
106 Definition::TypeAlias(it) => it.name(db),
107 Definition::BuiltinType(it) => it.name(),
108 Definition::SelfType(_) => return None,
109 Definition::Local(it) => it.name(db)?,
110 Definition::GenericParam(it) => it.name(db),
111 Definition::Label(it) => it.name(db),
112 Definition::BuiltinAttr(_) => return None, // FIXME
113 Definition::ToolModule(_) => return None, // FIXME
119 pub enum IdentClass {
120 NameClass(NameClass),
121 NameRefClass(NameRefClass),
125 pub fn classify_node(sema: &Semantics<RootDatabase>, node: &SyntaxNode) -> Option<IdentClass> {
128 ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass),
129 ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(IdentClass::NameRefClass),
130 ast::Lifetime(lifetime) => {
131 NameClass::classify_lifetime(sema, &lifetime)
132 .map(IdentClass::NameClass)
133 .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
140 pub fn classify_token(
141 sema: &Semantics<RootDatabase>,
143 ) -> Option<IdentClass> {
144 let parent = token.parent()?;
145 // resolve derives if possible
146 if let Some(ident) = ast::Ident::cast(token.clone()) {
147 let attr = ast::TokenTree::cast(parent.clone())
148 .and_then(|tt| tt.parent_meta())
149 .and_then(|meta| meta.parent_attr());
150 if let Some(attr) = attr {
151 return NameRefClass::classify_derive(sema, &attr, &ident)
152 .map(IdentClass::NameRefClass);
155 Self::classify_node(sema, &parent)
158 pub fn definitions(self) -> ArrayVec<Definition, 2> {
159 let mut res = ArrayVec::new();
161 IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
164 IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
165 res.push(Definition::Local(local_def));
166 res.push(Definition::Field(field_ref));
168 IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
169 IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
170 res.push(Definition::Local(local_ref));
171 res.push(Definition::Field(field_ref));
178 /// On a first blush, a single `ast::Name` defines a single definition at some
179 /// scope. That is, that, by just looking at the syntactical category, we can
180 /// unambiguously define the semantic category.
182 /// Sadly, that's not 100% true, there are special cases. To make sure that
183 /// callers handle all the special cases correctly via exhaustive matching, we
184 /// add a [`NameClass`] enum which lists all of them!
186 /// A model special case is `None` constant in pattern.
189 Definition(Definition),
190 /// `None` in `if let None = Some(82) {}`.
191 /// Syntactically, it is a name, but semantically it is a reference.
192 ConstReference(Definition),
193 /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces
194 /// a definition into a local scope, and refers to an existing definition.
202 /// `Definition` defined by this name.
203 pub fn defined(self) -> Option<Definition> {
204 let res = match self {
205 NameClass::Definition(it) => it,
206 NameClass::ConstReference(_) => return None,
207 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
208 Definition::Local(local_def)
214 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
215 let _p = profile::span("classify_name");
217 let parent = name.syntax().parent()?;
219 let def = if let Some(item) = ast::Item::cast(parent.clone()) {
221 ast::Item::MacroRules(it) => {
222 Definition::Macro(sema.to_def(&ast::Macro::MacroRules(it))?)
224 ast::Item::MacroDef(it) => {
225 Definition::Macro(sema.to_def(&ast::Macro::MacroDef(it))?)
227 ast::Item::Const(it) => Definition::Const(sema.to_def(&it)?),
228 ast::Item::Fn(it) => {
229 let def = sema.to_def(&it)?;
230 def.as_proc_macro(sema.db)
231 .map(Definition::Macro)
232 .unwrap_or(Definition::Function(def))
234 ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
235 ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
236 ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
237 ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
238 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
239 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
240 ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
243 } else if let Some(it) = ast::IdentPat::cast(parent.clone()) {
244 if let Some(def) = sema.resolve_bind_pat_to_const(&it) {
245 return Some(NameClass::ConstReference(Definition::from(def)));
248 let local = sema.to_def(&it)?;
249 let pat_parent = it.syntax().parent();
250 if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
251 if record_pat_field.name_ref().is_none() {
252 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
253 return Some(NameClass::PatFieldShorthand {
261 Definition::Local(local)
262 } else if let Some(it) = ast::Rename::cast(parent.clone()) {
263 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
264 let path = use_tree.path()?;
265 let path_segment = path.segment()?;
266 let name_ref = path_segment.name_ref()?;
267 let name_ref = if name_ref.self_token().is_some() {
272 // Skip over UseTreeList
274 let use_tree = it.parent().and_then(ast::UseTree::cast)?;
275 let path = use_tree.path()?;
276 let path_segment = path.segment()?;
277 path_segment.name_ref()
283 let name_ref_class = NameRefClass::classify(sema, &name_ref)?;
285 match name_ref_class {
286 NameRefClass::Definition(def) => def,
287 NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
288 Definition::Field(field_ref)
292 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
293 let krate = sema.resolve_extern_crate(&extern_crate)?;
294 let root_module = krate.root_module(sema.db);
295 Definition::Module(root_module)
300 ast::SelfParam(it) => Definition::Local(sema.to_def(&it)?),
301 ast::RecordField(it) => Definition::Field(sema.to_def(&it)?),
302 ast::Variant(it) => Definition::Variant(sema.to_def(&it)?),
303 ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
304 ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
310 Some(NameClass::Definition(def))
313 pub fn classify_lifetime(
314 sema: &Semantics<RootDatabase>,
315 lifetime: &ast::Lifetime,
316 ) -> Option<NameClass> {
317 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
318 let parent = lifetime.syntax().parent()?;
322 ast::LifetimeParam(it) => {
323 let def = sema.to_def(&it)?;
324 Some(NameClass::Definition(Definition::GenericParam(def.into())))
327 let def = sema.to_def(&it)?;
328 Some(NameClass::Definition(Definition::Label(def)))
336 /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
337 /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
338 /// reference most of the time, but there are a couple of annoying exceptions.
340 /// A model special case is field shorthand syntax, which uses a single
341 /// reference to point to two different defs.
343 pub enum NameRefClass {
344 Definition(Definition),
345 FieldShorthand { local_ref: Local, field_ref: Field },
349 // Note: we don't have unit-tests for this rather important function.
350 // It is primarily exercised via goto definition tests in `ide`.
352 sema: &Semantics<RootDatabase>,
353 name_ref: &ast::NameRef,
354 ) -> Option<NameRefClass> {
355 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
357 let parent = name_ref.syntax().parent()?;
359 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
360 if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
361 let res = match local {
362 None => NameRefClass::Definition(Definition::Field(field)),
364 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
371 if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) {
372 if path.parent_path().is_none() {
373 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
374 // Only use this to resolve to macro calls for last segments as qualifiers resolve
376 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
377 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
381 return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
386 ast::MethodCallExpr(method_call) => {
387 sema.resolve_method_call(&method_call)
388 .map(Definition::Function)
389 .map(NameRefClass::Definition)
391 ast::FieldExpr(field_expr) => {
392 sema.resolve_field(&field_expr)
393 .map(Definition::Field)
394 .map(NameRefClass::Definition)
396 ast::RecordPatField(record_pat_field) => {
397 sema.resolve_record_pat_field(&record_pat_field)
398 .map(Definition::Field)
399 .map(NameRefClass::Definition)
401 ast::AssocTypeArg(_) => {
402 // `Trait<Assoc = Ty>`
404 let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
405 let resolved = sema.resolve_path(&containing_path)?;
406 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
407 // FIXME: resolve in supertraits
411 .filter_map(|&assoc| match assoc {
412 hir::AssocItem::TypeAlias(it) => Some(it),
415 .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str())
417 return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
422 ast::ExternCrate(extern_crate) => {
423 let krate = sema.resolve_extern_crate(&extern_crate)?;
424 let root_module = krate.root_module(sema.db);
425 Some(NameRefClass::Definition(Definition::Module(root_module)))
432 pub fn classify_lifetime(
433 sema: &Semantics<RootDatabase>,
434 lifetime: &ast::Lifetime,
435 ) -> Option<NameRefClass> {
436 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
437 let parent = lifetime.syntax().parent()?;
438 match parent.kind() {
439 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
440 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
442 SyntaxKind::LIFETIME_ARG
443 | SyntaxKind::SELF_PARAM
444 | SyntaxKind::TYPE_BOUND
445 | SyntaxKind::WHERE_PRED
446 | SyntaxKind::REF_TYPE => sema
447 .resolve_lifetime_param(lifetime)
448 .map(GenericParam::LifetimeParam)
449 .map(Definition::GenericParam)
450 .map(NameRefClass::Definition),
451 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
452 // if our lifetime is in a LifetimeParam without being the constrained lifetime
453 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
456 sema.resolve_lifetime_param(lifetime)
457 .map(GenericParam::LifetimeParam)
458 .map(Definition::GenericParam)
459 .map(NameRefClass::Definition)
465 pub fn classify_derive(
466 sema: &Semantics<RootDatabase>,
469 ) -> Option<NameRefClass> {
470 sema.resolve_derive_ident(&attr, &ident).map(Definition::from).map(NameRefClass::Definition)
475 Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
480 impl From<Impl> for Definition {
481 fn from(impl_: Impl) -> Self {
482 Definition::SelfType(impl_)
486 impl AsAssocItem for Definition {
487 fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
489 Definition::Function(it) => it.as_assoc_item(db),
490 Definition::Const(it) => it.as_assoc_item(db),
491 Definition::TypeAlias(it) => it.as_assoc_item(db),
497 impl From<AssocItem> for Definition {
498 fn from(assoc_item: AssocItem) -> Self {
500 AssocItem::Function(it) => Definition::Function(it),
501 AssocItem::Const(it) => Definition::Const(it),
502 AssocItem::TypeAlias(it) => Definition::TypeAlias(it),
507 impl From<PathResolution> for Definition {
508 fn from(path_resolution: PathResolution) -> Self {
509 match path_resolution {
510 PathResolution::Def(def) => def.into(),
511 PathResolution::AssocItem(item) => {
512 let def: ModuleDef = match item {
513 hir::AssocItem::Function(it) => it.into(),
514 hir::AssocItem::Const(it) => it.into(),
515 hir::AssocItem::TypeAlias(it) => it.into(),
519 PathResolution::Local(local) => Definition::Local(local),
520 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
521 PathResolution::Macro(def) => Definition::Macro(def),
522 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
523 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
524 PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
525 PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
530 impl From<ModuleDef> for Definition {
531 fn from(def: ModuleDef) -> Self {
533 ModuleDef::Module(it) => Definition::Module(it),
534 ModuleDef::Function(it) => Definition::Function(it),
535 ModuleDef::Adt(it) => Definition::Adt(it),
536 ModuleDef::Variant(it) => Definition::Variant(it),
537 ModuleDef::Const(it) => Definition::Const(it),
538 ModuleDef::Static(it) => Definition::Static(it),
539 ModuleDef::Trait(it) => Definition::Trait(it),
540 ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
541 ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
546 impl From<Definition> for Option<ItemInNs> {
547 fn from(def: Definition) -> Self {
548 let item = match def {
549 Definition::Module(it) => ModuleDef::Module(it),
550 Definition::Function(it) => ModuleDef::Function(it),
551 Definition::Adt(it) => ModuleDef::Adt(it),
552 Definition::Variant(it) => ModuleDef::Variant(it),
553 Definition::Const(it) => ModuleDef::Const(it),
554 Definition::Static(it) => ModuleDef::Static(it),
555 Definition::Trait(it) => ModuleDef::Trait(it),
556 Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
557 Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
560 Some(ItemInNs::from(item))