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