mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
#[hook]: clippy::multiple_bound_locations lint no longer triggered (#3803)
This is achieved by reworking the logic for rewriting the function signature of the hook.
This commit is contained in:
parent
3bccc1e7e6
commit
48cdc3dff4
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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 span = self.inner.span();
|
||||
|
||||
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 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!(
|
||||
@ -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,
|
||||
value: Expr::Lit(syn::ExprLit {
|
||||
attrs: vec![],
|
||||
lit: literal.into(),
|
||||
}),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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(¶m.ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CollectArgs {
|
||||
needs_boxing: bool,
|
||||
@ -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;
|
||||
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();
|
||||
|
||||
parse_quote!(#hook_lifetime, #(#elided_lifetimes,)* #params)
|
||||
};
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
let mut where_clause = generics
|
||||
.where_clause
|
||||
.clone()
|
||||
.unwrap_or_else(|| WhereClause {
|
||||
where_token: token::Where {
|
||||
span: Span::mixed_site(),
|
||||
},
|
||||
predicates: Default::default(),
|
||||
});
|
||||
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 elided in lifetimes.elided.iter() {
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(#elided: #hook_lifetime));
|
||||
GenericParam::Const(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
for explicit in lifetimes.explicit.iter() {
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(#explicit: #hook_lifetime));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user