Skip to main content

cairo_lang_semantic/
path.rs

1//! Module for generating contextualized paths to items.
2//!
3//! Provides functionality to find the shortest valid path to an item from a module context,
4//! considering hierarchical relationships (direct, super::, crate::).
5//!
6//! # Limitations
7//! - Only handles a module context.
8
9use cairo_lang_debug::DebugWithDb;
10use cairo_lang_defs::ids::{
11    ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, FunctionTitleId,
12    GenericTypeId, ImplAliasId, ImplDefId, ImplFunctionId, ImportableId, LanguageElementId,
13    MacroDeclarationId, ModuleId, ModuleItemId, ModuleTypeAliasId, NamedLanguageElementId,
14    StructId, SubmoduleId, TopLevelLanguageElementId, TraitFunctionId, TraitId, VariantId,
15};
16use cairo_lang_diagnostics::Maybe;
17use cairo_lang_filesystem::db::{FilesGroup, default_crate_settings};
18use cairo_lang_filesystem::ids::{CrateId, SmolStrId, Tracked};
19use itertools::{Itertools, chain};
20use salsa::Database;
21use tracing::trace;
22
23use crate::expr::inference::InferenceId;
24use crate::items::functions::GenericFunctionId;
25use crate::items::module::ModuleSemantic;
26use crate::resolve::{ResolvedGenericItem, Resolver};
27
28/// Data about how an item can be accessed from a specific context.
29#[derive(Clone, Debug, PartialEq, Eq, salsa::SalsaValue)]
30pub struct ItemAccessInfo<'db> {
31    /// The shortest path segments to access the item from the context, in reverse order.
32    pub path_segments: Vec<ImportableId<'db>>,
33    /// How the item is accessed.
34    pub access_kind: ItemAccessKind<'db>,
35    /// Name of the item being accessed.
36    pub name: SmolStrId<'db>,
37}
38
39impl<'db> ItemAccessInfo<'db> {
40    /// Creates a new ItemAccessInfo from path segments.
41    pub fn new(
42        access_kind: ItemAccessKind<'db>,
43        path_segments: Vec<ImportableId<'db>>,
44        name: SmolStrId<'db>,
45    ) -> Self {
46        Self { path_segments, access_kind, name }
47    }
48
49    /// Returns the path as a string.
50    pub fn path(&self, db: &'db dyn Database) -> String {
51        let prefix = match self.access_kind {
52            ItemAccessKind::ViaCrate => "crate::",
53            ItemAccessKind::DirectInModule
54            | ItemAccessKind::ViaUse(_)
55            | ItemAccessKind::ViaPrelude
56            | ItemAccessKind::ViaGlobalUse
57            | ItemAccessKind::FullPath(_) => "",
58        };
59        // The path_segments are held in reversed order, so rev is needed here.
60        let path = chain!(
61            self.path_segments.iter().rev().map(|id| id.name(db).long(db)),
62            [self.name.long(db)]
63        )
64        .format("::");
65        format!("{prefix}{path}")
66    }
67
68    /// Returns the number of path segments.
69    pub fn segment_count(&self) -> usize {
70        self.path_segments.len()
71    }
72}
73
74/// How an item is accessed from a context.
75#[derive(Clone, Debug, PartialEq, Eq, salsa::SalsaValue)]
76pub enum ItemAccessKind<'db> {
77    /// The item is directly defined in the current module.
78    DirectInModule,
79    /// The item is accessed via a `use` statement. Contains the alias name used.
80    ViaUse(String),
81    /// The item is in the prelude.
82    ViaPrelude,
83    /// The item is accessed via a `use *` (global use).
84    ViaGlobalUse,
85    /// The item is accessed via `crate::`.
86    ViaCrate,
87    /// The item requires its full path.
88    FullPath(CrateId<'db>),
89}
90
91/// Computes the shortest valid path to access an item from a given module context.
92///
93/// # Arguments
94/// * `db` - The database
95/// * `item` - The item to find a path to
96/// * `context_module` - The module from which we want to access the item
97///
98/// # Returns
99/// The shortest valid path to the item.
100fn get_contextualized_path<'db>(
101    db: &'db dyn Database,
102    item: ImportableId<'db>,
103    context_module: ModuleId<'db>,
104) -> Maybe<ItemAccessInfo<'db>> {
105    // A crate has no parent to shorten the path through - it is always accessed by its name.
106    if let ImportableId::Crate(crate_id) = item {
107        return Ok(ItemAccessInfo::new(ItemAccessKind::FullPath(crate_id), vec![], item.name(db)));
108    }
109
110    let mut resolver = Resolver::new(db, context_module, InferenceId::NoContext);
111
112    if let Some(access_info) = check_prelude(db, item, context_module, &mut resolver)? {
113        return Ok(access_info);
114    }
115
116    // Try to find the item in the context module (includes use statements, macro expansions,
117    // and star uses)
118    let item_name = item.name(db);
119    if let Some(info) = resolver.resolve_item_in_module(context_module, item_name)
120        && let Ok::<ImportableId<'_>, _>(found_item) = info.item_id.try_into()
121        && found_item == item
122    {
123        return Ok(ItemAccessInfo::new(ItemAccessKind::DirectInModule, vec![], item_name));
124    }
125
126    let (owning_crate, path_items) = ancestors(db, item);
127    for (i, ancestor) in path_items.iter().enumerate() {
128        // Check if the ancestor is accessible from the context (includes use statements, macro
129        // expansions, and star uses)
130        if let Some(info) = resolver.resolve_item_in_module(context_module, ancestor.name(db))
131            && let Ok::<ImportableId<'_>, _>(found_item) = info.item_id.try_into()
132            && found_item == *ancestor
133        {
134            return Ok(ItemAccessInfo::new(
135                ItemAccessKind::DirectInModule,
136                path_items[..i + 1].iter().copied().collect_vec(),
137                item_name,
138            ));
139        }
140
141        // Check if the ancestor is in the prelude.
142        if let Some(access_info) = check_prelude(db, *ancestor, context_module, &mut resolver)? {
143            let mut res_path_segments = path_items[..i + 1].iter().copied().collect_vec();
144            res_path_segments.extend(access_info.path_segments);
145            return Ok(ItemAccessInfo::new(
146                ItemAccessKind::ViaPrelude,
147                res_path_segments,
148                item_name,
149            ));
150        }
151    }
152
153    if owning_crate == context_module.owning_crate(db) {
154        // Removing the crate name for the provided path, as `crate::` prefix already denotes the
155        // crate root.
156        let mut path_items = path_items;
157        path_items.pop();
158        return Ok(ItemAccessInfo::new(ItemAccessKind::ViaCrate, path_items, item_name));
159    }
160
161    Ok(ItemAccessInfo::new(ItemAccessKind::FullPath(owning_crate), path_items, item_name))
162}
163
164/// Check if the given item is in the prelude module, and its name is not shadowed by a different
165/// item in the context module.
166fn check_prelude<'db>(
167    db: &'db dyn Database,
168    item: ImportableId<'db>,
169    context_module: ModuleId<'db>,
170    resolver: &mut Resolver<'db>,
171) -> Maybe<Option<ItemAccessInfo<'db>>> {
172    let settings = db
173        .crate_config(context_module.owning_crate(db))
174        .map(|c| &c.settings)
175        .unwrap_or_else(|| default_crate_settings(db));
176    if let Some(prelude_module) = db.get_prelude_submodule(settings)
177        && let Some(module_item_id) = db.module_item_by_name(prelude_module, item.name(db))?
178    {
179        trace!("Found item {} in prelude module as {module_item_id:?}", item.name(db).long(db));
180        // Deal with use statements
181        let is_prelude = match (item, ResolvedGenericItem::from_module_item(db, module_item_id)?) {
182            (ImportableId::Constant(id), ResolvedGenericItem::GenericConstant(resolved_id)) => {
183                id == resolved_id
184            }
185            (
186                ImportableId::Submodule(id),
187                ResolvedGenericItem::Module(ModuleId::Submodule(resolved_id)),
188            ) => id == resolved_id,
189            (
190                ImportableId::FreeFunction(id),
191                ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(resolved_id)),
192            ) => id == resolved_id,
193            (
194                ImportableId::ExternFunction(id),
195                ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(resolved_id)),
196            ) => id == resolved_id,
197            (
198                ImportableId::Struct(id),
199                ResolvedGenericItem::GenericType(GenericTypeId::Struct(resolved_id)),
200            ) => id == resolved_id,
201            (
202                ImportableId::Enum(id),
203                ResolvedGenericItem::GenericType(GenericTypeId::Enum(resolved_id)),
204            ) => id == resolved_id,
205            (
206                ImportableId::ExternType(id),
207                ResolvedGenericItem::GenericType(GenericTypeId::Extern(resolved_id)),
208            ) => id == resolved_id,
209            (ImportableId::TypeAlias(id), ResolvedGenericItem::GenericTypeAlias(resolved_id)) => {
210                id == resolved_id
211            }
212            (ImportableId::ImplAlias(id), ResolvedGenericItem::GenericImplAlias(resolved_id)) => {
213                id == resolved_id
214            }
215            (ImportableId::Trait(id), ResolvedGenericItem::Trait(resolved_id)) => id == resolved_id,
216            (ImportableId::Impl(id), ResolvedGenericItem::Impl(resolved_id)) => id == resolved_id,
217            (ImportableId::MacroDeclaration(id), ResolvedGenericItem::Macro(resolved_id)) => {
218                id == resolved_id
219            }
220            (ImportableId::Variant(id), ResolvedGenericItem::Variant(resolved_id)) => {
221                id == resolved_id.id
222            }
223            _ => false,
224        };
225
226        if is_prelude {
227            // A different item bound to the same name in the context module shadows the prelude
228            // item, making the short name refer to the wrong item.
229            if let Some(info) = resolver.resolve_item_in_module(context_module, item.name(db))
230                && !matches!(
231                    info.item_id.try_into(),
232                    Ok::<ImportableId<'_>, _>(found_item) if found_item == item
233                )
234            {
235                return Ok(None);
236            }
237            return Ok(Some(ItemAccessInfo::new(
238                ItemAccessKind::ViaPrelude,
239                vec![],
240                item.name(db),
241            )));
242        }
243    }
244    Ok(None)
245}
246
247/// Returns the objects in the path to the item, from item to crate root (first item is
248/// parent if exists).
249fn ancestors<'db>(
250    db: &'db dyn Database,
251    item: ImportableId<'db>,
252) -> (CrateId<'db>, Vec<ImportableId<'db>>) {
253    let mut current = item;
254    let mut path = vec![];
255    while let Some(parent) = get_importable_parent(db, current) {
256        path.push(parent);
257        current = parent;
258        if let ImportableId::Crate(crate_id) = current {
259            return (crate_id, path);
260        }
261    }
262    unreachable!("Ancestor path must end with a crate root.")
263}
264
265/// Returns the importable parent of an item.
266fn get_importable_parent<'db>(
267    db: &'db dyn Database,
268    item: ImportableId<'db>,
269) -> Option<ImportableId<'db>> {
270    match item {
271        ImportableId::Variant(id) => Some(ImportableId::Enum(id.enum_id(db))),
272        _ => {
273            let mut module_id = item.parent_module(db)?;
274            loop {
275                match module_id {
276                    ModuleId::CrateRoot(id) => return Some(ImportableId::Crate(id)),
277                    ModuleId::Submodule(id) => return Some(ImportableId::Submodule(id)),
278                    ModuleId::MacroCall { id, .. } => {
279                        module_id = id.parent_module(db);
280                    }
281                }
282            }
283        }
284    }
285}
286
287/// Query implementation of [PathSemantic::contextualized_path].
288#[salsa::tracked(returns(clone))]
289fn contextualized_path_query<'db>(
290    db: &'db dyn Database,
291    _tracked: Tracked,
292    item: ImportableId<'db>,
293    context_module: ModuleId<'db>,
294) -> Maybe<ItemAccessInfo<'db>> {
295    let info = get_contextualized_path(db, item, context_module);
296    trace!(?item, ?context_module, ?info);
297    info
298}
299
300/// A trait for contextualizing the full path of an item.
301pub trait ContextualizePath<'db> {
302    /// Returns the shortest valid path to access an item from a given module context.
303    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String;
304}
305
306/// Marker trait for contextualizing the full path of an item implementation based on ImportableId.
307trait IntoImportableMarker<'db>: Into<ImportableId<'db>> {}
308
309impl<'db, T> ContextualizePath<'db> for T
310where
311    for<'a> &'a T: IntoImportableMarker<'db>,
312    T: TopLevelLanguageElementId<'db>,
313{
314    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
315        if let Ok(info) = contextualized_path_query(db, (), self.into(), context_module) {
316            info.path(db)
317        } else {
318            self.full_path(db)
319        }
320    }
321}
322
323impl<'db> IntoImportableMarker<'db> for &SubmoduleId<'db> {}
324impl<'db> IntoImportableMarker<'db> for &ConstantId<'db> {}
325impl<'db> IntoImportableMarker<'db> for &FreeFunctionId<'db> {}
326impl<'db> IntoImportableMarker<'db> for &StructId<'db> {}
327impl<'db> IntoImportableMarker<'db> for &EnumId<'db> {}
328impl<'db> IntoImportableMarker<'db> for &VariantId<'db> {}
329impl<'db> IntoImportableMarker<'db> for &ModuleTypeAliasId<'db> {}
330impl<'db> IntoImportableMarker<'db> for &ImplAliasId<'db> {}
331impl<'db> IntoImportableMarker<'db> for &TraitId<'db> {}
332impl<'db> IntoImportableMarker<'db> for &ImplDefId<'db> {}
333impl<'db> IntoImportableMarker<'db> for &ExternTypeId<'db> {}
334impl<'db> IntoImportableMarker<'db> for &ExternFunctionId<'db> {}
335impl<'db> IntoImportableMarker<'db> for &MacroDeclarationId<'db> {}
336
337impl<'db> ContextualizePath<'db> for CrateId<'db> {
338    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
339        if let Ok(info) = contextualized_path_query(db, (), self.into(), context_module) {
340            info.path(db)
341        } else {
342            self.long(db).name().long(db).to_string()
343        }
344    }
345}
346
347impl<'db> ContextualizePath<'db> for ModuleId<'db> {
348    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
349        match self {
350            ModuleId::CrateRoot(crate_id) => crate_id.contextualized_path(db, context_module),
351            ModuleId::Submodule(submodule_id) => {
352                submodule_id.contextualized_path(db, context_module)
353            }
354            ModuleId::MacroCall { .. } => self.full_path(db),
355        }
356    }
357}
358
359impl<'db> ContextualizePath<'db> for ModuleItemId<'db> {
360    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
361        if let Ok(item) = (*self).try_into()
362            && let Ok(info) = contextualized_path_query(db, (), item, context_module)
363        {
364            info.path(db)
365        } else {
366            self.full_path(db)
367        }
368    }
369}
370
371impl<'db> ContextualizePath<'db> for TraitFunctionId<'db> {
372    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
373        let contextualized_path = self.trait_id(db).contextualized_path(db, context_module);
374        format!("{contextualized_path}::{}", self.name(db).long(db))
375    }
376}
377
378impl<'db> ContextualizePath<'db> for ImplFunctionId<'db> {
379    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
380        let contextualized_path = self.impl_def_id(db).contextualized_path(db, context_module);
381        format!("{contextualized_path}::{}", self.name(db).long(db))
382    }
383}
384
385impl<'db> ContextualizePath<'db> for FunctionTitleId<'db> {
386    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
387        match self {
388            FunctionTitleId::Free(id) => id.contextualized_path(db, context_module),
389            FunctionTitleId::Extern(id) => id.contextualized_path(db, context_module),
390            FunctionTitleId::Trait(id) => id.contextualized_path(db, context_module),
391            FunctionTitleId::Impl(id) => id.contextualized_path(db, context_module),
392        }
393    }
394}
395
396impl<'db> ContextualizePath<'db> for crate::ConcreteTraitId<'db> {
397    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
398        self.long(db).contextualized_path(db, context_module)
399    }
400}
401
402impl<'db> ContextualizePath<'db> for crate::items::trt::ConcreteTraitLongId<'db> {
403    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
404        let path = self.trait_id.contextualized_path(db, context_module);
405        if self.generic_args.is_empty() {
406            path
407        } else {
408            let args = self
409                .generic_args
410                .iter()
411                .map(|arg| arg.contextualized_path(db, context_module))
412                .format(", ");
413            format!("{path}::<{args}>")
414        }
415    }
416}
417
418impl<'db> ContextualizePath<'db> for GenericTypeId<'db> {
419    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
420        match self {
421            GenericTypeId::Struct(id) => id.contextualized_path(db, context_module),
422            GenericTypeId::Enum(id) => id.contextualized_path(db, context_module),
423            GenericTypeId::Extern(id) => id.contextualized_path(db, context_module),
424        }
425    }
426}
427
428impl<'db> ContextualizePath<'db> for crate::ConcreteTypeId<'db> {
429    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
430        let path = self.generic_type(db).contextualized_path(db, context_module);
431        let generic_args = self.generic_args(db);
432        if generic_args.is_empty() {
433            path
434        } else {
435            let args = generic_args
436                .iter()
437                .map(|arg| arg.contextualized_path(db, context_module))
438                .format(", ");
439            format!("{path}::<{args}>")
440        }
441    }
442}
443
444impl<'db> ContextualizePath<'db> for crate::TypeId<'db> {
445    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
446        self.long(db).contextualized_path(db, context_module)
447    }
448}
449
450impl<'db> ContextualizePath<'db> for crate::TypeLongId<'db> {
451    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
452        match self {
453            crate::TypeLongId::Concrete(concrete) => {
454                concrete.contextualized_path(db, context_module)
455            }
456            crate::TypeLongId::Tuple(inner_types) => {
457                let inner = inner_types.iter().map(|ty| ty.contextualized_path(db, context_module));
458                match inner.exactly_one() {
459                    Ok(single) => format!("({single},)"),
460                    Err(other) => format!("({})", other.format(", ")),
461                }
462            }
463            crate::TypeLongId::Snapshot(ty) => {
464                format!("@{}", ty.contextualized_path(db, context_module))
465            }
466            crate::TypeLongId::FixedSizeArray { type_id, size } => {
467                format!(
468                    "[{}; {}]",
469                    type_id.contextualized_path(db, context_module),
470                    size.contextualized_path(db, context_module)
471                )
472            }
473            crate::TypeLongId::Coupon(function_id) => {
474                format!("{}::Coupon", function_id.contextualized_path(db, context_module))
475            }
476            crate::TypeLongId::ImplType(impl_type_id) => format!(
477                "{}::{}",
478                impl_type_id.impl_id().contextualized_path(db, context_module),
479                impl_type_id.ty().name(db).long(db)
480            ),
481            _ => self.format(db),
482        }
483    }
484}
485
486impl<'db> ContextualizePath<'db> for crate::GenericArgumentId<'db> {
487    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
488        match self {
489            crate::GenericArgumentId::Type(ty) => ty.contextualized_path(db, context_module),
490            crate::GenericArgumentId::Constant(value) => {
491                value.contextualized_path(db, context_module)
492            }
493            crate::GenericArgumentId::Impl(imp) => imp.contextualized_path(db, context_module),
494            crate::GenericArgumentId::NegImpl(_) => self.format(db),
495        }
496    }
497}
498
499impl<'db> ContextualizePath<'db> for crate::items::constant::ConstValueId<'db> {
500    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
501        self.long(db).contextualized_path(db, context_module)
502    }
503}
504
505impl<'db> ContextualizePath<'db> for crate::items::constant::ConstValue<'db> {
506    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
507        use crate::items::constant::ConstValue;
508        match self {
509            ConstValue::Int(value, _) => value.to_string(),
510            ConstValue::Enum(variant, payload) => {
511                let variant_path = variant.id.contextualized_path(db, context_module);
512                // A unit payload (e.g. of `None`, `true`, `false`) is an empty struct - elide it.
513                if matches!(payload.long(db), ConstValue::Struct(members, _) if members.is_empty())
514                {
515                    variant_path
516                } else {
517                    format!("{variant_path}({})", payload.contextualized_path(db, context_module))
518                }
519            }
520            ConstValue::NonZero(inner) => inner.contextualized_path(db, context_module),
521            ConstValue::ImplConstant(impl_constant_id) => format!(
522                "{}::{}",
523                impl_constant_id.impl_id().contextualized_path(db, context_module),
524                impl_constant_id.trait_constant_id().name(db).long(db)
525            ),
526            ConstValue::Generic(_)
527            | ConstValue::Struct(..)
528            | ConstValue::Var(..)
529            | ConstValue::Missing(_) => format!("{:?}", self.debug(db)),
530        }
531    }
532}
533
534impl<'db> ContextualizePath<'db> for crate::items::imp::ImplId<'db> {
535    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
536        self.long(db).contextualized_path(db, context_module)
537    }
538}
539
540impl<'db> ContextualizePath<'db> for crate::items::imp::ImplLongId<'db> {
541    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
542        use crate::items::imp::ImplLongId;
543        match self {
544            ImplLongId::Concrete(concrete_impl) => {
545                let long = concrete_impl.long(db);
546                let path = long.impl_def_id.contextualized_path(db, context_module);
547                if long.generic_args.is_empty() {
548                    path
549                } else {
550                    let args = long
551                        .generic_args
552                        .iter()
553                        .map(|arg| arg.contextualized_path(db, context_module))
554                        .format(", ");
555                    format!("{path}::<{args}>")
556                }
557            }
558            ImplLongId::GenericParameter(param) => {
559                param.name(db).map(|name| name.to_string(db)).unwrap_or_else(|| self.format(db))
560            }
561            ImplLongId::SelfImpl(concrete_trait) => {
562                concrete_trait.contextualized_path(db, context_module)
563            }
564            ImplLongId::ImplImpl(impl_impl) => format!(
565                "{}::{}",
566                impl_impl.impl_id().contextualized_path(db, context_module),
567                impl_impl.trait_impl_id().name(db).long(db)
568            ),
569            // Inferred and generated impls keep the plain formatting.
570            ImplLongId::ImplVar(_) | ImplLongId::GeneratedImpl(_) => self.format(db),
571        }
572    }
573}
574
575impl<'db> ContextualizePath<'db> for GenericFunctionId<'db> {
576    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
577        match self {
578            GenericFunctionId::Free(id) => id.contextualized_path(db, context_module),
579            GenericFunctionId::Extern(id) => id.contextualized_path(db, context_module),
580            GenericFunctionId::Impl(id) => format!(
581                "{}::{}",
582                id.impl_id.contextualized_path(db, context_module),
583                id.function.name(db).long(db)
584            ),
585        }
586    }
587}
588
589impl<'db> ContextualizePath<'db> for crate::items::functions::ConcreteFunction<'db> {
590    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
591        let path = self.generic_function.contextualized_path(db, context_module);
592        if self.generic_args.is_empty() {
593            path
594        } else {
595            let args = self
596                .generic_args
597                .iter()
598                .map(|arg| arg.contextualized_path(db, context_module))
599                .format(", ");
600            format!("{path}::<{args}>")
601        }
602    }
603}
604
605impl<'db> ContextualizePath<'db> for crate::FunctionId<'db> {
606    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
607        self.long(db).contextualized_path(db, context_module)
608    }
609}
610
611impl<'db> ContextualizePath<'db> for crate::items::functions::FunctionLongId<'db> {
612    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
613        self.function.contextualized_path(db, context_module)
614    }
615}
616
617impl<'db> ContextualizePath<'db> for crate::items::functions::ConcreteFunctionWithBodyId<'db> {
618    fn contextualized_path(&self, db: &'db dyn Database, context_module: ModuleId<'db>) -> String {
619        match self.function_id(db) {
620            Ok(id) => id.contextualized_path(db, context_module),
621            Err(_) => self.full_path(db),
622        }
623    }
624}