Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 70 additions & 15 deletions src/parse/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,9 @@
dbg!(&generics);
assert_eq!(generics.len(), 2);
assert_eq!(generics[0].ident(), "A");
assert_eq!(generics[0].constraints().len(), 5);
assert_eq!(generics[1].ident(), "B");
assert_eq!(generics[1].constraints().len(), 0);

let stream = &mut token_stream("struct Bar<A = ()>");
let (data_type, ident) = super::DataType::take(stream).unwrap();
Expand All @@ -376,6 +378,40 @@
} else {
panic!("Expected simple generic, got {:?}", generics[0]);
}

let stream = &mut token_stream("struct Bar<A: Debug = ()>");
let (data_type, ident) = super::DataType::take(stream).unwrap();
assert_eq!(data_type, super::DataType::Struct);
assert_eq!(ident, "Bar");
let generics = Generics::try_take(stream).unwrap().unwrap();
dbg!(&generics);
assert_eq!(generics.len(), 1);
if let Generic::Generic(generic) = &generics[0] {
assert_eq!(generic.ident, "A");
assert_eq!(generic.constraints.len(), 1);
assert_eq!(generic.constraints[0].to_string(), "Debug");
assert_eq!(generic.default_value.len(), 1);
assert_eq!(generic.default_value[0].to_string(), "()");
} else {
panic!("Expected simple generic, got {:?}", generics[0]);
}

let stream = &mut token_stream("struct Bar<const S: usize = 8>");
let (data_type, ident) = super::DataType::take(stream).unwrap();
assert_eq!(data_type, super::DataType::Struct);
assert_eq!(ident, "Bar");
let generics = Generics::try_take(stream).unwrap().unwrap();
dbg!(&generics);
assert_eq!(generics.len(), 1);
if let Generic::Const(generic) = &generics[0] {
assert_eq!(generic.ident, "S");
assert_eq!(generic.constraints.len(), 1);
assert_eq!(generic.constraints[0].to_string(), "usize");
assert_eq!(generic.default_value.len(), 1);
assert_eq!(generic.default_value[0].to_string(), "8");
} else {
panic!("Expected simple generic, got {:?}", generics[0]);
}
}

/// a lifetime generic parameter, e.g. `struct Foo<'a> { ... }`
Expand Down Expand Up @@ -445,22 +481,31 @@
pub default_value: Vec<TokenTree>,
}

fn try_as_punct_char(tt: &TokenTree) -> Option<char> {
if let TokenTree::Punct(punct) = tt {
Some(punct.as_char())
} else {
None
}
}

impl SimpleGeneric {
pub(crate) fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
let ident = assume_ident(input.next());
let mut constraints = Vec::new();
let mut default_value = Vec::new();
if let Some(TokenTree::Punct(punct)) = input.peek() {
let punct_char = punct.as_char();
if punct_char == ':' {
assume_punct(input.next(), ':');
constraints = read_tokens_until_punct(input, &['>', ','])?;
}
if punct_char == '=' {
assume_punct(input.next(), '=');
default_value = read_tokens_until_punct(input, &['>', ','])?;
}

let mut punct_char = input.peek().and_then(try_as_punct_char);
if punct_char == Some(':') {
assume_punct(input.next(), ':');
constraints = read_tokens_until_punct(input, &['>', ',', '='])?;
punct_char = input.peek().and_then(try_as_punct_char);
}
if punct_char == Some('=') {
assume_punct(input.next(), '=');
default_value = read_tokens_until_punct(input, &['>', ','])?;
}

Ok(Self {
ident,
constraints,
Expand All @@ -483,23 +528,33 @@
pub ident: Ident,
/// The "constraints" (type) of this generic, e.g. the `usize` from `const N: usize`
pub constraints: Vec<TokenTree>,
/// The default value of this generic, e.g. `const S = 8`
pub default_value: Vec<TokenTree>,
}

impl ConstGeneric {
pub(crate) fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
let const_token = assume_ident(input.next());
let ident = assume_ident(input.next());
let mut constraints = Vec::new();
if let Some(TokenTree::Punct(punct)) = input.peek() {
if punct.as_char() == ':' {
assume_punct(input.next(), ':');
constraints = read_tokens_until_punct(input, &['>', ','])?;
}
let mut default_value = Vec::new();

let mut punct_char = input.peek().and_then(try_as_punct_char);
if punct_char == Some(':') {
assume_punct(input.next(), ':');
constraints = read_tokens_until_punct(input, &['>', ',', '='])?;
punct_char = input.peek().and_then(try_as_punct_char);
}
if punct_char == Some('=') {
assume_punct(input.next(), '=');
default_value = read_tokens_until_punct(input, &['>', ','])?;
}

Ok(Self {
const_token,
ident,
constraints,
default_value,
})
}
}
Expand Down Expand Up @@ -563,7 +618,7 @@
constraint: impl AsRef<str>,
) -> Result<()> {
let mut builder = StreamBuilder::new();
let last_constraint_was_comma = self.constraints.last().map_or(

Check failure on line 621 in src/parse/generics.rs

View workflow job for this annotation

GitHub Actions / Lints

this `map_or` can be simplified
false,
|l| matches!(l, TokenTree::Punct(c) if c.as_char() == ','),
);
Expand Down
4 changes: 3 additions & 1 deletion test/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Debug;

bitflags::bitflags! {
#[derive(virtue_test_derive::RetHi)]
pub struct Foo: u8 {
Expand All @@ -7,7 +9,7 @@ bitflags::bitflags! {
}

#[derive(virtue_test_derive::RetHi)]
pub struct DefaultGeneric<T = ()> {
pub struct DefaultGeneric<T: Debug = ()> {
pub t: T,
}

Expand Down
Loading