cairo_lang_doc/
documentable_formatter.rs

1use std::fmt;
2use std::fmt::Write;
3use std::option::Option;
4
5use cairo_lang_defs::ids::TraitItemId::Function;
6use cairo_lang_defs::ids::{
7    ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, GenericImplItemId,
8    GenericItemId, GenericKind, GenericModuleItemId, GenericParamId, GenericTraitItemId,
9    ImplAliasId, ImplConstantDefId, ImplDefId, ImplFunctionId, ImplItemId, ImplTypeDefId,
10    LanguageElementId, LookupItemId, MemberId, ModuleId, ModuleItemId, ModuleTypeAliasId,
11    NamedLanguageElementId, StructId, TopLevelLanguageElementId, TraitConstantId, TraitFunctionId,
12    TraitId, TraitItemId, TraitTypeId, VariantId,
13};
14use cairo_lang_semantic::expr::inference::InferenceId;
15use cairo_lang_semantic::items::constant::ConstValue;
16use cairo_lang_semantic::items::functions::GenericFunctionId;
17use cairo_lang_semantic::items::generics::GenericArgumentId;
18use cairo_lang_semantic::items::modifiers::get_relevant_modifier;
19use cairo_lang_semantic::items::visibility::Visibility;
20use cairo_lang_semantic::types::TypeId;
21use cairo_lang_semantic::{ConcreteTypeId, Expr, GenericParam, TypeLongId};
22use cairo_lang_syntax::attribute::structured::Attribute;
23use cairo_lang_syntax::node::kind::SyntaxKind;
24use cairo_lang_syntax::node::{SyntaxNode, TypedStablePtr, TypedSyntaxNode, green};
25use cairo_lang_utils::LookupIntern;
26use itertools::Itertools;
27
28use crate::db::DocGroup;
29use crate::documentable_item::DocumentableItemId;
30use crate::location_links::{LocationLink, format_signature};
31use crate::signature_data::{
32    DocumentableItemSignatureData, get_constant_signature_data, get_enum_signature_data,
33    get_extern_function_full_signature, get_extern_type_full_signature,
34    get_free_function_signature_data, get_impl_alias_signature_data,
35    get_impl_constant_signature_data, get_impl_def_signature_data,
36    get_impl_function_signature_data, get_impl_type_def_full_signature, get_member_signature_data,
37    get_module_type_alias_full_signature, get_struct_signature_data,
38    get_trait_const_signature_data, get_trait_function_signature_data, get_trait_signature_data,
39    get_trait_type_full_signature,
40};
41use crate::signature_errors::SignatureError;
42
43/// Used for indenting children items of complex data type signature e.g. struct members.
44const INDENT: &str = "    ";
45/// Returned when item's signature could not be determined.
46const MISSING: &str = "<missing>";
47
48/// Gets the signature of an item (i.e., item without its body).
49pub fn get_item_signature(db: &dyn DocGroup, item_id: DocumentableItemId) -> Option<String> {
50    get_item_signature_with_links(db, item_id).0
51}
52
53/// Gets the signature of an item and a list of [`LocationLink`]s to enable mapping
54/// signature slices on documentable items.
55pub fn get_item_signature_with_links(
56    db: &dyn DocGroup,
57    item_id: DocumentableItemId,
58) -> (Option<String>, Vec<LocationLink>) {
59    let mut f = HirFormatter::new(db);
60    match item_id {
61        DocumentableItemId::LookupItem(item_id) => match item_id {
62            LookupItemId::ModuleItem(item_id) => match item_id {
63                ModuleItemId::Struct(item_id) => item_id.get_signature_with_links(&mut f),
64                ModuleItemId::Enum(item_id) => item_id.get_signature_with_links(&mut f),
65                ModuleItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
66                ModuleItemId::FreeFunction(item_id) => item_id.get_signature_with_links(&mut f),
67                ModuleItemId::TypeAlias(item_id) => item_id.get_signature_with_links(&mut f),
68                ModuleItemId::ImplAlias(item_id) => item_id.get_signature_with_links(&mut f),
69                ModuleItemId::Trait(item_id) => item_id.get_signature_with_links(&mut f),
70                ModuleItemId::Impl(item_id) => item_id.get_signature_with_links(&mut f),
71                ModuleItemId::ExternType(item_id) => item_id.get_signature_with_links(&mut f),
72                ModuleItemId::ExternFunction(item_id) => item_id.get_signature_with_links(&mut f),
73                ModuleItemId::Submodule(_) => (None, vec![]),
74                ModuleItemId::Use(_) => (None, vec![]),
75                ModuleItemId::MacroDeclaration(_) => (None, vec![]),
76            },
77            LookupItemId::TraitItem(item_id) => match item_id {
78                TraitItemId::Function(item_id) => item_id.get_signature_with_links(&mut f),
79                TraitItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
80                TraitItemId::Type(item_id) => item_id.get_signature_with_links(&mut f),
81                TraitItemId::Impl(_) => (None, vec![]),
82            },
83            LookupItemId::ImplItem(item_id) => match item_id {
84                ImplItemId::Function(item_id) => item_id.get_signature_with_links(&mut f),
85                ImplItemId::Constant(item_id) => item_id.get_signature_with_links(&mut f),
86                ImplItemId::Type(item_id) => item_id.get_signature_with_links(&mut f),
87                ImplItemId::Impl(_) => (None, vec![]),
88            },
89        },
90        DocumentableItemId::Member(item_id) => item_id.get_signature_with_links(&mut f),
91        DocumentableItemId::Variant(item_id) => item_id.get_signature_with_links(&mut f),
92        DocumentableItemId::Crate(_) => (None, vec![]),
93    }
94}
95
96pub trait HirDisplay {
97    /// Formats signature.
98    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError>;
99
100    /// Gets the signature of an item (i.e., item without its body).
101    fn get_signature(&self, f: &mut HirFormatter<'_>) -> Option<String> {
102        match self.hir_fmt(f) {
103            Ok(_) => Some(f.buf.clone()),
104            Err(_) => None,
105        }
106    }
107
108    /// Gets the signature of an item and a list of [`LocationLink`]s to enable mapping
109    /// signature slices on documentable items.
110    fn get_signature_with_links(
111        &self,
112        f: &mut HirFormatter<'_>,
113    ) -> (Option<String>, Vec<LocationLink>) {
114        let signature = self.get_signature(f);
115        (signature, f.location_links.clone())
116    }
117}
118
119/// Documentable items signature formatter.
120pub struct HirFormatter<'a> {
121    /// The database handle.
122    db: &'a dyn DocGroup,
123    /// A buffer to intercept writes with.
124    buf: String,
125    /// Linkable signature items.
126    location_links: Vec<LocationLink>,
127}
128
129impl fmt::Write for HirFormatter<'_> {
130    fn write_str(&mut self, s: &str) -> fmt::Result {
131        self.buf.push_str(s);
132        Ok(())
133    }
134}
135
136/// [`HirFormatter`] implementation.
137impl<'a> HirFormatter<'a> {
138    /// Creates new instance of [`HirFormatter`].
139    pub fn new(db: &'a dyn DocGroup) -> Self {
140        Self { db, buf: String::new(), location_links: Vec::new() }
141    }
142
143    /// Adds a [`LocationLink`] to [`HirFormatter`] instance.
144    fn add_location_link(&mut self, start: usize, end: usize, item_id: DocumentableItemId) {
145        self.location_links.push(LocationLink { start, end, item_id })
146    }
147
148    /// Adds type's [`LocationLink`] to [`HirFormatter`] instance, formats and writes relevant
149    /// signature slice.
150    fn write_type(
151        &mut self,
152        prefix: Option<&str>,
153        element_type: TypeId,
154        postfix: Option<&str>,
155        full_path: &String,
156    ) -> fmt::Result {
157        self.write_str(prefix.unwrap_or_default())?;
158        let formatted_element_type = element_type.format(self.db);
159
160        if let TypeLongId::Tuple(vec_types) = element_type.lookup_intern(self.db) {
161            self.write_str("(")?;
162            let mut count = vec_types.len();
163            for t in vec_types {
164                self.write_type(None, t, if count == 1 { None } else { Some(", ") }, full_path)?;
165                count -= 1;
166            }
167            self.write_str(")")?;
168        } else if is_the_same_root(full_path, &formatted_element_type) {
169            let documentable_id = resolve_type(self.db, element_type);
170            match documentable_id {
171                Some(documentable_id) => {
172                    let start_offset = self.buf.len();
173                    self.write_str(&extract_and_format(&formatted_element_type))?;
174                    let end_offset = self.buf.len();
175                    self.add_location_link(start_offset, end_offset, documentable_id);
176                }
177                None => {
178                    self.write_str(&extract_and_format(&formatted_element_type))?;
179                }
180            }
181        } else {
182            self.write_str(&extract_and_format(&formatted_element_type))?;
183        }
184        self.write_str(postfix.unwrap_or_default())
185    }
186
187    /// Adds [`LocationLink`] to [`HirFormatter`] instance, writes `name` argument into signature
188    /// buf.
189    fn write_link(
190        &mut self,
191        name: String,
192        documentable_id: Option<DocumentableItemId>,
193    ) -> fmt::Result {
194        match documentable_id {
195            Some(documentable_id) => {
196                let start_offset = self.buf.len();
197                self.write_str(&name)?;
198                let end_offset = self.buf.len();
199                self.add_location_link(start_offset, end_offset, documentable_id);
200                Ok(())
201            }
202            None => self.write_str(&extract_and_format(&name)),
203        }
204    }
205
206    /// Applies extra formatting to item signature.
207    /// Avoid using for types whose signatures are invalid cairo code
208    /// (such as struct members or enum variants).
209    fn format(&mut self) {
210        let (formatted_signature, moved_location_links) = format_signature(
211            std::mem::take(&mut self.buf),
212            std::mem::take(&mut self.location_links),
213        );
214        self.buf = formatted_signature;
215        self.location_links = moved_location_links;
216    }
217}
218
219impl HirDisplay for VariantId {
220    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
221        let name = self.name(f.db);
222        let variant_semantic =
223            f.db.variant_semantic(self.enum_id(f.db), *self)
224                .map_err(|_| SignatureError::FailedRetrievingSemanticData(self.full_path(f.db)))?;
225        if !variant_semantic.ty.is_unit(f.db) {
226            f.write_type(
227                Some(&format!("{name}: ")),
228                variant_semantic.ty,
229                None,
230                &self.full_path(f.db),
231            )
232        } else {
233            f.write_str(name.as_str())
234        }
235        .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))
236    }
237}
238
239impl HirDisplay for EnumId {
240    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
241        let enum_full_signature = get_enum_signature_data(f.db, *self)?;
242        write!(
243            f,
244            "{}enum {} {{",
245            get_syntactic_visibility(&enum_full_signature.visibility),
246            enum_full_signature.name,
247        )
248        .map_err(|_| {
249            SignatureError::FailedWritingSignature(enum_full_signature.full_path.clone())
250        })?;
251        let variants = enum_full_signature.variants;
252        if let Some(variants) = variants {
253            let is_variants_empty = variants.is_empty();
254            for (name, variant_type) in variants {
255                if !variant_type.is_unit(f.db) {
256                    f.write_type(
257                        Some(&format!("\n{INDENT}{name}: ",)),
258                        variant_type,
259                        Some(","),
260                        &enum_full_signature.full_path,
261                    )
262                } else {
263                    write!(f, "\n{INDENT}{name},")
264                }
265                .map_err(|_| {
266                    SignatureError::FailedWritingSignature(enum_full_signature.full_path.clone())
267                })?;
268            }
269            f.write_str(if is_variants_empty { "}" } else { "\n}" })
270        } else {
271            f.write_str("}")
272        }
273        .map_err(|_| {
274            SignatureError::FailedWritingSignature(enum_full_signature.full_path.clone())
275        })?;
276        f.format();
277        Ok(())
278    }
279}
280
281impl HirDisplay for MemberId {
282    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
283        let member_full_signature = get_member_signature_data(f.db, *self)?;
284        if let Some(return_type) = member_full_signature.return_type {
285            if return_type.is_unit(f.db) {
286                write!(
287                    f,
288                    "{}{}",
289                    get_syntactic_visibility(&member_full_signature.visibility),
290                    member_full_signature.name
291                )
292            } else {
293                f.write_type(
294                    Some(&format!(
295                        "{}{}: ",
296                        get_syntactic_visibility(&member_full_signature.visibility),
297                        member_full_signature.name,
298                    )),
299                    return_type,
300                    None,
301                    &member_full_signature.full_path,
302                )
303            }
304            .map_err(|_| SignatureError::FailedWritingType(member_full_signature.full_path.clone()))
305        } else {
306            Err(SignatureError::FailedRetrievingSemanticData(self.full_path(f.db)))
307        }
308    }
309}
310
311impl HirDisplay for StructId {
312    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
313        let struct_full_signature = get_struct_signature_data(f.db, *self)?;
314        if let Some(attributes) = struct_full_signature.attributes {
315            write_struct_attributes_syntax(attributes, f).map_err(|_| {
316                SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
317            })?;
318        }
319        write!(
320            f,
321            "{}struct {}",
322            get_syntactic_visibility(&struct_full_signature.visibility),
323            struct_full_signature.name,
324        )
325        .map_err(|_| {
326            SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
327        })?;
328        if let Some(generic_params) = struct_full_signature.generic_params {
329            write_generic_params(generic_params, f).map_err(|_| {
330                SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
331            })?;
332        }
333        f.write_str(" {").map_err(|_| {
334            SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
335        })?;
336
337        if let Some(members) = struct_full_signature.members {
338            let is_members_empty = members.is_empty();
339            for member in members {
340                let (name, member_type, visibility) = member;
341                f.write_type(
342                    Some(
343                        &format!("\n{INDENT}{}{}: ", get_syntactic_visibility(&visibility), name,),
344                    ),
345                    member_type,
346                    Some(","),
347                    &struct_full_signature.full_path,
348                )
349                .map_err(|_| {
350                    SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
351                })?;
352            }
353            f.write_str(if is_members_empty { "}" } else { "\n}" }).map_err(|_| {
354                SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone())
355            })?;
356        };
357        f.format();
358        Ok(())
359    }
360}
361
362impl HirDisplay for FreeFunctionId {
363    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
364        let free_function_full_signature = get_free_function_signature_data(f.db, *self)?;
365        write_function_signature(f, free_function_full_signature, "".to_string())
366            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
367        f.format();
368        Ok(())
369    }
370}
371
372impl HirDisplay for ConstantId {
373    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
374        let constant_full_signature = get_constant_signature_data(f.db, *self)?;
375        write!(
376            f,
377            "{}const {}: ",
378            get_syntactic_visibility(&constant_full_signature.visibility),
379            constant_full_signature.name,
380        )
381        .map_err(|_| {
382            SignatureError::FailedWritingSignature(constant_full_signature.full_path.clone())
383        })?;
384        if let Some(return_type) = constant_full_signature.return_type {
385            f.write_type(None, return_type, Some(" = "), &constant_full_signature.full_path)
386                .map_err(|_| {
387                    SignatureError::FailedWritingSignature(
388                        constant_full_signature.full_path.clone(),
389                    )
390                })?;
391        }
392        if let Some(return_value_expr) = constant_full_signature.return_value_expr {
393            match return_value_expr {
394                Expr::Literal(v) => write!(f, "{};", v.value,).map_err(|_| {
395                    SignatureError::FailedWritingSignature(
396                        constant_full_signature.full_path.clone(),
397                    )
398                }),
399                Expr::FunctionCall(_) => {
400                    let const_value_id = f.db.constant_const_value(*self).map_err(|_| {
401                        SignatureError::FailedRetrievingSemanticData(
402                            constant_full_signature.full_path.clone(),
403                        )
404                    })?;
405                    let constant_value = f.db.lookup_intern_const_value(const_value_id);
406                    if let ConstValue::Int(value, _) = constant_value {
407                        write_syntactic_evaluation(f, constant_full_signature.item_id).map_err(
408                            |_| {
409                                SignatureError::FailedWritingSignature(
410                                    constant_full_signature.full_path.clone(),
411                                )
412                            },
413                        )?;
414                        write!(f, " // = {value}")
415                    } else {
416                        write_syntactic_evaluation(f, constant_full_signature.item_id)
417                    }
418                    .map_err(|_| {
419                        SignatureError::FailedWritingSignature(
420                            constant_full_signature.full_path.clone(),
421                        )
422                    })
423                }
424                _ => write_syntactic_evaluation(f, constant_full_signature.item_id).map_err(|_| {
425                    SignatureError::FailedWritingSignature(
426                        constant_full_signature.full_path.clone(),
427                    )
428                }),
429            }
430        } else {
431            Err(SignatureError::FailedRetrievingSemanticData(self.full_path(f.db)))
432        }
433    }
434}
435
436impl HirDisplay for ImplConstantDefId {
437    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
438        let constant_full_signature = get_impl_constant_signature_data(f.db, *self)?;
439        if let Some(return_type) = constant_full_signature.return_type {
440            f.write_type(
441                Some(&format!("const {}: ", constant_full_signature.name,)),
442                return_type,
443                Some(" = "),
444                &constant_full_signature.full_path,
445            )
446            .map_err(|_| {
447                SignatureError::FailedWritingSignature(constant_full_signature.full_path.clone())
448            })?;
449        }
450        write_syntactic_evaluation(f, constant_full_signature.item_id).map_err(|_| {
451            SignatureError::FailedWritingSignature(constant_full_signature.full_path)
452        })?;
453        f.format();
454        Ok(())
455    }
456}
457
458impl HirDisplay for TraitFunctionId {
459    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
460        let free_function_full_signature = get_trait_function_signature_data(f.db, *self)?;
461        write_function_signature(f, free_function_full_signature, "".to_string())
462            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
463        f.format();
464        Ok(())
465    }
466}
467
468impl HirDisplay for ImplFunctionId {
469    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
470        let impl_function_full_signature = get_impl_function_signature_data(f.db, *self)?;
471        write_function_signature(f, impl_function_full_signature, "".to_string())
472            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
473        f.format();
474        Ok(())
475    }
476}
477
478impl HirDisplay for TraitId {
479    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
480        let trait_full_signature = get_trait_signature_data(f.db, *self)?;
481        write!(
482            f,
483            "{}trait {}",
484            get_syntactic_visibility(&trait_full_signature.visibility),
485            trait_full_signature.name,
486        )
487        .map_err(|_| {
488            SignatureError::FailedWritingSignature(trait_full_signature.full_path.clone())
489        })?;
490        if let Some(generic_params) = trait_full_signature.generic_params {
491            write_generic_params(generic_params, f).map_err(|_| {
492                SignatureError::FailedWritingSignature(trait_full_signature.full_path)
493            })?
494        };
495        f.format();
496        Ok(())
497    }
498}
499
500impl HirDisplay for TraitConstantId {
501    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
502        let trait_const_full_signature = get_trait_const_signature_data(f.db, *self)?;
503        if let Some(return_type) = trait_const_full_signature.return_type {
504            write!(
505                f,
506                "const {}: {};",
507                trait_const_full_signature.name,
508                extract_and_format(&return_type.format(f.db)),
509            )
510            .map_err(|_| {
511                SignatureError::FailedWritingSignature(trait_const_full_signature.full_path)
512            })?;
513        } else {
514            Err(SignatureError::FailedRetrievingSemanticData(self.full_path(f.db)))?;
515        }
516        f.format();
517        Ok(())
518    }
519}
520
521impl HirDisplay for ImplDefId {
522    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
523        let impl_def_full_signature = get_impl_def_signature_data(f.db, *self)?;
524        let trait_id = f.db.impl_def_trait(*self).map_err(|_| {
525            SignatureError::FailedRetrievingSemanticData(impl_def_full_signature.full_path.clone())
526        })?;
527        if let Some(resolver_generic_params) = impl_def_full_signature.resolver_generic_params {
528            let resolver_generic_params =
529                format_resolver_generic_params(f.db, resolver_generic_params);
530            write!(
531                f,
532                "{}impl {}{} of {}",
533                get_syntactic_visibility(&impl_def_full_signature.visibility),
534                impl_def_full_signature.name,
535                resolver_generic_params,
536                trait_id.name(f.db),
537            )
538            .map_err(|_| {
539                SignatureError::FailedWritingSignature(impl_def_full_signature.full_path.clone())
540            })?;
541        }
542        if let Some(generic_args) = impl_def_full_signature.generic_args {
543            write_generic_args(generic_args, f).map_err(|_| {
544                SignatureError::FailedWritingSignature(impl_def_full_signature.full_path.clone())
545            })?;
546        }
547        f.write_str(";").map_err(|_| {
548            SignatureError::FailedWritingSignature(impl_def_full_signature.full_path)
549        })?;
550        f.format();
551        Ok(())
552    }
553}
554
555impl HirDisplay for ImplAliasId {
556    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
557        let impl_alias_full_signature = get_impl_alias_signature_data(f.db, *self)?;
558        write!(
559            f,
560            "{}impl {} = ",
561            get_syntactic_visibility(&impl_alias_full_signature.visibility),
562            self.name(f.db),
563        )
564        .map_err(|_| {
565            SignatureError::FailedWritingSignature(impl_alias_full_signature.full_path.clone())
566        })?;
567        write_syntactic_evaluation(f, impl_alias_full_signature.item_id).map_err(|_| {
568            SignatureError::FailedWritingSignature(impl_alias_full_signature.full_path)
569        })?;
570        f.format();
571        Ok(())
572    }
573}
574
575impl HirDisplay for ModuleTypeAliasId {
576    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
577        let module_type_alias_full_signature = get_module_type_alias_full_signature(f.db, *self)?;
578        write_type_signature(f, module_type_alias_full_signature, false)
579            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
580        f.format();
581        Ok(())
582    }
583}
584
585impl HirDisplay for TraitTypeId {
586    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
587        let trait_type_full_signature = get_trait_type_full_signature(f.db, *self)?;
588        write_type_signature(f, trait_type_full_signature, false)
589            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
590        f.format();
591        Ok(())
592    }
593}
594
595impl HirDisplay for ImplTypeDefId {
596    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
597        let impl_type_def_full_signature = get_impl_type_def_full_signature(f.db, *self)?;
598        write_type_signature(f, impl_type_def_full_signature, false)
599            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
600        f.format();
601        Ok(())
602    }
603}
604
605impl HirDisplay for ExternTypeId {
606    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
607        let extern_type_full_signature = get_extern_type_full_signature(f.db, *self)?;
608        write_type_signature(f, extern_type_full_signature, true)
609            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
610        f.format();
611        Ok(())
612    }
613}
614
615impl HirDisplay for ExternFunctionId {
616    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), SignatureError> {
617        let extern_function_full_signature = get_extern_function_full_signature(f.db, *self)?;
618        let signature = match f.db.extern_function_signature(*self) {
619            Ok(signature) => signature,
620            _ => {
621                return Err(SignatureError::FailedRetrievingSemanticData(
622                    extern_function_full_signature.full_path,
623                ));
624            }
625        };
626        write_function_signature(f, extern_function_full_signature, "extern ".to_string())
627            .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
628        if !signature.implicits.is_empty() {
629            f.write_str(" implicits(")
630                .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
631            let mut count = signature.implicits.len();
632            for type_id in &signature.implicits {
633                write!(
634                    f,
635                    "{}{}",
636                    extract_and_format(&type_id.format(f.db)),
637                    if count == 1 { ")".to_string() } else { ", ".to_string() }
638                )
639                .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
640                count -= 1;
641            }
642        }
643        if !signature.panicable {
644            f.write_str(" nopanic")
645                .map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))?;
646        };
647        f.write_str(";").map_err(|_| SignatureError::FailedWritingSignature(self.full_path(f.db)))
648    }
649}
650
651/// Formats the text of the [`Visibility`] to a relevant string slice.
652pub fn get_syntactic_visibility(semantic_visibility: &Visibility) -> &str {
653    match semantic_visibility {
654        Visibility::Public => "pub ",
655        Visibility::PublicInCrate => "pub(crate) ",
656        Visibility::Private => "",
657    }
658}
659
660/// Checks if given paths come from the same root.
661fn is_the_same_root(path1: &str, path2: &str) -> bool {
662    fn extract_root(input: &str) -> &str {
663        if let Some(index) = input.find("::") { &input[..index] } else { input }
664    }
665    extract_root(path1) == extract_root(path2)
666}
667
668/// Formats complex types full paths. For example "Result<Error::NotFound, System::Error>" input
669/// results in "Result<NotFound, Error>" output.
670fn extract_and_format(input: &str) -> String {
671    let delimiters = [',', '<', '>', '(', ')', '[', ']'];
672    let mut output = String::new();
673    let mut slice_start = 0;
674    let mut in_slice = false;
675
676    for (i, c) in input.char_indices() {
677        if delimiters.contains(&c) {
678            if in_slice {
679                let slice = &input[slice_start..i];
680                output.push_str(&format_final_part(slice));
681                in_slice = false;
682            }
683            output.push(c);
684            slice_start = i + 1;
685        } else {
686            in_slice = true;
687        }
688    }
689    if in_slice {
690        let slice = &input[slice_start..];
691        output.push_str(&format_final_part(slice));
692    }
693    output
694}
695
696/// Formats single type path. For example "core::felt252" input results in "felt252" output.
697fn format_final_part(slice: &str) -> String {
698    let parts: Vec<&str> = slice.split("::").collect();
699    let ensure_whitespace =
700        if let Some(first) = parts.first() { first.starts_with(" ") } else { false };
701    let result = {
702        match parts[..] {
703            [.., before_last, ""] => before_last.to_string(),
704            [.., last] => last.to_string(),
705            _ => slice.to_string(),
706        }
707    };
708    if ensure_whitespace && !result.starts_with(' ') { format!(" {result}") } else { result }
709}
710
711/// Takes a list of [`GenericParamId`]s and formats it into a String representation used for
712/// signature documentation.
713fn format_resolver_generic_params(db: &dyn DocGroup, params: Vec<GenericParamId>) -> String {
714    if !params.is_empty() {
715        format!(
716            "<{}>",
717            params
718                .iter()
719                .map(|param| {
720                    if matches!(param.kind(db), GenericKind::Impl) {
721                        let param_formatted = param.format(db);
722                        if param_formatted.starts_with("+") {
723                            param_formatted
724                        } else {
725                            match db.generic_param_semantic(*param) {
726                                Ok(generic_param) => match generic_param {
727                                    GenericParam::Impl(generic_param_impl) => {
728                                        match generic_param_impl.concrete_trait {
729                                            Ok(concrete_trait) => {
730                                                format!(
731                                                    "impl {param_formatted}: {}<{}>",
732                                                    concrete_trait.name(db),
733                                                    concrete_trait
734                                                        .generic_args(db)
735                                                        .iter()
736                                                        .map(|arg| arg.format(db))
737                                                        .collect::<Vec<_>>()
738                                                        .join(", "),
739                                                )
740                                            }
741                                            Err(_) => param_formatted,
742                                        }
743                                    }
744                                    _ => param_formatted,
745                                },
746                                Err(_) => param_formatted,
747                            }
748                        }
749                    } else {
750                        param.format(db)
751                    }
752                })
753                .join(", ")
754        )
755    } else {
756        "".to_string()
757    }
758}
759
760/// A utility function used for formatting documentable functions data. Use with
761/// [`DocumentableItemSignatureData`] argument created for [`FreeFunctionId`], [`TraitFunctionId`],
762/// [`ImplFunctionId`] or [`ExternFunctionId`]. As those are the items for which a
763/// [`cairo_lang_semantic::items::functions::Signature`] can be retrieved.
764fn write_function_signature(
765    f: &mut HirFormatter<'_>,
766    documentable_signature: DocumentableItemSignatureData,
767    syntactic_kind: String,
768) -> Result<(), fmt::Error> {
769    let resolver_generic_params = match documentable_signature.resolver_generic_params {
770        Some(params) => format_resolver_generic_params(f.db, params),
771        None => "".to_string(),
772    };
773
774    write!(
775        f,
776        "{}{}fn {}{}",
777        get_syntactic_visibility(&documentable_signature.visibility),
778        syntactic_kind,
779        documentable_signature.name,
780        resolver_generic_params,
781    )?;
782    if let Some(generic_args) = documentable_signature.generic_args {
783        write_generic_args(generic_args, f)?;
784    }
785    f.write_str("(")?;
786    if let Some(params) = documentable_signature.params {
787        let mut count = params.len();
788        let mut postfix = String::from(", ");
789        for param in params {
790            if count == 1 {
791                postfix = "".to_string();
792            }
793            let syntax_node = param.id.stable_location(f.db).syntax_node(f.db);
794            let modifier = get_relevant_modifier(&param.mutability);
795            let modifier_postfix = if modifier.is_empty() { "" } else { " " };
796            if param.ty.is_fully_concrete(f.db) {
797                f.write_type(
798                    Some(&format!("{modifier}{modifier_postfix}{}: ", param.name)),
799                    param.ty,
800                    Some(&postfix),
801                    &documentable_signature.full_path,
802                )?;
803            } else {
804                let type_definition = get_type_clause(syntax_node, f.db).unwrap_or_default();
805                write!(f, "{modifier}{modifier_postfix}{}{type_definition}{postfix}", param.name,)?;
806            }
807            count -= 1;
808        }
809    }
810    f.write_str(")")?;
811
812    if let Some(return_type) = documentable_signature.return_type {
813        if !return_type.is_unit(f.db) {
814            f.write_type(Some(" -> "), return_type, None, &documentable_signature.full_path)?;
815        }
816    }
817    Ok(())
818}
819
820/// Retrieves [`SyntaxKind::TypeClause`] text from [`SyntaxNode`].
821fn get_type_clause(syntax_node: SyntaxNode, db: &dyn DocGroup) -> Option<String> {
822    for child in syntax_node.get_children(db).iter() {
823        if child.kind(db) == SyntaxKind::TypeClause {
824            return Some(child.get_text_without_all_comment_trivia(db));
825        }
826    }
827    Some(String::from(MISSING))
828}
829
830/// Formats and writes [`GenericParam`]s data into [`HirFormatter`]'s buff.
831fn write_generic_params(
832    generic_params: Vec<GenericParam>,
833    f: &mut HirFormatter<'_>,
834) -> Result<(), fmt::Error> {
835    if !generic_params.is_empty() {
836        let mut count = generic_params.len();
837        f.write_str("<")?;
838        for param in generic_params {
839            match param {
840                GenericParam::Type(param_type) => {
841                    let name = extract_and_format(&param_type.id.format(f.db));
842                    write!(f, "{}{}", name, if count == 1 { "" } else { ", " })?;
843                }
844                GenericParam::Const(param_const) => {
845                    let name = extract_and_format(&param_const.id.format(f.db));
846                    write!(f, "const {}{}", name, if count == 1 { "" } else { ", " })?;
847                }
848                GenericParam::Impl(param_impl) => {
849                    let name = extract_and_format(&param_impl.id.format(f.db));
850                    match param_impl.concrete_trait {
851                        Ok(concrete_trait) => {
852                            let documentable_id =
853                                DocumentableItemId::from(LookupItemId::ModuleItem(
854                                    ModuleItemId::Trait(concrete_trait.trait_id(f.db)),
855                                ));
856                            if name.starts_with("+") {
857                                f.write_link(name, Some(documentable_id))?;
858                            } else {
859                                write!(f, "impl {name}: ")?;
860                                let concrete_trait_name = concrete_trait.name(f.db);
861                                let concrete_trait_generic_args_formatted = concrete_trait
862                                    .generic_args(f.db)
863                                    .iter()
864                                    .map(|arg| extract_and_format(&arg.format(f.db)))
865                                    .collect::<Vec<_>>()
866                                    .join(", ");
867                                f.write_link(
868                                    concrete_trait_name.to_string(),
869                                    Some(documentable_id),
870                                )?;
871                                if !concrete_trait_generic_args_formatted.is_empty() {
872                                    write!(f, "<{concrete_trait_generic_args_formatted}>")?;
873                                }
874                            }
875                        }
876                        Err(_) => {
877                            write!(f, "{}{}", name, if count == 1 { "" } else { ", " })?;
878                        }
879                    }
880                }
881                GenericParam::NegImpl(_) => f.write_str(MISSING)?,
882            };
883            count -= 1;
884        }
885        f.write_str(">")
886    } else {
887        Ok(())
888    }
889}
890
891/// Formats syntax of generic arguments and writes it into [`HirFormatter`].
892fn write_generic_args(
893    generic_args: Vec<GenericArgumentId>,
894    f: &mut HirFormatter<'_>,
895) -> Result<(), fmt::Error> {
896    let mut count = generic_args.len();
897    if !generic_args.is_empty() {
898        f.write_str("<")?;
899    }
900    for arg in &generic_args {
901        let documentable_id = resolve_generic_arg(*arg, f.db);
902        let _ = f.write_link(extract_and_format(&arg.format(f.db)), documentable_id);
903        let _ = f.write_str(if count == 1 { ">" } else { ", " });
904        count -= 1;
905    }
906    Ok(())
907}
908
909/// Formats syntax of struct attributes and writes it into [`HirFormatter`].
910fn write_struct_attributes_syntax(
911    attributes: Vec<Attribute>,
912    f: &mut HirFormatter<'_>,
913) -> Result<(), fmt::Error> {
914    for attribute in attributes {
915        let syntax_node = attribute.stable_ptr.lookup(f.db).as_syntax_node();
916        for child in syntax_node.get_children(f.db).iter() {
917            let to_text = child.get_text_without_all_comment_trivia(f.db);
918            let cleaned_text = to_text.replace("\n", "");
919            f.write_str(&cleaned_text)?;
920        }
921        f.write_str("\n")?;
922    }
923    Ok(())
924}
925
926/// Formats syntax of documentable item and writes it into [`HirFormatter`].
927fn write_syntactic_evaluation(
928    f: &mut HirFormatter<'_>,
929    item_id: DocumentableItemId,
930) -> Result<(), fmt::Error> {
931    if let Some(stable_location) = item_id.stable_location(f.db) {
932        let syntax_node = stable_location.syntax_node(f.db);
933        if matches!(&syntax_node.green_node(f.db).details, green::GreenNodeDetails::Node { .. }) {
934            let mut is_after_evaluation_value = false;
935            for child in syntax_node.get_children(f.db).iter() {
936                let kind = child.kind(f.db);
937                if !matches!(kind, SyntaxKind::Trivia) {
938                    if matches!(kind, SyntaxKind::TerminalSemicolon) {
939                        f.buf.write_str(";")?;
940                        return Ok(());
941                    }
942                    if is_after_evaluation_value {
943                        f.buf.write_str(&SyntaxNode::get_text_without_all_comment_trivia(
944                            child, f.db,
945                        ))?;
946                    };
947                    if matches!(kind, SyntaxKind::TerminalEq) {
948                        is_after_evaluation_value = true;
949                    }
950                }
951            }
952        };
953        Ok(())
954    } else {
955        Err(fmt::Error)
956    }
957}
958
959/// A utility function used for formatting documentable types data. Use with
960/// [`DocumentableItemSignatureData`] argument created for [`ModuleTypeAliasId`], [`TraitTypeId`],
961/// [`ImplTypeDefId`] or [`ExternTypeId`]. Because of the same signature structure.
962fn write_type_signature(
963    f: &mut HirFormatter<'_>,
964    documentable_signature: DocumentableItemSignatureData,
965    is_extern_type: bool,
966) -> Result<(), fmt::Error> {
967    write!(
968        f,
969        "{}{}type {}",
970        get_syntactic_visibility(&documentable_signature.visibility),
971        if is_extern_type { "extern " } else { "" },
972        documentable_signature.name
973    )?;
974    if let Some(generic_params) = documentable_signature.generic_params {
975        write_generic_params(generic_params, f)?;
976    }
977    if let Some(return_type) = documentable_signature.return_type {
978        write!(f, " = ")?;
979        f.write_type(None, return_type, None, &documentable_signature.full_path)?;
980    };
981    write!(f, ";")?;
982    Ok(())
983}
984
985/// Returns relevant [`DocumentableItemId`] for [`GenericItemId`] if one can be retrieved.
986fn resolve_generic_item(
987    generic_item_id: GenericItemId,
988    db: &dyn DocGroup,
989) -> Option<DocumentableItemId> {
990    match generic_item_id {
991        GenericItemId::ModuleItem(module_item_id) => {
992            Some(resolve_generic_module_item(module_item_id))
993        }
994        GenericItemId::TraitItem(generic_trait_item_id) => match generic_trait_item_id {
995            GenericTraitItemId::Type(trait_type_id) => Some(DocumentableItemId::from(
996                LookupItemId::ModuleItem(ModuleItemId::Trait(trait_type_id.trait_id(db))),
997            )),
998        },
999        GenericItemId::ImplItem(generic_impl_item_id) => match generic_impl_item_id {
1000            GenericImplItemId::Type(impl_type_def_id) => Some(DocumentableItemId::from(
1001                LookupItemId::ModuleItem(ModuleItemId::Impl(impl_type_def_id.impl_def_id(db))),
1002            )),
1003        },
1004    }
1005}
1006
1007/// Returns relevant [`DocumentableItemId`] for [`GenericModuleItemId`].
1008fn resolve_generic_module_item(generic_module_item_id: GenericModuleItemId) -> DocumentableItemId {
1009    match generic_module_item_id {
1010        GenericModuleItemId::FreeFunc(id) => {
1011            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::FreeFunction(id)))
1012        }
1013        GenericModuleItemId::ExternFunc(id) => {
1014            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternFunction(id)))
1015        }
1016        GenericModuleItemId::TraitFunc(id) => {
1017            DocumentableItemId::from(LookupItemId::TraitItem(TraitItemId::Function(id)))
1018        }
1019        GenericModuleItemId::ImplFunc(id) => {
1020            DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Function(id)))
1021        }
1022        GenericModuleItemId::Trait(id) => {
1023            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(id)))
1024        }
1025        GenericModuleItemId::Impl(id) => {
1026            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Impl(id)))
1027        }
1028        GenericModuleItemId::Struct(id) => {
1029            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Struct(id)))
1030        }
1031        GenericModuleItemId::Enum(id) => {
1032            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Enum(id)))
1033        }
1034        GenericModuleItemId::ExternType(id) => {
1035            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternType(id)))
1036        }
1037        GenericModuleItemId::TypeAlias(id) => {
1038            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::TypeAlias(id)))
1039        }
1040        GenericModuleItemId::ImplAlias(id) => {
1041            DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(id)))
1042        }
1043    }
1044}
1045
1046/// Returns relevant [`DocumentableItemId`] for [`GenericArgumentId`] if one can be retrieved.
1047fn resolve_generic_arg(
1048    generic_arg_id: GenericArgumentId,
1049    db: &dyn DocGroup,
1050) -> Option<DocumentableItemId> {
1051    match generic_arg_id {
1052        GenericArgumentId::Type(type_id) => resolve_type(db, type_id),
1053        GenericArgumentId::Constant(constant_value_id) => match constant_value_id.ty(db) {
1054            Ok(type_id) => resolve_type(db, type_id),
1055            Err(_) => None,
1056        },
1057        GenericArgumentId::Impl(impl_id) => match impl_id.concrete_trait(db) {
1058            Ok(concrete_trait) => {
1059                let trait_id = concrete_trait.trait_id(db);
1060                Some(DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(
1061                    trait_id,
1062                ))))
1063            }
1064            Err(_) => None,
1065        },
1066        GenericArgumentId::NegImpl => None,
1067    }
1068}
1069
1070/// Returns relevant [`DocumentableItemId`] for [`TypeId`] if one can be retrieved.
1071fn resolve_type(db: &dyn DocGroup, type_id: TypeId) -> Option<DocumentableItemId> {
1072    let intern = type_id.lookup_intern(db);
1073    match intern {
1074        TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
1075            ConcreteTypeId::Struct(struct_id) => Some(DocumentableItemId::from(
1076                LookupItemId::ModuleItem(ModuleItemId::Struct(struct_id.struct_id(db))),
1077            )),
1078            ConcreteTypeId::Enum(enum_id) => Some(DocumentableItemId::from(
1079                LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id.enum_id(db))),
1080            )),
1081            ConcreteTypeId::Extern(extern_id) => Some(DocumentableItemId::from(
1082                LookupItemId::ModuleItem(ModuleItemId::ExternType(extern_id.extern_type_id(db))),
1083            )),
1084        },
1085        TypeLongId::Tuple(_) => None,
1086        TypeLongId::Snapshot(type_id) => resolve_type(db, type_id),
1087        TypeLongId::GenericParameter(generic_param_id) => {
1088            let item = generic_param_id.generic_item(db);
1089            resolve_generic_item(item, db)
1090        }
1091        TypeLongId::Var(type_var) => match type_var.inference_id {
1092            InferenceId::LookupItemDeclaration(lookup_item_id)
1093            | InferenceId::LookupItemGenerics(lookup_item_id)
1094            | InferenceId::LookupItemDefinition(lookup_item_id) => {
1095                Some(DocumentableItemId::from(lookup_item_id))
1096            }
1097            InferenceId::ImplDefTrait(impl_def_id) => Some(DocumentableItemId::from(
1098                LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)),
1099            )),
1100            InferenceId::ImplAliasImplDef(impl_alias_id) => Some(DocumentableItemId::from(
1101                LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)),
1102            )),
1103            InferenceId::GenericParam(generic_param_id) => {
1104                let item = generic_param_id.generic_item(db);
1105                resolve_generic_item(item, db)
1106            }
1107            InferenceId::GenericImplParamTrait(generic_param_id) => {
1108                let item = generic_param_id.generic_item(db);
1109                resolve_generic_item(item, db)
1110            }
1111            InferenceId::GlobalUseStar(global_use_id) => {
1112                match db.priv_global_use_imported_module(global_use_id) {
1113                    Ok(module_id) => match module_id {
1114                        ModuleId::CrateRoot(crate_id) => Some(DocumentableItemId::from(crate_id)),
1115                        ModuleId::Submodule(submodule_id) => Some(DocumentableItemId::from(
1116                            LookupItemId::ModuleItem(ModuleItemId::Submodule(submodule_id)),
1117                        )),
1118                    },
1119                    Err(_) => None,
1120                }
1121            }
1122            InferenceId::Canonical => None,
1123            InferenceId::NoContext => None,
1124        },
1125        TypeLongId::Coupon(function_id) => {
1126            let concrete_function = function_id.get_concrete(db);
1127            match concrete_function.generic_function {
1128                GenericFunctionId::Free(function_id) => Some(DocumentableItemId::from(
1129                    LookupItemId::ModuleItem(ModuleItemId::FreeFunction(function_id)),
1130                )),
1131                GenericFunctionId::Extern(function_id) => Some(DocumentableItemId::from(
1132                    LookupItemId::ModuleItem(ModuleItemId::ExternFunction(function_id)),
1133                )),
1134                GenericFunctionId::Impl(function_id) => Some(DocumentableItemId::from(
1135                    LookupItemId::TraitItem(Function(function_id.function)),
1136                )),
1137            }
1138        }
1139        TypeLongId::FixedSizeArray { type_id: _, size: _ } => resolve_type(db, type_id),
1140        TypeLongId::ImplType(impl_type_id) => match impl_type_id.impl_id().concrete_trait(db) {
1141            Ok(concrete_trait_id) => Some(DocumentableItemId::from(LookupItemId::ModuleItem(
1142                ModuleItemId::Trait(concrete_trait_id.trait_id(db)),
1143            ))),
1144            Err(_) => None,
1145        },
1146        TypeLongId::Closure(closure_type_id) => resolve_type(db, closure_type_id.ret_ty),
1147        TypeLongId::Missing(_) => None,
1148    }
1149}