1 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | |
3 | use std::mem; |
4 | |
5 | use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; |
6 | use quote::{quote, quote_spanned, ToTokens}; |
7 | use syn::{ |
8 | parse::{Parse, ParseBuffer, ParseStream}, |
9 | parse_quote, |
10 | punctuated::Punctuated, |
11 | token, |
12 | visit_mut::{self, VisitMut}, |
13 | Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeParam, Macro, |
14 | PatStruct, PatTupleStruct, Path, PathArguments, PredicateType, QSelf, Result, Token, Type, |
15 | TypeParamBound, TypePath, Variant, Visibility, WherePredicate, |
16 | }; |
17 | |
18 | pub(crate) type Variants = Punctuated<Variant, Token![,]>; |
19 | |
20 | macro_rules! parse_quote_spanned { |
21 | ($span:expr => $($tt:tt)*) => { |
22 | syn::parse2(quote::quote_spanned!($span => $($tt)*)).unwrap_or_else(|e| panic!("{}" , e)) |
23 | }; |
24 | } |
25 | |
26 | /// Determines the lifetime names. Ensure it doesn't overlap with any existing |
27 | /// lifetime names. |
28 | pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut Generics) { |
29 | struct CollectLifetimes(Vec<String>); |
30 | |
31 | impl VisitMut for CollectLifetimes { |
32 | fn visit_lifetime_param_mut(&mut self, def: &mut LifetimeParam) { |
33 | self.0.push(def.lifetime.to_string()); |
34 | } |
35 | } |
36 | |
37 | debug_assert!(lifetime_name.starts_with(' \'' )); |
38 | |
39 | let mut lifetimes: CollectLifetimes = CollectLifetimes(vec![]); |
40 | lifetimes.visit_generics_mut(generics); |
41 | |
42 | while lifetimes.0.iter().any(|name: &String| name.starts_with(&**lifetime_name)) { |
43 | lifetime_name.push(ch:'_' ); |
44 | } |
45 | } |
46 | |
47 | /// Like `insert_lifetime`, but also generates a bound of the form |
48 | /// `OriginalType<A, B>: 'lifetime`. Used when generating the definition |
49 | /// of a projection type |
50 | pub(crate) fn insert_lifetime_and_bound( |
51 | generics: &mut Generics, |
52 | lifetime: Lifetime, |
53 | orig_generics: &Generics, |
54 | orig_ident: &Ident, |
55 | ) -> WherePredicate { |
56 | insert_lifetime(generics, lifetime.clone()); |
57 | |
58 | let orig_type: Type = parse_quote!(#orig_ident #orig_generics); |
59 | let mut punct: Punctuated = Punctuated::new(); |
60 | punct.push(TypeParamBound::Lifetime(lifetime)); |
61 | |
62 | WherePredicate::Type(PredicateType { |
63 | lifetimes: None, |
64 | bounded_ty: orig_type, |
65 | colon_token: <Token![:]>::default(), |
66 | bounds: punct, |
67 | }) |
68 | } |
69 | |
70 | /// Inserts a `lifetime` at position `0` of `generics.params`. |
71 | pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { |
72 | generics.lt_token.get_or_insert_with(<Token![<]>::default); |
73 | generics.gt_token.get_or_insert_with(<Token![>]>::default); |
74 | generics.params.insert(index:0, value:LifetimeParam::new(lifetime).into()); |
75 | } |
76 | |
77 | /// Determines the visibility of the projected types and projection methods. |
78 | /// |
79 | /// If given visibility is `pub`, returned visibility is `pub(crate)`. |
80 | /// Otherwise, returned visibility is the same as given visibility. |
81 | pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { |
82 | if let Visibility::Public(token: &Pub) = vis { |
83 | parse_quote_spanned!(token.span => pub(crate)) |
84 | } else { |
85 | vis.clone() |
86 | } |
87 | } |
88 | |
89 | pub(crate) fn respan<T>(node: &T, span: Span) -> T |
90 | where |
91 | T: ToTokens + Parse, |
92 | { |
93 | let tokens: TokenStream = node.to_token_stream(); |
94 | let respanned: TokenStream = respan_tokens(tokens, span); |
95 | syn::parse2(tokens:respanned).unwrap() |
96 | } |
97 | |
98 | fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream { |
99 | tokensimpl Iterator |
100 | .into_iter() |
101 | .map(|mut token: TokenTree| { |
102 | token.set_span(span); |
103 | token |
104 | }) |
105 | .collect() |
106 | } |
107 | |
108 | // ================================================================================================= |
109 | // extension traits |
110 | |
111 | pub(crate) trait SliceExt { |
112 | fn position_exact(&self, ident: &str) -> Result<Option<usize>>; |
113 | fn find(&self, ident: &str) -> Option<&Attribute>; |
114 | } |
115 | |
116 | impl SliceExt for [Attribute] { |
117 | /// # Errors |
118 | /// |
119 | /// - There are multiple specified attributes. |
120 | /// - The `Attribute::tokens` field of the specified attribute is not empty. |
121 | fn position_exact(&self, ident: &str) -> Result<Option<usize>> { |
122 | self.iter() |
123 | .try_fold((0, None), |(i, mut prev), attr| { |
124 | if attr.path().is_ident(ident) { |
125 | if prev.replace(i).is_some() { |
126 | bail!(attr, "duplicate #[ {}] attribute" , ident); |
127 | } |
128 | attr.meta.require_path_only()?; |
129 | } |
130 | Ok((i + 1, prev)) |
131 | }) |
132 | .map(|(_, pos: Option)| pos) |
133 | } |
134 | |
135 | fn find(&self, ident: &str) -> Option<&Attribute> { |
136 | self.iter().position(|attr: &Attribute| attr.path().is_ident(ident)).map(|i: usize| &self[i]) |
137 | } |
138 | } |
139 | |
140 | pub(crate) trait ParseBufferExt<'a> { |
141 | fn parenthesized(self) -> Result<ParseBuffer<'a>>; |
142 | } |
143 | |
144 | impl<'a> ParseBufferExt<'a> for ParseStream<'a> { |
145 | fn parenthesized(self) -> Result<ParseBuffer<'a>> { |
146 | let content: ParseBuffer<'_>; |
147 | let _: token::Paren = syn::parenthesized!(content in self); |
148 | Ok(content) |
149 | } |
150 | } |
151 | |
152 | impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> { |
153 | fn parenthesized(self) -> Result<ParseBuffer<'a>> { |
154 | let content: ParseBuffer<'_>; |
155 | let _: token::Paren = syn::parenthesized!(content in self); |
156 | Ok(content) |
157 | } |
158 | } |
159 | |
160 | // ================================================================================================= |
161 | // visitors |
162 | |
163 | // Replace `self`/`Self` with `__self`/`self_ty`. |
164 | // Based on: |
165 | // - https://github.com/dtolnay/async-trait/blob/0.1.35/src/receiver.rs |
166 | // - https://github.com/dtolnay/async-trait/commit/6029cbf375c562ca98fa5748e9d950a8ff93b0e7 |
167 | |
168 | pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a TypePath); |
169 | |
170 | impl ReplaceReceiver<'_> { |
171 | fn self_ty(&self, span: Span) -> TypePath { |
172 | respan(self.0, span) |
173 | } |
174 | |
175 | fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) { |
176 | if path.leading_colon.is_some() { |
177 | return; |
178 | } |
179 | |
180 | let first = &path.segments[0]; |
181 | if first.ident != "Self" || !first.arguments.is_empty() { |
182 | return; |
183 | } |
184 | |
185 | if path.segments.len() == 1 { |
186 | self.self_to_expr_path(path); |
187 | return; |
188 | } |
189 | |
190 | let span = first.ident.span(); |
191 | *qself = Some(QSelf { |
192 | lt_token: Token![<](span), |
193 | ty: Box::new(self.self_ty(span).into()), |
194 | position: 0, |
195 | as_token: None, |
196 | gt_token: Token![>](span), |
197 | }); |
198 | |
199 | path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap()); |
200 | |
201 | let segments = mem::take(&mut path.segments); |
202 | path.segments = segments.into_pairs().skip(1).collect(); |
203 | } |
204 | |
205 | fn self_to_expr_path(&self, path: &mut Path) { |
206 | if path.leading_colon.is_some() { |
207 | return; |
208 | } |
209 | |
210 | let first = &path.segments[0]; |
211 | if first.ident != "Self" || !first.arguments.is_empty() { |
212 | return; |
213 | } |
214 | |
215 | let self_ty = self.self_ty(first.ident.span()); |
216 | let variant = mem::replace(path, self_ty.path); |
217 | for segment in &mut path.segments { |
218 | if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments { |
219 | if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() { |
220 | bracketed.colon2_token = Some(<Token![::]>::default()); |
221 | } |
222 | } |
223 | } |
224 | if variant.segments.len() > 1 { |
225 | path.segments.push_punct(<Token![::]>::default()); |
226 | path.segments.extend(variant.segments.into_pairs().skip(1)); |
227 | } |
228 | } |
229 | |
230 | fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool { |
231 | let mut out = vec![]; |
232 | let mut modified = false; |
233 | let mut iter = tokens.clone().into_iter().peekable(); |
234 | while let Some(tt) = iter.next() { |
235 | match tt { |
236 | TokenTree::Ident(mut ident) => { |
237 | modified |= prepend_underscore_to_self(&mut ident); |
238 | if ident == "Self" { |
239 | modified = true; |
240 | let self_ty = self.self_ty(ident.span()); |
241 | match iter.peek() { |
242 | Some(TokenTree::Punct(p)) |
243 | if p.as_char() == ':' && p.spacing() == Spacing::Joint => |
244 | { |
245 | let next = iter.next().unwrap(); |
246 | match iter.peek() { |
247 | Some(TokenTree::Punct(p)) if p.as_char() == ':' => { |
248 | let span = ident.span(); |
249 | out.extend(quote_spanned!(span=> <#self_ty>)); |
250 | } |
251 | _ => out.extend(quote!(#self_ty)), |
252 | } |
253 | out.push(next); |
254 | } |
255 | _ => out.extend(quote!(#self_ty)), |
256 | } |
257 | } else { |
258 | out.push(TokenTree::Ident(ident)); |
259 | } |
260 | } |
261 | TokenTree::Group(group) => { |
262 | let mut content = group.stream(); |
263 | modified |= self.visit_token_stream(&mut content); |
264 | let mut new = Group::new(group.delimiter(), content); |
265 | new.set_span(group.span()); |
266 | out.push(TokenTree::Group(new)); |
267 | } |
268 | other => out.push(other), |
269 | } |
270 | } |
271 | if modified { |
272 | *tokens = TokenStream::from_iter(out); |
273 | } |
274 | modified |
275 | } |
276 | } |
277 | |
278 | impl VisitMut for ReplaceReceiver<'_> { |
279 | // `Self` -> `Receiver` |
280 | fn visit_type_mut(&mut self, ty: &mut Type) { |
281 | if let Type::Path(node) = ty { |
282 | if node.qself.is_none() && node.path.is_ident("Self" ) { |
283 | *ty = self.self_ty(node.path.segments[0].ident.span()).into(); |
284 | } else { |
285 | self.visit_type_path_mut(node); |
286 | } |
287 | } else { |
288 | visit_mut::visit_type_mut(self, ty); |
289 | } |
290 | } |
291 | |
292 | // `Self::Assoc` -> `<Receiver>::Assoc` |
293 | fn visit_type_path_mut(&mut self, ty: &mut TypePath) { |
294 | if ty.qself.is_none() { |
295 | self.self_to_qself(&mut ty.qself, &mut ty.path); |
296 | } |
297 | visit_mut::visit_type_path_mut(self, ty); |
298 | } |
299 | |
300 | // `Self::method` -> `<Receiver>::method` |
301 | fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { |
302 | if expr.qself.is_none() { |
303 | self.self_to_qself(&mut expr.qself, &mut expr.path); |
304 | } |
305 | visit_mut::visit_expr_path_mut(self, expr); |
306 | } |
307 | |
308 | fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) { |
309 | self.self_to_expr_path(&mut expr.path); |
310 | visit_mut::visit_expr_struct_mut(self, expr); |
311 | } |
312 | |
313 | fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) { |
314 | self.self_to_expr_path(&mut pat.path); |
315 | visit_mut::visit_pat_struct_mut(self, pat); |
316 | } |
317 | |
318 | fn visit_pat_tuple_struct_mut(&mut self, pat: &mut PatTupleStruct) { |
319 | self.self_to_expr_path(&mut pat.path); |
320 | visit_mut::visit_pat_tuple_struct_mut(self, pat); |
321 | } |
322 | |
323 | fn visit_path_mut(&mut self, path: &mut Path) { |
324 | if path.segments.len() == 1 { |
325 | // Replace `self`, but not `self::function`. |
326 | prepend_underscore_to_self(&mut path.segments[0].ident); |
327 | } |
328 | for segment in &mut path.segments { |
329 | self.visit_path_arguments_mut(&mut segment.arguments); |
330 | } |
331 | } |
332 | |
333 | fn visit_item_mut(&mut self, item: &mut Item) { |
334 | match item { |
335 | // Visit `macro_rules!` because locally defined macros can refer to `self`. |
336 | Item::Macro(item) if item.mac.path.is_ident("macro_rules" ) => { |
337 | self.visit_macro_mut(&mut item.mac); |
338 | } |
339 | // Otherwise, do not recurse into nested items. |
340 | _ => {} |
341 | } |
342 | } |
343 | |
344 | fn visit_macro_mut(&mut self, mac: &mut Macro) { |
345 | // We can't tell in general whether `self` inside a macro invocation |
346 | // refers to the self in the argument list or a different self |
347 | // introduced within the macro. Heuristic: if the macro input contains |
348 | // `fn`, then `self` is more likely to refer to something other than the |
349 | // outer function's self argument. |
350 | if !contains_fn(mac.tokens.clone()) { |
351 | self.visit_token_stream(&mut mac.tokens); |
352 | } |
353 | } |
354 | } |
355 | |
356 | fn contains_fn(tokens: TokenStream) -> bool { |
357 | tokens.into_iter().any(|tt: TokenTree| match tt { |
358 | TokenTree::Ident(ident: Ident) => ident == "fn" , |
359 | TokenTree::Group(group: Group) => contains_fn(tokens:group.stream()), |
360 | _ => false, |
361 | }) |
362 | } |
363 | |
364 | pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) -> bool { |
365 | let modified: bool = ident == "self" ; |
366 | if modified { |
367 | *ident = Ident::new(string:"__self" , ident.span()); |
368 | } |
369 | modified |
370 | } |
371 | |