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