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, Crate, DeriveHelper, Field,
11 Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
12 Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
17 match_ast, 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 DeriveHelper(DeriveHelper),
41 BuiltinAttr(BuiltinAttr),
42 ToolModule(ToolModule),
46 pub fn canonical_module_path(&self, db: &RootDatabase) -> Option<impl Iterator<Item = Module>> {
47 self.module(db).map(|it| it.path_to_root(db).into_iter().rev())
50 pub fn krate(&self, db: &RootDatabase) -> Option<Crate> {
52 Definition::Module(m) => m.krate(),
53 _ => self.module(db)?.krate(),
57 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
58 let module = match self {
59 Definition::Macro(it) => it.module(db),
60 Definition::Module(it) => it.parent(db)?,
61 Definition::Field(it) => it.parent_def(db).module(db),
62 Definition::Function(it) => it.module(db),
63 Definition::Adt(it) => it.module(db),
64 Definition::Const(it) => it.module(db),
65 Definition::Static(it) => it.module(db),
66 Definition::Trait(it) => it.module(db),
67 Definition::TypeAlias(it) => it.module(db),
68 Definition::Variant(it) => it.module(db),
69 Definition::SelfType(it) => it.module(db),
70 Definition::Local(it) => it.module(db),
71 Definition::GenericParam(it) => it.module(db),
72 Definition::Label(it) => it.module(db),
73 Definition::DeriveHelper(it) => it.derive().module(db),
74 Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
81 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
82 let vis = match self {
83 Definition::Field(sf) => sf.visibility(db),
84 Definition::Module(it) => it.visibility(db),
85 Definition::Function(it) => it.visibility(db),
86 Definition::Adt(it) => it.visibility(db),
87 Definition::Const(it) => it.visibility(db),
88 Definition::Static(it) => it.visibility(db),
89 Definition::Trait(it) => it.visibility(db),
90 Definition::TypeAlias(it) => it.visibility(db),
91 Definition::Variant(it) => it.visibility(db),
92 Definition::BuiltinType(_) => Visibility::Public,
93 Definition::Macro(_) => return None,
94 Definition::BuiltinAttr(_)
95 | Definition::ToolModule(_)
96 | Definition::SelfType(_)
97 | Definition::Local(_)
98 | Definition::GenericParam(_)
99 | Definition::Label(_)
100 | Definition::DeriveHelper(_) => return None,
105 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
106 let name = match self {
107 Definition::Macro(it) => it.name(db),
108 Definition::Field(it) => it.name(db),
109 Definition::Module(it) => it.name(db)?,
110 Definition::Function(it) => it.name(db),
111 Definition::Adt(it) => it.name(db),
112 Definition::Variant(it) => it.name(db),
113 Definition::Const(it) => it.name(db)?,
114 Definition::Static(it) => it.name(db),
115 Definition::Trait(it) => it.name(db),
116 Definition::TypeAlias(it) => it.name(db),
117 Definition::BuiltinType(it) => it.name(),
118 Definition::SelfType(_) => return None,
119 Definition::Local(it) => it.name(db),
120 Definition::GenericParam(it) => it.name(db),
121 Definition::Label(it) => it.name(db),
122 Definition::BuiltinAttr(_) => return None, // FIXME
123 Definition::ToolModule(_) => return None, // FIXME
124 Definition::DeriveHelper(it) => it.name(db),
130 // FIXME: IdentClass as a name no longer fits
132 pub enum IdentClass {
133 NameClass(NameClass),
134 NameRefClass(NameRefClass),
135 Operator(OperatorClass),
139 pub fn classify_node(
140 sema: &Semantics<'_, RootDatabase>,
142 ) -> Option<IdentClass> {
145 ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass),
146 ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(IdentClass::NameRefClass),
147 ast::Lifetime(lifetime) => {
148 NameClass::classify_lifetime(sema, &lifetime)
149 .map(IdentClass::NameClass)
150 .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
152 ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
153 ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
154 ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
155 ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator),
156 ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator),
162 pub fn classify_token(
163 sema: &Semantics<'_, RootDatabase>,
165 ) -> Option<IdentClass> {
166 let parent = token.parent()?;
167 Self::classify_node(sema, &parent)
170 pub fn classify_lifetime(
171 sema: &Semantics<'_, RootDatabase>,
172 lifetime: &ast::Lifetime,
173 ) -> Option<IdentClass> {
174 NameRefClass::classify_lifetime(sema, lifetime)
175 .map(IdentClass::NameRefClass)
176 .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
179 pub fn definitions(self) -> ArrayVec<Definition, 2> {
180 let mut res = ArrayVec::new();
182 IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
185 IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
186 res.push(Definition::Local(local_def));
187 res.push(Definition::Field(field_ref));
189 IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
190 IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
191 res.push(Definition::Local(local_ref));
192 res.push(Definition::Field(field_ref));
194 IdentClass::Operator(
195 OperatorClass::Await(func)
196 | OperatorClass::Prefix(func)
197 | OperatorClass::Bin(func)
198 | OperatorClass::Index(func)
199 | OperatorClass::Try(func),
200 ) => res.push(Definition::Function(func)),
205 pub fn definitions_no_ops(self) -> ArrayVec<Definition, 2> {
206 let mut res = ArrayVec::new();
208 IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
211 IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
212 res.push(Definition::Local(local_def));
213 res.push(Definition::Field(field_ref));
215 IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
216 IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
217 res.push(Definition::Local(local_ref));
218 res.push(Definition::Field(field_ref));
220 IdentClass::Operator(_) => (),
226 /// On a first blush, a single `ast::Name` defines a single definition at some
227 /// scope. That is, that, by just looking at the syntactical category, we can
228 /// unambiguously define the semantic category.
230 /// Sadly, that's not 100% true, there are special cases. To make sure that
231 /// callers handle all the special cases correctly via exhaustive matching, we
232 /// add a [`NameClass`] enum which lists all of them!
234 /// A model special case is `None` constant in pattern.
237 Definition(Definition),
238 /// `None` in `if let None = Some(82) {}`.
239 /// Syntactically, it is a name, but semantically it is a reference.
240 ConstReference(Definition),
241 /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces
242 /// a definition into a local scope, and refers to an existing definition.
250 /// `Definition` defined by this name.
251 pub fn defined(self) -> Option<Definition> {
252 let res = match self {
253 NameClass::Definition(it) => it,
254 NameClass::ConstReference(_) => return None,
255 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
256 Definition::Local(local_def)
262 pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<NameClass> {
263 let _p = profile::span("classify_name");
265 let parent = name.syntax().parent()?;
267 let definition = match_ast! {
269 ast::Item(it) => classify_item(sema, it)?,
270 ast::IdentPat(it) => return classify_ident_pat(sema, it),
271 ast::Rename(it) => classify_rename(sema, it)?,
272 ast::SelfParam(it) => Definition::Local(sema.to_def(&it)?),
273 ast::RecordField(it) => Definition::Field(sema.to_def(&it)?),
274 ast::Variant(it) => Definition::Variant(sema.to_def(&it)?),
275 ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
276 ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
280 return Some(NameClass::Definition(definition));
283 sema: &Semantics<'_, RootDatabase>,
285 ) -> Option<Definition> {
286 let definition = match item {
287 ast::Item::MacroRules(it) => {
288 Definition::Macro(sema.to_def(&ast::Macro::MacroRules(it))?)
290 ast::Item::MacroDef(it) => {
291 Definition::Macro(sema.to_def(&ast::Macro::MacroDef(it))?)
293 ast::Item::Const(it) => Definition::Const(sema.to_def(&it)?),
294 ast::Item::Fn(it) => {
295 let def = sema.to_def(&it)?;
296 def.as_proc_macro(sema.db)
297 .map(Definition::Macro)
298 .unwrap_or(Definition::Function(def))
300 ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
301 ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
302 ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
303 ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
304 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
305 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
306 ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
312 fn classify_ident_pat(
313 sema: &Semantics<'_, RootDatabase>,
314 ident_pat: ast::IdentPat,
315 ) -> Option<NameClass> {
316 if let Some(def) = sema.resolve_bind_pat_to_const(&ident_pat) {
317 return Some(NameClass::ConstReference(Definition::from(def)));
320 let local = sema.to_def(&ident_pat)?;
321 let pat_parent = ident_pat.syntax().parent();
322 if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
323 if record_pat_field.name_ref().is_none() {
324 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
325 return Some(NameClass::PatFieldShorthand {
332 Some(NameClass::Definition(Definition::Local(local)))
336 sema: &Semantics<'_, RootDatabase>,
338 ) -> Option<Definition> {
339 if let Some(use_tree) = rename.syntax().parent().and_then(ast::UseTree::cast) {
340 let path = use_tree.path()?;
341 sema.resolve_path(&path).map(Definition::from)
343 let extern_crate = rename.syntax().parent().and_then(ast::ExternCrate::cast)?;
344 let krate = sema.resolve_extern_crate(&extern_crate)?;
345 let root_module = krate.root_module(sema.db);
346 Some(Definition::Module(root_module))
351 pub fn classify_lifetime(
352 sema: &Semantics<'_, RootDatabase>,
353 lifetime: &ast::Lifetime,
354 ) -> Option<NameClass> {
355 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
356 let parent = lifetime.syntax().parent()?;
358 if let Some(it) = ast::LifetimeParam::cast(parent.clone()) {
359 sema.to_def(&it).map(Into::into).map(Definition::GenericParam)
360 } else if let Some(it) = ast::Label::cast(parent) {
361 sema.to_def(&it).map(Definition::Label)
365 .map(NameClass::Definition)
370 pub enum OperatorClass {
379 pub fn classify_await(
380 sema: &Semantics<'_, RootDatabase>,
381 await_expr: &ast::AwaitExpr,
382 ) -> Option<OperatorClass> {
383 sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await)
386 pub fn classify_prefix(
387 sema: &Semantics<'_, RootDatabase>,
388 prefix_expr: &ast::PrefixExpr,
389 ) -> Option<OperatorClass> {
390 sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix)
394 sema: &Semantics<'_, RootDatabase>,
395 try_expr: &ast::TryExpr,
396 ) -> Option<OperatorClass> {
397 sema.resolve_try_expr(try_expr).map(OperatorClass::Try)
400 pub fn classify_index(
401 sema: &Semantics<'_, RootDatabase>,
402 index_expr: &ast::IndexExpr,
403 ) -> Option<OperatorClass> {
404 sema.resolve_index_expr(index_expr).map(OperatorClass::Index)
408 sema: &Semantics<'_, RootDatabase>,
409 bin_expr: &ast::BinExpr,
410 ) -> Option<OperatorClass> {
411 sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin)
415 /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
416 /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
417 /// reference most of the time, but there are a couple of annoying exceptions.
419 /// A model special case is field shorthand syntax, which uses a single
420 /// reference to point to two different defs.
422 pub enum NameRefClass {
423 Definition(Definition),
424 FieldShorthand { local_ref: Local, field_ref: Field },
428 // Note: we don't have unit-tests for this rather important function.
429 // It is primarily exercised via goto definition tests in `ide`.
431 sema: &Semantics<'_, RootDatabase>,
432 name_ref: &ast::NameRef,
433 ) -> Option<NameRefClass> {
434 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
436 let parent = name_ref.syntax().parent()?;
438 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
439 if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
440 let res = match local {
441 None => NameRefClass::Definition(Definition::Field(field)),
443 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
450 if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) {
451 if path.parent_path().is_none() {
452 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
453 // Only use this to resolve to macro calls for last segments as qualifiers resolve
455 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
456 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
460 return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
465 ast::MethodCallExpr(method_call) => {
466 sema.resolve_method_call(&method_call)
467 .map(Definition::Function)
468 .map(NameRefClass::Definition)
470 ast::FieldExpr(field_expr) => {
471 sema.resolve_field(&field_expr)
472 .map(Definition::Field)
473 .map(NameRefClass::Definition)
475 ast::RecordPatField(record_pat_field) => {
476 sema.resolve_record_pat_field(&record_pat_field)
477 .map(Definition::Field)
478 .map(NameRefClass::Definition)
480 ast::AssocTypeArg(_) => {
481 // `Trait<Assoc = Ty>`
483 let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
484 let resolved = sema.resolve_path(&containing_path)?;
485 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
487 .items_with_supertraits(sema.db)
489 .filter_map(|&assoc| match assoc {
490 hir::AssocItem::TypeAlias(it) => Some(it),
493 .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str())
495 return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
500 ast::ExternCrate(extern_crate) => {
501 let krate = sema.resolve_extern_crate(&extern_crate)?;
502 let root_module = krate.root_module(sema.db);
503 Some(NameRefClass::Definition(Definition::Module(root_module)))
510 pub fn classify_lifetime(
511 sema: &Semantics<'_, RootDatabase>,
512 lifetime: &ast::Lifetime,
513 ) -> Option<NameRefClass> {
514 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
515 let parent = lifetime.syntax().parent()?;
516 match parent.kind() {
517 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
518 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
520 SyntaxKind::LIFETIME_ARG
521 | SyntaxKind::SELF_PARAM
522 | SyntaxKind::TYPE_BOUND
523 | SyntaxKind::WHERE_PRED
524 | SyntaxKind::REF_TYPE => sema
525 .resolve_lifetime_param(lifetime)
526 .map(GenericParam::LifetimeParam)
527 .map(Definition::GenericParam)
528 .map(NameRefClass::Definition),
529 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
530 // if our lifetime is in a LifetimeParam without being the constrained lifetime
531 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
534 sema.resolve_lifetime_param(lifetime)
535 .map(GenericParam::LifetimeParam)
536 .map(Definition::GenericParam)
537 .map(NameRefClass::Definition)
545 Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
546 GenericParam, Label, Macro
550 impl From<Impl> for Definition {
551 fn from(impl_: Impl) -> Self {
552 Definition::SelfType(impl_)
556 impl AsAssocItem for Definition {
557 fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
559 Definition::Function(it) => it.as_assoc_item(db),
560 Definition::Const(it) => it.as_assoc_item(db),
561 Definition::TypeAlias(it) => it.as_assoc_item(db),
567 impl From<AssocItem> for Definition {
568 fn from(assoc_item: AssocItem) -> Self {
570 AssocItem::Function(it) => Definition::Function(it),
571 AssocItem::Const(it) => Definition::Const(it),
572 AssocItem::TypeAlias(it) => Definition::TypeAlias(it),
577 impl From<PathResolution> for Definition {
578 fn from(path_resolution: PathResolution) -> Self {
579 match path_resolution {
580 PathResolution::Def(def) => def.into(),
581 PathResolution::Local(local) => Definition::Local(local),
582 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
583 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
584 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
585 PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
586 PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
587 PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper),
592 impl From<ModuleDef> for Definition {
593 fn from(def: ModuleDef) -> Self {
595 ModuleDef::Module(it) => Definition::Module(it),
596 ModuleDef::Function(it) => Definition::Function(it),
597 ModuleDef::Adt(it) => Definition::Adt(it),
598 ModuleDef::Variant(it) => Definition::Variant(it),
599 ModuleDef::Const(it) => Definition::Const(it),
600 ModuleDef::Static(it) => Definition::Static(it),
601 ModuleDef::Trait(it) => Definition::Trait(it),
602 ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
603 ModuleDef::Macro(it) => Definition::Macro(it),
604 ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
609 impl From<Definition> for Option<ItemInNs> {
610 fn from(def: Definition) -> Self {
611 let item = match def {
612 Definition::Module(it) => ModuleDef::Module(it),
613 Definition::Function(it) => ModuleDef::Function(it),
614 Definition::Adt(it) => ModuleDef::Adt(it),
615 Definition::Variant(it) => ModuleDef::Variant(it),
616 Definition::Const(it) => ModuleDef::Const(it),
617 Definition::Static(it) => ModuleDef::Static(it),
618 Definition::Trait(it) => ModuleDef::Trait(it),
619 Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
620 Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
623 Some(ItemInNs::from(item))