Skip to content

Commit 6355acb

Browse files
committed
feat: flatted associated types for retvals and error for unresolved associated types
1 parent c8cd59e commit 6355acb

1 file changed

Lines changed: 77 additions & 28 deletions

File tree

soroban-sdk-macros/src/syn_ext.rs

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ impl Parse for HasFnsItem {
195195
Ok(HasFnsItem::Trait(t))
196196
} else if lookahead.peek(Token![impl]) {
197197
let mut imp = input.parse()?;
198-
flatten_associated_items_in_impl_fns(&mut imp);
198+
flatten_associated_items_in_impl_fns(&mut imp)?;
199199
Ok(HasFnsItem::Impl(imp))
200200
} else {
201201
Err(lookahead.error())
@@ -306,46 +306,95 @@ fn unpack_result(typ: &Type) -> Option<(Type, Type)> {
306306
}
307307
}
308308

309-
fn flatten_associated_items_in_impl_fns(imp: &mut ItemImpl) {
309+
fn flatten_associated_items_in_impl_fns(imp: &mut ItemImpl) -> Result<(), Error> {
310310
// TODO: Flatten associated consts used in functions.
311311
// Flatten associated types used in functions.
312-
let associated_types = imp
312+
let associated_types: HashMap<Ident, Type> = imp
313313
.items
314314
.iter()
315315
.filter_map(|item| match item {
316316
ImplItem::Type(i) => Some((i.ident.clone(), i.ty.clone())),
317317
_ => None,
318318
})
319-
.collect::<HashMap<_, _>>();
320-
let fn_input_types = imp
321-
.items
322-
.iter_mut()
323-
.filter_map(|item| match item {
324-
ImplItem::Fn(f) => Some(f.sig.inputs.iter_mut().filter_map(|input| match input {
325-
FnArg::Typed(t) => Some(&mut t.ty),
326-
_ => None,
327-
})),
328-
_ => None,
329-
})
330-
.flatten();
331-
for t in fn_input_types {
332-
if let Type::Path(TypePath { qself: None, path }) = t.as_mut() {
333-
let segments = &path.segments;
334-
if segments.len() == 2
335-
&& segments.first() == Some(&PathSegment::from(format_ident!("Self")))
336-
{
337-
if let Some(PathSegment {
338-
arguments: PathArguments::None,
339-
ident,
340-
}) = segments.get(1)
341-
{
342-
if let Some(resolved_ty) = associated_types.get(ident) {
343-
*t.as_mut() = resolved_ty.clone();
319+
.collect();
320+
321+
// Resolve Self::* in function input types and return types.
322+
for item in imp.items.iter_mut() {
323+
if let ImplItem::Fn(f) = item {
324+
for input in f.sig.inputs.iter_mut() {
325+
if let FnArg::Typed(t) = input {
326+
if let Some(resolved) = resolve_self_type(&t.ty, &associated_types) {
327+
*t.ty = resolved;
328+
}
329+
}
330+
}
331+
if let ReturnType::Type(_, ty) = &mut f.sig.output {
332+
if let Some(resolved) = resolve_self_type(ty, &associated_types) {
333+
**ty = resolved;
334+
}
335+
}
336+
}
337+
}
338+
339+
// Check for any remaining unresolved Self::* types in fn signatures.
340+
for item in imp.items.iter() {
341+
if let ImplItem::Fn(f) = item {
342+
for input in f.sig.inputs.iter() {
343+
if let FnArg::Typed(t) = input {
344+
if let Some(ident) = self_type_ident(&t.ty) {
345+
return Err(Error::new(
346+
t.ty.span(),
347+
format!(
348+
"unresolved associated type `Self::{ident}` in function \
349+
parameter; use a concrete type instead"
350+
),
351+
));
344352
}
345353
}
346354
}
355+
if let ReturnType::Type(_, ty) = &f.sig.output {
356+
if let Some(ident) = self_type_ident(ty) {
357+
return Err(Error::new(
358+
ty.span(),
359+
format!(
360+
"unresolved associated type `Self::{ident}` in return type; \
361+
use a concrete type instead"
362+
),
363+
));
364+
}
365+
}
366+
}
367+
}
368+
369+
Ok(())
370+
}
371+
372+
/// If the type is `Self::Ident` and `Ident` exists in `associated_types`,
373+
/// return the resolved type. Otherwise return `None`.
374+
fn resolve_self_type(ty: &Type, associated_types: &HashMap<Ident, Type>) -> Option<Type> {
375+
match self_type_ident(ty) {
376+
Some(ident) => associated_types.get(ident).cloned(),
377+
None => None,
378+
}
379+
}
380+
381+
/// If the type is `Self::Ident`, return the `Ident`. Otherwise return `None`.
382+
fn self_type_ident(ty: &Type) -> Option<&Ident> {
383+
if let Type::Path(TypePath { qself: None, path }) = ty {
384+
let segments = &path.segments;
385+
if segments.len() == 2
386+
&& segments.first() == Some(&PathSegment::from(format_ident!("Self")))
387+
{
388+
if let Some(PathSegment {
389+
arguments: PathArguments::None,
390+
ident,
391+
}) = segments.get(1)
392+
{
393+
return Some(ident);
394+
}
347395
}
348396
}
397+
None
349398
}
350399

351400
pub fn ty_to_safe_ident_str(ty: &Type) -> String {

0 commit comments

Comments
 (0)