xref: /linux/rust/pin-init/internal/src/util.rs (revision 003a8953d2f1c310550ac477a508b7be910b9827)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use proc_macro2::{Ident, TokenStream};
4 use quote::{format_ident, ToTokens};
5 use syn::{Attribute, GenericParam, Generics, Index, Member, Token};
6 
7 use crate::DiagCtxt;
8 
9 pub(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 
18 impl 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 
55 pub(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 
65 impl 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 
81 pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);
82 pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);
83 pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);
84 
85 impl 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 
98 impl 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 
138 impl 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 
194 impl 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 }
240