Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions packages/yew-macro/src/hook/lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use syn::{
// borrowed from the awesome async-trait crate.
pub struct CollectLifetimes {
pub elided: Vec<Lifetime>,
pub explicit: Vec<Lifetime>,
pub name: &'static str,
pub default_span: Span,

Expand All @@ -23,7 +22,6 @@ impl CollectLifetimes {
pub fn new(name: &'static str, default_span: Span) -> Self {
CollectLifetimes {
elided: Vec::new(),
explicit: Vec::new(),
name,
default_span,

Expand Down Expand Up @@ -55,8 +53,6 @@ impl CollectLifetimes {
fn visit_lifetime(&mut self, lifetime: &mut Lifetime) {
if lifetime.ident == "_" {
*lifetime = self.next_lifetime(lifetime.span());
} else {
self.explicit.push(lifetime.clone());
}
}

Expand Down
51 changes: 37 additions & 14 deletions packages/yew-macro/src/hook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use proc_macro2::{Span, TokenStream};
use proc_macro_error::emit_error;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{
parse_file, parse_quote, visit_mut, Attribute, Ident, ItemFn, LitStr, ReturnType, Signature,
Type,
visit_mut, AttrStyle, Attribute, Block, Expr, ExprPath, File, Ident, Item, ItemFn, LitStr,
Meta, MetaNameValue, ReturnType, Signature, Stmt, Token, Type,
};

mod body;
Expand Down Expand Up @@ -51,16 +52,26 @@ impl Parse for HookFn {

impl HookFn {
fn doc_attr(&self) -> Attribute {
let vis = &self.inner.vis;
let sig = &self.inner.sig;

let sig_s = quote! { #vis #sig {
__yew_macro_dummy_function_body__
} }
.to_string();

let sig_file = parse_file(&sig_s).unwrap();
let sig_formatted = prettyplease::unparse(&sig_file);
let span = self.inner.span();

let sig_formatted = prettyplease::unparse(&File {
shebang: None,
attrs: vec![],
items: vec![Item::Fn(ItemFn {
block: Box::new(Block {
brace_token: Default::default(),
stmts: vec![Stmt::Expr(
Expr::Path(ExprPath {
attrs: vec![],
qself: None,
path: Ident::new("__yew_macro_dummy_function_body__", span).into(),
}),
None,
)],
}),
..self.inner.clone()
})],
});

let literal = LitStr::new(
&format!(
Expand All @@ -78,10 +89,22 @@ When used in function components and hooks, this hook is equivalent to:
"/* implementation omitted */"
)
),
Span::mixed_site(),
span,
);

parse_quote!(#[doc = #literal])
Attribute {
pound_token: Default::default(),
style: AttrStyle::Outer,
bracket_token: Default::default(),
meta: Meta::NameValue(MetaNameValue {
path: Ident::new("doc", span).into(),
eq_token: Token![=](span),
value: Expr::Lit(syn::ExprLit {
attrs: vec![],
lit: literal.into(),
}),
}),
}
}
}

Expand Down
120 changes: 80 additions & 40 deletions packages/yew-macro/src/hook/signature.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use std::iter::once;
use std::mem::take;

use proc_macro2::{Span, TokenStream};
use proc_macro_error::emit_error;
use quote::{quote, ToTokens};
use syn::punctuated::{Pair, Punctuated};
use syn::spanned::Spanned;
use syn::visit_mut::VisitMut;
use syn::{
parse_quote, parse_quote_spanned, token, visit_mut, FnArg, GenericParam, Ident, Lifetime, Pat,
Receiver, ReturnType, Signature, Type, TypeImplTrait, TypeReference, WhereClause,
parse_quote, parse_quote_spanned, visit_mut, FnArg, GenericParam, Ident, Lifetime,
LifetimeParam, Pat, Receiver, ReturnType, Signature, Type, TypeImplTrait, TypeParam,
TypeParamBound, TypeReference, WherePredicate,
};

use super::lifetime;

fn type_is_generic(ty: &Type, param: &TypeParam) -> bool {
match ty {
Type::Path(path) => path.path.is_ident(&param.ident),
_ => false,
}
}

#[derive(Default)]
pub struct CollectArgs {
needs_boxing: bool,
Expand Down Expand Up @@ -99,48 +111,68 @@ impl HookSignature {
..
} = sig;

let hook_lifetime = {
let hook_lifetime = Lifetime::new("'hook", Span::mixed_site());
generics.params = {
let elided_lifetimes = &lifetimes.elided;
let params = &generics.params;

parse_quote!(#hook_lifetime, #(#elided_lifetimes,)* #params)
};

let mut where_clause = generics
.where_clause
.clone()
.unwrap_or_else(|| WhereClause {
where_token: token::Where {
span: Span::mixed_site(),
},
predicates: Default::default(),
});
let hook_lifetime = Lifetime::new("'hook", Span::mixed_site());
let mut params: Punctuated<_, _> = once(hook_lifetime.clone())
.chain(lifetimes.elided)
.map(|lifetime| {
GenericParam::Lifetime(LifetimeParam {
attrs: vec![],
lifetime,
colon_token: None,
bounds: Default::default(),
})
})
.map(|param| Pair::new(param, Some(Default::default())))
.chain(take(&mut generics.params).into_pairs())
.collect();

for elided in lifetimes.elided.iter() {
where_clause
.predicates
.push(parse_quote!(#elided: #hook_lifetime));
}
for type_param in params.iter_mut().skip(1) {
match type_param {
GenericParam::Lifetime(param) => {
if let Some(predicate) = generics
.where_clause
.iter_mut()
.flat_map(|c| &mut c.predicates)
.find_map(|predicate| match predicate {
WherePredicate::Lifetime(p) if p.lifetime == param.lifetime => Some(p),
_ => None,
})
{
predicate.bounds.push(hook_lifetime.clone());
} else {
param.colon_token = Some(param.colon_token.unwrap_or_default());
param.bounds.push(hook_lifetime.clone());
}
}

for explicit in lifetimes.explicit.iter() {
where_clause
.predicates
.push(parse_quote!(#explicit: #hook_lifetime));
}
GenericParam::Type(param) => {
if let Some(predicate) = generics
.where_clause
.iter_mut()
.flat_map(|c| &mut c.predicates)
.find_map(|predicate| match predicate {
WherePredicate::Type(p) if type_is_generic(&p.bounded_ty, param) => {
Some(p)
}
_ => None,
})
{
predicate
.bounds
.push(TypeParamBound::Lifetime(hook_lifetime.clone()));
} else {
param.colon_token = Some(param.colon_token.unwrap_or_default());
param
.bounds
.push(TypeParamBound::Lifetime(hook_lifetime.clone()));
}
}

for type_param in generics.type_params() {
let type_param_ident = &type_param.ident;
where_clause
.predicates
.push(parse_quote!(#type_param_ident: #hook_lifetime));
GenericParam::Const(_) => {}
}
}

generics.where_clause = Some(where_clause);

hook_lifetime
};
generics.params = params;

let (output, output_type) = Self::rewrite_return_type(&hook_lifetime, return_type);
sig.output = output;
Expand All @@ -165,7 +197,15 @@ impl HookSignature {
self.sig
.generics
.lifetimes()
.map(|life| parse_quote! { &#life () })
.map(|life| TypeReference {
and_token: Default::default(),
lifetime: Some(life.lifetime.clone()),
mutability: None,
elem: Box::new(Type::Tuple(syn::TypeTuple {
paren_token: Default::default(),
elems: Default::default(),
})),
})
.collect()
}

Expand Down
4 changes: 2 additions & 2 deletions packages/yew-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub fn classes(input: TokenStream) -> TokenStream {

#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn function_component(attr: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
pub fn function_component(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as FunctionComponent);
let attr = parse_macro_input!(attr as FunctionComponentName);

Expand All @@ -151,7 +151,7 @@ pub fn function_component(attr: TokenStream, item: TokenStream) -> proc_macro::T

#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn hook(attr: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
pub fn hook(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as HookFn);

if let Some(m) = proc_macro2::TokenStream::from(attr).into_iter().next() {
Expand Down
Loading