xref: /linux/rust/pin-init/internal/src/util.rs (revision bb29b957eaaa15be33243550d9ee576c2d601669)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use std::collections::{BTreeMap, BTreeSet};
4 
5 use proc_macro2::{Ident, TokenStream};
6 use quote::{format_ident, ToTokens};
7 use syn::{
8     parse_quote, visit::Visit, visit_mut::VisitMut, Attribute, BoundLifetimes, GenericParam,
9     Generics, Index, Lifetime, Member, Token, Type, TypePath,
10 };
11 
12 use crate::DiagCtxt;
13 
14 pub(crate) trait AttrListExt {
15     fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>;
16 
17     /// Extract attribute with identifier `path`.
18     ///
19     /// Report error if the attribute appears multiple times.
20     fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute>;
21 }
22 
23 impl 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         // FIXME: Replace with `extract_if` when MSRV >= 1.85.
43         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 
60 pub(crate) trait MemberExt {
61     /// Returns an identifier for the member.
62     ///
63     /// Tuple fields have no name of their own, so they are named `_0`, `_1`, ... instead.
64     fn as_ident(&self) -> Ident;
65 
66     /// Obtain a display name for the member in diagnostics.
67     fn display_name(&self) -> String;
68 }
69 
70 impl 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 
86 pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);
87 pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);
88 pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);
89 pub(crate) struct CombinedWhereClauses<'a>(&'a CombinedGenerics<'a>);
90 
91 impl 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 
107 impl ToTokens for CombinedGenerics<'_> {
108     fn to_tokens(&self, tokens: &mut TokenStream) {
109         // Most of the time we are only adding lifetimes, so we prefer to place
110         // the user provided generics last.
111         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         // Output lifetimes first.
120         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 
147 impl 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         // Output lifetimes first.
159         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                 // Leave out defaults.
171                 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 
203 impl 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         // Output lifetimes first.
215         for generics in self.0 .0.iter() {
216             for param in generics.params.pairs() {
217                 if let GenericParam::Lifetime(lt) = param.value() {
218                     // Leave out bounds
219                     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                 // Leave out bounds and defaults.
228                 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 
250 impl 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 
275 pub(crate) trait LifetimeExt {
276     /// Get a visitor that call the provided function for all unbound lifetimes.
277     fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a>;
278 
279     /// Obtain a lifetime from a identifier.
280     ///
281     /// The created lifetime has the same span.
282     fn from_ident(ident: &Ident) -> Self;
283 }
284 
285 impl 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 
301 struct LifetimeVisitor<'a, F> {
302     bound: BTreeSet<&'a Lifetime>,
303     visit: F,
304 }
305 
306 impl<'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         // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`,
313         // the lifetimes in the binder are bound and should not be visited.
314 
315         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(&&lt.lifetime) {
322                     self.bound.insert(&lt.lifetime);
323                     to_remove.push(&lt.lifetime);
324                 }
325             }
326         }
327 
328         f(self);
329 
330         for lt in to_remove {
331             self.bound.remove(lt);
332         }
333     }
334 }
335 
336 impl<'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 
363 pub(crate) trait GenericParamExt {
364     fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a>;
365 }
366 
367 impl 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 
390 pub(crate) trait TypeExt {
391     /// Check if the type includes macro invocations.
392     ///
393     /// Proc-macros cannot expand macros and peek into them, so if macro is involved sometimes
394     /// special handling is required.
395     fn has_macro(&self) -> bool;
396 
397     fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type;
398 }
399 
400 impl 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 the type has macro, we cannot peek into it. Use some different approach to replace
421         // the type using GAT.
422         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                 // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`,
443                 // temporarily remove them from to_replace if they're.
444 
445                 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(&lt.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 }
501