Skip to content

Commit 7dab4d2

Browse files
authored
Merge pull request #1655 from dtolnay/assocgroup
Group associated functions by self type
2 parents d0f22e5 + d995abe commit 7dab4d2

File tree

3 files changed

+102
-12
lines changed

3 files changed

+102
-12
lines changed

macro/src/expand.rs

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::syntax::attrs::{self, OtherAttrs};
33
use crate::syntax::cfg::{CfgExpr, ComputedCfg};
44
use crate::syntax::file::Module;
55
use crate::syntax::instantiate::{ImplKey, NamedImplKey};
6+
use crate::syntax::map::OrderedMap;
67
use crate::syntax::message::Message;
78
use crate::syntax::namespace::Namespace;
89
use crate::syntax::qualified::QualifiedName;
@@ -70,6 +71,7 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types)
7071
Api::Include(_) | Api::Impl(_) => {}
7172
Api::Struct(strct) => {
7273
expanded.extend(expand_struct(strct));
74+
expanded.extend(expand_associated_functions(&strct.name.rust, types));
7375
hidden.extend(expand_struct_nonempty(strct));
7476
hidden.extend(expand_struct_operators(strct));
7577
forbid.extend(expand_struct_forbid_drop(strct));
@@ -81,19 +83,24 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types)
8183
hidden.extend(expand_extern_shared_struct(ety, &ffi));
8284
} else if !types.enums.contains_key(ident) {
8385
expanded.extend(expand_cxx_type(ety));
86+
expanded.extend(expand_associated_functions(&ety.name.rust, types));
8487
hidden.extend(expand_cxx_type_assert_pinned(ety, types));
8588
}
8689
}
8790
Api::CxxFunction(efn) => {
88-
expanded.extend(expand_cxx_function_shim(efn, types));
91+
if efn.self_type().is_none() {
92+
expanded.extend(expand_cxx_function_shim(efn, types));
93+
}
8994
}
9095
Api::RustType(ety) => {
9196
expanded.extend(expand_rust_type_impl(ety));
97+
expanded.extend(expand_associated_functions(&ety.name.rust, types));
9298
hidden.extend(expand_rust_type_layout(ety, types));
9399
}
94100
Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)),
95101
Api::TypeAlias(alias) => {
96102
expanded.extend(expand_type_alias(alias));
103+
expanded.extend(expand_associated_functions(&alias.name.rust, types));
97104
hidden.extend(expand_type_alias_verify(alias, types));
98105
}
99106
}
@@ -586,6 +593,85 @@ fn expand_extern_shared_struct(ety: &ExternType, ffi: &Module) -> TokenStream {
586593
}
587594
}
588595

