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}