Skip to content

Commit

Permalink
Consider associated types of type parameters for implied bounds (#399)
Browse files Browse the repository at this point in the history
Related to #387

## Synopsis

After #387, the following snippet fails to compile:
```rust
#[derive(Debug)]
struct AssocType<I: Iterator> {
    iter: I,
    elem: Option<I::Item>,
}
```
This happens, because the implied bound `Option<I::Item>: Debug` is not
generated.

## Solution

Correct the `ContainsGenericsExt::contains_generics()` implementations
to consider associated types of the type parameter.
  • Loading branch information
liveseed authored and tyranron committed Aug 20, 2024
1 parent 0ef4cbb commit ed0f416
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## 1.0.1 - Unreleased

### Fixed

- Associated types of type parameters not being treated as generics in `Debug`
and `Display` expansions.
([#399](https://github.com/JelteF/derive_more/pull/399))


## 1.0.0 - 2024-08-07
Expand Down
8 changes: 6 additions & 2 deletions impl/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,8 +632,12 @@ impl ContainsGenericsExt for syn::Path {
}
self.segments
.iter()
.any(|segment| match &segment.arguments {
syn::PathArguments::None => false,
.enumerate()
.any(|(n, segment)| match &segment.arguments {
syn::PathArguments::None => {
// `TypeParam::AssocType` case.
(n == 0) && type_params.contains(&&segment.ident)
}
syn::PathArguments::AngleBracketed(
syn::AngleBracketedGenericArguments { args, .. },
) => args.iter().any(|generic| match generic {
Expand Down
49 changes: 46 additions & 3 deletions tests/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2019,11 +2019,13 @@ mod type_variables {
mod our_alloc {
#[cfg(not(feature = "std"))]
pub use alloc::{boxed::Box, format, vec, vec::Vec};
#[cfg(not(feature = "std"))]
pub use core::iter;
#[cfg(feature = "std")]
pub use std::{boxed::Box, format, vec, vec::Vec};
pub use std::{boxed::Box, format, iter, vec, vec::Vec};
}

use our_alloc::{format, vec, Box, Vec};
use our_alloc::{format, iter, vec, Box, Vec};

use derive_more::Debug;

Expand Down Expand Up @@ -2110,6 +2112,25 @@ mod type_variables {
t: Box<dyn MyTrait<T>>,
}

#[derive(Debug)]
struct AssocType<I: Iterator> {
iter: I,
elem: Option<I::Item>,
}

#[derive(derive_more::Debug)]
struct CollidedPathName<Item> {
item: Item,
elem: Option<some_path::Item>,
}

mod some_path {
use super::Debug;

#[derive(Debug)]
pub struct Item;
}

#[test]
fn assert() {
assert_eq!(
Expand Down Expand Up @@ -2148,6 +2169,28 @@ mod type_variables {
assert_eq!(
format!("{item:?}"),
"Node { children: [Node { children: [], inner: 0 }, Leaf { inner: 1 }], inner: 2 }",
)
);

assert_eq!(
format!(
"{:?}",
AssocType {
iter: iter::empty::<bool>(),
elem: None,
},
),
"AssocType { iter: Empty, elem: None }",
);

assert_eq!(
format!(
"{:?}",
CollidedPathName {
item: true,
elem: None,
},
),
"CollidedPathName { item: true, elem: None }",
);
}
}
49 changes: 46 additions & 3 deletions tests/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2388,11 +2388,13 @@ mod type_variables {
mod our_alloc {
#[cfg(not(feature = "std"))]
pub use alloc::{boxed::Box, format, vec::Vec};
#[cfg(not(feature = "std"))]
pub use core::iter;
#[cfg(feature = "std")]
pub use std::{boxed::Box, format, vec::Vec};
pub use std::{boxed::Box, format, iter, vec::Vec};
}

use our_alloc::{format, Box};
use our_alloc::{format, iter, Box};

// We want `Vec` in scope to test that code generation works if it is there.
#[allow(unused_imports)]
Expand Down Expand Up @@ -2512,6 +2514,25 @@ mod type_variables {
t: Box<dyn MyTrait<T>>,
}

#[derive(Display)]
#[display("{iter:?} with {elem:?}")]
struct AssocType<I: Iterator> {
iter: I,
elem: Option<I::Item>,
}

#[derive(Display)]
#[display("{item:?} with {elem:?}")]
struct CollidedPathName<Item> {
item: Item,
elem: Option<some_path::Item>,
}

mod some_path {
#[derive(Debug)]
pub struct Item;
}

#[test]
fn assert() {
assert_eq!(
Expand Down Expand Up @@ -2560,6 +2581,28 @@ mod type_variables {
assert_eq!(
format!("{item}"),
"Some(Variant2 { next: OptionalBox { inner: None } })",
)
);

assert_eq!(
format!(
"{}",
AssocType {
iter: iter::empty::<bool>(),
elem: None,
},
),
"Empty with None",
);

assert_eq!(
format!(
"{}",
CollidedPathName {
item: false,
elem: Some(some_path::Item),
},
),
"false with Some(Item)",
);
}
}

0 comments on commit ed0f416

Please sign in to comment.