596+
fn expand_associated_functions(self_type: &Ident, types: &Types) -> TokenStream {
597+
let Some(functions) = types.associated_fn.get(self_type) else {
598+
return TokenStream::new();
599+
};
600+
601+
let resolve = types.resolve(self_type);
602+
let self_type_cfg_attrs = resolve.attrs.cfg();
603+
let elided_lifetime = Lifetime::new("'_", Span::call_site());
604+
let mut group_by_lifetimes = OrderedMap::new();
605+
let mut tokens = TokenStream::new();
606+
607+
for efn in functions {
608+
match efn.lang {
609+
Lang::Cxx | Lang::CxxUnwind => {}
610+
Lang::Rust => continue,
611+
}
612+
let mut impl_lifetimes = Vec::new();
613+
let mut self_type_lifetimes = Vec::new();
614+
let self_lt_token;
615+
let self_gt_token;
616+
match &efn.kind {
617+
FnKind::Method(receiver) if receiver.ty.generics.lt_token.is_some() => {
618+
for lifetime in &receiver.ty.generics.lifetimes {
619+
if lifetime.ident != "_"
620+
&& efn
621+
.generics
622+
.lifetimes()
623+
.any(|param| param.lifetime == *lifetime)
624+
{
625+
impl_lifetimes.push(lifetime);
626+
}
627+
self_type_lifetimes.push(lifetime);
628+
}
629+
self_lt_token = receiver.ty.generics.lt_token;
630+
self_gt_token = receiver.ty.generics.gt_token;
631+
}
632+
_ => {
633+
self_type_lifetimes.resize(resolve.generics.lifetimes.len(), &elided_lifetime);
634+
self_lt_token = resolve.generics.lt_token;
635+
self_gt_token = resolve.generics.gt_token;
636+
}
637+
}
638+
if efn.undeclared_lifetimes().is_empty()
639+
&& self_type_lifetimes.len() == resolve.generics.lifetimes.len()
640+
{
641+
group_by_lifetimes
642+
.entry((impl_lifetimes, self_type_lifetimes))
643+
.or_insert_with(Vec::new)
644+
.push(efn);
645+
} else {
646+
let impl_token = Token![impl](efn.name.rust.span());
647+
let impl_lt_token = efn.generics.lt_token;
648+
let impl_gt_token = efn.generics.gt_token;
649+
let self_type = efn.self_type().unwrap();
650+
let function = expand_cxx_function_shim(efn, types);
651+
tokens.extend(quote! {
652+
#self_type_cfg_attrs
653+
#impl_token #impl_lt_token #(#impl_lifetimes),* #impl_gt_token #self_type #self_lt_token #(#self_type_lifetimes),* #self_gt_token {
654+
#function
655+
}
656+
});
657+
}
658+
}
659+
660+
for ((impl_lifetimes, self_type_lifetimes), functions) in &group_by_lifetimes {
661+
let functions = functions
662+
.iter()
663+
.map(|efn| expand_cxx_function_shim(efn, types));
664+
tokens.extend(quote! {
665+
#self_type_cfg_attrs
666+
impl <#(#impl_lifetimes),*> #self_type <#(#self_type_lifetimes),*> {
667+
#(#functions)*
668+
}
669+
});
670+
}
671+
672+
tokens
673+
}
674+
589675
fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
590676
let receiver = efn.receiver().into_iter().map(|receiver| {
591677
if types.is_considered_improper_ctype(&receiver.ty) {
@@ -897,7 +983,6 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
897983
Some(self_type) => {
898984
let elided_generics;
899985
let resolve = types.resolve(self_type);
900-
let self_type_cfg_attrs = resolve.attrs.cfg();
901986
let self_type_generics = match &efn.kind {
902987
FnKind::Method(receiver) if receiver.ty.generics.lt_token.is_some() => {
903988
&receiver.ty.generics
@@ -926,21 +1011,15 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
9261011
self_type_lifetimes.insert(lifetime);
9271012
}
9281013
}
929-
let impl_lifetimes = generics
930-
.lifetimes()
931-
.filter(|param| self_type_lifetimes.contains(&param.lifetime));
9321014
let fn_lifetimes = generics
9331015
.lifetimes()
9341016
.filter(|param| !self_type_lifetimes.contains(&param.lifetime));
9351017
let lt_token = generics.lt_token;
9361018
let gt_token = generics.gt_token;
9371019
quote_spanned! {ident.span()=>
938-
#self_type_cfg_attrs
939-
impl #lt_token #(#impl_lifetimes),* #gt_token #self_type #self_type_generics {
940-
#doc
941-
#all_attrs
942-
#visibility #unsafety #fn_token #ident #lt_token #(#fn_lifetimes),* #gt_token #arg_list #ret #fn_body
943-
}
1020+
#doc
1021+
#all_attrs
1022+
#visibility #unsafety #fn_token #ident #lt_token #(#fn_lifetimes),* #gt_token #arg_list #ret #fn_body
9441023
}
9451024
}
9461025
}

macro/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
clippy::nonminimal_bool,
1414
clippy::redundant_else,
1515
clippy::ref_option,
16+
clippy::similar_names,
1617
clippy::single_match_else,
1718
clippy::struct_field_names,
1819
clippy::too_many_arguments,

syntax/types.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::syntax::trivial::{self, TrivialReason};
1111
use crate::syntax::unpin::{self, UnpinReason};
1212
use crate::syntax::visit::{self, Visit};
1313
use crate::syntax::{
14-
toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
14+
toposort, Api, Atom, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
1515
};
1616
use indexmap::map::Entry;
1717
use proc_macro2::Ident;
@@ -30,6 +30,8 @@ pub(crate) struct Types<'a> {
3030
pub required_unpin: UnorderedMap<&'a Ident, UnpinReason<'a>>,
3131
pub impls: OrderedMap<ImplKey<'a>, ConditionalImpl<'a>>,
3232
pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
33+
#[cfg_attr(not(proc_macro), expect(dead_code))]
34+
pub associated_fn: UnorderedMap<&'a Ident, Vec<&'a ExternFn>>,
3335
pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
3436
pub toposorted_structs: Vec<&'a Struct>,
3537
}
@@ -53,6 +55,7 @@ impl<'a> Types<'a> {
5355
let mut untrusted = UnorderedMap::new();
5456
let mut impls = OrderedMap::new();
5557
let mut resolutions = UnorderedMap::new();
58+
let mut associated_fn = UnorderedMap::new();
5659
let struct_improper_ctypes = UnorderedSet::new();
5760
let toposorted_structs = Vec::new();
5861

@@ -177,6 +180,12 @@ impl<'a> Types<'a> {
177180
// Note: duplication of the C++ name is fine because C++ has
178181
// function overloading.
179182
let self_type = efn.self_type();
183+
if let Some(self_type) = self_type {
184+
associated_fn
185+
.entry(self_type)
186+
.or_insert_with(Vec::new)
187+
.push(efn);
188+
}
180189
if !self_type.is_some_and(|self_type| self_type == "Self")
181190
&& !function_names.insert((self_type, &efn.name.rust))
182191
{
@@ -253,6 +262,7 @@ impl<'a> Types<'a> {
253262
required_unpin,
254263
impls,
255264
resolutions,
265+
associated_fn,
256266
struct_improper_ctypes,
257267
toposorted_structs,
258268
};

0 commit comments

Comments
 (0)