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