ide_db/
defs.rs

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 std::borrow::Cow;
9
10use crate::RootDatabase;
11use crate::documentation::{Documentation, HasDocs};
12use crate::famous_defs::FamousDefs;
13use arrayvec::ArrayVec;
14use either::Either;
15use hir::{
16    Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
17    Const, Crate, DefWithBody, DeriveHelper, DisplayTarget, DocLinkDef, EnumVariant,
18    ExpressionStoreOwner, ExternAssocItem, ExternCrateDecl, Field, Function, GenericDef,
19    GenericParam, GenericSubstitution, HasContainer, HasVisibility, HirDisplay, Impl,
20    InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, Name, PathResolution,
21    Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TupleField, TypeAlias, Variant,
22    Visibility,
23};
24use span::Edition;
25use stdx::{format_to, impl_from};
26use syntax::{
27    SyntaxKind, SyntaxNode, SyntaxToken,
28    ast::{self, AstNode},
29    match_ast,
30};
31
32// FIXME: a more precise name would probably be `Symbol`?
33#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
34pub enum Definition {
35    Macro(Macro),
36    Field(Field),
37    TupleField(TupleField),
38    Module(Module),
39    Crate(Crate),
40    Function(Function),
41    Adt(Adt),
42    EnumVariant(EnumVariant),
43    Const(Const),
44    Static(Static),
45    Trait(Trait),
46    TypeAlias(TypeAlias),
47    SelfType(Impl),
48    GenericParam(GenericParam),
49    Local(Local),
50    Label(Label),
51    DeriveHelper(DeriveHelper),
52    BuiltinType(BuiltinType),
53    BuiltinLifetime(StaticLifetime),
54    BuiltinAttr(BuiltinAttr),
55    ToolModule(ToolModule),
56    ExternCrateDecl(ExternCrateDecl),
57    InlineAsmRegOrRegClass(()),
58    InlineAsmOperand(InlineAsmOperand),
59}
60
61impl Definition {
62    pub fn canonical_module_path(&self, db: &RootDatabase) -> Option<impl Iterator<Item = Module>> {
63        self.module(db).map(|it| it.path_to_root(db).into_iter().rev())
64    }
65
66    pub fn krate(&self, db: &RootDatabase) -> Option<Crate> {
67        Some(match self {
68            Definition::Module(m) => m.krate(db),
69            &Definition::Crate(it) => it,
70            _ => self.module(db)?.krate(db),
71        })
72    }
73
74    /// Returns the module this definition resides in.
75    ///
76    /// As such, for modules themselves this will return the parent module.
77    pub fn module(&self, db: &RootDatabase) -> Option<Module> {
78        let module = match self {
79            Definition::Macro(it) => it.module(db),
80            Definition::Module(it) => it.parent(db)?,
81            Definition::Crate(_) => return None,
82            Definition::Field(it) => it.parent_def(db).module(db),
83            Definition::Function(it) => it.module(db),
84            Definition::Adt(it) => it.module(db),
85            Definition::Const(it) => it.module(db),
86            Definition::Static(it) => it.module(db),
87            Definition::Trait(it) => it.module(db),
88            Definition::TypeAlias(it) => it.module(db),
89            Definition::EnumVariant(it) => it.module(db),
90            Definition::SelfType(it) => it.module(db),
91            Definition::Local(it) => it.module(db),
92            Definition::GenericParam(it) => it.module(db),
93            Definition::Label(it) => it.module(db),
94            Definition::ExternCrateDecl(it) => it.module(db),
95            Definition::DeriveHelper(it) => it.derive().module(db),
96            Definition::InlineAsmOperand(it) => it.parent(db).module(db),
97            Definition::ToolModule(t) => t.krate().root_module(db),
98            Definition::BuiltinAttr(_)
99            | Definition::BuiltinType(_)
100            | Definition::BuiltinLifetime(_)
101            | Definition::TupleField(_)
102            | Definition::InlineAsmRegOrRegClass(_) => return None,
103        };
104        Some(module)
105    }
106
107    pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> {
108        fn container_to_definition(container: ItemContainer) -> Option<Definition> {
109            match container {
110                ItemContainer::Trait(it) => Some(it.into()),
111                ItemContainer::Impl(it) => Some(it.into()),
112                ItemContainer::Module(it) => Some(it.into()),
113                ItemContainer::ExternBlock(_) | ItemContainer::Crate(_) => None,
114            }
115        }
116        match self {
117            Definition::Macro(it) => Some(it.module(db).into()),
118            Definition::Module(it) => it.parent(db).map(Definition::Module),
119            Definition::Crate(_) => None,
120            Definition::Field(it) => Some(it.parent_def(db).into()),
121            Definition::Function(it) => container_to_definition(it.container(db)),
122            Definition::Adt(it) => Some(it.module(db).into()),
123            Definition::Const(it) => container_to_definition(it.container(db)),
124            Definition::Static(it) => container_to_definition(it.container(db)),
125            Definition::Trait(it) => container_to_definition(it.container(db)),
126            Definition::TypeAlias(it) => container_to_definition(it.container(db)),
127            Definition::EnumVariant(it) => Some(Adt::Enum(it.parent_enum(db)).into()),
128            Definition::SelfType(it) => Some(it.module(db).into()),
129            Definition::Local(it) => it.parent(db).try_into().ok(),
130            Definition::GenericParam(it) => Some(it.parent().into()),
131            Definition::Label(it) => it.parent(db).try_into().ok(),
132            Definition::ExternCrateDecl(it) => container_to_definition(it.container(db)),
133            Definition::DeriveHelper(it) => Some(it.derive().module(db).into()),
134            Definition::InlineAsmOperand(it) => it.parent(db).try_into().ok(),
135            Definition::BuiltinAttr(_)
136            | Definition::BuiltinType(_)
137            | Definition::BuiltinLifetime(_)
138            | Definition::TupleField(_)
139            | Definition::ToolModule(_)
140            | Definition::InlineAsmRegOrRegClass(_) => None,
141        }
142    }
143
144    pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
145        let vis = match self {
146            Definition::Field(sf) => sf.visibility(db),
147            Definition::Module(it) => it.visibility(db),
148            Definition::Crate(_) => return None,
149            Definition::Function(it) => it.visibility(db),
150            Definition::Adt(it) => it.visibility(db),
151            Definition::Const(it) => it.visibility(db),
152            Definition::Static(it) => it.visibility(db),
153            Definition::Trait(it) => it.visibility(db),
154            Definition::TypeAlias(it) => it.visibility(db),
155            Definition::EnumVariant(it) => it.visibility(db),
156            Definition::ExternCrateDecl(it) => it.visibility(db),
157            Definition::Macro(it) => it.visibility(db),
158            Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public,
159            Definition::BuiltinAttr(_)
160            | Definition::BuiltinLifetime(_)
161            | Definition::ToolModule(_)
162            | Definition::SelfType(_)
163            | Definition::Local(_)
164            | Definition::GenericParam(_)
165            | Definition::Label(_)
166            | Definition::DeriveHelper(_)
167            | Definition::InlineAsmRegOrRegClass(_)
168            | Definition::InlineAsmOperand(_) => return None,
169        };
170        Some(vis)
171    }
172
173    pub fn name(&self, db: &RootDatabase) -> Option<Name> {
174        let name = match self {
175            Definition::Macro(it) => it.name(db),
176            Definition::Field(it) => it.name(db),
177            Definition::Module(it) => it.name(db)?,
178            Definition::Crate(it) => {
179                Name::new_symbol_root(it.display_name(db)?.crate_name().symbol().clone())
180            }
181            Definition::Function(it) => it.name(db),
182            Definition::Adt(it) => it.name(db),
183            Definition::EnumVariant(it) => it.name(db),
184            Definition::Const(it) => it.name(db)?,
185            Definition::Static(it) => it.name(db),
186            Definition::Trait(it) => it.name(db),
187            Definition::TypeAlias(it) => it.name(db),
188            Definition::BuiltinType(it) => it.name(),
189            Definition::TupleField(it) => it.name(),
190            Definition::SelfType(_) => return None,
191            Definition::Local(it) => it.name(db),
192            Definition::GenericParam(it) => it.name(db),
193            Definition::Label(it) => it.name(db),
194            Definition::BuiltinLifetime(it) => it.name(),
195            Definition::BuiltinAttr(_) => return None, // FIXME
196            Definition::ToolModule(_) => return None,  // FIXME
197            Definition::DeriveHelper(it) => it.name(db),
198            Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
199            Definition::InlineAsmRegOrRegClass(_) => return None,
200            Definition::InlineAsmOperand(op) => return op.name(db),
201        };
202        Some(name)
203    }
204
205    pub fn docs<'db>(
206        &self,
207        db: &'db RootDatabase,
208        famous_defs: Option<&FamousDefs<'_, '_>>,
209        display_target: DisplayTarget,
210    ) -> Option<Documentation<'db>> {
211        self.docs_with_rangemap(db, famous_defs, display_target).map(|docs| match docs {
212            Either::Left(Cow::Borrowed(docs)) => Documentation::new_borrowed(docs.docs()),
213            Either::Left(Cow::Owned(docs)) => Documentation::new_owned(docs.into_docs()),
214            Either::Right(docs) => docs,
215        })
216    }
217
218    pub fn docs_with_rangemap<'db>(
219        &self,
220        db: &'db RootDatabase,
221        famous_defs: Option<&FamousDefs<'_, '_>>,
222        display_target: DisplayTarget,
223    ) -> Option<Either<Cow<'db, hir::Docs>, Documentation<'db>>> {
224        let docs = match self {
225            Definition::Macro(it) => it.docs_with_rangemap(db),
226            Definition::Field(it) => it.docs_with_rangemap(db),
227            Definition::Module(it) => it.docs_with_rangemap(db),
228            Definition::Crate(it) => it.docs_with_rangemap(db),
229            Definition::Function(it) => it.docs_with_rangemap(db),
230            Definition::Adt(it) => it.docs_with_rangemap(db),
231            Definition::EnumVariant(it) => it.docs_with_rangemap(db),
232            Definition::Const(it) => it.docs_with_rangemap(db),
233            Definition::Static(it) => it.docs_with_rangemap(db),
234            Definition::Trait(it) => it.docs_with_rangemap(db),
235            Definition::TypeAlias(it) => {
236                it.docs_with_rangemap(db).or_else(|| {
237                    // docs are missing, try to fall back to the docs of the aliased item.
238                    let adt = it.ty(db).as_adt()?;
239                    let mut docs = adt.docs_with_rangemap(db)?.into_owned();
240                    let header_docs = format!(
241                        "*This is the documentation for* `{}`\n\n",
242                        adt.display(db, display_target)
243                    );
244                    docs.prepend_str(&header_docs);
245                    Some(Cow::Owned(docs))
246                })
247            }
248            Definition::BuiltinType(it) => {
249                famous_defs.and_then(|fd| {
250                    // std exposes prim_{} modules with docstrings on the root to document the builtins
251                    let primitive_mod =
252                        format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
253                    let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
254                    doc_owner.docs_with_rangemap(db)
255                })
256            }
257            Definition::BuiltinLifetime(StaticLifetime) => None,
258            Definition::Local(_) => None,
259            Definition::SelfType(impl_def) => {
260                impl_def.self_ty(db).as_adt().map(|adt| adt.docs_with_rangemap(db))?
261            }
262            Definition::GenericParam(_) => None,
263            Definition::Label(_) => None,
264            Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db),
265
266            Definition::BuiltinAttr(it) => {
267                let name = it.name();
268                let AttributeTemplate { word, list, name_value_str } = it.template()?;
269                let mut docs = "Valid forms are:".to_owned();
270                if word {
271                    format_to!(docs, "\n - #\\[{}]", name.display(db, display_target.edition));
272                }
273                if let Some(list) = list {
274                    format_to!(
275                        docs,
276                        "\n - #\\[{}({})]",
277                        name.display(db, display_target.edition),
278                        list
279                    );
280                }
281                if let Some(name_value_str) = name_value_str {
282                    format_to!(
283                        docs,
284                        "\n - #\\[{} = {}]",
285                        name.display(db, display_target.edition),
286                        name_value_str
287                    );
288                }
289
290                return Some(Either::Right(Documentation::new_owned(docs.replace('*', "\\*"))));
291            }
292            Definition::ToolModule(_) => None,
293            Definition::DeriveHelper(_) => None,
294            Definition::TupleField(_) => None,
295            Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None,
296        };
297
298        docs.or_else(|| {
299            // docs are missing, for assoc items of trait impls try to fall back to the docs of the
300            // original item of the trait
301            let assoc = self.as_assoc_item(db)?;
302            let trait_ = assoc.implemented_trait(db)?;
303            let name = Some(assoc.name(db)?);
304            let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
305            item.docs_with_rangemap(db)
306        })
307        .map(Either::Left)
308    }
309
310    pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {
311        match *self {
312            Definition::Macro(it) => it.display(db, display_target).to_string(),
313            Definition::Field(it) => it.display(db, display_target).to_string(),
314            Definition::TupleField(it) => it.display(db, display_target).to_string(),
315            Definition::Module(it) => it.display(db, display_target).to_string(),
316            Definition::Crate(it) => it.display(db, display_target).to_string(),
317            Definition::Function(it) => it.display(db, display_target).to_string(),
318            Definition::Adt(it) => it.display(db, display_target).to_string(),
319            Definition::EnumVariant(it) => it.display(db, display_target).to_string(),
320            Definition::Const(it) => it.display(db, display_target).to_string(),
321            Definition::Static(it) => it.display(db, display_target).to_string(),
322            Definition::Trait(it) => it.display(db, display_target).to_string(),
323            Definition::TypeAlias(it) => it.display(db, display_target).to_string(),
324            Definition::BuiltinType(it) => {
325                it.name().display(db, display_target.edition).to_string()
326            }
327            Definition::BuiltinLifetime(it) => {
328                it.name().display(db, display_target.edition).to_string()
329            }
330            Definition::Local(it) => {
331                let ty = it.ty(db);
332                let ty_display = ty.display_truncated(db, None, display_target);
333                let is_mut = if it.is_mut(db) { "mut " } else { "" };
334                if it.is_self(db) {
335                    format!("{is_mut}self: {ty_display}")
336                } else {
337                    let name = it.name(db);
338                    let let_kw = if it.is_param(db) { "" } else { "let " };
339                    format!(
340                        "{let_kw}{is_mut}{}: {ty_display}",
341                        name.display(db, display_target.edition)
342                    )
343                }
344            }
345            Definition::SelfType(impl_def) => {
346                let self_ty = &impl_def.self_ty(db);
347                match self_ty.as_adt() {
348                    Some(it) => it.display(db, display_target).to_string(),
349                    None => self_ty.display(db, display_target).to_string(),
350                }
351            }
352            Definition::GenericParam(it) => it.display(db, display_target).to_string(),
353            Definition::Label(it) => it.name(db).display(db, display_target.edition).to_string(),
354            Definition::ExternCrateDecl(it) => it.display(db, display_target).to_string(),
355            Definition::BuiltinAttr(it) => {
356                format!("#[{}]", it.name().display(db, display_target.edition))
357            }
358            Definition::ToolModule(it) => {
359                it.name(db).display(db, display_target.edition).to_string()
360            }
361            Definition::DeriveHelper(it) => {
362                format!("derive_helper {}", it.name(db).display(db, display_target.edition))
363            }
364            // FIXME
365            Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
366            Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(),
367        }
368    }
369}
370
371pub fn find_std_module(
372    famous_defs: &FamousDefs<'_, '_>,
373    name: &str,
374    edition: Edition,
375) -> Option<hir::Module> {
376    let db = famous_defs.0.db;
377    let std_crate = famous_defs.std()?;
378    let std_root_module = std_crate.root_module(famous_defs.0.db);
379    std_root_module.children(db).find(|module| {
380        module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name)
381    })
382}
383
384// FIXME: IdentClass as a name no longer fits
385#[derive(Debug)]
386pub enum IdentClass<'db> {
387    NameClass(NameClass<'db>),
388    NameRefClass(NameRefClass<'db>),
389    Operator(OperatorClass),
390}
391
392impl<'db> IdentClass<'db> {
393    pub fn classify_node(
394        sema: &Semantics<'db, RootDatabase>,
395        node: &SyntaxNode,
396    ) -> Option<IdentClass<'db>> {
397        match_ast! {
398            match node {
399                ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass),
400                ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(IdentClass::NameRefClass),
401                ast::Lifetime(lifetime) => {
402                    NameClass::classify_lifetime(sema, &lifetime)
403                        .map(IdentClass::NameClass)
404                        .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
405                },
406                ast::RangePat(range_pat) => OperatorClass::classify_range_pat(sema, &range_pat).map(IdentClass::Operator),
407                ast::RangeExpr(range_expr) => OperatorClass::classify_range_expr(sema, &range_expr).map(IdentClass::Operator),
408                ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
409                ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
410                ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
411                ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema, &prefix_expr).map(IdentClass::Operator),
412                ast::TryExpr(try_expr) => OperatorClass::classify_try(sema, &try_expr).map(IdentClass::Operator),
413                _ => None,
414            }
415        }
416    }
417
418    pub fn classify_token(
419        sema: &Semantics<'db, RootDatabase>,
420        token: &SyntaxToken,
421    ) -> Option<IdentClass<'db>> {
422        let parent = token.parent()?;
423        Self::classify_node(sema, &parent)
424    }
425
426    pub fn classify_lifetime(
427        sema: &Semantics<'db, RootDatabase>,
428        lifetime: &ast::Lifetime,
429    ) -> Option<IdentClass<'db>> {
430        NameRefClass::classify_lifetime(sema, lifetime)
431            .map(IdentClass::NameRefClass)
432            .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
433    }
434
435    pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution<'db>>), 2> {
436        let mut res = ArrayVec::new();
437        match self {
438            IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
439                res.push((it, None))
440            }
441            IdentClass::NameClass(NameClass::PatFieldShorthand {
442                local_def,
443                field_ref,
444                adt_subst,
445            }) => {
446                res.push((Definition::Local(local_def), None));
447                res.push((Definition::Field(field_ref), Some(adt_subst)));
448            }
449            IdentClass::NameRefClass(NameRefClass::Definition(it, subst)) => res.push((it, subst)),
450            IdentClass::NameRefClass(NameRefClass::FieldShorthand {
451                local_ref,
452                field_ref,
453                adt_subst,
454            }) => {
455                res.push((Definition::Local(local_ref), None));
456                res.push((Definition::Field(field_ref), Some(adt_subst)));
457            }
458            IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
459                res.push((Definition::ExternCrateDecl(decl), None));
460                res.push((Definition::Crate(krate), None));
461            }
462            IdentClass::Operator(
463                OperatorClass::Await(func)
464                | OperatorClass::Prefix(func)
465                | OperatorClass::Bin(func)
466                | OperatorClass::Index(func)
467                | OperatorClass::Try(func),
468            ) => res.push((Definition::Function(func), None)),
469            IdentClass::Operator(OperatorClass::Range(struct0)) => {
470                res.push((Definition::Adt(Adt::Struct(struct0)), None))
471            }
472        }
473        res
474    }
475
476    pub fn definitions_no_ops(self) -> ArrayVec<Definition, 2> {
477        let mut res = ArrayVec::new();
478        match self {
479            IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
480                res.push(it)
481            }
482            IdentClass::NameClass(NameClass::PatFieldShorthand {
483                local_def,
484                field_ref,
485                adt_subst: _,
486            }) => {
487                res.push(Definition::Local(local_def));
488                res.push(Definition::Field(field_ref));
489            }
490            IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it),
491            IdentClass::NameRefClass(NameRefClass::FieldShorthand {
492                local_ref,
493                field_ref,
494                adt_subst: _,
495            }) => {
496                res.push(Definition::Local(local_ref));
497                res.push(Definition::Field(field_ref));
498            }
499            IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
500                res.push(Definition::ExternCrateDecl(decl));
501                res.push(Definition::Crate(krate));
502            }
503            IdentClass::Operator(_) => (),
504        }
505        res
506    }
507}
508
509/// On a first blush, a single `ast::Name` defines a single definition at some
510/// scope. That is, that, by just looking at the syntactical category, we can
511/// unambiguously define the semantic category.
512///
513/// Sadly, that's not 100% true, there are special cases. To make sure that
514/// callers handle all the special cases correctly via exhaustive matching, we
515/// add a [`NameClass`] enum which lists all of them!
516///
517/// A model special case is `None` constant in pattern.
518#[derive(Debug)]
519pub enum NameClass<'db> {
520    Definition(Definition),
521    /// `None` in `if let None = Some(82) {}`.
522    /// Syntactically, it is a name, but semantically it is a reference.
523    ConstReference(Definition),
524    /// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces
525    /// a definition into a local scope, and refers to an existing definition.
526    PatFieldShorthand {
527        local_def: Local,
528        field_ref: Field,
529        adt_subst: GenericSubstitution<'db>,
530    },
531}
532
533impl<'db> NameClass<'db> {
534    /// `Definition` defined by this name.
535    pub fn defined(self) -> Option<Definition> {
536        let res = match self {
537            NameClass::Definition(it) => it,
538            NameClass::ConstReference(_) => return None,
539            NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
540                Definition::Local(local_def)
541            }
542        };
543        Some(res)
544    }
545
546    pub fn classify(
547        sema: &Semantics<'db, RootDatabase>,
548        name: &ast::Name,
549    ) -> Option<NameClass<'db>> {
550        let _p = tracing::info_span!("NameClass::classify").entered();
551
552        let parent = name.syntax().parent()?;
553        let definition = match_ast! {
554            match parent {
555                ast::Item(it) => classify_item(sema, it)?,
556                ast::IdentPat(it) => return classify_ident_pat(sema, it),
557                ast::Rename(it) => classify_rename(sema, it)?,
558                ast::SelfParam(it) => Definition::Local(sema.to_def(&it)?),
559                ast::RecordField(it) => Definition::Field(sema.to_def(&it)?),
560                ast::Variant(it) => Definition::EnumVariant(sema.to_def(&it)?),
561                ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
562                ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
563                ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?),
564                _ => return None,
565            }
566        };
567        return Some(NameClass::Definition(definition));
568
569        fn classify_item(
570            sema: &Semantics<'_, RootDatabase>,
571            item: ast::Item,
572        ) -> Option<Definition> {
573            let definition = match item {
574                ast::Item::MacroRules(it) => {
575                    Definition::Macro(sema.to_def(&ast::Macro::MacroRules(it))?)
576                }
577                ast::Item::MacroDef(it) => {
578                    Definition::Macro(sema.to_def(&ast::Macro::MacroDef(it))?)
579                }
580                ast::Item::Const(it) => Definition::Const(sema.to_def(&it)?),
581                ast::Item::Fn(it) => {
582                    let def = sema.to_def(&it)?;
583                    def.as_proc_macro(sema.db)
584                        .map(Definition::Macro)
585                        .unwrap_or(Definition::Function(def))
586                }
587                ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
588                ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
589                ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
590                ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
591                ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
592                ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
593                ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
594                ast::Item::ExternCrate(it) => Definition::ExternCrateDecl(sema.to_def(&it)?),
595                _ => return None,
596            };
597            Some(definition)
598        }
599
600        fn classify_ident_pat<'db>(
601            sema: &Semantics<'db, RootDatabase>,
602            ident_pat: ast::IdentPat,
603        ) -> Option<NameClass<'db>> {
604            if let Some(def) = sema.resolve_bind_pat_to_const(&ident_pat) {
605                return Some(NameClass::ConstReference(Definition::from(def)));
606            }
607
608            let local = sema.to_def(&ident_pat)?;
609            let pat_parent = ident_pat.syntax().parent();
610            if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast)
611                && record_pat_field.name_ref().is_none()
612                && let Some((field, _, adt_subst)) =
613                    sema.resolve_record_pat_field_with_subst(&record_pat_field)
614            {
615                return Some(NameClass::PatFieldShorthand {
616                    local_def: local,
617                    field_ref: field,
618                    adt_subst,
619                });
620            }
621            Some(NameClass::Definition(Definition::Local(local)))
622        }
623
624        fn classify_rename(
625            sema: &Semantics<'_, RootDatabase>,
626            rename: ast::Rename,
627        ) -> Option<Definition> {
628            if let Some(use_tree) = rename.syntax().parent().and_then(ast::UseTree::cast) {
629                let path = use_tree.path()?;
630                sema.resolve_path(&path).map(Definition::from)
631            } else {
632                sema.to_def(&rename.syntax().parent().and_then(ast::ExternCrate::cast)?)
633                    .map(Definition::ExternCrateDecl)
634            }
635        }
636    }
637
638    pub fn classify_lifetime(
639        sema: &Semantics<'db, RootDatabase>,
640        lifetime: &ast::Lifetime,
641    ) -> Option<NameClass<'db>> {
642        let _p = tracing::info_span!("NameClass::classify_lifetime", ?lifetime).entered();
643        let parent = lifetime.syntax().parent()?;
644
645        if let Some(it) = ast::LifetimeParam::cast(parent.clone()) {
646            sema.to_def(&it).map(Into::into).map(Definition::GenericParam)
647        } else if let Some(it) = ast::Label::cast(parent) {
648            sema.to_def(&it).map(Definition::Label)
649        } else {
650            None
651        }
652        .map(NameClass::Definition)
653    }
654}
655
656#[derive(Debug)]
657pub enum OperatorClass {
658    Range(Struct),
659    Await(Function),
660    Prefix(Function),
661    Index(Function),
662    Try(Function),
663    Bin(Function),
664}
665
666impl OperatorClass {
667    pub fn classify_range_pat(
668        sema: &Semantics<'_, RootDatabase>,
669        range_pat: &ast::RangePat,
670    ) -> Option<OperatorClass> {
671        sema.resolve_range_pat(range_pat).map(OperatorClass::Range)
672    }
673
674    pub fn classify_range_expr(
675        sema: &Semantics<'_, RootDatabase>,
676        range_expr: &ast::RangeExpr,
677    ) -> Option<OperatorClass> {
678        sema.resolve_range_expr(range_expr).map(OperatorClass::Range)
679    }
680
681    pub fn classify_await(
682        sema: &Semantics<'_, RootDatabase>,
683        await_expr: &ast::AwaitExpr,
684    ) -> Option<OperatorClass> {
685        sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await)
686    }
687
688    pub fn classify_prefix(
689        sema: &Semantics<'_, RootDatabase>,
690        prefix_expr: &ast::PrefixExpr,
691    ) -> Option<OperatorClass> {
692        sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix)
693    }
694
695    pub fn classify_try(
696        sema: &Semantics<'_, RootDatabase>,
697        try_expr: &ast::TryExpr,
698    ) -> Option<OperatorClass> {
699        sema.resolve_try_expr(try_expr).map(OperatorClass::Try)
700    }
701
702    pub fn classify_index(
703        sema: &Semantics<'_, RootDatabase>,
704        index_expr: &ast::IndexExpr,
705    ) -> Option<OperatorClass> {
706        sema.resolve_index_expr(index_expr).map(OperatorClass::Index)
707    }
708
709    pub fn classify_bin(
710        sema: &Semantics<'_, RootDatabase>,
711        bin_expr: &ast::BinExpr,
712    ) -> Option<OperatorClass> {
713        sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin)
714    }
715}
716
717/// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
718/// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
719/// reference most of the time, but there are a couple of annoying exceptions.
720///
721/// A model special case is field shorthand syntax, which uses a single
722/// reference to point to two different defs.
723#[derive(Debug)]
724pub enum NameRefClass<'db> {
725    Definition(Definition, Option<GenericSubstitution<'db>>),
726    FieldShorthand {
727        local_ref: Local,
728        field_ref: Field,
729        adt_subst: GenericSubstitution<'db>,
730    },
731    /// The specific situation where we have an extern crate decl without a rename
732    /// Here we have both a declaration and a reference.
733    /// ```rs
734    /// extern crate foo;
735    /// ```
736    ExternCrateShorthand {
737        decl: ExternCrateDecl,
738        krate: Crate,
739    },
740}
741
742impl<'db> NameRefClass<'db> {
743    // Note: we don't have unit-tests for this rather important function.
744    // It is primarily exercised via goto definition tests in `ide`.
745    pub fn classify(
746        sema: &Semantics<'db, RootDatabase>,
747        name_ref: &ast::NameRef,
748    ) -> Option<NameRefClass<'db>> {
749        let _p = tracing::info_span!("NameRefClass::classify", ?name_ref).entered();
750
751        let parent = name_ref.syntax().parent()?;
752
753        if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref)
754            && let Some((field, local, _, adt_subst)) =
755                sema.resolve_record_field_with_substitution(&record_field)
756        {
757            let res = match local {
758                None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)),
759                Some(local) => {
760                    NameRefClass::FieldShorthand { field_ref: field, local_ref: local, adt_subst }
761                }
762            };
763            return Some(res);
764        }
765
766        if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) {
767            if path.parent_path().is_none()
768                && let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast)
769            {
770                // Only use this to resolve to macro calls for last segments as qualifiers resolve
771                // to modules below.
772                if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
773                    return Some(NameRefClass::Definition(Definition::Macro(macro_def), None));
774                }
775            }
776            return sema
777                .resolve_path_with_subst(&path)
778                .map(|(res, subst)| NameRefClass::Definition(res.into(), subst));
779        }
780
781        match_ast! {
782            match parent {
783                ast::MethodCallExpr(method_call) => {
784                    sema.resolve_method_call_fallback(&method_call)
785                        .map(|(def, subst)| {
786                            match def {
787                                Either::Left(def) => NameRefClass::Definition(def.into(), subst),
788                                Either::Right(def) => NameRefClass::Definition(def.into(), subst),
789                            }
790                        })
791                },
792                ast::FieldExpr(field_expr) => {
793                    sema.resolve_field_fallback(&field_expr)
794                        .map(|(def, subst)| {
795                            match def {
796                                Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst),
797                                Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst),
798                                Either::Right(def) => NameRefClass::Definition(def.into(), subst),
799                            }
800                        })
801                },
802                ast::RecordPatField(record_pat_field) => {
803                    sema.resolve_record_pat_field_with_subst(&record_pat_field)
804                        .map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
805                },
806                ast::RecordExprField(record_expr_field) => {
807                    sema.resolve_record_field_with_substitution(&record_expr_field)
808                        .map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
809                },
810                ast::AssocTypeArg(_) => {
811                    // `Trait<Assoc = Ty>`
812                    //        ^^^^^
813                    let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
814                    let resolved = sema.resolve_path(&containing_path)?;
815                    if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved
816                        && let Some(ty) = tr
817                            .items_with_supertraits(sema.db)
818                            .iter()
819                            .filter_map(|&assoc| match assoc {
820                                hir::AssocItem::TypeAlias(it) => Some(it),
821                                _ => None,
822                            })
823                            .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#"))
824                        {
825                            // No substitution, this can only occur in type position.
826                            return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None));
827                        }
828                    None
829                },
830                ast::UseBoundGenericArgs(_) => {
831                    // No substitution, this can only occur in type position.
832                    sema.resolve_use_type_arg(name_ref)
833                        .map(GenericParam::TypeParam)
834                        .map(Definition::GenericParam)
835                        .map(|it| NameRefClass::Definition(it, None))
836                },
837                ast::ExternCrate(extern_crate_ast) => {
838                    let extern_crate = sema.to_def(&extern_crate_ast)?;
839                    let krate = extern_crate.resolved_crate(sema.db)?;
840                    Some(if extern_crate_ast.rename().is_some() {
841                        NameRefClass::Definition(Definition::Crate(krate), None)
842                    } else {
843                        NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
844                    })
845                },
846                ast::AsmRegSpec(_) => {
847                    Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None))
848                },
849                ast::OffsetOfExpr(_) => {
850                    let (def, subst) = sema.resolve_offset_of_field(name_ref)?;
851                    let def = match def {
852                        Either::Left(variant) => Definition::EnumVariant(variant),
853                        Either::Right(field) => Definition::Field(field),
854                    };
855                    Some(NameRefClass::Definition(def, Some(subst)))
856                },
857                _ => None
858            }
859        }
860    }
861
862    pub fn classify_lifetime(
863        sema: &Semantics<'db, RootDatabase>,
864        lifetime: &ast::Lifetime,
865    ) -> Option<NameRefClass<'db>> {
866        let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
867        if lifetime.text() == "'static" {
868            return Some(NameRefClass::Definition(
869                Definition::BuiltinLifetime(StaticLifetime),
870                None,
871            ));
872        }
873        let parent = lifetime.syntax().parent()?;
874        match parent.kind() {
875            SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema
876                .resolve_label(lifetime)
877                .map(Definition::Label)
878                .map(|it| NameRefClass::Definition(it, None)),
879            SyntaxKind::LIFETIME_ARG
880            | SyntaxKind::USE_BOUND_GENERIC_ARGS
881            | SyntaxKind::SELF_PARAM
882            | SyntaxKind::TYPE_BOUND
883            | SyntaxKind::WHERE_PRED
884            | SyntaxKind::REF_TYPE => sema
885                .resolve_lifetime_param(lifetime)
886                .map(GenericParam::LifetimeParam)
887                .map(Definition::GenericParam)
888                .map(|it| NameRefClass::Definition(it, None)),
889            _ => None,
890        }
891    }
892}
893
894impl_from!(
895    Field, Module, Function, Adt, EnumVariant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
896    GenericParam, Label, Macro, ExternCrateDecl
897    for Definition
898);
899
900impl From<Impl> for Definition {
901    fn from(impl_: Impl) -> Self {
902        Definition::SelfType(impl_)
903    }
904}
905
906impl From<InlineAsmOperand> for Definition {
907    fn from(value: InlineAsmOperand) -> Self {
908        Definition::InlineAsmOperand(value)
909    }
910}
911
912impl From<Either<PathResolution, InlineAsmOperand>> for Definition {
913    fn from(value: Either<PathResolution, InlineAsmOperand>) -> Self {
914        value.either(Definition::from, Definition::from)
915    }
916}
917
918impl AsAssocItem for Definition {
919    fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
920        match self {
921            Definition::Function(it) => it.as_assoc_item(db),
922            Definition::Const(it) => it.as_assoc_item(db),
923            Definition::TypeAlias(it) => it.as_assoc_item(db),
924            _ => None,
925        }
926    }
927}
928
929impl AsExternAssocItem for Definition {
930    fn as_extern_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<ExternAssocItem> {
931        match self {
932            Definition::Function(it) => it.as_extern_assoc_item(db),
933            Definition::Static(it) => it.as_extern_assoc_item(db),
934            Definition::TypeAlias(it) => it.as_extern_assoc_item(db),
935            _ => None,
936        }
937    }
938}
939
940impl From<AssocItem> for Definition {
941    fn from(assoc_item: AssocItem) -> Self {
942        match assoc_item {
943            AssocItem::Function(it) => Definition::Function(it),
944            AssocItem::Const(it) => Definition::Const(it),
945            AssocItem::TypeAlias(it) => Definition::TypeAlias(it),
946        }
947    }
948}
949
950impl From<PathResolution> for Definition {
951    fn from(path_resolution: PathResolution) -> Self {
952        match path_resolution {
953            PathResolution::Def(def) => def.into(),
954            PathResolution::Local(local) => Definition::Local(local),
955            PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
956            PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
957            PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
958            PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
959            PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
960            PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper),
961        }
962    }
963}
964
965impl From<ModuleDef> for Definition {
966    fn from(def: ModuleDef) -> Self {
967        match def {
968            ModuleDef::Module(it) => Definition::Module(it),
969            ModuleDef::Function(it) => Definition::Function(it),
970            ModuleDef::Adt(it) => Definition::Adt(it),
971            ModuleDef::EnumVariant(it) => Definition::EnumVariant(it),
972            ModuleDef::Const(it) => Definition::Const(it),
973            ModuleDef::Static(it) => Definition::Static(it),
974            ModuleDef::Trait(it) => Definition::Trait(it),
975            ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
976            ModuleDef::Macro(it) => Definition::Macro(it),
977            ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
978        }
979    }
980}
981
982impl From<DocLinkDef> for Definition {
983    fn from(def: DocLinkDef) -> Self {
984        match def {
985            DocLinkDef::ModuleDef(it) => it.into(),
986            DocLinkDef::Field(it) => it.into(),
987            DocLinkDef::SelfType(it) => it.into(),
988        }
989    }
990}
991
992impl From<Variant> for Definition {
993    fn from(def: Variant) -> Self {
994        ModuleDef::from(def).into()
995    }
996}
997
998impl TryFrom<DefWithBody> for Definition {
999    type Error = ();
1000    fn try_from(def: DefWithBody) -> Result<Self, Self::Error> {
1001        match def {
1002            DefWithBody::Function(it) => Ok(it.into()),
1003            DefWithBody::Static(it) => Ok(it.into()),
1004            DefWithBody::Const(it) => Ok(it.into()),
1005            DefWithBody::EnumVariant(it) => Ok(it.into()),
1006        }
1007    }
1008}
1009
1010impl From<GenericDef> for Definition {
1011    fn from(def: GenericDef) -> Self {
1012        match def {
1013            GenericDef::Function(it) => it.into(),
1014            GenericDef::Adt(it) => it.into(),
1015            GenericDef::Trait(it) => it.into(),
1016            GenericDef::TypeAlias(it) => it.into(),
1017            GenericDef::Impl(it) => it.into(),
1018            GenericDef::Const(it) => it.into(),
1019            GenericDef::Static(it) => it.into(),
1020        }
1021    }
1022}
1023
1024impl TryFrom<ExpressionStoreOwner> for Definition {
1025    type Error = ();
1026    fn try_from(def: ExpressionStoreOwner) -> Result<Self, Self::Error> {
1027        match def {
1028            ExpressionStoreOwner::Body(def_with_body) => def_with_body.try_into(),
1029            ExpressionStoreOwner::Signature(generic_def) => Ok(generic_def.into()),
1030            ExpressionStoreOwner::VariantFields(it) => Ok(it.into()),
1031        }
1032    }
1033}
1034
1035impl TryFrom<Definition> for GenericDef {
1036    type Error = ();
1037    fn try_from(def: Definition) -> Result<Self, Self::Error> {
1038        match def {
1039            Definition::Function(it) => Ok(it.into()),
1040            Definition::Adt(it) => Ok(it.into()),
1041            Definition::Trait(it) => Ok(it.into()),
1042            Definition::TypeAlias(it) => Ok(it.into()),
1043            Definition::SelfType(it) => Ok(it.into()),
1044            Definition::Const(it) => Ok(it.into()),
1045            _ => Err(()),
1046        }
1047    }
1048}