1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use std::mem;
4
5use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree};
6use quote::{quote, quote_spanned, ToTokens};
7use 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
18pub(crate) type Variants = Punctuated<Variant, Token![,]>;
19
20macro_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.
28pub(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
50pub(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`.
71pub(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.
81pub(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
89pub(crate) fn respan<T>(node: &T, span: Span) -> T
90where
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
98fn 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
111pub(crate) trait SliceExt {
112 fn position_exact(&self, ident: &str) -> Result<Option<usize>>;
113 fn find(&self, ident: &str) -> Option<&Attribute>;
114}
115
116impl 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
140pub(crate) trait ParseBufferExt<'a> {
141 fn parenthesized(self) -> Result<ParseBuffer<'a>>;
142}
143
144impl<'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
152impl<'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
168pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a TypePath);
169
170impl 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
278impl 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
356fn 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
364pub(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