1use std::collections::{BTreeMap, BTreeSet};
4
5use proc_macro2::{Ident, TokenStream};
6use quote::{format_ident, ToTokens};
7use syn::{
8 parse_quote, visit::Visit, visit_mut::VisitMut, Attribute, BoundLifetimes, GenericParam,
9 Generics, Index, Lifetime, Member, Token, Type, TypePath,
10};
11
12use crate::DiagCtxt;
13
14pub(crate) trait AttrListExt {
15 fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>;
16
17 fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute>;
21}
22
23impl AttrListExt for Vec<Attribute> {
24 fn extract_cfg_attrs(&mut self) -> Vec<TokenStream> {
25 let cfg: Vec<_> = self
26 .iter()
27 .filter(|a| a.path().is_ident("cfg"))
28 .map(|a| {
29 a.parse_args::<TokenStream>()
30 .expect("parse as token stream cannot fail")
31 })
32 .collect();
33
34 if !cfg.is_empty() {
35 self.retain(|a| !a.path().is_ident("cfg"));
36 }
37
38 cfg
39 }
40
41 fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute> {
42 let attr_pos = self.iter().position(|attr| attr.path().is_ident(path))?;
44 let attr = self.remove(attr_pos);
45 self.retain(|attr| {
46 if !attr.path().is_ident(path) {
47 return true;
48 }
49
50 dcx.error(
51 attr,
52 format!("`#[{path}]` attribute specified more than once"),
53 );
54 false
55 });
56 Some(attr)
57 }
58}
59
60pub(crate) trait MemberExt {
61 fn as_ident(&self) -> Ident;
65
66 fn display_name(&self) -> String;
68}
69
70impl MemberExt for Member {
71 fn as_ident(&self) -> Ident {
72 match self {
73 Member::Named(ident) => ident.clone(),
74 Member::Unnamed(Index { index, span }) => format_ident!("_{index}", span = *span),
75 }
76 }
77
78 fn display_name(&self) -> String {
79 match self {
80 Member::Named(ident) => format!("`{ident}`"),
81 Member::Unnamed(Index { index, .. }) => format!("index `{index}`"),
82 }
83 }
84}
85
86pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);
87pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);
88pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);
89pub(crate) struct CombinedWhereClauses<'a>(&'a CombinedGenerics<'a>);
90
91impl CombinedGenerics<'_> {
92 pub(crate) fn split_for_impl(
93 &self,
94 ) -> (
95 CombinedImplGenerics<'_>,
96 CombinedTypeGenerics<'_>,
97 CombinedWhereClauses<'_>,
98 ) {
99 (
100 CombinedImplGenerics(self),
101 CombinedTypeGenerics(self),
102 CombinedWhereClauses(self),
103 )
104 }
105}
106
107impl ToTokens for CombinedGenerics<'_> {
108 fn to_tokens(&self, tokens: &mut TokenStream) {
109 self.0
112 .last()
113 .and_then(|x| x.lt_token)
114 .unwrap_or_default()
115 .to_tokens(tokens);
116
117 let comma: Token![,] = Default::default();
118
119 for generics in self.0.iter() {
121 for param in generics.params.pairs() {
122 if let GenericParam::Lifetime(lt) = param.value() {
123 lt.to_tokens(tokens);
124 param.punct().unwrap_or(&&comma).to_tokens(tokens);
125 }
126 }
127 }
128
129 for generics in self.0.iter() {
130 for param in generics.params.pairs() {
131 if let GenericParam::Lifetime(_) = param.value() {
132 continue;
133 };
134 param.value().to_tokens(tokens);
135 param.punct().unwrap_or(&&comma).to_tokens(tokens);
136 }
137 }
138
139 self.0
140 .last()
141 .and_then(|x| x.gt_token)
142 .unwrap_or_default()
143 .to_tokens(tokens);
144 }
145}
146
147impl ToTokens for CombinedImplGenerics<'_> {
148 fn to_tokens(&self, tokens: &mut TokenStream) {
149 self.0
150 .0
151 .last()
152 .and_then(|x| x.lt_token)
153 .unwrap_or_default()
154 .to_tokens(tokens);
155
156 let comma: Token![,] = Default::default();
157
158 for generics in self.0 .0.iter() {
160 for param in generics.params.pairs() {
161 if let GenericParam::Lifetime(lt) = param.value() {
162 lt.to_tokens(tokens);
163 param.punct().unwrap_or(&&comma).to_tokens(tokens);
164 }
165 }
166 }
167
168 for generics in self.0 .0.iter() {
169 for param in generics.params.pairs() {
170 match param.value() {
172 GenericParam::Lifetime(_) => continue,
173 GenericParam::Type(param) => {
174 param.ident.to_tokens(tokens);
175 if !param.bounds.is_empty() {
176 param
177 .colon_token
178 .unwrap_or_else(Default::default)
179 .to_tokens(tokens);
180 param.bounds.to_tokens(tokens);
181 }
182 }
183 GenericParam::Const(param) => {
184 param.const_token.to_tokens(tokens);
185 param.ident.to_tokens(tokens);
186 param.colon_token.to_tokens(tokens);
187 param.ty.to_tokens(tokens);
188 }
189 }
190 param.punct().unwrap_or(&&comma).to_tokens(tokens);
191 }
192 }
193
194 self.0
195 .0
196 .last()
197 .and_then(|x| x.gt_token)
198 .unwrap_or_default()
199 .to_tokens(tokens);
200 }
201}
202
203impl ToTokens for CombinedTypeGenerics<'_> {
204 fn to_tokens(&self, tokens: &mut TokenStream) {
205 self.0
206 .0
207 .last()
208 .and_then(|x| x.lt_token)
209 .unwrap_or_default()
210 .to_tokens(tokens);
211
212 let comma: Token![,] = Default::default();
213
214 for generics in self.0 .0.iter() {
216 for param in generics.params.pairs() {
217 if let GenericParam::Lifetime(lt) = param.value() {
218 lt.lifetime.to_tokens(tokens);
220 param.punct().unwrap_or(&&comma).to_tokens(tokens);
221 }
222 }
223 }
224
225 for generics in self.0 .0.iter() {
226 for param in generics.params.pairs() {
227 match param.value() {
229 GenericParam::Lifetime(_) => continue,
230 GenericParam::Type(param) => {
231 param.ident.to_tokens(tokens);
232 }
233 GenericParam::Const(param) => {
234 param.ident.to_tokens(tokens);
235 }
236 }
237 param.punct().unwrap_or(&&comma).to_tokens(tokens);
238 }
239 }
240
241 self.0
242 .0
243 .last()
244 .and_then(|x| x.gt_token)
245 .unwrap_or_default()
246 .to_tokens(tokens);
247 }
248}
249
250impl ToTokens for CombinedWhereClauses<'_> {
251 fn to_tokens(&self, tokens: &mut TokenStream) {
252 self.0
253 .0
254 .iter()
255 .filter_map(|x| Some(x.where_clause.as_ref()?.where_token))
256 .next_back()
257 .unwrap_or_default()
258 .to_tokens(tokens);
259
260 let comma: Token![,] = Default::default();
261
262 for generics in self.0 .0.iter() {
263 let Some(where_clause) = &generics.where_clause else {
264 continue;
265 };
266
267 where_clause.predicates.to_tokens(tokens);
268 if !where_clause.predicates.empty_or_trailing() {
269 comma.to_tokens(tokens);
270 }
271 }
272 }
273}
274
275pub(crate) trait LifetimeExt {
276 fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a>;
278
279 fn from_ident(ident: &Ident) -> Self;
283}
284
285impl LifetimeExt for Lifetime {
286 fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a> {
287 LifetimeVisitor {
288 bound: BTreeSet::new(),
289 visit: f,
290 }
291 }
292
293 fn from_ident(ident: &Ident) -> Self {
294 Lifetime {
295 apostrophe: ident.span(),
296 ident: ident.clone(),
297 }
298 }
299}
300
301struct LifetimeVisitor<'a, F> {
302 bound: BTreeSet<&'a Lifetime>,
303 visit: F,
304}
305
306impl<'a, F> LifetimeVisitor<'a, F> {
307 fn with_bound_lifetimes(
308 &mut self,
309 bound: Option<&'a BoundLifetimes>,
310 f: impl FnOnce(&mut Self),
311 ) {
312 let mut to_remove = Vec::new();
316 if let Some(bound) = bound {
317 for lt in &bound.lifetimes {
318 let GenericParam::Lifetime(lt) = lt else {
319 continue;
320 };
321 if !self.bound.contains(&<.lifetime) {
322 self.bound.insert(<.lifetime);
323 to_remove.push(<.lifetime);
324 }
325 }
326 }
327
328 f(self);
329
330 for lt in to_remove {
331 self.bound.remove(lt);
332 }
333 }
334}
335
336impl<'a, F: FnMut(&'a Lifetime)> Visit<'a> for LifetimeVisitor<'a, F> {
337 fn visit_lifetime(&mut self, lt: &'a Lifetime) {
338 if lt.ident == "static" {
339 return;
340 }
341
342 if !self.bound.contains(lt) {
343 (self.visit)(lt);
344 }
345 }
346
347 fn visit_trait_bound(&mut self, bound: &'a syn::TraitBound) {
348 self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| {
349 this.visit_path(&bound.path)
350 });
351 }
352
353 fn visit_type_bare_fn(&mut self, bare_fn: &'a syn::TypeBareFn) {
354 self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| {
355 for input in bare_fn.inputs.iter() {
356 this.visit_bare_fn_arg(input);
357 }
358 this.visit_return_type(&bare_fn.output);
359 });
360 }
361}
362
363pub(crate) trait GenericParamExt {
364 fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a>;
365}
366
367impl GenericParamExt for GenericParam {
368 fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a> {
369 struct TypeParamVisitor<F>(F);
370
371 impl<'a, F> Visit<'a> for TypeParamVisitor<F>
372 where
373 F: FnMut(&'a Ident),
374 {
375 fn visit_type_path(&mut self, ty: &'a TypePath) {
376 if ty.qself.is_none()
377 && ty.path.leading_colon.is_none()
378 && ty.path.segments[0].arguments.is_none()
379 {
380 (self.0)(&ty.path.segments[0].ident);
381 }
382 syn::visit::visit_type_path(self, ty);
383 }
384 }
385
386 TypeParamVisitor(f)
387 }
388}
389
390pub(crate) trait TypeExt {
391 fn has_macro(&self) -> bool;
396
397 fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type;
398}
399
400impl TypeExt for Type {
401 fn has_macro(&self) -> bool {
402 struct HasMacro(bool);
403
404 impl<'ast> Visit<'ast> for HasMacro {
405 fn visit_macro(&mut self, _: &'ast syn::Macro) {
406 self.0 = true;
407 }
408 }
409
410 let mut visitor = HasMacro(false);
411 visitor.visit_type(self);
412 visitor.0
413 }
414
415 fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type {
416 if needle.is_empty() {
417 return self.clone();
418 }
419
420 if self.has_macro() {
423 return parse_quote!(
424 <
425 for<#(#needle,)*> fn(#(&#needle (),)*) -> #self
426 as
427 ::pin_init::__internal::FnOutput<(#(&#replacement (),)*)>
428 >::Output
429 );
430 }
431
432 struct LifetimeReplacer<'a> {
433 to_replace: BTreeMap<&'a Lifetime, &'a Lifetime>,
434 }
435
436 impl<'a> LifetimeReplacer<'a> {
437 fn with_bound_lifetimes(
438 &mut self,
439 bound: Option<&BoundLifetimes>,
440 f: impl FnOnce(&mut Self),
441 ) {
442 let mut removed = Vec::new();
446 if let Some(bound) = bound {
447 for lt in &bound.lifetimes {
448 let GenericParam::Lifetime(lt) = lt else {
449 continue;
450 };
451 if let Some(entry) = self.to_replace.remove_entry(<.lifetime) {
452 removed.push(entry);
453 }
454 }
455 }
456
457 f(self);
458
459 for (key, val) in removed {
460 self.to_replace.insert(key, val);
461 }
462 }
463 }
464
465 impl VisitMut for LifetimeReplacer<'_> {
466 fn visit_lifetime_mut(&mut self, lt: &mut syn::Lifetime) {
467 if let Some(&replacement) = self.to_replace.get(lt) {
468 *lt = replacement.clone();
469 }
470 }
471
472 fn visit_trait_bound_mut(&mut self, bound: &mut syn::TraitBound) {
473 self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| {
474 this.visit_path_mut(&mut bound.path)
475 });
476 }
477
478 fn visit_type_bare_fn_mut(&mut self, bare_fn: &mut syn::TypeBareFn) {
479 self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| {
480 for input in bare_fn.inputs.iter_mut() {
481 this.visit_bare_fn_arg_mut(input);
482 }
483
484 this.visit_return_type_mut(&mut bare_fn.output);
485 });
486 }
487 }
488
489 let mut ret = self.clone();
490 LifetimeReplacer {
491 to_replace: needle
492 .iter()
493 .copied()
494 .zip(replacement.iter().copied())
495 .collect(),
496 }
497 .visit_type_mut(&mut ret);
498 ret
499 }
500}