cargo_docs_md/generator/
impls.rs

1//! Implementation block rendering for documentation generation.
2//!
3//! This module provides the [`ImplRenderer`] struct which handles rendering
4//! impl blocks (both inherent and trait implementations) to markdown format.
5//!
6//! # Path Types in `rustdoc_types::Impl`
7//!
8//! When working with impl blocks, there are two different path representations:
9//!
10//! - **`Impl.trait_: Option<Path>`** - The trait being implemented
11//!   - `Path.path` is a `String` like `"Clone"` or `"std::fmt::Debug"`
12//!   - Use for display/rendering in documentation output
13//!   - Extract trait name with `path.rsplit("::").next()`
14//!
15//! - **`ItemSummary.path: Vec<String>`** - Structured path from `krate.paths`
16//!   - Example: `["std", "clone", "Clone"]`
17//!   - Use for lookups and resolution via item IDs
18//!
19//! The `Path.path` string is already formatted for display, while
20//! `ItemSummary.path` is structured for programmatic manipulation.
21
22use std::borrow::Cow;
23use std::collections::HashSet;
24use std::fmt::Write;
25
26use rustdoc_types::{
27    AssocItemConstraintKind, Crate, GenericArg, GenericArgs, GenericBound, GenericParamDefKind, Id,
28    Impl, Item, ItemEnum, Term, Type, WherePredicate,
29};
30
31use crate::generator::context::RenderContext;
32use crate::generator::render_shared::{RendererInternals, RendererUtils};
33use crate::linker::ImplContext;
34use crate::types::TypeRenderer;
35use crate::utils::PathUtils;
36
37/// Blanket trait implementations to filter from output.
38///
39/// These are automatically derived by the compiler and add noise to documentation
40/// without providing useful information. Users who want them can use `--include-blanket-impls`.
41const BLANKET_TRAITS: &[&str] = &[
42    // Identity/conversion traits (blanket impls)
43    "From",
44    "Into",
45    "TryFrom",
46    "TryInto",
47    // Reflection
48    "Any",
49    // Borrowing (trivial impls)
50    "Borrow",
51    "BorrowMut",
52    // Ownership
53    "ToOwned",
54    "CloneToUninit",
55];
56
57/// Trivial derive trait implementations that can be collapsed.
58///
59/// These are traits commonly derived via `#[derive(...)]` that have standard,
60/// predictable implementations. When `RenderConfig.hide_trivial_derives` is enabled,
61/// these are grouped into a collapsible `<details>` block with a summary table.
62///
63/// The list includes:
64/// - **Cloning**: `Clone`, `Copy`
65/// - **Formatting**: `Debug`
66/// - **Default values**: `Default`
67/// - **Equality**: `PartialEq`, `Eq`
68/// - **Hashing**: `Hash`
69/// - **Ordering**: `PartialOrd`, `Ord`
70pub const TRIVIAL_DERIVE_TRAITS: &[&str] = &[
71    // Cloning traits
72    "Clone",
73    "Copy",
74    // Formatting
75    "Debug",
76    // Default values
77    "Default",
78    // Equality comparison
79    "PartialEq",
80    "Eq",
81    // Hashing
82    "Hash",
83    // Ordering comparison
84    "PartialOrd",
85    "Ord",
86];
87
88/// Short descriptions for trivial derive traits, used in summary tables.
89///
90/// Maps trait names to brief descriptions for the collapsible summary table.
91pub const TRIVIAL_DERIVE_DESCRIPTIONS: &[(&str, &str)] = &[
92    ("Clone", "Returns a copy of the value"),
93    ("Copy", "Marker trait for types with copy semantics"),
94    ("Debug", "Formats the value for debugging"),
95    ("Default", "Returns the default value"),
96    ("PartialEq", "Compares for equality"),
97    ("Eq", "Marker for total equality"),
98    ("Hash", "Feeds this value into a Hasher"),
99    ("PartialOrd", "Partial ordering comparison"),
100    ("Ord", "Total ordering comparison"),
101];
102
103/// Utility functions to work with impl's and generics
104pub struct ImplUtils;
105
106impl ImplUtils {
107    /// Check if an impl block is for a trivial derive trait.
108    ///
109    /// Returns `true` if the impl is for one of the commonly derived traits
110    /// `(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)`.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use rustdoc_types::Impl;
116    /// // For an impl block with trait "Clone", returns true
117    /// // For an impl block with trait "Display", returns false
118    /// ```
119    #[must_use]
120    pub fn is_trivial_derive_impl(impl_block: &Impl) -> bool {
121        let Some(trait_ref) = &impl_block.trait_ else {
122            return false;
123        };
124
125        // Extract the trait name (last segment of the path)
126        let trait_name = PathUtils::short_name(&trait_ref.path);
127
128        TRIVIAL_DERIVE_TRAITS.contains(&trait_name)
129    }
130
131    /// Get the description for a trivial derive trait.
132    ///
133    /// Returns `None` if the trait is not in the trivial derives list.
134    #[must_use]
135    pub fn get_trivial_derive_description(trait_name: &str) -> Option<&'static str> {
136        // Extract just the trait name if it's a full path
137        let name = PathUtils::short_name(trait_name);
138
139        TRIVIAL_DERIVE_DESCRIPTIONS
140            .iter()
141            .find(|(t, _)| *t == name)
142            .map(|(_, desc)| *desc)
143    }
144
145    /// Check if an impl block is for a blanket trait that should be filtered.
146    ///
147    /// Returns `true` if the impl is for one of the commonly auto-derived traits
148    /// that add noise to documentation (From, Into, Any, Borrow, etc.).
149    #[must_use]
150    pub fn is_blanket_impl(impl_block: &Impl) -> bool {
151        let Some(trait_ref) = &impl_block.trait_ else {
152            return false;
153        };
154
155        // Extract the trait name (last segment of the path)
156        // Using rsplit().next() is more efficient than split().last()
157        let trait_name = trait_ref
158            .path
159            .rsplit("::")
160            .next()
161            .unwrap_or(&trait_ref.path);
162
163        BLANKET_TRAITS.contains(&trait_name)
164    }
165
166    /// Check if a type is generic (contains a type parameter like `T`).
167    ///
168    /// This is used to determine whether to show generic parameters in impl blocks.
169    /// For blanket impls like `impl<T> Trait for T`, we only show the generics if
170    /// the `for_` type is actually generic. When the impl is instantiated for a
171    /// concrete type like `TocEntry`, we hide the generics.
172    ///
173    /// # Examples
174    ///
175    /// ```text
176    /// // Generic type - returns true
177    /// is_generic_type(&Type::Generic("T")) == true
178    ///
179    /// // Concrete type - returns false
180    /// is_generic_type(&Type::ResolvedPath { name: "TocEntry", .. }) == false
181    ///
182    /// // Container with generic - returns true
183    /// is_generic_type(&Type::ResolvedPath {
184    ///     name: "Vec",
185    ///     args: Some(GenericArgs::AngleBracketed { args: [Type::Generic("T")] })
186    /// }) == true
187    /// ```
188    #[must_use]
189    pub fn is_generic_type(ty: &Type) -> bool {
190        match ty {
191            // Direct generic parameter like `T`
192            Type::Generic(_) => true,
193
194            // Check nested types for generic parameters
195            Type::ResolvedPath(path) => {
196                // Check generic args of the path
197                path.args
198                    .as_ref()
199                    .is_some_and(|args| Self::generic_args_contain_generic(args))
200            },
201
202            // References, raw pointers, and arrays: check the inner type
203            Type::BorrowedRef { type_, .. }
204            | Type::RawPointer { type_, .. }
205            | Type::Array { type_, .. } => Self::is_generic_type(type_),
206
207            // Slices: check the element type
208            Type::Slice(inner) => Self::is_generic_type(inner),
209
210            // Tuples: check all element types
211            Type::Tuple(types) => types.iter().any(Self::is_generic_type),
212
213            // Qualified paths like `<T as Trait>::Item`
214            Type::QualifiedPath { self_type, .. } => Self::is_generic_type(self_type),
215
216            // Primitives and other types are not generic
217            _ => false,
218        }
219    }
220
221    /// Check if generic args contain any generic type parameters.
222    fn generic_args_contain_generic(args: &GenericArgs) -> bool {
223        match args {
224            GenericArgs::AngleBracketed { args, .. } => args.iter().any(|arg| match arg {
225                GenericArg::Type(ty) => Self::is_generic_type(ty),
226
227                GenericArg::Const(_) | GenericArg::Lifetime(_) | GenericArg::Infer => false,
228            }),
229
230            GenericArgs::Parenthesized { inputs, output } => {
231                inputs.iter().any(Self::is_generic_type)
232                    || output.as_ref().is_some_and(Self::is_generic_type)
233            },
234
235            GenericArgs::ReturnTypeNotation => false,
236        }
237    }
238
239    /// Extract generic parameter names that appear in the impl signature (for_ and trait).
240    ///
241    /// This extracts generics from only the visible parts of the impl header:
242    /// 1. The `for_` type: `impl<T> Trait for Foo<T>` → extracts `T` from `Foo<T>`
243    /// 2. The trait path: `impl<U> Trait<U> for Foo` → extracts `U` from `Trait<U>`
244    ///
245    /// Unlike [`extract_impl_visible_generics`], this does NOT extract from where clauses.
246    /// Use this for rendering purposes when deciding which generic params to show in
247    /// `impl<...>`. Generics that only appear in where clauses should not be shown in
248    /// the impl header since the where clause itself is not rendered.
249    ///
250    /// # Examples
251    ///
252    /// ```text
253    /// impl<T: Sized> IntoEither for T
254    ///   └─ for_ type is Generic("T") → extracts ["T"]
255    ///   └─ When rendered for `Either<L, R>`, for_ becomes `Either<L, R>`
256    ///   └─ Extracts ["L", "R"], NOT ["T"]
257    ///   └─ T is only in where clause, so `impl<T>` should NOT be shown
258    ///
259    /// impl<T> Clone for Wrapper<T>
260    ///   └─ for_ type has `Wrapper<T>` → extracts ["T"]
261    ///   └─ Result: render as `impl<T>`
262    ///
263    /// impl<T, U> Trait<U> for Foo<T>
264    ///   └─ for_ type has `Foo<T>` → extracts ["T"]
265    ///   └─ trait path has `Trait<U>` → extracts ["U"]
266    ///   └─ Result: render as `impl<T, U>` (both visible)
267    /// ```
268    #[must_use]
269    pub fn extract_impl_signature_generics(impl_block: &Impl) -> HashSet<String> {
270        let mut names = HashSet::new();
271
272        // Extract from for_ type (the type being implemented for)
273        Self::extract_type_generics_into(&impl_block.for_, &mut names);
274
275        // Extract from trait path generic arguments
276        if let Some(trait_ref) = &impl_block.trait_
277            && let Some(args) = &trait_ref.args
278        {
279            Self::extract_generic_args_into(args, &mut names);
280        }
281
282        names
283    }
284
285    /// Extract all generic parameter names visible in an impl block's signature.
286    ///
287    /// # Why This Is Needed
288    ///
289    /// Rustdoc JSON stores impl generics in two places that can have DIFFERENT names:
290    /// - `impl_block.generics.params` - the declared params (e.g., `T` from `impl<T>`)
291    /// - `impl_block.for_` - the actual type (e.g., `StyledObject<D>`)
292    ///
293    /// For blanket impls, these names often differ! The compiler transforms:
294    /// ```text
295    /// impl<D: Display> ToString for StyledObject<D>
296    /// ```
297    /// Into JSON where `generics.params` has `T` but `for_` has `StyledObject<D>`.
298    /// If we blindly use `generics.params`, we'd render `impl<T> ToString for StyledObject<D>`.
299    ///
300    /// # Solution
301    ///
302    /// Extract generics from what's VISIBLE in the signature:
303    /// 1. The `for_` type: `Foo<T>` → extracts `T`
304    /// 2. The trait path: `Iterator<Item = U>` → extracts `U`
305    /// 3. Where clauses: `where T: Clone` → extracts `T`
306    ///
307    /// Then filter `generics.params` to only include names that appear in visible locations.
308    ///
309    /// **Note:** For rendering the impl header, use [`extract_impl_signature_generics`] instead,
310    /// which excludes where clause generics.
311    ///
312    /// # Examples
313    ///
314    /// ```text
315    /// impl<T> Clone for Wrapper<T>
316    ///   └─ for_ type has `Wrapper<T>` → extracts ["T"]
317    ///   └─ Result: render as `impl<T>`
318    ///
319    /// impl<T, U> Trait<U> for Foo<T> where T: Clone
320    ///   └─ for_ type has `Foo<T>` → extracts ["T"]
321    ///   └─ trait path has `Trait<U>` → extracts ["U"]
322    ///   └─ where clause has `T: Clone` → extracts ["T"]
323    ///   └─ Result: render as `impl<T, U>` (both visible)
324    ///
325    /// impl<D: Display> ToString for StyledObject<D>
326    ///   └─ for_ type has `StyledObject<D>` → extracts ["D"]
327    ///   └─ Result: render as `impl<D>` (not `impl<T>`)
328    /// ```
329    #[must_use]
330    pub fn extract_impl_visible_generics(impl_block: &Impl) -> HashSet<String> {
331        // Start with signature generics (for_ type and trait path)
332        let mut names = Self::extract_impl_signature_generics(impl_block);
333
334        // Also extract from where clause predicates
335        for pred in &impl_block.generics.where_predicates {
336            match pred {
337                // `where T: Clone + Iterator<Item = U>` extracts both T and U
338                WherePredicate::BoundPredicate { type_, bounds, .. } => {
339                    Self::extract_type_generics_into(type_, &mut names);
340
341                    for bound in bounds {
342                        Self::extract_bound_generics_into(bound, &mut names);
343                    }
344                },
345
346                // `where <T as Trait>::Assoc = U` extracts both T and U
347                WherePredicate::EqPredicate { lhs, rhs, .. } => {
348                    Self::extract_type_generics_into(lhs, &mut names);
349
350                    Self::extract_term_generics_into(rhs, &mut names);
351                },
352
353                // Lifetime predicates like `where 'a: 'b` don't have type params
354                WherePredicate::LifetimePredicate { .. } => {},
355            }
356        }
357
358        names
359    }
360
361    /// Extract generic parameter names from a type into a set.
362    ///
363    /// Recursively traverses the type structure to find all `Type::Generic` variants.
364    /// This handles all the ways generics can be nested in complex types.
365    ///
366    /// # Type Tree Visualization
367    ///
368    /// ```text
369    /// HashMap<K, Vec<T>>
370    /// └─ ResolvedPath("HashMap")
371    ///    └─ args: [K, Vec<T>]
372    ///             │  └─ ResolvedPath("Vec")
373    ///             │     └─ args: [T]
374    ///             │              └─ Generic("T") ← extracted!
375    ///             └─ Generic("K") ← extracted!
376    /// ```
377    fn extract_type_generics_into(ty: &Type, names: &mut HashSet<String>) {
378        match ty {
379            // Base case: A bare generic parameter like `T`, `U`, `Item`
380            // Example: In `fn foo<T>(x: T)`, the parameter type is Generic("T")
381            Type::Generic(name) => {
382                names.insert(name.clone());
383            },
384
385            // Named types like `Vec<T>`, `HashMap<K, V>`, `Option<T>`
386            // The generics are inside the `args` field
387            // Example: `Vec<T>` → ResolvedPath { path: "Vec", args: [Generic("T")] }
388            Type::ResolvedPath(path) => {
389                if let Some(args) = &path.args {
390                    Self::extract_generic_args_into(args, names);
391                }
392            },
393
394            // References and pointers: `&T`, `&mut T`, `*const T`, `*mut T`
395            // Example: `&Vec<T>` → BorrowedRef { type_: ResolvedPath("Vec<T>") }
396            Type::BorrowedRef { type_, .. } | Type::RawPointer { type_, .. } => {
397                Self::extract_type_generics_into(type_, names);
398            },
399
400            // Slices and arrays: `[T]`, `[T; N]`
401            // Example: `[T]` → Slice(Generic("T"))
402            // Example: `[u8; 32]` → Array { type_: Primitive("u8"), len: 32 }
403            Type::Slice(t) | Type::Array { type_: t, .. } => {
404                Self::extract_type_generics_into(t, names);
405            },
406
407            // Tuples: `(T, U, V)`
408            // Example: `(T, u32, V)` → Tuple([Generic("T"), Primitive, Generic("V")])
409            Type::Tuple(types) => {
410                for t in types {
411                    Self::extract_type_generics_into(t, names);
412                }
413            },
414
415            // Qualified paths: `<T as Trait>::Item`, `<I as Iterator>::Item`
416            // These appear in associated type definitions
417            // Example: `<I as Iterator>::Item` → QualifiedPath {
418            //            self_type: Generic("I"),
419            //            trait_: Some(Path("Iterator")),
420            //            name: "Item"
421            //          }
422            Type::QualifiedPath {
423                self_type, trait_, ..
424            } => {
425                // Extract from the self type (e.g., `I` in `<I as Iterator>::Item`)
426                Self::extract_type_generics_into(self_type, names);
427
428                // Also check trait generic args (e.g., `<T as Trait<U>>::Item`)
429                if let Some(tr) = trait_
430                    && let Some(args) = &tr.args
431                {
432                    Self::extract_generic_args_into(args, names);
433                }
434            },
435
436            // Function pointers: `fn(T) -> U`, `fn(&T, V) -> W`
437            // Example: `fn(T) -> U` → FunctionPointer {
438            //            inputs: [("_", Generic("T"))],
439            //            output: Some(Generic("U"))
440            //          }
441            Type::FunctionPointer(fp) => {
442                // Function pointer inputs are (name, type) tuples - we only need the type
443                for (_, input_ty) in &fp.sig.inputs {
444                    Self::extract_type_generics_into(input_ty, names);
445                }
446
447                if let Some(output) = &fp.sig.output {
448                    Self::extract_type_generics_into(output, names);
449                }
450            },
451
452            // impl Trait: `impl Iterator<Item = T>`, `impl Clone + Send`
453            // Example: `impl Iterator<Item = T>` → ImplTrait([TraitBound(Iterator<Item=T>)])
454            Type::ImplTrait(bounds) => {
455                for bound in bounds {
456                    Self::extract_bound_generics_into(bound, names);
457                }
458            },
459
460            // dyn Trait: `dyn Iterator<Item = T>`, `dyn Fn(T) -> U`
461            // Example: `dyn Iterator<Item = T>` → DynTrait with trait bounds
462            Type::DynTrait(dyn_trait) => {
463                for poly in &dyn_trait.traits {
464                    if let Some(args) = &poly.trait_.args {
465                        Self::extract_generic_args_into(args, names);
466                    }
467                }
468            },
469
470            // Leaf types with no generics
471            // - Primitive: `i32`, `bool`, `str`, etc.
472            // - Infer: `_` (the compiler infers this)
473            // - Pat: pattern types (experimental)
474            Type::Primitive(_) | Type::Infer | Type::Pat { .. } => {},
475        }
476    }
477
478    /// Extract generic parameter names from generic arguments.
479    ///
480    /// Generic arguments come in three forms:
481    ///
482    /// # Forms
483    ///
484    /// ```text
485    /// AngleBracketed: Vec<T, U>           → args: [T, U], constraints: []
486    ///                 Iterator<Item = T>  → args: [], constraints: [Item = T]
487    ///
488    /// Parenthesized:  Fn(T, U) -> V       → inputs: [T, U], output: Some(V)
489    ///                 FnOnce(T)           → inputs: [T], output: None
490    ///
491    /// ReturnTypeNotation: method(..)      → (no generics to extract)
492    /// ```
493    fn extract_generic_args_into(args: &GenericArgs, names: &mut HashSet<String>) {
494        match args {
495            // Angle-bracketed args: `<T, U, Item = V>`
496            //
497            // Two parts:
498            // - `args`: positional type args like `T` and `U`
499            // - `constraints`: associated type bindings like `Item = V`
500            //
501            // Example: `Iterator<Item = T>` has constraints with `Item = T`
502            // Example: `HashMap<K, V>` has args [K, V] with no constraints
503            GenericArgs::AngleBracketed { args, constraints } => {
504                // Extract from positional type arguments: <T, U, ...>
505                for arg in args {
506                    // Only Type args have generics - Lifetime('a) and Const(N) don't
507                    if let GenericArg::Type(ty) = arg {
508                        Self::extract_type_generics_into(ty, names);
509                    }
510                }
511
512                // Extract from associated type constraints: <Item = T, Output = U>
513                // These can be either:
514                // - Equality: `Item = T` (term is a Type or Const)
515                // - Constraint: `Item: Clone` (has trait bounds)
516                for constraint in constraints {
517                    match &constraint.binding {
518                        // `Item = T` → extract T from the right-hand side
519                        AssocItemConstraintKind::Equality(term) => {
520                            Self::extract_term_generics_into(term, names);
521                        },
522
523                        // `Item: Iterator<Item = U>` → extract from bounds
524                        AssocItemConstraintKind::Constraint(bounds) => {
525                            for bound in bounds {
526                                Self::extract_bound_generics_into(bound, names);
527                            }
528                        },
529                    }
530                }
531            },
532
533            // Parenthesized args: `(T, U) -> V` (used for Fn traits)
534            //
535            // Example: `Fn(i32, T) -> U` → inputs: [i32, T], output: Some(U)
536            // Example: `FnMut(&T)` → inputs: [&T], output: None (returns ())
537            GenericArgs::Parenthesized { inputs, output } => {
538                for input in inputs {
539                    Self::extract_type_generics_into(input, names);
540                }
541
542                if let Some(out) = output {
543                    Self::extract_type_generics_into(out, names);
544                }
545            },
546
547            // Return type notation: `method(..)` - experimental, no generics
548            GenericArgs::ReturnTypeNotation => {},
549        }
550    }
551
552    /// Extract generic parameter names from a type or const term.
553    ///
554    /// A `Term` appears in associated type bindings like `Iterator<Item = T>`.
555    /// - `Term::Type(T)` - the `T` in `Item = T`
556    /// - `Term::Constant(N)` - const generics like `Item = 42`
557    fn extract_term_generics_into(term: &Term, names: &mut HashSet<String>) {
558        match term {
559            // Type term: `Item = T` → extract T
560            Term::Type(ty) => Self::extract_type_generics_into(ty, names),
561
562            // Const term: `Item = 42` → no type generics to extract
563            Term::Constant(_) => {},
564        }
565    }
566
567    /// Extract generic parameter names from a generic bound.
568    ///
569    /// Bounds appear in where clauses: `where T: Clone + Iterator<Item = U>`
570    ///
571    /// We only care about `TraitBound` (not `Outlives` like `'a: 'b`).
572    /// From the trait bound, we extract any generic args on the trait itself.
573    ///
574    /// # Example
575    ///
576    /// ```text
577    /// where T: Iterator<Item = U>
578    ///          └─ TraitBound { trait_: Path("Iterator"), args: [Item = U] }
579    ///                                                              └─ extracts U
580    /// ```
581    fn extract_bound_generics_into(bound: &GenericBound, names: &mut HashSet<String>) {
582        // Only TraitBound has generic args - Outlives is for lifetimes only
583        if let GenericBound::TraitBound { trait_, .. } = bound
584            && let Some(args) = &trait_.args
585        {
586            Self::extract_generic_args_into(args, names);
587        }
588    }
589
590    /// Check if an impl has associated types referencing generics NOT visible in the signature.
591    ///
592    /// # The Problem
593    ///
594    /// Some impl blocks have generics that only appear in associated types, not in the
595    /// visible signature. When rendered, this produces confusing output:
596    ///
597    /// ```text
598    /// impl DoubleEndedIterator for Foo    ← No `I` visible!
599    ///     type Item = <I as Iterator>::Item   ← Where does `I` come from?
600    /// ```
601    ///
602    /// # Detection Strategy
603    ///
604    /// 1. Extract "visible" generics from: `for_` type, trait path, where clause
605    /// 2. Extract "declared" generics from: `impl_block.generics.params`
606    /// 3. Compute "hidden" = declared - visible
607    /// 4. Check if any associated type references a hidden generic
608    ///
609    /// # Examples
610    ///
611    /// ```text
612    /// // FILTER THIS - `I` is hidden but used in associated type
613    /// impl<I: Iterator> DoubleEndedIterator for Foo {
614    ///     type Item = <I as Iterator>::Item;
615    /// }
616    /// visible = {}        (Foo has no generics)
617    /// declared = {I}      (from impl<I>)
618    /// hidden = {I}        (declared - visible)
619    /// assoc type uses I   → FILTER!
620    ///
621    /// // KEEP THIS - `T` is visible in for_ type
622    /// impl<T> Iterator for Wrapper<T> {
623    ///     type Item = T;
624    /// }
625    /// visible = {T}       (from Wrapper<T>)
626    /// declared = {T}      (from impl<T>)
627    /// hidden = {}         (all visible)
628    /// → KEEP!
629    ///
630    /// // KEEP THIS - `U` is visible in trait path
631    /// impl<T, U> Trait<U> for Foo<T> {
632    ///     type Output = (T, U);
633    /// }
634    /// visible = {T, U}    (T from Foo<T>, U from Trait<U>)
635    /// declared = {T, U}   (from impl<T, U>)
636    /// hidden = {}         (all visible)
637    /// → KEEP!
638    /// ```
639    #[must_use]
640    pub fn has_hidden_generic_refs(impl_block: &Impl, krate: &Crate) -> bool {
641        // ┌─────────────────────────────────────────────────────────────────────┐
642        // │ Step 1: Get generics that appear in the VISIBLE signature           │
643        // │                                                                     │
644        // │ These come from: for_ type, trait path, where clause                │
645        // │ Example: `impl<T> Trait for Foo<T> where T: Clone`                  │
646        // │          visible = {T} (appears in Foo<T> and where clause)         │
647        // └─────────────────────────────────────────────────────────────────────┘
648        let visible_generics = Self::extract_impl_visible_generics(impl_block);
649
650        // ┌─────────────────────────────────────────────────────────────────────┐
651        // │ Step 2: Get generics that are DECLARED in impl<...>                 │
652        // │                                                                     │
653        // │ Only type parameters, not lifetimes ('a) or const generics (N)      │
654        // │ Example: `impl<'a, T, const N: usize>` → declared = {T}             │
655        // └─────────────────────────────────────────────────────────────────────┘
656        let impl_generics: HashSet<_> = impl_block
657            .generics
658            .params
659            .iter()
660            .filter_map(|p| match &p.kind {
661                // Only type parameters can be "hidden" in our sense
662                GenericParamDefKind::Type { .. } => Some(p.name.clone()),
663
664                // Const and lifetime params don't cause the issue we're filtering
665                GenericParamDefKind::Const { .. } | GenericParamDefKind::Lifetime { .. } => None,
666            })
667            .collect();
668
669        // ┌─────────────────────────────────────────────────────────────────────┐
670        // │ Step 3: Compute hidden = declared - visible                         │
671        // │                                                                     │
672        // │ These are params declared in impl<...> but not appearing anywhere   │
673        // │ in the visible signature (for_, trait path, where clause)           │
674        // └─────────────────────────────────────────────────────────────────────┘
675        let hidden_generics: HashSet<_> = impl_generics
676            .difference(&visible_generics)
677            .cloned()
678            .collect();
679
680        // If nothing is hidden, the impl is fine - no filtering needed
681        if hidden_generics.is_empty() {
682            return false;
683        }
684
685        // ┌─────────────────────────────────────────────────────────────────────┐
686        // │ Step 4: Check if any associated type USES a hidden generic          │
687        // │                                                                     │
688        // │ Having hidden generics is only a problem if they're actually used.  │
689        // │ We check each associated type definition in the impl block.         │
690        // │                                                                     │
691        // │ Example: `type Item = <I as Iterator>::Item` uses hidden `I`        │
692        // └─────────────────────────────────────────────────────────────────────┘
693        for item_id in &impl_block.items {
694            // Look up the item in the crate's index
695            let Some(item) = krate.index.get(item_id) else {
696                continue;
697            };
698
699            // We only care about associated types with a concrete definition
700            // (not `type Item;` without a `= ...` part)
701            if let ItemEnum::AssocType {
702                type_: Some(ty), ..
703            } = &item.inner
704            {
705                // Extract all generics referenced in the associated type's definition
706                let mut referenced = HashSet::new();
707                Self::extract_type_generics_into(ty, &mut referenced);
708
709                // If any referenced generic is hidden, this impl should be filtered
710                if referenced.iter().any(|name| hidden_generics.contains(name)) {
711                    return true;
712                }
713            }
714        }
715
716        // No hidden generics are used in associated types - impl is okay to render
717        false
718    }
719}
720
721/// Renders impl blocks to markdown.
722///
723/// This struct handles:
724/// - Inherent implementations (`impl MyType { ... }`)
725/// - Trait implementations (`impl Trait for MyType { ... }`)
726/// - Method signatures within impl blocks
727/// - Associated types and constants
728///
729/// The renderer is generic over [`RenderContext`], allowing it to work with
730/// both single-crate (`GeneratorContext`) and multi-crate (`SingleCrateView`) modes.
731pub struct ImplRenderer<'a> {
732    /// Reference to the render context (either single-crate or multi-crate).
733    ctx: &'a dyn RenderContext,
734
735    /// Path of the current file being generated (for relative link calculation).
736    current_file: &'a str,
737
738    /// Cached type renderer to avoid repeated construction.
739    type_renderer: TypeRenderer<'a>,
740}
741
742impl<'a> ImplRenderer<'a> {
743    /// Create a new impl renderer with the given context.
744    ///
745    /// # Arguments
746    ///
747    /// * `ctx` - Render context (implements `RenderContext` trait)
748    /// * `current_file` - Path of the current file (for relative link calculation)
749    pub fn new(ctx: &'a dyn RenderContext, current_file: &'a str) -> Self {
750        let type_renderer = TypeRenderer::new(ctx.krate());
751        Self {
752            ctx,
753            current_file,
754            type_renderer,
755        }
756    }
757
758    /// Process documentation string to resolve intra-doc links.
759    ///
760    /// Delegates to the render context's `process_docs` method, which handles
761    /// both single-crate and multi-crate link resolution.
762    fn process_docs(&self, item: &Item) -> Option<String> {
763        self.ctx.process_docs(item, self.current_file)
764    }
765
766    /// Render impl blocks for a given type.
767    ///
768    /// This method looks up all impl blocks for a type and renders them
769    /// in two sections:
770    ///
771    /// 1. **Implementations** - Inherent impls (methods defined directly on the type)
772    /// 2. **Trait Implementations** - Trait impls (`impl Trait for Type`)
773    ///
774    /// # Impl Block Categories
775    ///
776    /// - **Inherent**: `impl MyType { fn method(&self) {} }`
777    /// - **Trait**: `impl Clone for MyType { ... }`
778    /// - **Synthetic**: Auto-derived by compiler (Send, Sync) - skipped
779    pub fn render_impl_blocks(&self, md: &mut String, item_id: Id) {
780        let Some(impls) = self.ctx.get_impls(&item_id) else {
781            return;
782        };
783
784        // Partition impls into trait and inherent
785        let (mut trait_impls, inherent_impls): (Vec<_>, Vec<_>) =
786            impls.iter().partition(|i| i.trait_.is_some());
787
788        // Sort trait impls by trait name + generics for deterministic output
789        trait_impls.sort_by(|a: &&&Impl, b: &&&Impl| {
790            let key_a = RendererInternals::impl_sort_key(a, &self.type_renderer);
791            let key_b = RendererInternals::impl_sort_key(b, &self.type_renderer);
792
793            key_a.cmp(&key_b)
794        });
795
796        // Deduplicate trait impls with the same signature
797        trait_impls.dedup_by(|a, b| {
798            RendererInternals::impl_sort_key(a, &self.type_renderer)
799                == RendererInternals::impl_sort_key(b, &self.type_renderer)
800        });
801
802        // === Inherent Implementations ===
803        if !inherent_impls.is_empty() {
804            _ = write!(md, "#### Implementations\n\n");
805
806            for impl_block in inherent_impls {
807                self.render_impl_methods(md, impl_block);
808            }
809        }
810
811        // === Trait Implementations ===
812        // Filter out:
813        // 1. Blanket impls (From, Into, Any, etc.) unless explicitly included
814        // 2. Impls with hidden generic references (Issue 5 fix)
815        //    e.g., `type Item = <I as Iterator>::Item` where I is not visible
816        let krate = self.ctx.krate();
817        let filtered_trait_impls: Vec<_> = if self.ctx.include_blanket_impls() {
818            trait_impls
819                .into_iter()
820                .filter(|i| !ImplUtils::has_hidden_generic_refs(i, krate))
821                .collect()
822        } else {
823            trait_impls
824                .into_iter()
825                .filter(|i| {
826                    !ImplUtils::is_blanket_impl(i) && !ImplUtils::has_hidden_generic_refs(i, krate)
827                })
828                .collect()
829        };
830
831        if filtered_trait_impls.is_empty() {
832            return;
833        }
834
835        _ = write!(md, "#### Trait Implementations\n\n");
836
837        // Check if we should collapse trivial derives
838        let hide_trivial = self.ctx.render_config().hide_trivial_derives;
839
840        if hide_trivial {
841            // Partition into trivial derives and non-trivial impls
842            let (trivial_impls, other_impls): (Vec<_>, Vec<_>) = filtered_trait_impls
843                .into_iter()
844                .partition(|i| ImplUtils::is_trivial_derive_impl(i));
845
846            // Render trivial derives in a collapsible block
847            if !trivial_impls.is_empty() {
848                Self::render_trivial_derives_collapsed(md, &trivial_impls);
849            }
850
851            // Render non-trivial trait impls normally
852            for impl_block in other_impls {
853                self.render_trait_impl(md, impl_block);
854            }
855        } else {
856            // Render all trait impls normally (no collapsing)
857            for impl_block in filtered_trait_impls {
858                self.render_trait_impl(md, impl_block);
859            }
860        }
861    }
862
863    /// Render trivial derive trait implementations in a collapsible block.
864    ///
865    /// Groups `Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord`
866    /// implementations into a `<details>` block with a summary table.
867    fn render_trivial_derives_collapsed(md: &mut String, impls: &[&&Impl]) {
868        let count = impls.len();
869        let summary = format!("Derived Traits ({count} implementations)");
870
871        _ = write!(
872            md,
873            "{}",
874            RendererInternals::render_collapsible_start(&summary)
875        );
876
877        // Render as a summary table
878        _ = write!(
879            md,
880            "| Trait | Description |\n\
881             | ----- | ----------- |\n"
882        );
883
884        for impl_block in impls {
885            if let Some(trait_ref) = &impl_block.trait_ {
886                let trait_name = trait_ref
887                    .path
888                    .rsplit("::")
889                    .next()
890                    .unwrap_or(&trait_ref.path);
891
892                let description = ImplUtils::get_trivial_derive_description(trait_name)
893                    .unwrap_or("Derived trait");
894
895                _ = writeln!(md, "| `{trait_name}` | {description} |");
896            }
897        }
898
899        _ = write!(md, "{}", RendererInternals::render_collapsible_end());
900    }
901
902    /// Render a single trait implementation block.
903    fn render_trait_impl(&self, md: &mut String, impl_block: &Impl) {
904        // Skip synthetic impls (auto-traits like Send, Sync, Unpin)
905        if impl_block.is_synthetic {
906            return;
907        }
908
909        // Build the trait name with generic args
910        let trait_name = impl_block
911            .trait_
912            .as_ref()
913            .map(|t| {
914                let mut name = RendererUtils::sanitize_path(&t.path).into_owned();
915
916                if let Some(args) = &t.args {
917                    _ = write!(name, "{}", &self.render_generic_args_for_impl(args));
918                }
919
920                name
921            })
922            .unwrap_or_default();
923
924        // Extract generics that appear in the signature (for_ type and trait path).
925        // This fixes the generic parameter mismatch issue where `impl_block.generics.params`
926        // might have different names (e.g., T) than what's used in for_ type (e.g., D).
927        //
928        // IMPORTANT: We use `extract_impl_signature_generics` which excludes where clause
929        // generics. For blanket impls like `impl<T: Sized> IntoEither for T`, when rendered
930        // for `Either<L, R>`, the `T` only appears in the where clause `where T: Sized`.
931        // We don't want to show `impl<T> IntoEither for Either<L, R>` since T doesn't appear
932        // in the visible header - it would be confusing to users.
933        let signature_generics = ImplUtils::extract_impl_signature_generics(impl_block);
934
935        let generics = if signature_generics.is_empty() {
936            String::new()
937        } else {
938            // Filter impl params to only include those that appear in for_ type or trait path
939            let filtered: Vec<_> = impl_block
940                .generics
941                .params
942                .iter()
943                .filter(|p| signature_generics.contains(&p.name))
944                .cloned()
945                .collect();
946
947            if filtered.is_empty() {
948                String::new()
949            } else {
950                self.type_renderer.render_generics(&filtered)
951            }
952        };
953
954        let for_type = self.type_renderer.render_type(&impl_block.for_);
955        let unsafe_str = if impl_block.is_unsafe { "unsafe " } else { "" };
956        let negative_str = if impl_block.is_negative { "!" } else { "" };
957
958        _ = write!(
959            md,
960            "##### `{unsafe_str}impl{generics} {negative_str}{trait_name} for {for_type}`\n\n"
961        );
962
963        self.render_impl_methods(md, impl_block);
964    }
965
966    /// Render the items (methods, constants, types) within an impl block.
967    ///
968    /// Each item is rendered as a bullet point. Items can be:
969    /// - **Functions/Methods**: Full signature with modifiers
970    /// - **Associated Constants**: `const NAME: Type`
971    /// - **Associated Types**: `type Name = Type`
972    ///
973    /// For methods, the first line of documentation is included as a brief summary.
974    /// Type links are added for resolvable types in method signatures.
975    /// Method anchors are generated for deep linking (e.g., `#typename-methodname`).
976    fn render_impl_methods(&self, md: &mut String, impl_block: &Impl) {
977        let krate = self.ctx.krate();
978
979        // Extract the type name for method anchor generation
980        let type_name = self.type_renderer.render_type(&impl_block.for_);
981
982        // Determine impl context for anchor generation:
983        // - For trait impls, include the trait name to avoid duplicate anchors
984        //   when multiple traits define the same associated type (e.g., Output)
985        // - For inherent impls, use simple anchors
986        let impl_ctx = impl_block.trait_.as_ref().map_or(
987            ImplContext::Inherent,
988            |t| ImplContext::Trait(PathUtils::short_name(&t.path)),
989        );
990
991        RendererInternals::render_impl_items(
992            md,
993            impl_block,
994            krate,
995            &self.type_renderer,
996            &Some(|item: &Item| self.process_docs(item)),
997            &Some(|id: Id| self.ctx.create_link(id, self.current_file)),
998            Some(type_name.as_ref()),
999            impl_ctx,
1000            self.ctx.render_config().full_method_docs,
1001        );
1002    }
1003
1004    /// Render generic arguments for impl block signatures.
1005    ///
1006    /// This handles the different forms of generic arguments:
1007    /// - **Angle bracketed**: `<T, U, Item = V>` (most common)
1008    /// - **Parenthesized**: `(A, B) -> C` (for Fn traits)
1009    /// - **Return type notation**: `(..)` (experimental)
1010    fn render_generic_args_for_impl(&self, args: &GenericArgs) -> String {
1011        match args {
1012            GenericArgs::AngleBracketed { args, constraints } => {
1013                let mut parts: Vec<Cow<str>> = args
1014                    .iter()
1015                    .map(|a| match a {
1016                        GenericArg::Lifetime(lt) => Cow::Borrowed(lt.as_str()),
1017
1018                        GenericArg::Type(ty) => self.type_renderer.render_type(ty),
1019
1020                        GenericArg::Const(c) => {
1021                            Cow::Borrowed(c.value.as_deref().unwrap_or(&c.expr))
1022                        },
1023
1024                        GenericArg::Infer => Cow::Borrowed("_"),
1025                    })
1026                    .collect();
1027
1028                parts.extend(constraints.iter().map(|c| {
1029                    let constraint_args = c
1030                        .args
1031                        .as_ref()
1032                        .map(|a| self.render_generic_args_for_impl(a))
1033                        .unwrap_or_default();
1034
1035                    match &c.binding {
1036                        AssocItemConstraintKind::Equality(term) => {
1037                            let term_str = match term {
1038                                Term::Type(ty) => self.type_renderer.render_type(ty),
1039
1040                                Term::Constant(c) => {
1041                                    Cow::Borrowed(c.value.as_deref().unwrap_or(&c.expr))
1042                                },
1043                            };
1044
1045                            Cow::Owned(format!("{}{constraint_args} = {term_str}", c.name))
1046                        },
1047
1048                        AssocItemConstraintKind::Constraint(bounds) => {
1049                            let bound_strs: Vec<Cow<str>> = bounds
1050                                .iter()
1051                                .map(|b| self.type_renderer.render_generic_bound(b))
1052                                .collect();
1053
1054                            Cow::Owned(format!(
1055                                "{}{constraint_args}: {}",
1056                                c.name,
1057                                bound_strs
1058                                    .iter()
1059                                    .map(AsRef::as_ref)
1060                                    .collect::<Vec<_>>()
1061                                    .join(" + ")
1062                            ))
1063                        },
1064                    }
1065                }));
1066
1067                if parts.is_empty() {
1068                    String::new()
1069                } else {
1070                    format!(
1071                        "<{}>",
1072                        parts
1073                            .iter()
1074                            .map(AsRef::as_ref)
1075                            .collect::<Vec<_>>()
1076                            .join(", ")
1077                    )
1078                }
1079            },
1080
1081            GenericArgs::Parenthesized { inputs, output } => {
1082                let input_strs: Vec<Cow<str>> = inputs
1083                    .iter()
1084                    .map(|t| self.type_renderer.render_type(t))
1085                    .collect();
1086                let ret = output
1087                    .as_ref()
1088                    .map(|t| format!(" -> {}", self.type_renderer.render_type(t)))
1089                    .unwrap_or_default();
1090
1091                format!(
1092                    "({}){ret}",
1093                    input_strs
1094                        .iter()
1095                        .map(AsRef::as_ref)
1096                        .collect::<Vec<_>>()
1097                        .join(", ")
1098                )
1099            },
1100
1101            GenericArgs::ReturnTypeNotation => " (..)".to_string(),
1102        }
1103    }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108    use super::*;
1109
1110    mod trivial_derive_traits_tests {
1111        use super::*;
1112
1113        #[test]
1114        fn trivial_traits_list_has_nine_entries() {
1115            assert_eq!(TRIVIAL_DERIVE_TRAITS.len(), 9);
1116        }
1117
1118        #[test]
1119        fn descriptions_match_traits_count() {
1120            assert_eq!(
1121                TRIVIAL_DERIVE_TRAITS.len(),
1122                TRIVIAL_DERIVE_DESCRIPTIONS.len()
1123            );
1124        }
1125
1126        #[test]
1127        fn all_traits_have_descriptions() {
1128            for trait_name in TRIVIAL_DERIVE_TRAITS {
1129                assert!(
1130                    ImplUtils::get_trivial_derive_description(trait_name).is_some(),
1131                    "Missing description for trait: {trait_name}"
1132                );
1133            }
1134        }
1135
1136        #[test]
1137        fn get_description_for_clone() {
1138            assert_eq!(
1139                ImplUtils::get_trivial_derive_description("Clone"),
1140                Some("Returns a copy of the value")
1141            );
1142        }
1143
1144        #[test]
1145        fn get_description_for_debug() {
1146            assert_eq!(
1147                ImplUtils::get_trivial_derive_description("Debug"),
1148                Some("Formats the value for debugging")
1149            );
1150        }
1151
1152        #[test]
1153        fn get_description_for_full_path() {
1154            // Should extract trait name from full path
1155            assert_eq!(
1156                ImplUtils::get_trivial_derive_description("std::clone::Clone"),
1157                Some("Returns a copy of the value")
1158            );
1159        }
1160
1161        #[test]
1162        fn get_description_for_unknown_trait() {
1163            assert_eq!(ImplUtils::get_trivial_derive_description("Display"), None);
1164        }
1165
1166        #[test]
1167        fn contains_cloning_traits() {
1168            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Clone"));
1169            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Copy"));
1170        }
1171
1172        #[test]
1173        fn contains_equality_traits() {
1174            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"PartialEq"));
1175            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Eq"));
1176        }
1177
1178        #[test]
1179        fn contains_ordering_traits() {
1180            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"PartialOrd"));
1181            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Ord"));
1182        }
1183
1184        #[test]
1185        fn contains_hash_trait() {
1186            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Hash"));
1187        }
1188
1189        #[test]
1190        fn contains_debug_and_default() {
1191            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Debug"));
1192            assert!(TRIVIAL_DERIVE_TRAITS.contains(&"Default"));
1193        }
1194
1195        #[test]
1196        fn does_not_contain_display() {
1197            assert!(!TRIVIAL_DERIVE_TRAITS.contains(&"Display"));
1198        }
1199
1200        #[test]
1201        fn does_not_contain_serialize() {
1202            assert!(!TRIVIAL_DERIVE_TRAITS.contains(&"Serialize"));
1203            assert!(!TRIVIAL_DERIVE_TRAITS.contains(&"Deserialize"));
1204        }
1205    }
1206}