Skip to main content

pin_init_internal/
util.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use proc_macro2::{Ident, TokenStream};
4use quote::{format_ident, ToTokens};
5use syn::{Attribute, GenericParam, Generics, Index, Member, Token};
6
7use crate::DiagCtxt;
8
9pub(crate) trait AttrListExt {
10    fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>;
11
12    /// Extract attribute with identifier `path`.
13    ///
14    /// Report error if the attribute appears multiple times.
15    fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute>;
16}
17
18impl AttrListExt for Vec<Attribute> {
19    fn extract_cfg_attrs(&mut self) -> Vec<TokenStream> {
20        let cfg: Vec<_> = self
21            .iter()
22            .filter(|a| a.path().is_ident("cfg"))
23            .map(|a| {
24                a.parse_args::<TokenStream>()
25                    .expect("parse as token stream cannot fail")
26            })
27            .collect();
28
29        if !cfg.is_empty() {
30            self.retain(|a| !a.path().is_ident("cfg"));
31        }
32
33        cfg
34    }
35
36    fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute> {
37        // FIXME: Replace with `extract_if` when MSRV >= 1.85.
38        let attr_pos = self.iter().position(|attr| attr.path().is_ident(path))?;
39        let attr = self.remove(attr_pos);
40        self.retain(|attr| {
41            if !attr.path().is_ident(path) {
42                return true;
43            }
44
45            dcx.error(
46                attr,
47                format!("`#[{path}]` attribute specified more than once"),
48            );
49            false
50        });
51        Some(attr)
52    }
53}
54
55pub(crate) trait MemberExt {
56    /// Returns an identifier for the member.
57    ///
58    /// Tuple fields have no name of their own, so they are named `_0`, `_1`, ... instead.
59    fn as_ident(&self) -> Ident;
60
61    /// Obtain a display name for the member in diagnostics.
62    fn display_name(&self) -> String;
63}
64
65impl MemberExt for Member {
66    fn as_ident(&self) -> Ident {
67        match self {
68            Member::Named(ident) => ident.clone(),
69            Member::Unnamed(Index { index, span }) => format_ident!("_{index}", span = *span),
70        }
71    }
72
73    fn display_name(&self) -> String {
74        match self {
75            Member::Named(ident) => format!("`{ident}`"),
76            Member::Unnamed(Index { index, .. }) => format!("index `{index}`"),
77        }
78    }
79}
80
81pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);
82pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);
83pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);
84
85impl CombinedGenerics<'_> {
86    pub(crate) fn split_for_impl(
87        &self,
88    ) -> (
89        CombinedImplGenerics<'_>,
90        CombinedTypeGenerics<'_>,
91        // A stub type so `split_for_impl` signature matches that of `syn`'s.
92        impl Sized,
93    ) {
94        (CombinedImplGenerics(self), CombinedTypeGenerics(self), ())
95    }
96}
97
98impl ToTokens for CombinedGenerics<'_> {
99    fn to_tokens(&self, tokens: &mut TokenStream) {
100        // Most of the time we are only adding lifetimes, so we prefer to place
101        // the user provided generics last.
102        self.0
103            .last()
104            .and_then(|x| x.lt_token)
105            .unwrap_or_default()
106            .to_tokens(tokens);
107
108        let comma: Token![,] = Default::default();
109
110        // Output lifetimes first.
111        for generics in self.0.iter() {
112            for param in generics.params.pairs() {
113                if let GenericParam::Lifetime(lt) = param.value() {
114                    lt.to_tokens(tokens);
115                    param.punct().unwrap_or(&&comma).to_tokens(tokens);
116                }
117            }
118        }
119
120        for generics in self.0.iter() {
121            for param in generics.params.pairs() {
122                if let GenericParam::Lifetime(_) = param.value() {
123                    continue;
124                };
125                param.value().to_tokens(tokens);
126                param.punct().unwrap_or(&&comma).to_tokens(tokens);
127            }
128        }
129
130        self.0
131            .last()
132            .and_then(|x| x.gt_token)
133            .unwrap_or_default()
134            .to_tokens(tokens);
135    }
136}
137
138impl ToTokens for CombinedImplGenerics<'_> {
139    fn to_tokens(&self, tokens: &mut TokenStream) {
140        self.0
141             .0
142            .last()
143            .and_then(|x| x.lt_token)
144            .unwrap_or_default()
145            .to_tokens(tokens);
146
147        let comma: Token![,] = Default::default();
148
149        // Output lifetimes first.
150        for generics in self.0 .0.iter() {
151            for param in generics.params.pairs() {
152                if let GenericParam::Lifetime(lt) = param.value() {
153                    lt.to_tokens(tokens);
154                    param.punct().unwrap_or(&&comma).to_tokens(tokens);
155                }
156            }
157        }
158
159        for generics in self.0 .0.iter() {
160            for param in generics.params.pairs() {
161                // Leave out defaults.
162                match param.value() {
163                    GenericParam::Lifetime(_) => continue,
164                    GenericParam::Type(param) => {
165                        param.ident.to_tokens(tokens);
166                        if !param.bounds.is_empty() {
167                            param
168                                .colon_token
169                                .unwrap_or_else(Default::default)
170                                .to_tokens(tokens);
171                            param.bounds.to_tokens(tokens);
172                        }
173                    }
174                    GenericParam::Const(param) => {
175                        param.const_token.to_tokens(tokens);
176                        param.ident.to_tokens(tokens);
177                        param.colon_token.to_tokens(tokens);
178                        param.ty.to_tokens(tokens);
179                    }
180                }
181                param.punct().unwrap_or(&&comma).to_tokens(tokens);
182            }
183        }
184
185        self.0
186             .0
187            .last()
188            .and_then(|x| x.gt_token)
189            .unwrap_or_default()
190            .to_tokens(tokens);
191    }
192}
193
194impl ToTokens for CombinedTypeGenerics<'_> {
195    fn to_tokens(&self, tokens: &mut TokenStream) {
196        self.0
197             .0
198            .last()
199            .and_then(|x| x.lt_token)
200            .unwrap_or_default()
201            .to_tokens(tokens);
202
203        let comma: Token![,] = Default::default();
204
205        // Output lifetimes first.
206        for generics in self.0 .0.iter() {
207            for param in generics.params.pairs() {
208                if let GenericParam::Lifetime(lt) = param.value() {
209                    // Leave out bounds
210                    lt.lifetime.to_tokens(tokens);
211                    param.punct().unwrap_or(&&comma).to_tokens(tokens);
212                }
213            }
214        }
215
216        for generics in self.0 .0.iter() {
217            for param in generics.params.pairs() {
218                // Leave out bounds and defaults.
219                match param.value() {
220                    GenericParam::Lifetime(_) => continue,
221                    GenericParam::Type(param) => {
222                        param.ident.to_tokens(tokens);
223                    }
224                    GenericParam::Const(param) => {
225                        param.ident.to_tokens(tokens);
226                    }
227                }
228                param.punct().unwrap_or(&&comma).to_tokens(tokens);
229            }
230        }
231
232        self.0
233             .0
234            .last()
235            .and_then(|x| x.gt_token)
236            .unwrap_or_default()
237            .to_tokens(tokens);
238    }
239}