/// A method declared in the `impl` associated with an FFI struct. /// Includes both static and non-static methods, which can be distinguished /// by inspecting [`Method::self_param`]. #[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)] #[non_exhaustive] pubstruct Method { /// The name of the method as initially declared. pub name: Ident,
/// Lines of documentation for the method. pub docs: Docs,
/// The name of the FFI function wrapping around the method. pub full_path_name: Ident,
/// The `self` param of the method, if any. pub self_param: Option<SelfParam>,
/// All non-`self` params taken by the method. pub params: Vec<Param>,
/// The return type of the method, if any. pub return_type: Option<TypeName>,
/// The lifetimes introduced in this method and surrounding impl block. pub lifetime_env: LifetimeEnv,
/// The list of `cfg` attributes (if any). /// /// These are strings instead of `syn::Attribute` or `proc_macro2::TokenStream` /// because those types are not `PartialEq`, `Hash`, `Serialize`, etc. pub attrs: Attrs,
}
impl Method { /// Extracts a [`Method`] from an AST node inside an `impl`. pubfn from_syn(
m: &syn::ImplItemFn,
self_path_type: PathType,
impl_generics: Option<&syn::Generics>,
impl_attrs: &Attrs,
) -> Method { letmut attrs = impl_attrs.clone();
attrs.add_attrs(&m.attrs);
let self_ident = self_path_type.path.elements.last().unwrap(); let method_ident = &m.sig.ident; let concat_method_ident = format!("{self_ident}_{method_ident}"); let extern_ident = syn::Ident::new(
&attrs.abi_rename.apply(concat_method_ident.into()),
m.sig.ident.span(),
);
let all_params = m
.sig
.inputs
.iter()
.filter_map(|a| match a {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(ref t) => Some(Param::from_syn(t, self_path_type.clone())),
})
.collect::<Vec<_>>();
let self_param = m
.sig
.receiver()
.map(|rec| SelfParam::from_syn(rec, self_path_type.clone()));
let return_ty = match &m.sig.output {
syn::ReturnType::Type(_, return_typ) => { // When we allow lifetime elision, this is where we would want to // support it so we can insert the expanded explicit lifetimes.
Some(TypeName::from_syn(
return_typ.as_ref(),
Some(self_path_type),
))
}
syn::ReturnType::Default => None,
};
let lifetime_env = LifetimeEnv::from_method_item(
m,
impl_generics,
self_param.as_ref(),
&all_params[..],
return_ty.as_ref(),
);
/// Returns the parameters that the output is lifetime-bound to. /// /// # Examples /// /// Given the following method: /// ```ignore /// fn foo<'a, 'b: 'a, 'c>(&'a self, bar: Bar<'b>, baz: Baz<'c>) -> FooBar<'a> { ... } /// ``` /// Then this method would return the `&'a self` and `bar: Bar<'b>` params /// because `'a` is in the return type, and `'b` must live at least as long /// as `'a`. It wouldn't include `baz: Baz<'c>` though, because the return /// type isn't bound by `'c` in any way. /// /// # Panics /// /// This method may panic if `TypeName::check_result_type_validity` (called by /// `Method::check_validity`) doesn't pass first, since the result type may /// contain elided lifetimes that we depend on for this method. The validity /// checks ensure that the return type doesn't elide any lifetimes, ensuring /// that this method will produce correct results. pubfn borrowed_params(&self) -> BorrowedParams { // To determine which params the return type is bound to, we just have to // find the params that contain a lifetime that's also in the return type. iflet Some(ref return_type) = self.return_type { // The lifetimes that must outlive the return type let lifetimes = return_type.longer_lifetimes(&self.lifetime_env);
let held_self_param = self.self_param.as_ref().filter(|self_param| { // Check if `self` is a reference with a lifetime in the return type. iflet Some((Lifetime::Named(ref name), _)) = self_param.reference { if lifetimes.contains(&name) { returntrue;
}
}
self_param.path_type.lifetimes.iter().any(|lt| { iflet Lifetime::Named(name) = lt {
lifetimes.contains(&name)
} else { false
}
})
});
// Collect all the params that contain a named lifetime that's also // in the return type. let held_params = self
.params
.iter()
.filter_map(|param| { letmut lt_kind = LifetimeKind::ReturnValue;
param
.ty
.visit_lifetimes(&mut |lt, _| { // Thanks to `TypeName::visit_lifetimes`, we can // traverse the lifetimes without allocations and // short-circuit if we find a match. match lt {
Lifetime::Named(name) if lifetimes.contains(&name) => { return ControlFlow::Break(());
}
Lifetime::Static => {
lt_kind = LifetimeKind::Static; return ControlFlow::Break(());
}
_ => {}
};
ControlFlow::Continue(())
})
.is_break()
.then(|| (param, lt_kind))
})
.collect();
/// Checks whether the method qualifies for special writeable handling. /// To qualify, a method must: /// - not return any value /// - have the last argument be an `&mut diplomat_runtime::DiplomatWriteable` /// /// Typically, methods of this form will be transformed in the bindings to a /// method that doesn't take the writeable as an argument but instead creates /// one locally and just returns the final string. pubfn is_writeable_out(&self) -> bool { let return_compatible = self
.return_type
.as_ref()
.map(|return_type| match return_type {
TypeName::Unit => true,
TypeName::Result(ok, _, _) => {
matches!(ok.as_ref(), TypeName::Unit)
}
_ => false,
})
.unwrap_or(true);
/// Checks if any parameters are writeable (regardless of other compatibilities for writeable output) pubfn has_writeable_param(&self) -> bool { self.params.iter().any(|p| p.is_writeable())
}
/// The `self` parameter taken by a [`Method`]. #[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)] #[non_exhaustive] pubstruct SelfParam { /// The lifetime and mutability of the `self` param, if it's a reference. pub reference: Option<(Lifetime, Mutability)>,
/// The type of the parameter, which will be a named reference to /// the associated struct, pub path_type: PathType,
}
impl SelfParam { pubfn to_typename(&self) -> TypeName { let typ = TypeName::Named(self.path_type.clone()); iflet Some((ref lifetime, ref mutability)) = self.reference { return TypeName::Reference(lifetime.clone(), *mutability, Box::new(typ));
}
typ
}
/// A parameter taken by a [`Method`], not including `self`. #[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)] #[non_exhaustive] pubstruct Param { /// The name of the parameter in the original method declaration. pub name: Ident,
/// The type of the parameter. pub ty: TypeName,
}
impl Param { /// Check if this parameter is a Writeable pubfn is_writeable(&self) -> bool { matchself.ty {
TypeName::Reference(_, Mutability::Mutable, ref w) => **w == TypeName::Writeable,
_ => false,
}
}
/// The type of lifetime. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pubenum LifetimeKind { /// Param must live at least as long as the returned object.
ReturnValue, /// Param must live for the duration of the program. Static,
}
#[derive(Default, Debug)] /// Parameters in a method that might be borrowed in the return type. #[non_exhaustive] pubstruct BorrowedParams<'a>( pub Option<&'a SelfParam>, pub Vec<(&'a Param, LifetimeKind)>,
);
impl BorrowedParams<'_> { /// Returns an [`Iterator`] through the names of the parameters that are borrowed /// for the lifetime of the return value, accepting an `Ident` that the `self` /// param will be called if present. pubfn return_names<'a>(&'a self, self_name: &'a Ident) -> impl Iterator<Item = &'a Ident> { self.0.iter().map(move |_| self_name).chain( self.1
.iter()
.filter(|(_, ltk)| (*ltk == LifetimeKind::ReturnValue))
.map(|(param, _)| ¶m.name),
)
}
/// Returns an [`Iterator`] through the names of the parameters that are borrowed for a /// static lifetime. pubfn static_names(&self) -> impl Iterator<Item = &'_ Ident> { self.1
.iter()
.filter(|(_, ltk)| (*ltk == LifetimeKind::Static))
.map(|(param, _)| ¶m.name)
}
/// Returns `true` if a provided param name is included in the borrowed params, /// otherwise `false`. /// /// This method doesn't check the `self` parameter. Use /// [`BorrowedParams::borrows_self`] instead. pubfn contains(&self, param_name: &Ident) -> bool { self.1.iter().any(|(param, _)| ¶m.name == param_name)
}
/// Returns `true` if there are no borrowed parameters, otherwise `false`. pubfn is_empty(&self) -> bool { self.0.is_none() && self.1.is_empty()
}
/// Returns `true` if the `self` param is borrowed, otherwise `false`. pubfn borrows_self(&self) -> bool { self.0.is_some()
}
/// Returns `true` if there are any borrowed params, otherwise `false`. pubfn borrows_params(&self) -> bool {
!self.1.is_empty()
}
/// Returns the number of borrowed params. pubfn len(&self) -> usize { self.1.len() + usize::from(self.0.is_some())
}
}
let borrowed_params = method.borrowed_params(); // The ident parser in syn doesn't allow `self`, so we use "this" as a placeholder // and then change it. letmut actual_return: Vec<&str> = borrowed_params.return_names(&Ident::THIS).map(|ident| ident.as_str()).collect(); if borrowed_params.0.is_some() {
actual_return[0] = "self";
} let expected_return: &[&str] = &[$(stringify!($return_param)),*];
assert_eq!(actual_return, expected_return); let actual_static: Vec<&str> = borrowed_params.static_names().map(|ident| ident.as_str()).collect(); let expected_static: &[&str] = &[$($(stringify!($static_param)),*)?];
assert_eq!(actual_static, expected_static);
}};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.