mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Bump syn/quote in style_derive
This commit is contained in:
parent
cf6330b6c1
commit
9faa579c89
12 changed files with 302 additions and 338 deletions
|
@ -4,22 +4,21 @@
|
|||
|
||||
use darling::{FromDeriveInput, FromField, FromVariant};
|
||||
use quote::{ToTokens, Tokens};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::iter;
|
||||
use syn::{self, AngleBracketedParameterData, Body, DeriveInput, Field, Ident};
|
||||
use syn::{ImplGenerics, Path, PathParameters, PathSegment, PolyTraitRef};
|
||||
use syn::{QSelf, TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound};
|
||||
use syn::{TypeBinding, Variant, WhereBoundPredicate, WherePredicate};
|
||||
use syn::visit::{self, Visitor};
|
||||
use synstructure::{self, BindOpts, BindStyle, BindingInfo};
|
||||
use syn::{self, DeriveInput, Field, Ident};
|
||||
use syn::{ImplGenerics, Path, PathArguments, PathSegment, AngleBracketedGenericArguments, GenericParam};
|
||||
use syn::{QSelf, Type, TypeGenerics, TypeParam};
|
||||
use syn::{TypeSlice, TypeArray, TypeTuple, TypePath, TypeParen};
|
||||
use syn::{Variant, WherePredicate, GenericArgument, Binding};
|
||||
use syn::visit::{self, Visit};
|
||||
use synstructure::{self, BindingInfo, BindStyle, VariantAst, VariantInfo};
|
||||
|
||||
pub struct WhereClause<'input, 'path> {
|
||||
pub inner: syn::WhereClause,
|
||||
pub params: &'input [TyParam],
|
||||
trait_path: &'path [&'path str],
|
||||
trait_output: Option<&'path str>,
|
||||
bounded_types: HashSet<Ty>,
|
||||
pub inner: Option<syn::WhereClause>,
|
||||
pub params: Vec<&'input TypeParam>,
|
||||
trait_path: &'path Path,
|
||||
trait_output: Option<Ident>,
|
||||
bounded_types: HashSet<Type>,
|
||||
}
|
||||
|
||||
impl<'input, 'path> ToTokens for WhereClause<'input, 'path> {
|
||||
|
@ -29,14 +28,13 @@ impl<'input, 'path> ToTokens for WhereClause<'input, 'path> {
|
|||
}
|
||||
|
||||
impl<'input, 'path> WhereClause<'input, 'path> {
|
||||
pub fn add_trait_bound(&mut self, ty: &Ty) {
|
||||
pub fn add_trait_bound(&mut self, ty: &Type) {
|
||||
let trait_path = self.trait_path;
|
||||
let params = self.params;
|
||||
let mut found = self.trait_output.map(|_| HashSet::new());
|
||||
if self.bounded_types.contains(&ty) {
|
||||
return;
|
||||
}
|
||||
if !is_parameterized(&ty, params, found.as_mut()) {
|
||||
if !is_parameterized(&ty, &self.params, found.as_mut()) {
|
||||
return;
|
||||
}
|
||||
self.bounded_types.insert(ty.clone());
|
||||
|
@ -44,19 +42,19 @@ impl<'input, 'path> WhereClause<'input, 'path> {
|
|||
let output = if let Some(output) = self.trait_output {
|
||||
output
|
||||
} else {
|
||||
self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
|
||||
self.add_predicate(where_predicate(ty.clone(), trait_path, None));
|
||||
return;
|
||||
};
|
||||
|
||||
if let Ty::Path(None, ref path) = *ty {
|
||||
if let Type::Path(syn::TypePath { ref path, .. }) = *ty {
|
||||
if path_to_ident(path).is_some() {
|
||||
self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
|
||||
self.add_predicate(where_predicate(ty.clone(), trait_path, None));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let output_type = map_type_params(ty, params, &mut |ident| {
|
||||
let ty = Ty::Path(None, ident.clone().into());
|
||||
let output_type = map_type_params(ty, &self.params, &mut |ident| {
|
||||
let ty = Type::Path(syn::TypePath { qself: None, path: ident.clone().into() });
|
||||
fmap_output_type(ty, trait_path, output)
|
||||
});
|
||||
|
||||
|
@ -66,20 +64,29 @@ impl<'input, 'path> WhereClause<'input, 'path> {
|
|||
Some((output, output_type)),
|
||||
);
|
||||
|
||||
self.inner.predicates.push(pred);
|
||||
self.add_predicate(pred);
|
||||
|
||||
if let Some(found) = found {
|
||||
for ident in found {
|
||||
let ty = Ty::Path(None, ident.into());
|
||||
let ty = Type::Path(syn::TypePath { qself: None, path: ident.into() });
|
||||
if !self.bounded_types.contains(&ty) {
|
||||
self.bounded_types.insert(ty.clone());
|
||||
self.inner.predicates.push(
|
||||
self.add_predicate(
|
||||
where_predicate(ty, trait_path, None),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_predicate(&mut self, pred: WherePredicate) {
|
||||
if let Some(ref mut inner) = self.inner {
|
||||
inner.predicates.push(pred);
|
||||
} else {
|
||||
self.inner = Some(parse_quote!(where));
|
||||
self.add_predicate(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmap_match<F>(
|
||||
|
@ -90,71 +97,77 @@ pub fn fmap_match<F>(
|
|||
where
|
||||
F: FnMut(BindingInfo) -> Tokens,
|
||||
{
|
||||
synstructure::each_variant(input, &bind_style.into(), |fields, variant| {
|
||||
let name = variant_ctor(input, variant);
|
||||
let (mapped, mapped_fields) = value(&name, variant, "mapped");
|
||||
let fields_pairs = fields.into_iter().zip(mapped_fields);
|
||||
let mut s = synstructure::Structure::new(input);
|
||||
s.variants_mut().iter_mut().for_each(|v| { v.bind_with(|_| bind_style); });
|
||||
s.each_variant(|variant| {
|
||||
let (mapped, mapped_fields) = value(variant, "mapped");
|
||||
let fields_pairs = variant.bindings().into_iter().zip(mapped_fields);
|
||||
let mut computations = quote!();
|
||||
computations.append_all(fields_pairs.map(|(field, mapped_field)| {
|
||||
let expr = f(field);
|
||||
let expr = f(field.clone());
|
||||
quote! { let #mapped_field = #expr; }
|
||||
}));
|
||||
computations.append(mapped);
|
||||
computations.append_all(mapped);
|
||||
Some(computations)
|
||||
})
|
||||
}
|
||||
|
||||
fn fmap_output_type(
|
||||
ty: Ty,
|
||||
trait_path: &[&str],
|
||||
trait_output: &str,
|
||||
) -> Ty {
|
||||
Ty::Path(
|
||||
Some(QSelf {
|
||||
ty: Box::new(ty),
|
||||
position: trait_path.len(),
|
||||
}),
|
||||
path(trait_path.iter().chain(iter::once(&trait_output))),
|
||||
)
|
||||
ty: Type,
|
||||
trait_path: &Path,
|
||||
trait_output: Ident,
|
||||
) -> Type {
|
||||
parse_quote!(<#ty as ::#trait_path>::#trait_output)
|
||||
}
|
||||
|
||||
pub fn fmap_trait_parts<'input, 'path>(
|
||||
input: &'input DeriveInput,
|
||||
trait_path: &'path [&'path str],
|
||||
trait_output: &'path str,
|
||||
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>, Path) {
|
||||
trait_path: &'path Path,
|
||||
trait_output: Ident,
|
||||
) -> (ImplGenerics<'input>, TypeGenerics<'input>, WhereClause<'input, 'path>, Path) {
|
||||
let (impl_generics, ty_generics, mut where_clause) = trait_parts(input, trait_path);
|
||||
where_clause.trait_output = Some(trait_output);
|
||||
let output_ty = PathSegment {
|
||||
ident: input.ident.clone(),
|
||||
parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
|
||||
lifetimes: input.generics.lifetimes.iter().map(|l| l.lifetime.clone()).collect(),
|
||||
types: input.generics.ty_params.iter().map(|ty| {
|
||||
fmap_output_type(
|
||||
Ty::Path(None, ty.ident.clone().into()),
|
||||
trait_path,
|
||||
trait_output,
|
||||
)
|
||||
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
args: input.generics.params.iter().map(|arg| {
|
||||
match arg {
|
||||
&GenericParam::Lifetime(ref data) => GenericArgument::Lifetime(data.lifetime.clone()),
|
||||
&GenericParam::Type(ref data) => {
|
||||
let ident = data.ident;
|
||||
GenericArgument::Type(
|
||||
fmap_output_type(
|
||||
parse_quote!(#ident),
|
||||
trait_path,
|
||||
trait_output
|
||||
)
|
||||
)
|
||||
},
|
||||
ref arg => panic!("arguments {:?} cannot be mapped yet", arg)
|
||||
}
|
||||
}).collect(),
|
||||
.. Default::default()
|
||||
}),
|
||||
}.into();
|
||||
(impl_generics, ty_generics, where_clause, output_ty)
|
||||
colon2_token: Default::default(),
|
||||
gt_token: Default::default(),
|
||||
lt_token: Default::default(),
|
||||
|
||||
})
|
||||
};
|
||||
(impl_generics, ty_generics, where_clause, output_ty.into())
|
||||
}
|
||||
|
||||
pub fn is_parameterized(
|
||||
ty: &Ty,
|
||||
params: &[TyParam],
|
||||
ty: &Type,
|
||||
params: &[&TypeParam],
|
||||
found: Option<&mut HashSet<Ident>>,
|
||||
) -> bool {
|
||||
struct IsParameterized<'a, 'b> {
|
||||
params: &'a [TyParam],
|
||||
params: &'a [&'a TypeParam],
|
||||
has_free: bool,
|
||||
found: Option<&'b mut HashSet<Ident>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Visitor for IsParameterized<'a, 'b> {
|
||||
fn visit_path(&mut self, path: &Path) {
|
||||
impl<'a, 'b, 'ast> Visit<'ast> for IsParameterized<'a, 'b> {
|
||||
fn visit_path(&mut self, path: &'ast Path) {
|
||||
if let Some(ident) = path_to_ident(path) {
|
||||
if self.params.iter().any(|param| param.ident == ident) {
|
||||
self.has_free = true;
|
||||
|
@ -163,100 +176,104 @@ pub fn is_parameterized(
|
|||
}
|
||||
}
|
||||
}
|
||||
visit::walk_path(self, path);
|
||||
visit::visit_path(self, path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = IsParameterized { params, has_free: false, found };
|
||||
visitor.visit_ty(ty);
|
||||
visitor.visit_type(ty);
|
||||
visitor.has_free
|
||||
}
|
||||
|
||||
pub fn map_type_params<F>(ty: &Ty, params: &[TyParam], f: &mut F) -> Ty
|
||||
pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type
|
||||
where
|
||||
F: FnMut(&Ident) -> Ty,
|
||||
F: FnMut(&Ident) -> Type,
|
||||
{
|
||||
match *ty {
|
||||
Ty::Slice(ref ty) => Ty::Slice(Box::new(map_type_params(ty, params, f))),
|
||||
Ty::Array(ref ty, ref expr) => {
|
||||
Ty::Array(Box::new(map_type_params(ty, params, f)), expr.clone())
|
||||
Type::Slice(ref inner) => {
|
||||
Type::from(TypeSlice { elem: Box::new(map_type_params(&inner.elem, params, f)), ..inner.clone() })
|
||||
},
|
||||
Ty::Never => Ty::Never,
|
||||
Ty::Tup(ref items) => {
|
||||
Ty::Tup(items.iter().map(|ty| map_type_params(ty, params, f)).collect())
|
||||
Type::Array(ref inner) => { //ref ty, ref expr) => {
|
||||
Type::from(TypeArray { elem: Box::new(map_type_params(&inner.elem, params, f)), ..inner.clone() })
|
||||
},
|
||||
Ty::Path(None, ref path) => {
|
||||
ref ty @ Type::Never(_) => ty.clone(),
|
||||
Type::Tuple(ref inner) => {
|
||||
Type::from(
|
||||
TypeTuple {
|
||||
elems: inner.elems.iter().map(|ty| map_type_params(&ty, params, f)).collect(),
|
||||
..inner.clone()
|
||||
}
|
||||
)
|
||||
},
|
||||
Type::Path(TypePath { qself: None, ref path }) => {
|
||||
if let Some(ident) = path_to_ident(path) {
|
||||
if params.iter().any(|param| param.ident == ident) {
|
||||
return f(ident);
|
||||
}
|
||||
}
|
||||
Ty::Path(None, map_type_params_in_path(path, params, f))
|
||||
Type::from(TypePath { qself: None, path: map_type_params_in_path(path, params, f) })
|
||||
}
|
||||
Ty::Path(ref qself, ref path) => {
|
||||
Ty::Path(
|
||||
qself.as_ref().map(|qself| {
|
||||
Type::Path(TypePath { ref qself, ref path }) => {
|
||||
Type::from(TypePath {
|
||||
qself: qself.as_ref().map(|qself| {
|
||||
QSelf {
|
||||
ty: Box::new(map_type_params(&qself.ty, params, f)),
|
||||
position: qself.position,
|
||||
..qself.clone()
|
||||
}
|
||||
}),
|
||||
map_type_params_in_path(path, params, f),
|
||||
)
|
||||
path: map_type_params_in_path(path, params, f),
|
||||
})
|
||||
},
|
||||
Type::Paren(ref inner) => {
|
||||
Type::from(TypeParen { elem: Box::new(map_type_params(&inner.elem, params, f)), ..inner.clone() })
|
||||
},
|
||||
Ty::Paren(ref ty) => Ty::Paren(Box::new(map_type_params(ty, params, f))),
|
||||
ref ty => panic!("type {:?} cannot be mapped yet", ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_type_params_in_path<F>(path: &Path, params: &[TyParam], f: &mut F) -> Path
|
||||
fn map_type_params_in_path<F>(path: &Path, params: &[&TypeParam], f: &mut F) -> Path
|
||||
where
|
||||
F: FnMut(&Ident) -> Ty,
|
||||
F: FnMut(&Ident) -> Type,
|
||||
{
|
||||
Path {
|
||||
global: path.global,
|
||||
leading_colon: path.leading_colon,
|
||||
segments: path.segments.iter().map(|segment| {
|
||||
PathSegment {
|
||||
ident: segment.ident.clone(),
|
||||
parameters: match segment.parameters {
|
||||
PathParameters::AngleBracketed(ref data) => {
|
||||
PathParameters::AngleBracketed(AngleBracketedParameterData {
|
||||
lifetimes: data.lifetimes.clone(),
|
||||
types: data.types.iter().map(|ty| {
|
||||
map_type_params(ty, params, f)
|
||||
}).collect(),
|
||||
bindings: data.bindings.iter().map(|binding| {
|
||||
TypeBinding {
|
||||
ident: binding.ident.clone(),
|
||||
ty: map_type_params(&binding.ty, params, f),
|
||||
arguments: match segment.arguments {
|
||||
PathArguments::AngleBracketed(ref data) => {
|
||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
args: data.args.iter().map(|arg| {
|
||||
match arg {
|
||||
ty @ &GenericArgument::Lifetime(_) => ty.clone(),
|
||||
&GenericArgument::Type(ref data) => {
|
||||
GenericArgument::Type(map_type_params(data, params, f))
|
||||
},
|
||||
&GenericArgument::Binding(ref data) => GenericArgument::Binding(Binding {
|
||||
ty: map_type_params(&data.ty, params, f),
|
||||
..data.clone()
|
||||
}),
|
||||
ref arg => panic!("arguments {:?} cannot be mapped yet", arg)
|
||||
}
|
||||
}).collect(),
|
||||
..data.clone()
|
||||
})
|
||||
},
|
||||
ref arg @ PathArguments::None => arg.clone(),
|
||||
ref parameters => {
|
||||
panic!("parameters {:?} cannot be mapped yet", parameters)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path<S>(segments: S) -> Path
|
||||
where
|
||||
S: IntoIterator,
|
||||
<S as IntoIterator>::Item: AsRef<str>,
|
||||
{
|
||||
Path {
|
||||
global: true,
|
||||
segments: segments.into_iter().map(|s| s.as_ref().into()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_ident(path: &Path) -> Option<&Ident> {
|
||||
match *path {
|
||||
Path { global: false, ref segments } if segments.len() == 1 => {
|
||||
if segments[0].parameters.is_empty() {
|
||||
Path { leading_colon: None, ref segments } if segments.len() == 1 => {
|
||||
if segments[0].arguments.is_empty() {
|
||||
Some(&segments[0].ident)
|
||||
} else {
|
||||
None
|
||||
|
@ -286,36 +303,41 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_variant_attrs<A>(variant: &Variant) -> A
|
||||
pub fn parse_variant_attrs<A>(variant: &VariantAst) -> A
|
||||
where
|
||||
A: FromVariant,
|
||||
{
|
||||
match A::from_variant(variant) {
|
||||
let v = Variant {
|
||||
ident: *variant.ident,
|
||||
attrs: variant.attrs.to_vec(),
|
||||
fields: variant.fields.clone(),
|
||||
discriminant: variant.discriminant.clone(),
|
||||
};
|
||||
match A::from_variant(&v) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => panic!("failed to parse variant attributes: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn ref_pattern<'a>(
|
||||
name: &Ident,
|
||||
variant: &'a Variant,
|
||||
variant: &'a VariantInfo,
|
||||
prefix: &str,
|
||||
) -> (Tokens, Vec<BindingInfo<'a>>) {
|
||||
synstructure::match_pattern(
|
||||
&name,
|
||||
&variant.data,
|
||||
&BindOpts::with_prefix(BindStyle::Ref, prefix.to_owned()),
|
||||
)
|
||||
let mut v = variant.clone();
|
||||
v.bind_with(|_| BindStyle::Ref);
|
||||
v.bindings_mut().iter_mut().for_each(|b| { b.binding = Ident::from(format!("{}_{}", b.binding, prefix)) });
|
||||
(v.pat(), v.bindings().iter().cloned().collect())
|
||||
}
|
||||
|
||||
pub fn trait_parts<'input, 'path>(
|
||||
input: &'input DeriveInput,
|
||||
trait_path: &'path [&'path str],
|
||||
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>) {
|
||||
trait_path: &'path Path,
|
||||
) -> (ImplGenerics<'input>, TypeGenerics<'input>, WhereClause<'input, 'path>) {
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let where_clause = WhereClause {
|
||||
inner: where_clause.clone(),
|
||||
params: &input.generics.ty_params,
|
||||
inner: where_clause.cloned(),
|
||||
params: input.generics.type_params().into_iter().collect::<Vec<&TypeParam>>(),
|
||||
trait_path,
|
||||
trait_output: None,
|
||||
bounded_types: HashSet::new()
|
||||
|
@ -323,88 +345,35 @@ pub fn trait_parts<'input, 'path>(
|
|||
(impl_generics, ty_generics, where_clause)
|
||||
}
|
||||
|
||||
fn trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path {
|
||||
let (name, parent) = path.split_last().unwrap();
|
||||
let last_segment = PathSegment {
|
||||
ident: (*name).into(),
|
||||
parameters: PathParameters::AngleBracketed(
|
||||
AngleBracketedParameterData {
|
||||
bindings: output.into_iter().map(|(param, ty)| {
|
||||
TypeBinding { ident: param.into(), ty }
|
||||
}).collect(),
|
||||
.. Default::default()
|
||||
}
|
||||
)
|
||||
fn trait_ref(path: &Path, output: Option<(Ident, Type)>) -> Path {
|
||||
let segments = path.segments.iter().collect::<Vec<&PathSegment>>();
|
||||
let (name, parent) = segments.split_last().unwrap();
|
||||
|
||||
let last_segment: PathSegment = if let Some((param, ty)) = output {
|
||||
parse_quote!(#name<#param = #ty>)
|
||||
} else {
|
||||
parse_quote!(#name)
|
||||
};
|
||||
Path {
|
||||
global: true,
|
||||
segments: {
|
||||
parent
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::into)
|
||||
.chain(iter::once(last_segment))
|
||||
.collect()
|
||||
},
|
||||
}
|
||||
parse_quote!(::#(#parent::)*#last_segment)
|
||||
}
|
||||
|
||||
pub fn value<'a>(
|
||||
name: &Ident,
|
||||
variant: &'a Variant,
|
||||
variant: &'a VariantInfo,
|
||||
prefix: &str,
|
||||
) -> (Tokens, Vec<BindingInfo<'a>>) {
|
||||
synstructure::match_pattern(
|
||||
&name,
|
||||
&variant.data,
|
||||
&BindOpts::with_prefix(BindStyle::Move, prefix.to_owned()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn variant_ctor<'a>(
|
||||
input: &'a DeriveInput,
|
||||
variant: &Variant,
|
||||
) -> Cow<'a, Ident> {
|
||||
match input.body {
|
||||
Body::Struct(_) => Cow::Borrowed(&input.ident),
|
||||
Body::Enum(_) => {
|
||||
Cow::Owned(Ident::from(
|
||||
format!("{}::{}", input.ident, variant.ident),
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn variants(input: &DeriveInput) -> Cow<[Variant]> {
|
||||
match input.body {
|
||||
Body::Enum(ref variants) => (&**variants).into(),
|
||||
Body::Struct(ref data) => {
|
||||
vec![Variant {
|
||||
ident: input.ident.clone(),
|
||||
attrs: input.attrs.clone(),
|
||||
data: data.clone(),
|
||||
discriminant: None,
|
||||
}].into()
|
||||
},
|
||||
}
|
||||
let mut v = variant.clone();
|
||||
v.bindings_mut().iter_mut().for_each(|b| { b.binding = Ident::from(format!("{}_{}", b.binding, prefix)) });
|
||||
v.bind_with(|_| BindStyle::Move);
|
||||
(v.pat(), v.bindings().iter().cloned().collect())
|
||||
}
|
||||
|
||||
pub fn where_predicate(
|
||||
bounded_ty: Ty,
|
||||
trait_path: &[&str],
|
||||
trait_output: Option<(&str, Ty)>,
|
||||
bounded_ty: Type,
|
||||
trait_path: &Path,
|
||||
trait_output: Option<(Ident, Type)>,
|
||||
) -> WherePredicate {
|
||||
WherePredicate::BoundPredicate(WhereBoundPredicate {
|
||||
bound_lifetimes: vec![],
|
||||
bounded_ty,
|
||||
bounds: vec![TyParamBound::Trait(
|
||||
PolyTraitRef {
|
||||
bound_lifetimes: vec![],
|
||||
trait_ref: trait_ref(trait_path, trait_output),
|
||||
},
|
||||
TraitBoundModifier::None
|
||||
)],
|
||||
})
|
||||
let trait_ref = trait_ref(trait_path, trait_output);
|
||||
parse_quote!(#bounded_ty: #trait_ref)
|
||||
}
|
||||
|
||||
/// Transforms "FooBar" to "foo-bar".
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue