cairo_lang_doc/
documentable_formatter.rs

1use std::fmt;
2use std::fmt::Write;
3use std::option::Option;
4
5use cairo_lang_defs::ids::{
6    ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, ImplAliasId,
7    ImplConstantDefId, ImplDefId, ImplFunctionId, ImplItemId, ImplTypeDefId, LanguageElementId,
8    LookupItemId, MacroDeclarationId, MemberId, ModuleItemId, ModuleTypeAliasId,
9    NamedLanguageElementId, StructId, TopLevelLanguageElementId, TraitConstantId, TraitFunctionId,
10    TraitId, TraitItemId, TraitTypeId, VariantId,
11};
12use cairo_lang_filesystem::ids::Tracked;
13use cairo_lang_semantic::items::constant::{ConstValue, ConstantSemantic};
14use cairo_lang_semantic::items::enm::EnumSemantic;
15use cairo_lang_semantic::items::extern_function::ExternFunctionSemantic;
16use cairo_lang_semantic::items::generics::GenericArgumentId;
17use cairo_lang_semantic::items::imp::ImplSemantic;
18use cairo_lang_semantic::items::macro_declaration::MacroDeclarationSemantic;
19use cairo_lang_semantic::items::modifiers::get_relevant_modifier;
20use cairo_lang_semantic::types::TypeId;
21use cairo_lang_semantic::{Expr, TypeLongId};
22use cairo_lang_syntax::node::ast::WrappedMacro;
23use cairo_lang_syntax::node::kind::SyntaxKind;
24use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
25use itertools::Itertools;
26use salsa::Database;
27
28use crate::documentable_item::DocumentableItemId;
29use crate::helpers::{
30    extract_and_format, format_resolver_generic_params, get_generic_params,
31    get_struct_attributes_syntax, get_syntactic_evaluation, get_syntactic_visibility, resolve_type,
32};
33use crate::location_links::{LocationLink, format_signature};
34use crate::signature_data::{DocumentableItemSignatureData, SignatureDataRetriever};
35use crate::signature_errors::SignatureError;
36
37/// Used for indenting children items of complex data type signature, e.g., struct members.
38const INDENT: &str = "    ";
39/// Returned when item's signature could not be determined.
40pub(crate) const MISSING: &str = "<missing>";
41
42/// Gets the signature of an item and a list of [`LocationLink`]s to enable mapping
43/// signature slices on documentable items.
44#[salsa::tracked]
45pub fn get_item_signature_with_links<'db>(
46    db: &'db dyn Database,
47    _tracked: Tracked,
48    item_id: DocumentableItemId<'db>,
49) -> (Option<String>, Vec<LocationLink<'db>>) {
50    let mut f = HirFormatter::new(db);
51    match item_id {
52        DocumentableItemId::LookupItem(item_id) => match item_id {
53            LookupItemId::ModuleItem(item_id) => match item_id {
54                ModuleItemId::Struct(item_id) => item_id.get_signature_with_links(&mut f),
55                ModuleItemId::Enum(item_id) => item_id.get_signature_with_links(&mut f),
56                ModuleItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
57                ModuleItemId::FreeFunction(item_id) => item_id.get_signature_with_links(&mut f),
58                ModuleItemId::TypeAlias(item_id) => item_id.get_signature_with_links(&mut f),
59                ModuleItemId::ImplAlias(item_id) => item_id.get_signature_with_links(&mut f),
60                ModuleItemId::Trait(item_id) => item_id.get_signature_with_links(&mut f),
61                ModuleItemId::Impl(item_id) => item_id.get_signature_with_links(&mut f),
62                ModuleItemId::ExternType(item_id) => item_id.get_signature_with_links(&mut f),
63                ModuleItemId::ExternFunction(item_id) => item_id.get_signature_with_links(&mut f),
64                ModuleItemId::Submodule(_) => (None, vec![]),
65                ModuleItemId::Use(_) => (None, vec![]),
66                ModuleItemId::MacroDeclaration(item_id) => item_id.get_signature_with_links(&mut f),
67            },
68            LookupItemId::TraitItem(item_id) => match item_id {
69                TraitItemId::Function(item_id) => item_id.get_signature_with_links(&mut f),
70                TraitItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
71                TraitItemId::Type(item_id) => item_id.get_signature_with_links(&mut f),
72                TraitItemId::Impl(_) => (None, vec![]),
73            },
74            LookupItemId::ImplItem(item_id) => match item_id {
75                ImplItemId::Function(item_id) => item_id.get_signature_with_links(&mut f),
76                ImplItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
77                ImplItemId::Type(item_id) => item_id.get_signature_with_links(&mut f),
78                ImplItemId::Impl(_) => (None, vec![]),
79            },
80        },
81        DocumentableItemId::Member(item_id) => item_id.get_signature_with_links(&mut f),
82        DocumentableItemId::Variant(item_id) => item_id.get_signature_with_links(&mut f),
83        DocumentableItemId::Crate(_) => (None, vec![]),
84    }
85}
86
87pub trait HirDisplay<'db> {
88    /// Formats signature.
89    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError>;
90
91    /// Gets the signature of an item (i.e., the item without its body).
92    fn get_signature(&self, f: &mut HirFormatter<'db>) -> Option<String> {
93        match self.hir_fmt(f) {
94            Ok(_) => Some(f.buf.clone()),
95            Err(_) => None,
96        }
97    }
98
99    /// Gets the signature of an item and a list of [`LocationLink`]s to enable mapping
100    /// signature slices on documentable items.
101    fn get_signature_with_links(
102        &self,
103        f: &mut HirFormatter<'db>,
104    ) -> (Option<String>, Vec<LocationLink<'db>>) {
105        let signature = self.get_signature(f);
106        (signature, f.location_links.clone())
107    }
108}
109
110/// Documentable items signature formatter.
111pub struct HirFormatter<'db> {
112    /// The database handle.
113    db: &'db dyn Database,
114    /// A buffer to intercept writes with.
115    buf: String,
116    /// Linkable signature items.
117    location_links: Vec<LocationLink<'db>>,
118}
119
120impl<'db> fmt::Write for HirFormatter<'db> {
121    fn write_str(&mut self, s: &str) -> fmt::Result {
122        self.buf.push_str(s);
123        Ok(())
124    }
125}
126
127/// [`HirFormatter`] implementation.
128impl<'db> HirFormatter<'db> {
129    /// Creates new instance of [`HirFormatter`].
130    pub fn new(db: &'db dyn Database) -> Self {
131        Self { db, buf: String::new(), location_links: Vec::new() }
132    }
133
134    /// Adds a [`LocationLink`] to [`HirFormatter`] instance.
135    fn add_location_link(&mut self, start: usize, end: usize, item_id: DocumentableItemId<'db>) {
136        self.location_links.push(LocationLink { start, end, item_id })
137    }
138
139    /// Wraps `HirFormatter::write_str` call to satisfy the `SignatureError` return type.
140    fn hir_write(&mut self, s: &str) -> Result<(), SignatureError> {
141        self.write_str(s)?;
142        Ok(())
143    }
144
145    /// Adds type's [`LocationLink`] to [`HirFormatter`] instance, formats and writes relevant
146    /// signature slice.
147    fn write_type(
148        &mut self,
149        prefix: Option<&str>,
150        element_type: TypeId<'db>,
151        postfix: Option<&str>,
152        full_path: &String,
153    ) -> Result<(), SignatureError> {
154        self.write_str(prefix.unwrap_or_default())?;
155        let formatted_element_type = element_type.format(self.db);
156
157        if let TypeLongId::Tuple(vec_types) = element_type.long(self.db) {
158            self.write_str("(")?;
159            let mut count = vec_types.len();
160            for t in vec_types {
161                self.write_type(None, *t, if count == 1 { None } else { Some(", ") }, full_path)?;
162                count -= 1;
163            }
164            self.write_str(")")?;
165        } else if is_the_same_root(full_path, &formatted_element_type) {
166            let documentable_id = resolve_type(self.db, element_type);
167            match documentable_id {
168                Some(documentable_id) => {
169                    let start_offset = self.buf.len();
170                    self.write_str(&extract_and_format(&formatted_element_type))?;
171                    let end_offset = self.buf.len();
172                    self.add_location_link(start_offset, end_offset, documentable_id);
173                }
174                None => {
175                    self.write_str(&extract_and_format(&formatted_element_type))?;
176                }
177            }
178        } else {
179            self.write_str(&extract_and_format(&formatted_element_type))?;
180        }
181        self.hir_write(postfix.unwrap_or_default())
182    }
183
184    /// Adds [`LocationLink`] to [`HirFormatter`] instance, writes `name` argument into signature
185    /// buf.
186    fn write_link(
187        &mut self,
188        name: String,
189        documentable_id: Option<DocumentableItemId<'db>>,
190    ) -> fmt::Result {
191        match documentable_id {
192            Some(documentable_id) => {
193                let start_offset = self.buf.len();
194                self.write_str(&name)?;
195                let end_offset = self.buf.len();
196                self.add_location_link(start_offset, end_offset, documentable_id);
197                Ok(())
198            }
199            None => self.write_str(&extract_and_format(&name)),
200        }
201    }
202
203    /// Applies extra formatting to item signature.
204    /// Avoid using for types whose signatures are invalid Cairo code
205    /// (such as struct members or enum variants).
206    fn format(&mut self) {
207        let (formatted_signature, moved_location_links) = format_signature(
208            self.db,
209            std::mem::take(&mut self.buf),
210            std::mem::take(&mut self.location_links),
211        );
212        self.buf = formatted_signature;
213        self.location_links = moved_location_links;
214    }
215
216    /// Writes a chunk of text with [`LocationLink`]s into [`HirFormatter`] instance.
217    fn write_chunk(
218        &mut self,
219        text: &str,
220        location_links: Vec<LocationLink<'db>>,
221    ) -> Result<(), SignatureError> {
222        let offset = self.buf.len();
223        self.buf.push_str(text);
224        for location_link in location_links {
225            self.add_location_link(
226                offset + location_link.start,
227                offset + location_link.end,
228                location_link.item_id,
229            );
230        }
231        Ok(())
232    }
233}
234
235impl<'db> HirDisplay<'db> for VariantId<'db> {
236    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
237        let name = self.name(f.db);
238        let variant_semantic = f.db.variant_semantic(self.enum_id(f.db), *self)?;
239        if !variant_semantic.ty.is_unit(f.db) {
240            f.write_type(
241                Some(&format!("{}: ", name.long(f.db))),
242                variant_semantic.ty,
243                None,
244                &self.full_path(f.db),
245            )
246        } else {
247            f.hir_write(name.long(f.db))
248        }
249    }
250}
251
252impl<'db> HirDisplay<'db> for EnumId<'db> {
253    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
254        let enum_full_signature = Self::retrieve_signature_data(f.db, *self)?;
255        write!(
256            f,
257            "{}enum {} {{",
258            get_syntactic_visibility(&enum_full_signature.visibility),
259            enum_full_signature.name.long(f.db),
260        )?;
261        let variants = enum_full_signature.variants;
262        if let Some(variants) = variants {
263            let is_variants_empty = variants.is_empty();
264            for (name, variant_type) in variants {
265                if !variant_type.is_unit(f.db) {
266                    f.write_type(
267                        Some(&format!("\n{INDENT}{}: ", name.long(f.db))),
268                        variant_type,
269                        Some(","),
270                        &enum_full_signature.full_path,
271                    )
272                } else {
273                    f.hir_write(&format!("\n{INDENT}{},", name.long(f.db)))
274                }?;
275            }
276            f.hir_write(if is_variants_empty { "}" } else { "\n}" })
277        } else {
278            f.hir_write("}")
279        }?;
280        f.format();
281        Ok(())
282    }
283}
284
285impl<'db> HirDisplay<'db> for MemberId<'db> {
286    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
287        let member_full_signature = Self::retrieve_signature_data(f.db, *self)?;
288        if let Some(return_type) = member_full_signature.return_type {
289            if return_type.is_unit(f.db) {
290                f.hir_write(&format!(
291                    "{}{} = ",
292                    get_syntactic_visibility(&member_full_signature.visibility),
293                    member_full_signature.name.long(f.db),
294                ))
295            } else {
296                f.write_type(
297                    Some(&format!(
298                        "{}{}: ",
299                        get_syntactic_visibility(&member_full_signature.visibility),
300                        member_full_signature.name.long(f.db),
301                    )),
302                    return_type,
303                    None,
304                    &member_full_signature.full_path,
305                )
306            }
307        } else {
308            Err(SignatureError::FailedWritingSignature(member_full_signature.full_path.clone()))
309        }
310    }
311}
312
313impl<'db> HirDisplay<'db> for StructId<'db> {
314    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
315        let struct_full_signature = Self::retrieve_signature_data(f.db, *self)?;
316        if let Some(attributes) = struct_full_signature.attributes {
317            let stx = get_struct_attributes_syntax(attributes, f.db)?;
318            f.hir_write(&stx)?;
319        }
320        write!(
321            f,
322            "{}struct {}",
323            get_syntactic_visibility(&struct_full_signature.visibility),
324            struct_full_signature.name.long(f.db),
325        )?;
326        if let Some(generic_params) = struct_full_signature.generic_params {
327            let (stx, lls) = get_generic_params(generic_params, f.db)?;
328            f.write_chunk(&stx, lls)?;
329        }
330
331        f.hir_write(" {")?;
332
333        if let Some(members) = struct_full_signature.members {
334            let is_members_empty = members.is_empty();
335            for member in members {
336                let (name, member_type, visibility) = member;
337                f.write_type(
338                    Some(&format!(
339                        "\n{INDENT}{}{}: ",
340                        get_syntactic_visibility(&visibility),
341                        name.long(f.db),
342                    )),
343                    member_type,
344                    Some(","),
345                    &struct_full_signature.full_path,
346                )?;
347            }
348            f.hir_write(if is_members_empty { "}" } else { "\n}" })?;
349        };
350        f.format();
351        Ok(())
352    }
353}
354
355impl<'db> HirDisplay<'db> for FreeFunctionId<'db> {
356    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
357        let free_function_full_signature = Self::retrieve_signature_data(f.db, *self)?;
358        write_function_signature(f, free_function_full_signature, "".to_string())?;
359        f.format();
360        Ok(())
361    }
362}
363
364impl<'db> HirDisplay<'db> for ConstantId<'db> {
365    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
366        let constant_full_signature = Self::retrieve_signature_data(f.db, *self)?;
367        write!(
368            f,
369            "{}const {}: ",
370            get_syntactic_visibility(&constant_full_signature.visibility),
371            constant_full_signature.name.long(f.db),
372        )?;
373        if let Some(return_type) = constant_full_signature.return_type {
374            f.write_type(None, return_type, Some(" = "), &constant_full_signature.full_path)?;
375        }
376        if let Some(return_value_expr) = constant_full_signature.return_value_expr {
377            match return_value_expr {
378                Expr::Literal(v) => write!(f, "{};", v.value),
379                Expr::FunctionCall(_) => {
380                    let const_value_id = f.db.constant_const_value(*self)?;
381                    let constant_value = const_value_id.long(f.db);
382                    if let ConstValue::Int(value, _) = constant_value {
383                        let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?;
384                        write!(f, "{stx} // = {value}")
385                    } else {
386                        let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?;
387                        write!(f, "{stx};")
388                    }
389                }
390                _ => write!(
391                    f,
392                    "{}",
393                    get_syntactic_evaluation(constant_full_signature.item_id, f.db)?
394                ),
395            }
396        } else {
397            write!(f, "{}", get_syntactic_evaluation(constant_full_signature.item_id, f.db)?)
398        }?;
399        f.format();
400        Ok(())
401    }
402}
403
404impl<'db> HirDisplay<'db> for ImplConstantDefId<'db> {
405    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
406        let constant_full_signature = Self::retrieve_signature_data(f.db, *self)?;
407        if let Some(return_type) = constant_full_signature.return_type {
408            f.write_type(
409                Some(&format!("const {}: ", constant_full_signature.name.long(f.db))),
410                return_type,
411                Some(" = "),
412                &constant_full_signature.full_path,
413            )?;
414        }
415        let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?;
416        f.hir_write(&stx)?;
417        f.format();
418        Ok(())
419    }
420}
421
422impl<'db> HirDisplay<'db> for TraitFunctionId<'db> {
423    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
424        let trait_function_full_signature = Self::retrieve_signature_data(f.db, *self)?;
425        write_function_signature(f, trait_function_full_signature, "".to_string())?;
426        f.format();
427        Ok(())
428    }
429}
430
431impl<'db> HirDisplay<'db> for ImplFunctionId<'db> {
432    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
433        let impl_function_full_signature = Self::retrieve_signature_data(f.db, *self)?;
434        write_function_signature(f, impl_function_full_signature, "".to_string())?;
435        f.format();
436        Ok(())
437    }
438}
439
440impl<'db> HirDisplay<'db> for TraitId<'db> {
441    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
442        let trait_full_signature = Self::retrieve_signature_data(f.db, *self)?;
443        write!(
444            f,
445            "{}trait {}",
446            get_syntactic_visibility(&trait_full_signature.visibility),
447            trait_full_signature.name.long(f.db),
448        )?;
449        if let Some(generic_params) = trait_full_signature.generic_params {
450            let (stx, lls) = get_generic_params(generic_params, f.db)?;
451            f.write_chunk(&stx, lls)?;
452        };
453        f.format();
454        Ok(())
455    }
456}
457
458impl<'db> HirDisplay<'db> for TraitConstantId<'db> {
459    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
460        let trait_const_full_signature = Self::retrieve_signature_data(f.db, *self)?;
461        let return_type = trait_const_full_signature.return_type.ok_or(
462            SignatureError::FailedRetrievingSemanticData(trait_const_full_signature.full_path),
463        )?;
464        write!(
465            f,
466            "const {}: {};",
467            trait_const_full_signature.name.long(f.db),
468            extract_and_format(&return_type.format(f.db)),
469        )?;
470
471        f.format();
472        Ok(())
473    }
474}
475
476impl<'db> HirDisplay<'db> for ImplDefId<'db> {
477    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
478        let impl_def_full_signature = Self::retrieve_signature_data(f.db, *self)?;
479        let trait_id = f.db.impl_def_trait(*self)?;
480
481        if let Some(resolver_generic_params) = impl_def_full_signature.resolver_generic_params {
482            let resolver_generic_params =
483                format_resolver_generic_params(f.db, resolver_generic_params);
484            write!(
485                f,
486                "{}impl {}{} of {}",
487                get_syntactic_visibility(&impl_def_full_signature.visibility),
488                impl_def_full_signature.name.long(f.db),
489                resolver_generic_params,
490                trait_id.name(f.db).long(f.db),
491            )?;
492        }
493        if let Some(generic_args) = impl_def_full_signature.generic_args {
494            write_generic_args(generic_args, f)?;
495        }
496        f.hir_write(";")?;
497        f.format();
498        Ok(())
499    }
500}
501
502impl<'db> HirDisplay<'db> for ImplAliasId<'db> {
503    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
504        let impl_alias_full_signature = Self::retrieve_signature_data(f.db, *self)?;
505        write!(
506            f,
507            "{}impl {} = ",
508            get_syntactic_visibility(&impl_alias_full_signature.visibility),
509            self.name(f.db).long(f.db),
510        )?;
511        let stx = get_syntactic_evaluation(impl_alias_full_signature.item_id, f.db)?;
512        write!(f, "{}", stx)?;
513        f.format();
514        Ok(())
515    }
516}
517
518impl<'db> HirDisplay<'db> for ModuleTypeAliasId<'db> {
519    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
520        let module_type_alias_full_signature = Self::retrieve_signature_data(f.db, *self)?;
521        write_type_signature(f, module_type_alias_full_signature, false)?;
522        f.format();
523        Ok(())
524    }
525}
526
527impl<'db> HirDisplay<'db> for TraitTypeId<'db> {
528    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
529        let trait_type_full_signature = Self::retrieve_signature_data(f.db, *self)?;
530        write_type_signature(f, trait_type_full_signature, false)?;
531        f.format();
532        Ok(())
533    }
534}
535
536impl<'db> HirDisplay<'db> for ImplTypeDefId<'db> {
537    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
538        let impl_type_def_full_signature = Self::retrieve_signature_data(f.db, *self)?;
539        write_type_signature(f, impl_type_def_full_signature, false)?;
540        f.format();
541        Ok(())
542    }
543}
544
545impl<'db> HirDisplay<'db> for ExternTypeId<'db> {
546    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
547        let extern_type_full_signature = Self::retrieve_signature_data(f.db, *self)?;
548        write_type_signature(f, extern_type_full_signature, true)?;
549        f.format();
550        Ok(())
551    }
552}
553
554impl<'db> HirDisplay<'db> for ExternFunctionId<'db> {
555    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
556        let extern_function_full_signature = Self::retrieve_signature_data(f.db, *self)?;
557        let signature = f.db.extern_function_signature(*self)?;
558        write_function_signature(f, extern_function_full_signature, "extern ".to_string())?;
559        if !signature.implicits.is_empty() {
560            f.write_str(" implicits(")?;
561            let mut count = signature.implicits.len();
562            for type_id in &signature.implicits {
563                write!(
564                    f,
565                    "{}{}",
566                    extract_and_format(&type_id.format(f.db)),
567                    if count == 1 { ")".to_string() } else { ", ".to_string() }
568                )?;
569                count -= 1;
570            }
571        }
572        if !signature.panicable {
573            f.write_str(" nopanic")?;
574        };
575        f.hir_write(";")
576    }
577}
578
579impl<'db> HirDisplay<'db> for MacroDeclarationId<'db> {
580    fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> {
581        let module_item_id = ModuleItemId::MacroDeclaration(*self);
582        f.write_str(&format!("macro {} {{", module_item_id.name(f.db).long(f.db)))?;
583        let macro_rules_data = f.db.macro_declaration_rules(*self)?;
584
585        for rule_data in macro_rules_data {
586            let (left_bracket, elements, right_bracket) = match rule_data.pattern {
587                WrappedMacro::Braced(m) => ("{", m.elements(f.db), "}"),
588                WrappedMacro::Bracketed(m) => ("[", m.elements(f.db), "]"),
589                WrappedMacro::Parenthesized(m) => ("(", m.elements(f.db), ")"),
590            };
591            let macro_match = elements
592                .elements_vec(f.db)
593                .iter()
594                .map(|element| element.as_syntax_node().get_text(f.db))
595                .join("")
596                .split_whitespace()
597                .join(" ");
598            f.write_str(
599                format!("\n    {}{}{} => {{ ... }};", left_bracket, macro_match, right_bracket)
600                    .as_str(),
601            )?;
602        }
603        f.hir_write("\n}")
604    }
605}
606
607/// Checks if the given paths come from the same root.
608fn is_the_same_root(path1: &str, path2: &str) -> bool {
609    fn extract_root(input: &str) -> &str {
610        if let Some(index) = input.find("::") { &input[..index] } else { input }
611    }
612    extract_root(path1) == extract_root(path2)
613}
614
615/// A utility function for formatting documentable function data. Use with
616/// [`DocumentableItemSignatureData`] argument created for [`FreeFunctionId`], [`TraitFunctionId`],
617/// [`ImplFunctionId`] or [`ExternFunctionId`]. As those are the items for which a
618/// [`cairo_lang_semantic::items::functions::Signature`] can be retrieved.
619fn write_function_signature<'db>(
620    f: &mut HirFormatter<'db>,
621    documentable_signature: DocumentableItemSignatureData<'db>,
622    syntactic_kind: String,
623) -> Result<(), SignatureError> {
624    let resolver_generic_params = match documentable_signature.resolver_generic_params {
625        Some(params) => format_resolver_generic_params(f.db, params),
626        None => "".to_string(),
627    };
628
629    write!(
630        f,
631        "{}{}fn {}{}",
632        get_syntactic_visibility(&documentable_signature.visibility),
633        syntactic_kind,
634        documentable_signature.name.long(f.db),
635        resolver_generic_params,
636    )?;
637    if let Some(generic_args) = documentable_signature.generic_args {
638        write_generic_args(generic_args, f)?;
639    }
640    f.write_str("(")?;
641    if let Some(params) = documentable_signature.params {
642        let mut count = params.len();
643        let mut postfix = String::from(", ");
644        for param in params {
645            if count == 1 {
646                postfix = "".to_string();
647            }
648            let syntax_node = param.id.stable_location(f.db).syntax_node(f.db);
649            let modifier = get_relevant_modifier(&param.mutability);
650            let modifier_postfix = if modifier.is_empty() { "" } else { " " };
651            if param.ty.is_fully_concrete(f.db) {
652                f.write_type(
653                    Some(&format!("{modifier}{modifier_postfix}{}: ", param.name.long(f.db))),
654                    param.ty,
655                    Some(&postfix),
656                    &documentable_signature.full_path,
657                )?;
658            } else {
659                let type_definition = get_type_clause(syntax_node, f.db).unwrap_or_default();
660                write!(
661                    f,
662                    "{modifier}{modifier_postfix}{}{type_definition}{postfix}",
663                    param.name.long(f.db)
664                )?;
665            }
666            count -= 1;
667        }
668    }
669    f.write_str(")")?;
670
671    if let Some(return_type) = documentable_signature.return_type
672        && !return_type.is_unit(f.db)
673    {
674        f.write_type(Some(" -> "), return_type, None, &documentable_signature.full_path)?;
675    }
676    Ok(())
677}
678
679/// Retrieves the [`SyntaxKind::TypeClause`] text from a [`SyntaxNode`].
680fn get_type_clause<'db>(syntax_node: SyntaxNode<'db>, db: &'db dyn Database) -> Option<String> {
681    for child in syntax_node.get_children(db).iter() {
682        if child.kind(db) == SyntaxKind::TypeClause {
683            return Some(child.get_text_without_all_comment_trivia(db));
684        }
685    }
686    Some(String::from(MISSING))
687}
688
689/// Formats the syntax of generic arguments and writes it into [`HirFormatter`].
690fn write_generic_args<'db>(
691    generic_args: Vec<GenericArgumentId<'db>>,
692    f: &mut HirFormatter<'db>,
693) -> Result<(), fmt::Error> {
694    let mut count = generic_args.len();
695    if !generic_args.is_empty() {
696        f.write_str("<")?;
697    }
698    for arg in &generic_args {
699        let documentable_id = resolve_generic_arg(*arg, f.db);
700        let _ = f.write_link(extract_and_format(&arg.format(f.db)), documentable_id);
701        let _ = f.write_str(if count == 1 { ">" } else { ", " });
702        count -= 1;
703    }
704    Ok(())
705}
706
707/// A utility function used for formatting documentable types data. Use with
708/// [`DocumentableItemSignatureData`] argument created for [`ModuleTypeAliasId`], [`TraitTypeId`],
709/// [`ImplTypeDefId`] or [`ExternTypeId`]. Because of the same signature structure.
710fn write_type_signature<'db>(
711    f: &mut HirFormatter<'db>,
712    documentable_signature: DocumentableItemSignatureData<'db>,
713    is_extern_type: bool,
714) -> Result<(), fmt::Error> {
715    write!(
716        f,
717        "{}{}type {}",
718        get_syntactic_visibility(&documentable_signature.visibility),
719        if is_extern_type { "extern " } else { "" },
720        documentable_signature.name.long(f.db)
721    )?;
722    if let Some(generic_params) = documentable_signature.generic_params {
723        let (stx, lls) = get_generic_params(generic_params, f.db)?;
724        f.write_chunk(&stx, lls)?;
725    }
726    if let Some(return_type) = documentable_signature.return_type {
727        write!(f, " = ")?;
728        f.write_type(None, return_type, None, &documentable_signature.full_path)?;
729    };
730    write!(f, ";")?;
731    Ok(())
732}
733
734/// Returns relevant [`DocumentableItemId`] for [`GenericArgumentId`] if one can be retrieved.
735fn resolve_generic_arg<'db>(
736    generic_arg_id: GenericArgumentId<'db>,
737    db: &'db dyn Database,
738) -> Option<DocumentableItemId<'db>> {
739    match generic_arg_id {
740        GenericArgumentId::Type(type_id) => resolve_type(db, type_id),
741        GenericArgumentId::Constant(constant_value_id) => match constant_value_id.ty(db) {
742            Ok(type_id) => resolve_type(db, type_id),
743            Err(_) => None,
744        },
745        GenericArgumentId::Impl(impl_id) => match impl_id.concrete_trait(db) {
746            Ok(concrete_trait) => {
747                let trait_id = concrete_trait.trait_id(db);
748                Some(DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(
749                    trait_id,
750                ))))
751            }
752            Err(_) => None,
753        },
754        GenericArgumentId::NegImpl(_) => None,
755    }
756}