cargo_docs_md/
types.rs

1//! Type rendering utilities for converting rustdoc types to string representations.
2//!
3//! This module provides the [`TypeRenderer`] struct to convert the complex type
4//! structures from rustdoc JSON into human-readable Rust type syntax. These
5//! rendered strings are used in the generated markdown documentation.
6//!
7//! # Overview
8//!
9//! Rustdoc JSON represents types as a tree structure (the `Type` enum). The
10//! [`TypeRenderer`] walks that tree and produces the string representation
11//! you'd write in code.
12//!
13//! # Usage
14//!
15//! ```ignore
16//! let renderer = TypeRenderer::new(&krate);
17//! let type_string = renderer.render_type(&some_type);
18//! let generics = renderer.render_generics(&generic_params);
19//! ```
20//!
21//! # Example Transformations
22//!
23//! | Rustdoc Type | Rendered String |
24//! |--------------|-----------------|
25//! | `Type::Primitive("u32")` | `"u32"` |
26//! | `Type::BorrowedRef { lifetime: Some("'a"), is_mutable: true, type_: ... }` | `"&'a mut T"` |
27//! | `Type::ResolvedPath { path: "Vec", args: ... }` | `"Vec<T>"` |
28//!
29//! # Performance
30//!
31//! Uses `Cow<str>` to avoid allocations for simple types like primitives,
32//! generics, and inferred types. Complex types that require string building
33//! return owned strings.
34
35use std::borrow::Cow;
36use std::fmt::Write;
37
38use rustdoc_types::{
39    AssocItemConstraint, AssocItemConstraintKind, Crate, GenericArg, GenericArgs, GenericBound,
40    GenericParamDef, GenericParamDefKind, Id, Term, TraitBoundModifier, Type, WherePredicate,
41};
42
43use crate::generator::render_shared::RendererUtils;
44use crate::utils::PathUtils;
45
46/// Type renderer for converting rustdoc types to Rust syntax strings.
47///
48/// This struct holds a reference to the crate context and provides methods
49/// to render various type constructs into their string representations.
50///
51/// # Design Note
52///
53/// The `krate` field is currently unused because the `Type` enum is self-contained.
54/// However, it is retained for:
55/// - **Future-proofing**: May need to look up items in `krate.index` for enhanced rendering
56/// - **API consistency**: Signals that the renderer is bound to a specific crate context
57/// - **Type safety**: Prevents accidentally mixing renderers across different crates
58///
59/// # Example
60///
61/// ```ignore
62/// let renderer = TypeRenderer::new(&krate);
63/// let type_str = renderer.render_type(&some_type);
64/// let generics = renderer.render_generics(&params);
65/// ```
66#[derive(Debug, Clone, Copy)]
67pub struct TypeRenderer<'a> {
68    /// Reference to the crate for looking up type information.
69    ///
70    /// Reserved for future use (e.g., resolving paths, getting item metadata).
71    #[expect(dead_code, reason = "TODO: Reserved for future use.")]
72    krate: &'a Crate,
73}
74
75impl<'a> TypeRenderer<'a> {
76    /// Create a new type renderer with the given crate context.
77    ///
78    /// # Arguments
79    ///
80    /// * `krate` - The parsed rustdoc crate containing type definitions
81    #[must_use]
82    pub const fn new(krate: &'a Crate) -> Self {
83        Self { krate }
84    }
85
86    /// Get the ID of a resolved type, if available.
87    ///
88    /// Returns `Some(Id)` for resolved path types (named types like structs,
89    /// enums, traits), `None` for primitives and other type variants.
90    ///
91    /// # Arguments
92    ///
93    /// * `ty` - The type to extract the ID from
94    #[must_use]
95    pub const fn get_type_id(&self, ty: &Type) -> Option<Id> {
96        match ty {
97            Type::ResolvedPath(path) => Some(path.id),
98
99            _ => None,
100        }
101    }
102
103    // ========================================================================
104    // Helper Methods
105    // ========================================================================
106
107    /// Render a sanitized path with optional generic arguments.
108    ///
109    /// Returns `Cow::Borrowed` when possible (no args, no sanitization needed),
110    /// preserving the zero-allocation fast path for simple types.
111    fn render_path_with_args<'p>(self, path: &'p str, args: Option<&GenericArgs>) -> Cow<'p, str> {
112        let sanitized: Cow<'_, str> = RendererUtils::sanitize_path(path);
113
114        // Fast path: no args and no sanitization needed
115        if args.is_none() && matches!(sanitized, Cow::Borrowed(_)) {
116            return Cow::Borrowed(path);
117        }
118
119        // Slow path: need to allocate
120        let mut result: String = sanitized.into_owned();
121
122        if let Some(args) = args {
123            _ = write!(result, "{}", &self.render_generic_args(args));
124        }
125
126        Cow::Owned(result)
127    }
128
129    /// Render bounds joined with " + " directly to a buffer.
130    ///
131    /// Avoids intermediate Vec allocation.
132    fn write_bounds_joined(self, out: &mut String, bounds: &[GenericBound]) {
133        let mut first = true;
134
135        for bound in bounds {
136            let rendered: Cow<'_, str> = self.render_generic_bound(bound);
137
138            // Skip empty bounds (Use bounds render to "")
139            if rendered.is_empty() {
140                continue;
141            }
142
143            if !first {
144                _ = write!(out, " + ");
145            }
146
147            _ = write!(out, "{}", &rendered);
148            first = false;
149        }
150    }
151
152    /// Write multiple rendered types separated by a delimiter.
153    ///
154    /// Avoids intermediate Vec allocation.
155    fn write_types_joined<'t, I>(self, out: &mut String, types: I, sep: &str)
156    where
157        I: Iterator<Item = &'t Type>,
158    {
159        let mut first = true;
160
161        for ty in types {
162            if !first {
163                _ = write!(out, "{sep}");
164            }
165
166            _ = write!(out, "{}", &self.render_type(ty));
167            first = false;
168        }
169    }
170
171    // ========================================================================
172    // Main Type Rendering
173    // ========================================================================
174
175    /// Render a rustdoc `Type` to its Rust syntax string representation.
176    ///
177    /// This is the main entry point for type rendering. It handles all variants
178    /// of the `Type` enum and recursively renders nested types.
179    ///
180    /// # Arguments
181    ///
182    /// * `ty` - The type to render
183    ///
184    /// # Returns
185    ///
186    /// A `Cow<str>` representing the type in Rust syntax. Simple types like
187    /// primitives and generics return borrowed strings to avoid allocation.
188    ///
189    /// # Supported Type Variants
190    ///
191    /// - Primitives: `u32`, `bool`, `str`, etc.
192    /// - References: `&T`, `&'a mut T`
193    /// - Pointers: `*const T`, `*mut T`
194    /// - Slices and arrays: `[T]`, `[T; N]`
195    /// - Tuples: `(A, B, C)`
196    /// - Paths: `std::vec::Vec<T>`
197    /// - Function pointers: `fn(A, B) -> C`
198    /// - Trait objects: `dyn Trait + Send`
199    /// - Impl trait: `impl Iterator<Item = T>`
200    /// - Qualified paths: `<T as Trait>::Item`
201    #[must_use]
202    pub fn render_type<'t>(&self, ty: &'t Type) -> Cow<'t, str> {
203        match ty {
204            // Named type path like `Vec<T>` or `std::collections::HashMap<K, V>`
205            Type::ResolvedPath(path) => {
206                // Use helper which preserves fast path
207                self.render_path_with_args(&path.path, path.args.as_deref())
208            },
209
210            // Trait object: `dyn Trait + OtherTrait`
211            Type::DynTrait(dyn_trait) => {
212                let traits: Vec<String> = dyn_trait
213                    .traits
214                    .iter()
215                    .map(|pt| {
216                        let sanitized: Cow<'_, str> = RendererUtils::sanitize_path(&pt.trait_.path);
217
218                        if pt.trait_.args.is_none() {
219                            sanitized.into_owned()
220                        } else {
221                            let mut s = sanitized.into_owned();
222
223                            if let Some(args) = &pt.trait_.args {
224                                _ = write!(s, "{}", &self.render_generic_args(args));
225                            }
226
227                            s
228                        }
229                    })
230                    .collect();
231
232                Cow::Owned(format!("dyn {}", traits.join(" + ")))
233            },
234
235            // Generic type parameter: `T`, `U`, etc.
236            // Primitive type: `u32`, `bool`, `str`, etc.
237            // Zero allocation - borrow from input
238            Type::Generic(name) | Type::Primitive(name) => Cow::Borrowed(name),
239
240            // Function pointer: `fn(A, B) -> C`
241            Type::FunctionPointer(fp) => {
242                let mut result = String::from("fn(");
243                self.write_types_joined(&mut result, fp.sig.inputs.iter().map(|(_, t)| t), ", ");
244
245                _ = write!(result, ")");
246
247                if let Some(output) = &fp.sig.output {
248                    _ = write!(result, " -> ");
249                    _ = write!(result, "{}", &self.render_type(output));
250                }
251
252                Cow::Owned(result)
253            },
254
255            // Tuple type: `(A, B, C)` or unit `()`
256            Type::Tuple(types) => {
257                if types.is_empty() {
258                    return Cow::Borrowed("()");
259                }
260
261                let mut result = String::from("(");
262                self.write_types_joined(&mut result, types.iter(), ", ");
263                _ = write!(result, ")");
264
265                Cow::Owned(result)
266            },
267
268            // Slice type: `[T]`
269            Type::Slice(inner) => Cow::Owned(format!("[{}]", self.render_type(inner))),
270
271            // Array type: `[T; N]` where N is a const expression
272            Type::Array { type_, len } => {
273                Cow::Owned(format!("[{}; {len}]", self.render_type(type_)))
274            },
275
276            // Pattern type (rare): just render the underlying type
277            Type::Pat { type_, .. } => self.render_type(type_),
278
279            // Impl trait in return position: `impl Iterator<Item = T>`
280            Type::ImplTrait(bounds) => {
281                let mut result = String::from("impl ");
282                self.write_bounds_joined(&mut result, bounds);
283
284                Cow::Owned(result)
285            },
286
287            // Inferred type: `_` (placeholder in turbofish)
288            // Zero allocation - static string
289            Type::Infer => Cow::Borrowed("_"),
290
291            // Raw pointer: `*const T` or `*mut T`
292            Type::RawPointer { is_mutable, type_ } => {
293                let mutability = if *is_mutable { "mut" } else { "const" };
294                Cow::Owned(format!("*{mutability} {}", self.render_type(type_)))
295            },
296
297            // Reference: `&T`, `&mut T`, `&'a T`, `&'a mut T`
298            Type::BorrowedRef {
299                lifetime,
300                is_mutable,
301                type_,
302            } => {
303                // Optional lifetime annotation
304                let lt = lifetime
305                    .as_ref()
306                    .map(|l| format!("{l} "))
307                    .unwrap_or_default();
308
309                // Optional mut keyword
310                let mutability = if *is_mutable { "mut " } else { "" };
311                Cow::Owned(format!("&{lt}{mutability}{}", self.render_type(type_)))
312            },
313
314            // Qualified path: `<T as Trait>::Item` or `T::Item`
315            Type::QualifiedPath {
316                name,
317                self_type,
318                trait_,
319                ..
320            } => {
321                let self_ty: Cow<'_, str> = self.render_type(self_type);
322
323                Cow::Owned(trait_.as_ref().map_or_else(
324                    || format!("{self_ty}::{name}"),
325                    |trait_path| {
326                        let sanitized_trait = RendererUtils::sanitize_path(&trait_path.path);
327
328                        format!("<{self_ty} as {sanitized_trait}>::{name}")
329                    },
330                ))
331            },
332        }
333    }
334
335    /// Render generic arguments in angle bracket or parenthesized form.
336    ///
337    /// Handles three syntaxes:
338    /// - Angle brackets: `<T, U, Item = V>` (most common)
339    /// - Parenthesized: `(A, B) -> C` (for Fn traits)
340    /// - Return type notation: `(..)` (experimental)
341    fn render_generic_args(self, args: &GenericArgs) -> String {
342        match args {
343            // Standard angle bracket syntax: `<T, U, Item = V>`
344            GenericArgs::AngleBracketed { args, constraints } => {
345                // Collect all generic arguments (types, lifetimes, consts)
346                let mut parts: Vec<Cow<str>> =
347                    args.iter().map(|a| self.render_generic_arg(a)).collect();
348
349                // Add associated type constraints (e.g., `Item = u32`)
350                parts.extend(
351                    constraints
352                        .iter()
353                        .map(|c| Cow::Owned(self.render_assoc_item_constraint(c))),
354                );
355
356                if parts.is_empty() {
357                    String::new()
358                } else {
359                    format!("<{}>", parts.join(", "))
360                }
361            },
362
363            // Parenthesized syntax for Fn traits: `Fn(A, B) -> C`
364            GenericArgs::Parenthesized { inputs, output } => {
365                let mut result = String::from("(");
366                self.write_types_joined(&mut result, inputs.iter(), ", ");
367                _ = write!(result, ")");
368
369                if let Some(out) = output {
370                    _ = write!(result, " -> ");
371                    _ = write!(result, "{}", &self.render_type(out));
372                }
373
374                result
375            },
376
377            // Return type notation (experimental feature)
378            GenericArgs::ReturnTypeNotation => " (..)".to_string(),
379        }
380    }
381
382    /// Render a single generic argument.
383    ///
384    /// Arguments can be:
385    /// - Lifetimes: `'a`, `'static`
386    /// - Types: `T`, `Vec<u32>`
387    /// - Const values: `N`, `{expr}`
388    /// - Inferred: `_`
389    fn render_generic_arg(self, arg: &GenericArg) -> Cow<'_, str> {
390        match arg {
391            // Zero allocation - borrow from input
392            GenericArg::Lifetime(lt) => Cow::Borrowed(lt),
393
394            GenericArg::Type(ty) => self.render_type(ty),
395
396            // For consts, prefer the computed value; fall back to the expression
397            // These are already owned strings in rustdoc_types
398            GenericArg::Const(c) => Cow::Borrowed(c.value.as_deref().unwrap_or(&c.expr)),
399
400            // Zero allocation - static string
401            GenericArg::Infer => Cow::Borrowed("_"),
402        }
403    }
404
405    /// Render an associated type constraint.
406    ///
407    /// These appear in generic bounds:
408    /// - Equality: `Item = u32`
409    /// - Bound: `Item: Display`
410    fn render_assoc_item_constraint(self, constraint: &AssocItemConstraint) -> String {
411        // The constraint may have its own generic args (rare)
412        let args = constraint
413            .args
414            .as_ref()
415            .map(|a| self.render_generic_args(a))
416            .unwrap_or_default();
417
418        match &constraint.binding {
419            // Equality constraint: `Item = SomeType`
420            AssocItemConstraintKind::Equality(term) => {
421                format!("{}{args} = {}", constraint.name, self.render_term(term))
422            },
423
424            // Bound constraint: `Item: SomeTrait + OtherTrait`
425            AssocItemConstraintKind::Constraint(bounds) => {
426                let mut result = format!("{}{args}: ", constraint.name);
427                self.write_bounds_joined(&mut result, bounds);
428                result
429            },
430        }
431    }
432
433    /// Render a term, which is either a type or a constant.
434    ///
435    /// Used in associated type constraints like `Item = u32`.
436    fn render_term(self, term: &Term) -> Cow<'_, str> {
437        match term {
438            Term::Type(ty) => self.render_type(ty),
439
440            // For consts, prefer the computed value; fall back to the expression
441            Term::Constant(c) => Cow::Borrowed(c.value.as_deref().unwrap_or(&c.expr)),
442        }
443    }
444
445    /// Render a generic bound (trait bound or lifetime bound).
446    ///
447    /// # Examples
448    ///
449    /// - Trait bound: `Clone`, `Iterator<Item = T>`
450    /// - Modified bound: `?Sized`, `~const Drop`
451    /// - Lifetime bound: `'static`, `'a`
452    #[must_use]
453    pub fn render_generic_bound<'t>(&self, bound: &'t GenericBound) -> Cow<'t, str> {
454        match bound {
455            // Trait bound: `Clone`, `?Sized`, `~const Drop`
456            GenericBound::TraitBound {
457                trait_, modifier, ..
458            } => {
459                let sanitized = RendererUtils::sanitize_path(&trait_.path);
460
461                // Simple case: no modifier, no generic args, no sanitization - borrow directly
462                if matches!(modifier, TraitBoundModifier::None)
463                    && trait_.args.is_none()
464                    && matches!(sanitized, Cow::Borrowed(_))
465                {
466                    return Cow::Borrowed(&trait_.path);
467                }
468
469                // Handle bound modifiers
470                let modifier_str = match modifier {
471                    TraitBoundModifier::None => "",
472
473                    TraitBoundModifier::Maybe => "?", // ?Sized
474
475                    TraitBoundModifier::MaybeConst => "~const ", // ~const Trait
476                };
477
478                // Build the trait path with any generic args
479                let mut result = format!("{modifier_str}{sanitized}");
480
481                if let Some(args) = &trait_.args {
482                    _ = write!(result, "{}", &self.render_generic_args(args));
483                }
484
485                Cow::Owned(result)
486            },
487
488            // Lifetime bound: `'static`, `'a`
489            // Zero allocation - borrow from input
490            GenericBound::Outlives(lt) => Cow::Borrowed(lt),
491
492            // Use bound (experimental) - typically empty in output
493            GenericBound::Use(_) => Cow::Borrowed(""),
494        }
495    }
496
497    /// Render a list of generic parameter definitions.
498    ///
499    /// Produces the `<T, U, const N: usize>` portion of a signature.
500    ///
501    /// # Arguments
502    ///
503    /// * `generics` - The list of generic parameters from rustdoc
504    ///
505    /// # Returns
506    ///
507    /// A string like `<T, U>` or empty string if no generics.
508    ///
509    /// # Filtering
510    ///
511    /// Synthetic parameters (generated by the compiler for `impl Trait`)
512    /// are filtered out since they don't appear in the source.
513    #[must_use]
514    pub fn render_generics(&self, generics: &[GenericParamDef]) -> String {
515        if generics.is_empty() {
516            return String::new();
517        }
518
519        // Filter out synthetic params and render each remaining one
520        let params: Vec<String> = generics
521            .iter()
522            .filter_map(|p| self.render_generic_param_def(p))
523            .collect();
524
525        if params.is_empty() {
526            String::new()
527        } else {
528            format!("<{}>", params.join(", "))
529        }
530    }
531
532    /// Render a single generic parameter definition.
533    ///
534    /// Returns `None` for synthetic parameters (compiler-generated).
535    ///
536    /// # Parameter Kinds
537    ///
538    /// - Lifetime: `'a`, `'a: 'b + 'c`
539    /// - Type: `T`, `T: Clone + Send`
540    /// - Const: `const N: usize`
541    fn render_generic_param_def(self, param: &GenericParamDef) -> Option<String> {
542        match &param.kind {
543            // Lifetime parameter: `'a` or `'a: 'b + 'c`
544            GenericParamDefKind::Lifetime { outlives } => {
545                let mut result = param.name.clone();
546
547                // Add outlives bounds if present
548                if !outlives.is_empty() {
549                    _ = write!(result, ": {}", outlives.join(" + "));
550                }
551
552                Some(result)
553            },
554
555            // Type parameter: `T` or `T: Clone + Send`
556            GenericParamDefKind::Type {
557                bounds,
558                is_synthetic,
559                ..
560            } => {
561                // Skip synthetic parameters (compiler-generated for impl Trait)
562                if *is_synthetic {
563                    return None;
564                }
565
566                let mut result = param.name.clone();
567
568                // Add trait bounds if present
569                if !bounds.is_empty() {
570                    _ = write!(result, ": ");
571                    self.write_bounds_joined(&mut result, bounds);
572                }
573
574                Some(result)
575            },
576
577            // Const parameter: `const N: usize`
578            GenericParamDefKind::Const { type_, .. } => {
579                Some(format!("const {}: {}", param.name, self.render_type(type_)))
580            },
581        }
582    }
583
584    /// Render where clause predicates.
585    ///
586    /// Produces the `where T: Clone, U: Send` portion of a signature.
587    ///
588    /// # Arguments
589    ///
590    /// * `where_predicates` - The list of where predicates from rustdoc
591    ///
592    /// # Returns
593    ///
594    /// A formatted where clause string, or empty string if no predicates.
595    ///
596    /// # Format
597    ///
598    /// ```text
599    /// where
600    ///     T: Clone,
601    ///     U: Send
602    /// ```
603    #[must_use]
604    pub fn render_where_clause(&self, where_predicates: &[WherePredicate]) -> String {
605        if where_predicates.is_empty() {
606            return String::new();
607        }
608
609        let clauses: Vec<String> = where_predicates
610            .iter()
611            .map(|p| self.render_where_predicate(p))
612            .collect();
613
614        // Format with newline and indentation for readability
615        format!("\nwhere\n    {}", clauses.join(",\n    "))
616    }
617
618    /// Render a single where predicate.
619    ///
620    /// # Predicate Types
621    ///
622    /// - Bound: `T: Clone + Send`
623    /// - Lifetime: `'a: 'b + 'c`
624    /// - Equality: `<T as Iterator>::Item = u32`
625    fn render_where_predicate(self, pred: &WherePredicate) -> String {
626        match pred {
627            // Type bound predicate: `T: Clone + Send`
628            WherePredicate::BoundPredicate { type_, bounds, .. } => {
629                let mut result = format!("{}: ", self.render_type(type_));
630                self.write_bounds_joined(&mut result, bounds);
631
632                result
633            },
634
635            // Lifetime predicate: `'a: 'b + 'c`
636            WherePredicate::LifetimePredicate { lifetime, outlives } => {
637                format!("{lifetime}: {}", outlives.join(" + "))
638            },
639
640            // Equality predicate: `<T as Trait>::Item = SomeType`
641            WherePredicate::EqPredicate { lhs, rhs } => {
642                format!("{} = {}", self.render_type(lhs), self.render_term(rhs))
643            },
644        }
645    }
646
647    /// Collect all linkable type names from a type.
648    ///
649    /// This extracts type names that could potentially be linked to their definitions.
650    /// Returns a set of (`ype_name`, `type_id`) pairs for `ResolvedPath` types.
651    ///
652    /// # Linkable Types
653    ///
654    /// - `ResolvedPath` types (e.g., `Vec`, `HashMap`, `MyStruct`)
655    /// - Nested types within generics, references, slices, etc.
656    ///
657    /// # Excluded
658    ///
659    /// - Primitives (e.g., `u32`, `bool`)
660    /// - Generic parameters (e.g., `T`, `U`)
661    /// - Inferred types (`_`)
662    #[must_use]
663    pub fn collect_linkable_types(&self, ty: &Type) -> Vec<(String, rustdoc_types::Id)> {
664        let mut result: Vec<(String, Id)> = Vec::new();
665        self.collect_types_recursive(ty, &mut result);
666
667        result
668    }
669
670    /// Recursively collect linkable types from a type tree.
671    fn collect_types_recursive(self, ty: &Type, result: &mut Vec<(String, rustdoc_types::Id)>) {
672        match ty {
673            Type::ResolvedPath(path) => {
674                // Extract the simple name (last segment of the path)
675                let name = PathUtils::short_name(&path.path);
676                result.push((name.to_string(), path.id));
677
678                // Also collect from generic arguments
679                if let Some(args) = &path.args {
680                    self.collect_from_generic_args(args, result);
681                }
682            },
683
684            Type::DynTrait(dyn_trait) => {
685                for pt in &dyn_trait.traits {
686                    let name = PathUtils::short_name(&pt.trait_.path);
687                    result.push((name.to_string(), pt.trait_.id));
688
689                    if let Some(args) = &pt.trait_.args {
690                        self.collect_from_generic_args(args, result);
691                    }
692                }
693            },
694
695            Type::BorrowedRef { type_, .. } | Type::RawPointer { type_, .. } => {
696                self.collect_types_recursive(type_, result);
697            },
698
699            Type::Slice(inner)
700            | Type::Array { type_: inner, .. }
701            | Type::Pat { type_: inner, .. } => {
702                self.collect_types_recursive(inner, result);
703            },
704
705            Type::Tuple(types) => {
706                for inner in types {
707                    self.collect_types_recursive(inner, result);
708                }
709            },
710
711            Type::FunctionPointer(fp) => {
712                for (_, input_ty) in &fp.sig.inputs {
713                    self.collect_types_recursive(input_ty, result);
714                }
715                if let Some(output) = &fp.sig.output {
716                    self.collect_types_recursive(output, result);
717                }
718            },
719
720            Type::ImplTrait(bounds) => {
721                for bound in bounds {
722                    if let GenericBound::TraitBound { trait_, .. } = bound {
723                        let name = PathUtils::short_name(&trait_.path);
724                        result.push((name.to_string(), trait_.id));
725
726                        if let Some(args) = &trait_.args {
727                            self.collect_from_generic_args(args, result);
728                        }
729                    }
730                }
731            },
732
733            Type::QualifiedPath {
734                self_type, trait_, ..
735            } => {
736                self.collect_types_recursive(self_type, result);
737
738                if let Some(t) = trait_ {
739                    let name = PathUtils::short_name(&t.path);
740
741                    result.push((name.to_string(), t.id));
742                }
743            },
744
745            // Primitives, generics, and inferred types are not linkable
746            Type::Primitive(_) | Type::Generic(_) | Type::Infer => {},
747        }
748    }
749
750    /// Collect types from generic arguments.
751    fn collect_from_generic_args(
752        self,
753        args: &GenericArgs,
754        result: &mut Vec<(String, rustdoc_types::Id)>,
755    ) {
756        match args {
757            GenericArgs::AngleBracketed { args, constraints } => {
758                for arg in args {
759                    if let GenericArg::Type(ty) = arg {
760                        self.collect_types_recursive(ty, result);
761                    }
762                }
763
764                for constraint in constraints {
765                    if let AssocItemConstraintKind::Equality(Term::Type(ty)) = &constraint.binding {
766                        self.collect_types_recursive(ty, result);
767                    }
768                }
769            },
770
771            GenericArgs::Parenthesized { inputs, output } => {
772                for input in inputs {
773                    self.collect_types_recursive(input, result);
774                }
775
776                if let Some(output) = output {
777                    self.collect_types_recursive(output, result);
778                }
779            },
780
781            GenericArgs::ReturnTypeNotation => {},
782        }
783    }
784}