Skip to content

Commit 48cdc3d

Browse files
#[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.
1 parent 3bccc1e commit 48cdc3d

File tree

4 files changed

+119
-60
lines changed

4 files changed

+119
-60
lines changed

packages/yew-macro/src/hook/lifetime.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use syn::{
1010
// borrowed from the awesome async-trait crate.
1111
pub struct CollectLifetimes {
1212
pub elided: Vec<Lifetime>,
13-
pub explicit: Vec<Lifetime>,
1413
pub name: &'static str,
1514
pub default_span: Span,
1615

@@ -23,7 +22,6 @@ impl CollectLifetimes {
2322
pub fn new(name: &'static str, default_span: Span) -> Self {
2423
CollectLifetimes {
2524
elided: Vec::new(),
26-
explicit: Vec::new(),
2725
name,
2826
default_span,
2927

@@ -55,8 +53,6 @@ impl CollectLifetimes {
5553
fn visit_lifetime(&mut self, lifetime: &mut Lifetime) {
5654
if lifetime.ident == "_" {
5755
*lifetime = self.next_lifetime(lifetime.span());
58-
} else {
59-
self.explicit.push(lifetime.clone());
6056
}
6157
}
6258

packages/yew-macro/src/hook/mod.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use proc_macro2::{Span, TokenStream};
22
use proc_macro_error::emit_error;
33
use quote::quote;
44
use syn::parse::{Parse, ParseStream};
5+
use syn::spanned::Spanned;
56
use syn::{
6-
parse_file, parse_quote, visit_mut, Attribute, Ident, ItemFn, LitStr, ReturnType, Signature,
7-
Type,
7+
visit_mut, AttrStyle, Attribute, Block, Expr, ExprPath, File, Ident, Item, ItemFn, LitStr,
8+
Meta, MetaNameValue, ReturnType, Signature, Stmt, Token, Type,
89
};
910

1011
mod body;
@@ -51,16 +52,26 @@ impl Parse for HookFn {
5152

5253
impl HookFn {
5354
fn doc_attr(&self) -> Attribute {
54-
let vis = &self.inner.vis;
55-
let sig = &self.inner.sig;
56-
57-
let sig_s = quote! { #vis #sig {
58-
__yew_macro_dummy_function_body__
59-
} }
60-
.to_string();
61-
62-
let sig_file = parse_file(&sig_s).unwrap();
63-
let sig_formatted = prettyplease::unparse(&sig_file);
55+
let span = self.inner.span();
56+
57+
let sig_formatted = prettyplease::unparse(&File {
58+
shebang: None,
59+
attrs: vec![],
60+
items: vec![Item::Fn(ItemFn {
61+
block: Box::new(Block {
62+
brace_token: Default::default(),
63+
stmts: vec![Stmt::Expr(
64+
Expr::Path(ExprPath {
65+
attrs: vec![],
66+
qself: None,
67+
path: Ident::new("__yew_macro_dummy_function_body__", span).into(),
68+
}),
69+
None,
70+
)],
71+
}),
72+
..self.inner.clone()
73+
})],
74+
});
6475

6576
let literal = LitStr::new(
6677
&format!(
@@ -78,10 +89,22 @@ When used in function components and hooks, this hook is equivalent to:
7889
"/* implementation omitted */"
7990
)
8091
),
81-
Span::mixed_site(),
92+
span,
8293
);
8394

84-
parse_quote!(#[doc = #literal])
95+
Attribute {
96+
pound_token: Default::default(),
97+
style: AttrStyle::Outer,
98+
bracket_token: Default::default(),
99+
meta: Meta::NameValue(MetaNameValue {
100+
path: Ident::new("doc", span).into(),
101+
eq_token: Token![=](span),
102+
value: Expr::Lit(syn::ExprLit {
103+
attrs: vec![],
104+
lit: literal.into(),
105+
}),
106+
}),
107+
}
85108
}
86109
}
87110

packages/yew-macro/src/hook/signature.rs

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1+
use std::iter::once;
2+
use std::mem::take;
3+
14
use proc_macro2::{Span, TokenStream};
25
use proc_macro_error::emit_error;
36
use quote::{quote, ToTokens};
7+
use syn::punctuated::{Pair, Punctuated};
48
use syn::spanned::Spanned;
59
use syn::visit_mut::VisitMut;
610
use syn::{
7-
parse_quote, parse_quote_spanned, token, visit_mut, FnArg, GenericParam, Ident, Lifetime, Pat,
8-
Receiver, ReturnType, Signature, Type, TypeImplTrait, TypeReference, WhereClause,
11+
parse_quote, parse_quote_spanned, visit_mut, FnArg, GenericParam, Ident, Lifetime,
12+
LifetimeParam, Pat, Receiver, ReturnType, Signature, Type, TypeImplTrait, TypeParam,
13+
TypeParamBound, TypeReference, WherePredicate,
914
};
1015

1116
use super::lifetime;
1217

18+
fn type_is_generic(ty: &Type, param: &TypeParam) -> bool {
19+
match ty {
20+
Type::Path(path) => path.path.is_ident(&param.ident),
21+
_ => false,
22+
}
23+
}
24+
1325
#[derive(Default)]
1426
pub struct CollectArgs {
1527
needs_boxing: bool,
@@ -99,48 +111,68 @@ impl HookSignature {
99111
..
100112
} = sig;
101113

102-
let hook_lifetime = {
103-
let hook_lifetime = Lifetime::new("'hook", Span::mixed_site());
104-
generics.params = {
105-
let elided_lifetimes = &lifetimes.elided;
106-
let params = &generics.params;
107-
108-
parse_quote!(#hook_lifetime, #(#elided_lifetimes,)* #params)
109-
};
110-
111-
let mut where_clause = generics
112-
.where_clause
113-
.clone()
114-
.unwrap_or_else(|| WhereClause {
115-
where_token: token::Where {
116-
span: Span::mixed_site(),
117-
},
118-
predicates: Default::default(),
119-
});
114+
let hook_lifetime = Lifetime::new("'hook", Span::mixed_site());
115+
let mut params: Punctuated<_, _> = once(hook_lifetime.clone())
116+
.chain(lifetimes.elided)
117+
.map(|lifetime| {
118+
GenericParam::Lifetime(LifetimeParam {
119+
attrs: vec![],
120+
lifetime,
121+
colon_token: None,
122+
bounds: Default::default(),
123+
})
124+
})
125+
.map(|param| Pair::new(param, Some(Default::default())))
126+
.chain(take(&mut generics.params).into_pairs())
127+
.collect();
120128

121-
for elided in lifetimes.elided.iter() {
122-
where_clause
123-
.predicates
124-
.push(parse_quote!(#elided: #hook_lifetime));
125-
}
129+
for type_param in params.iter_mut().skip(1) {
130+
match type_param {
131+
GenericParam::Lifetime(param) => {
132+
if let Some(predicate) = generics
133+
.where_clause
134+
.iter_mut()
135+
.flat_map(|c| &mut c.predicates)
136+
.find_map(|predicate| match predicate {
137+
WherePredicate::Lifetime(p) if p.lifetime == param.lifetime => Some(p),
138+
_ => None,
139+
})
140+
{
141+
predicate.bounds.push(hook_lifetime.clone());
142+
} else {
143+
param.colon_token = Some(param.colon_token.unwrap_or_default());
144+
param.bounds.push(hook_lifetime.clone());
145+
}
146+
}
126147

127-
for explicit in lifetimes.explicit.iter() {
128-
where_clause
129-
.predicates
130-
.push(parse_quote!(#explicit: #hook_lifetime));
131-
}
148+
GenericParam::Type(param) => {
149+
if let Some(predicate) = generics
150+
.where_clause
151+
.iter_mut()
152+
.flat_map(|c| &mut c.predicates)
153+
.find_map(|predicate| match predicate {
154+
WherePredicate::Type(p) if type_is_generic(&p.bounded_ty, param) => {
155+
Some(p)
156+
}
157+
_ => None,
158+
})
159+
{
160+
predicate
161+
.bounds
162+
.push(TypeParamBound::Lifetime(hook_lifetime.clone()));
163+
} else {
164+
param.colon_token = Some(param.colon_token.unwrap_or_default());
165+
param
166+
.bounds
167+
.push(TypeParamBound::Lifetime(hook_lifetime.clone()));
168+
}
169+
}
132170

133-
for type_param in generics.type_params() {
134-
let type_param_ident = &type_param.ident;
135-
where_clause
136-
.predicates
137-
.push(parse_quote!(#type_param_ident: #hook_lifetime));
171+
GenericParam::Const(_) => {}
138172
}
173+
}
139174

140-
generics.where_clause = Some(where_clause);
141-
142-
hook_lifetime
143-
};
175+
generics.params = params;
144176

145177
let (output, output_type) = Self::rewrite_return_type(&hook_lifetime, return_type);
146178
sig.output = output;
@@ -165,7 +197,15 @@ impl HookSignature {
165197
self.sig
166198
.generics
167199
.lifetimes()
168-
.map(|life| parse_quote! { &#life () })
200+
.map(|life| TypeReference {
201+
and_token: Default::default(),
202+
lifetime: Some(life.lifetime.clone()),
203+
mutability: None,
204+
elem: Box::new(Type::Tuple(syn::TypeTuple {
205+
paren_token: Default::default(),
206+
elems: Default::default(),
207+
})),
208+
})
169209
.collect()
170210
}
171211

packages/yew-macro/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ pub fn classes(input: TokenStream) -> TokenStream {
140140

141141
#[proc_macro_error::proc_macro_error]
142142
#[proc_macro_attribute]
143-
pub fn function_component(attr: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
143+
pub fn function_component(attr: TokenStream, item: TokenStream) -> TokenStream {
144144
let item = parse_macro_input!(item as FunctionComponent);
145145
let attr = parse_macro_input!(attr as FunctionComponentName);
146146

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

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

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

0 commit comments

Comments
 (0)