rust: pin-init: internal: add utility to handle generics merging

There're many cases where we want to merge multiple generics together.
Currently this is done by cloning generics and modifying it, making the
code less readable.

Add a utility code that allow printing multiple generics together, ensuring
correct ordering between lifetime and other parameters.

Acked-by: Benno Lossin <lossin@kernel.org>
Link: https://patch.msgid.link/20261006-dev-selfref-dep-v1-3-da716f740d6d@garyguo.net
Signed-off-by: Gary Guo <gary@garyguo.net>
This commit is contained in:
Gary Guo 2026-10-06 17:29:33 +02:00
parent da2b9009d4
commit 25e82b177c
2 changed files with 168 additions and 6 deletions

View File

@ -321,10 +321,12 @@ fn generate_projections(
is_tuple_struct: bool,
fields: &[FieldInfo<'_>],
) -> TokenStream {
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
let pin_lt_generics: Generics = parse_quote!(<'__pin>);
let generics_with_pin_lt = CombinedGenerics(vec![&pin_lt_generics, generics]);
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
let (_, ty_generics_with_pin_lt, _) = generics_with_pin_lt.split_for_impl();
let projection = format_ident!("{ident}Projection");
let this = format_ident!("this");

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
use syn::{Attribute, Index, Member};
use quote::{format_ident, ToTokens};
use syn::{Attribute, GenericParam, Generics, Index, Member, Token};
pub(crate) trait AttrListExt {
fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>;
@ -52,3 +52,163 @@ fn display_name(&self) -> String {
}
}
}
pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);
pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);
pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);
impl CombinedGenerics<'_> {
pub(crate) fn split_for_impl(
&self,
) -> (
CombinedImplGenerics<'_>,
CombinedTypeGenerics<'_>,
// A stub type so `split_for_impl` signature matches that of `syn`'s.
impl Sized,
) {
(CombinedImplGenerics(self), CombinedTypeGenerics(self), ())
}
}
impl ToTokens for CombinedGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Most of the time we are only adding lifetimes, so we prefer to place
// the user provided generics last.
self.0
.last()
.and_then(|x| x.lt_token)
.unwrap_or_default()
.to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first.
for generics in self.0.iter() {
for param in generics.params.pairs() {
if let GenericParam::Lifetime(lt) = param.value() {
lt.to_tokens(tokens);
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
}
for generics in self.0.iter() {
for param in generics.params.pairs() {
if let GenericParam::Lifetime(_) = param.value() {
continue;
};
param.value().to_tokens(tokens);
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
self.0
.last()
.and_then(|x| x.gt_token)
.unwrap_or_default()
.to_tokens(tokens);
}
}
impl ToTokens for CombinedImplGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0
.0
.last()
.and_then(|x| x.lt_token)
.unwrap_or_default()
.to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first.
for generics in self.0 .0.iter() {
for param in generics.params.pairs() {
if let GenericParam::Lifetime(lt) = param.value() {
lt.to_tokens(tokens);
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
}
for generics in self.0 .0.iter() {
for param in generics.params.pairs() {
// Leave out defaults.
match param.value() {
GenericParam::Lifetime(_) => continue,
GenericParam::Type(param) => {
param.ident.to_tokens(tokens);
if !param.bounds.is_empty() {
param
.colon_token
.unwrap_or_else(Default::default)
.to_tokens(tokens);
param.bounds.to_tokens(tokens);
}
}
GenericParam::Const(param) => {
param.const_token.to_tokens(tokens);
param.ident.to_tokens(tokens);
param.colon_token.to_tokens(tokens);
param.ty.to_tokens(tokens);
}
}
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
self.0
.0
.last()
.and_then(|x| x.gt_token)
.unwrap_or_default()
.to_tokens(tokens);
}
}
impl ToTokens for CombinedTypeGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0
.0
.last()
.and_then(|x| x.lt_token)
.unwrap_or_default()
.to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first.
for generics in self.0 .0.iter() {
for param in generics.params.pairs() {
if let GenericParam::Lifetime(lt) = param.value() {
// Leave out bounds
lt.lifetime.to_tokens(tokens);
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
}
for generics in self.0 .0.iter() {
for param in generics.params.pairs() {
// Leave out bounds and defaults.
match param.value() {
GenericParam::Lifetime(_) => continue,
GenericParam::Type(param) => {
param.ident.to_tokens(tokens);
}
GenericParam::Const(param) => {
param.ident.to_tokens(tokens);
}
}
param.punct().unwrap_or(&&comma).to_tokens(tokens);
}
}
self.0
.0
.last()
.and_then(|x| x.gt_token)
.unwrap_or_default()
.to_tokens(tokens);
}
}