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(¶ms);
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 ¶m.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}