mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
[bindings] Replace associated_types HashMaps with common Generics
Instead of handling associated types separately, we can just shove them into the same generics resolution logic we use for template types. While we should probably have some precedence logic, aliasing type names seems like a bad idea anyway so no effort is made to handle it. This removes a good chunk of code and, more importantly, tees us up for supporting `Type<Self::AssociatedType>`-style generics.
This commit is contained in:
parent
70440a529e
commit
086434f0c5
3 changed files with 75 additions and 62 deletions
|
@ -1,7 +1,6 @@
|
||||||
//! Printing logic for basic blocks of Rust-mapped code - parts of functions and declarations but
|
//! Printing logic for basic blocks of Rust-mapped code - parts of functions and declarations but
|
||||||
//! not the full mapping logic.
|
//! not the full mapping logic.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use proc_macro2::{TokenTree, Span};
|
use proc_macro2::{TokenTree, Span};
|
||||||
|
@ -77,7 +76,7 @@ pub fn writeln_docs<W: std::io::Write>(w: &mut W, attrs: &[syn::Attribute], pref
|
||||||
///
|
///
|
||||||
/// this_param is used when returning Self or accepting a self parameter, and should be the
|
/// this_param is used when returning Self or accepting a self parameter, and should be the
|
||||||
/// concrete, mapped type.
|
/// concrete, mapped type.
|
||||||
pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, associated_types: &HashMap<&syn::Ident, &syn::Ident>, this_param: &str, types: &mut TypeResolver, generics: Option<&GenericTypes>, self_ptr: bool, fn_decl: bool) {
|
pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, this_param: &str, types: &mut TypeResolver, generics: Option<&GenericTypes>, self_ptr: bool, fn_decl: bool) {
|
||||||
if sig.constness.is_some() || sig.asyncness.is_some() || sig.unsafety.is_some() ||
|
if sig.constness.is_some() || sig.asyncness.is_some() || sig.unsafety.is_some() ||
|
||||||
sig.abi.is_some() || sig.variadic.is_some() {
|
sig.abi.is_some() || sig.variadic.is_some() {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
|
@ -140,26 +139,16 @@ pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, a
|
||||||
syn::ReturnType::Type(_, rtype) => {
|
syn::ReturnType::Type(_, rtype) => {
|
||||||
write!(w, " -> ").unwrap();
|
write!(w, " -> ").unwrap();
|
||||||
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
|
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
|
||||||
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
|
if remaining_path.next().is_none() {
|
||||||
// We're returning an associated type in a trait impl. Its probably a safe bet
|
|
||||||
// that its also a trait, so just return the trait type.
|
|
||||||
let real_type = associated_types.get(associated_seg).unwrap();
|
|
||||||
types.write_c_type(w, &syn::Type::Path(syn::TypePath { qself: None,
|
|
||||||
path: syn::PathSegment {
|
|
||||||
ident: (*real_type).clone(),
|
|
||||||
arguments: syn::PathArguments::None
|
|
||||||
}.into()
|
|
||||||
}), generics, true);
|
|
||||||
} else {
|
|
||||||
write!(w, "{}", this_param).unwrap();
|
write!(w, "{}", this_param).unwrap();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if let syn::Type::Reference(r) = &**rtype {
|
||||||
|
// We can't return a reference, cause we allocate things on the stack.
|
||||||
|
types.write_c_type(w, &*r.elem, generics, true);
|
||||||
} else {
|
} else {
|
||||||
if let syn::Type::Reference(r) = &**rtype {
|
types.write_c_type(w, &*rtype, generics, true);
|
||||||
// We can't return a reference, cause we allocate things on the stack.
|
|
||||||
types.write_c_type(w, &*r.elem, generics, true);
|
|
||||||
} else {
|
|
||||||
types.write_c_type(w, &*rtype, generics, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
@ -222,7 +211,7 @@ pub fn write_method_var_decl_body<W: std::io::Write>(w: &mut W, sig: &syn::Signa
|
||||||
///
|
///
|
||||||
/// The return value is expected to be bound to a variable named `ret` which is available after a
|
/// The return value is expected to be bound to a variable named `ret` which is available after a
|
||||||
/// method-call-ending semicolon.
|
/// method-call-ending semicolon.
|
||||||
pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, associated_types: &HashMap<&syn::Ident, &syn::Ident>, extra_indent: &str, types: &TypeResolver, generics: Option<&GenericTypes>, this_type: &str, to_c: bool) {
|
pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, extra_indent: &str, types: &TypeResolver, generics: Option<&GenericTypes>, this_type: &str, to_c: bool) {
|
||||||
let mut first_arg = true;
|
let mut first_arg = true;
|
||||||
let mut num_unused = 0;
|
let mut num_unused = 0;
|
||||||
for inp in sig.inputs.iter() {
|
for inp in sig.inputs.iter() {
|
||||||
|
@ -291,8 +280,16 @@ pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signatu
|
||||||
} else if !to_c && first_seg_self(&*rtype).is_some() {
|
} else if !to_c && first_seg_self(&*rtype).is_some() {
|
||||||
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
|
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
|
||||||
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
|
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
|
||||||
let real_type = associated_types.get(associated_seg).unwrap();
|
// Build a fake path with only associated_seg and resolve it:
|
||||||
if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_type).unwrap()) {
|
let mut segments = syn::punctuated::Punctuated::new();
|
||||||
|
segments.push(syn::PathSegment {
|
||||||
|
ident: associated_seg.clone(), arguments: syn::PathArguments::None });
|
||||||
|
let (_, real_path) = generics.unwrap().maybe_resolve_path(&syn::Path {
|
||||||
|
leading_colon: None, segments }).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(real_path.segments.len(), 1);
|
||||||
|
let real_ident = &real_path.segments.iter().next().unwrap().ident;
|
||||||
|
if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_ident).unwrap()) {
|
||||||
// We're returning an associated trait from a Rust fn call to a C trait
|
// We're returning an associated trait from a Rust fn call to a C trait
|
||||||
// object.
|
// object.
|
||||||
writeln!(w, "let mut rust_obj = {} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }};", this_type).unwrap();
|
writeln!(w, "let mut rust_obj = {} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }};", this_type).unwrap();
|
||||||
|
|
|
@ -109,31 +109,6 @@ macro_rules! walk_supertraits { ($t: expr, $types: expr, ($( $pat: pat => $e: ex
|
||||||
}
|
}
|
||||||
} } }
|
} } }
|
||||||
|
|
||||||
/// Gets a HashMap from name idents to the bounding trait for associated types.
|
|
||||||
/// eg if a native trait has a "type T = TraitA", this will return a HashMap containing a mapping
|
|
||||||
/// from "T" to "TraitA".
|
|
||||||
fn learn_associated_types<'a>(t: &'a syn::ItemTrait) -> HashMap<&'a syn::Ident, &'a syn::Ident> {
|
|
||||||
let mut associated_types = HashMap::new();
|
|
||||||
for item in t.items.iter() {
|
|
||||||
match item {
|
|
||||||
&syn::TraitItem::Type(ref t) => {
|
|
||||||
if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
|
|
||||||
let mut bounds_iter = t.bounds.iter();
|
|
||||||
match bounds_iter.next().unwrap() {
|
|
||||||
syn::TypeParamBound::Trait(tr) => {
|
|
||||||
assert_simple_bound(&tr);
|
|
||||||
associated_types.insert(&t.ident, assert_single_path_seg(&tr.path));
|
|
||||||
},
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
if bounds_iter.next().is_some() { unimplemented!(); }
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
associated_types
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints a C-mapped trait object containing a void pointer and a jump table for each function in
|
/// Prints a C-mapped trait object containing a void pointer and a jump table for each function in
|
||||||
/// the original trait.
|
/// the original trait.
|
||||||
/// Implements the native Rust trait and relevant parent traits for the new C-mapped trait.
|
/// Implements the native Rust trait and relevant parent traits for the new C-mapped trait.
|
||||||
|
@ -150,10 +125,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
|
||||||
|
|
||||||
let mut gen_types = GenericTypes::new();
|
let mut gen_types = GenericTypes::new();
|
||||||
assert!(gen_types.learn_generics(&t.generics, types));
|
assert!(gen_types.learn_generics(&t.generics, types));
|
||||||
|
gen_types.learn_associated_types(&t, types);
|
||||||
|
|
||||||
writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap();
|
writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap();
|
||||||
writeln!(w, "\tpub this_arg: *mut c_void,").unwrap();
|
writeln!(w, "\tpub this_arg: *mut c_void,").unwrap();
|
||||||
let associated_types = learn_associated_types(t);
|
|
||||||
let mut generated_fields = Vec::new(); // Every field's name except this_arg, used in Clone generation
|
let mut generated_fields = Vec::new(); // Every field's name except this_arg, used in Clone generation
|
||||||
for item in t.items.iter() {
|
for item in t.items.iter() {
|
||||||
match item {
|
match item {
|
||||||
|
@ -210,7 +185,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
|
||||||
|
|
||||||
write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap();
|
write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap();
|
||||||
generated_fields.push(format!("{}", m.sig.ident));
|
generated_fields.push(format!("{}", m.sig.ident));
|
||||||
write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false);
|
write_method_params(w, &m.sig, "c_void", types, Some(&gen_types), true, false);
|
||||||
writeln!(w, ",").unwrap();
|
writeln!(w, ",").unwrap();
|
||||||
|
|
||||||
gen_types.pop_ctx();
|
gen_types.pop_ctx();
|
||||||
|
@ -363,7 +338,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
|
||||||
}
|
}
|
||||||
write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true);
|
write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true);
|
||||||
write!(w, "(self.{})(", m.sig.ident).unwrap();
|
write!(w, "(self.{})(", m.sig.ident).unwrap();
|
||||||
write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true);
|
write_method_call_params(w, &m.sig, "\t", types, Some(&gen_types), "", true);
|
||||||
|
|
||||||
writeln!(w, "\n\t}}").unwrap();
|
writeln!(w, "\n\t}}").unwrap();
|
||||||
gen_types.pop_ctx();
|
gen_types.pop_ctx();
|
||||||
|
@ -605,7 +580,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
|
||||||
// That's great, except that they are unresolved idents, so if we learn
|
// That's great, except that they are unresolved idents, so if we learn
|
||||||
// mappings from a trai defined in a different file, we may mis-resolve or
|
// mappings from a trai defined in a different file, we may mis-resolve or
|
||||||
// fail to resolve the mapped types.
|
// fail to resolve the mapped types.
|
||||||
let trait_associated_types = learn_associated_types(trait_obj);
|
gen_types.learn_associated_types(trait_obj, types);
|
||||||
let mut impl_associated_types = HashMap::new();
|
let mut impl_associated_types = HashMap::new();
|
||||||
for item in i.items.iter() {
|
for item in i.items.iter() {
|
||||||
match item {
|
match item {
|
||||||
|
@ -706,7 +681,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
|
||||||
write!(w, "extern \"C\" fn {}_{}_{}(", ident, trait_obj.ident, $m.sig.ident).unwrap();
|
write!(w, "extern \"C\" fn {}_{}_{}(", ident, trait_obj.ident, $m.sig.ident).unwrap();
|
||||||
gen_types.push_ctx();
|
gen_types.push_ctx();
|
||||||
assert!(gen_types.learn_generics(&$m.sig.generics, types));
|
assert!(gen_types.learn_generics(&$m.sig.generics, types));
|
||||||
write_method_params(w, &$m.sig, &trait_associated_types, "c_void", types, Some(&gen_types), true, true);
|
write_method_params(w, &$m.sig, "c_void", types, Some(&gen_types), true, true);
|
||||||
write!(w, " {{\n\t").unwrap();
|
write!(w, " {{\n\t").unwrap();
|
||||||
write_method_var_decl_body(w, &$m.sig, "", types, Some(&gen_types), false);
|
write_method_var_decl_body(w, &$m.sig, "", types, Some(&gen_types), false);
|
||||||
let mut takes_self = false;
|
let mut takes_self = false;
|
||||||
|
@ -732,7 +707,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
write_method_call_params(w, &$m.sig, &trait_associated_types, "", types, Some(&gen_types), &real_type, false);
|
write_method_call_params(w, &$m.sig, "", types, Some(&gen_types), &real_type, false);
|
||||||
gen_types.pop_ctx();
|
gen_types.pop_ctx();
|
||||||
write!(w, "\n}}\n").unwrap();
|
write!(w, "\n}}\n").unwrap();
|
||||||
if let syn::ReturnType::Type(_, rtype) = &$m.sig.output {
|
if let syn::ReturnType::Type(_, rtype) = &$m.sig.output {
|
||||||
|
@ -819,7 +794,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
|
||||||
};
|
};
|
||||||
gen_types.push_ctx();
|
gen_types.push_ctx();
|
||||||
assert!(gen_types.learn_generics(&m.sig.generics, types));
|
assert!(gen_types.learn_generics(&m.sig.generics, types));
|
||||||
write_method_params(w, &m.sig, &HashMap::new(), &ret_type, types, Some(&gen_types), false, true);
|
write_method_params(w, &m.sig, &ret_type, types, Some(&gen_types), false, true);
|
||||||
write!(w, " {{\n\t").unwrap();
|
write!(w, " {{\n\t").unwrap();
|
||||||
write_method_var_decl_body(w, &m.sig, "", types, Some(&gen_types), false);
|
write_method_var_decl_body(w, &m.sig, "", types, Some(&gen_types), false);
|
||||||
let mut takes_self = false;
|
let mut takes_self = false;
|
||||||
|
@ -837,7 +812,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
|
||||||
} else {
|
} else {
|
||||||
write!(w, "{}::{}::{}(", types.orig_crate, resolved_path, m.sig.ident).unwrap();
|
write!(w, "{}::{}::{}(", types.orig_crate, resolved_path, m.sig.ident).unwrap();
|
||||||
}
|
}
|
||||||
write_method_call_params(w, &m.sig, &HashMap::new(), "", types, Some(&gen_types), &ret_type, false);
|
write_method_call_params(w, &m.sig, "", types, Some(&gen_types), &ret_type, false);
|
||||||
gen_types.pop_ctx();
|
gen_types.pop_ctx();
|
||||||
writeln!(w, "\n}}\n").unwrap();
|
writeln!(w, "\n}}\n").unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1018,11 +993,11 @@ fn writeln_fn<'a, 'b, W: std::io::Write>(w: &mut W, f: &'a syn::ItemFn, types: &
|
||||||
if !gen_types.learn_generics(&f.sig.generics, types) { return; }
|
if !gen_types.learn_generics(&f.sig.generics, types) { return; }
|
||||||
|
|
||||||
write!(w, "#[no_mangle]\npub extern \"C\" fn {}(", f.sig.ident).unwrap();
|
write!(w, "#[no_mangle]\npub extern \"C\" fn {}(", f.sig.ident).unwrap();
|
||||||
write_method_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), false, true);
|
write_method_params(w, &f.sig, "", types, Some(&gen_types), false, true);
|
||||||
write!(w, " {{\n\t").unwrap();
|
write!(w, " {{\n\t").unwrap();
|
||||||
write_method_var_decl_body(w, &f.sig, "", types, Some(&gen_types), false);
|
write_method_var_decl_body(w, &f.sig, "", types, Some(&gen_types), false);
|
||||||
write!(w, "{}::{}::{}(", types.orig_crate, types.module_path, f.sig.ident).unwrap();
|
write!(w, "{}::{}::{}(", types.orig_crate, types.module_path, f.sig.ident).unwrap();
|
||||||
write_method_call_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), "", false);
|
write_method_call_params(w, &f.sig, "", types, Some(&gen_types), "", false);
|
||||||
writeln!(w, "\n}}\n").unwrap();
|
writeln!(w, "\n}}\n").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,6 @@ pub fn get_single_remaining_path_seg<'a, I: Iterator<Item=&'a syn::PathSegment>>
|
||||||
} else { None }
|
} else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_single_path_seg<'a>(p: &'a syn::Path) -> &'a syn::Ident {
|
|
||||||
if p.leading_colon.is_some() { unimplemented!(); }
|
|
||||||
get_single_remaining_path_seg(&mut p.segments.iter()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn single_ident_generic_path_to_ident(p: &syn::Path) -> Option<&syn::Ident> {
|
pub fn single_ident_generic_path_to_ident(p: &syn::Path) -> Option<&syn::Ident> {
|
||||||
if p.segments.len() == 1 {
|
if p.segments.len() == 1 {
|
||||||
Some(&p.segments.iter().next().unwrap().ident)
|
Some(&p.segments.iter().next().unwrap().ident)
|
||||||
|
@ -134,6 +129,7 @@ impl<'a> GenericTypes<'a> {
|
||||||
|
|
||||||
/// Learn the generics in generics in the current context, given a TypeResolver.
|
/// Learn the generics in generics in the current context, given a TypeResolver.
|
||||||
pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
|
pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
|
||||||
|
// First learn simple generics...
|
||||||
for generic in generics.params.iter() {
|
for generic in generics.params.iter() {
|
||||||
match generic {
|
match generic {
|
||||||
syn::GenericParam::Type(type_param) => {
|
syn::GenericParam::Type(type_param) => {
|
||||||
|
@ -161,6 +157,7 @@ impl<'a> GenericTypes<'a> {
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Then find generics where we are required to pass a Deref<Target=X> and pretend its just X.
|
||||||
if let Some(wh) = &generics.where_clause {
|
if let Some(wh) = &generics.where_clause {
|
||||||
for pred in wh.predicates.iter() {
|
for pred in wh.predicates.iter() {
|
||||||
if let syn::WherePredicate::Type(t) = pred {
|
if let syn::WherePredicate::Type(t) = pred {
|
||||||
|
@ -193,6 +190,38 @@ impl<'a> GenericTypes<'a> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Learn the associated types from the trait in the current context.
|
||||||
|
pub fn learn_associated_types<'b, 'c>(&mut self, t: &'a syn::ItemTrait, types: &'b TypeResolver<'a, 'c>) {
|
||||||
|
for item in t.items.iter() {
|
||||||
|
match item {
|
||||||
|
&syn::TraitItem::Type(ref t) => {
|
||||||
|
if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
|
||||||
|
let mut bounds_iter = t.bounds.iter();
|
||||||
|
match bounds_iter.next().unwrap() {
|
||||||
|
syn::TypeParamBound::Trait(tr) => {
|
||||||
|
assert_simple_bound(&tr);
|
||||||
|
if let Some(mut path) = types.maybe_resolve_path(&tr.path, None) {
|
||||||
|
if types.skip_path(&path) { continue; }
|
||||||
|
// In general we handle Deref<Target=X> as if it were just X (and
|
||||||
|
// implement Deref<Target=Self> for relevant types). We don't
|
||||||
|
// bother to implement it for associated types, however, so we just
|
||||||
|
// ignore such bounds.
|
||||||
|
let new_ident = if path != "std::ops::Deref" {
|
||||||
|
path = "crate::".to_string() + &path;
|
||||||
|
Some(&tr.path)
|
||||||
|
} else { None };
|
||||||
|
self.typed_generics.last_mut().unwrap().insert(&t.ident, (path, new_ident));
|
||||||
|
} else { unimplemented!(); }
|
||||||
|
},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
if bounds_iter.next().is_some() { unimplemented!(); }
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to resolve an Ident as a generic parameter and return the full path.
|
/// Attempt to resolve an Ident as a generic parameter and return the full path.
|
||||||
pub fn maybe_resolve_ident<'b>(&'b self, ident: &syn::Ident) -> Option<&'b String> {
|
pub fn maybe_resolve_ident<'b>(&'b self, ident: &syn::Ident) -> Option<&'b String> {
|
||||||
for gen in self.typed_generics.iter().rev() {
|
for gen in self.typed_generics.iter().rev() {
|
||||||
|
@ -211,6 +240,18 @@ impl<'a> GenericTypes<'a> {
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Associated types are usually specified as "Self::Generic", so we check for that
|
||||||
|
// explicitly here.
|
||||||
|
let mut it = path.segments.iter();
|
||||||
|
if path.segments.len() == 2 && format!("{}", it.next().unwrap().ident) == "Self" {
|
||||||
|
let ident = &it.next().unwrap().ident;
|
||||||
|
for gen in self.typed_generics.iter().rev() {
|
||||||
|
if let Some(res) = gen.get(ident).map(|(a, b)| (a, b.unwrap())) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue