impl_tools_lib/autoimpl/
for_deref.rs1use crate::generics::{GenericParam, Generics, TypeParamBound, WherePredicate};
9use proc_macro2::{Span, TokenStream};
10use proc_macro_error2::{emit_call_site_error, emit_error};
11use quote::{quote, ToTokens, TokenStreamExt};
12use syn::punctuated::Punctuated;
13use syn::spanned::Spanned;
14use syn::token::{Comma, Eq, PathSep};
15use syn::{parse_quote, FnArg, Ident, Item, Pat, Token, TraitItem, Type, TypePath};
16
17pub struct ForDeref {
19 generics: Generics,
20 definitive: Ident,
21 targets: Punctuated<Type, Comma>,
22}
23
24mod parsing {
25 use super::*;
26 use syn::parse::{Error, Parse, ParseStream, Result};
27
28 impl Parse for ForDeref {
29 fn parse(input: ParseStream) -> Result<Self> {
30 let _ = input.parse::<Token![for]>()?;
31 let mut generics: Generics = input.parse()?;
32
33 let targets = Punctuated::parse_separated_nonempty(input)?;
34
35 let mut lookahead = input.lookahead1();
36 if lookahead.peek(Token![where]) {
37 generics.where_clause = Some(input.parse()?);
38 lookahead = input.lookahead1();
39 }
40
41 if !input.is_empty() {
42 return Err(lookahead.error());
43 }
44
45 let mut definitive: Option<Ident> = None;
46 for param in &generics.params {
47 if let GenericParam::Type(param) = param {
48 for bound in ¶m.bounds {
49 if matches!(bound, TypeParamBound::TraitSubst(_)) {
50 definitive = Some(param.ident.clone());
51 break;
52 }
53 }
54 }
55 }
56 if definitive.is_none() {
57 if let Some(clause) = generics.where_clause.as_ref() {
58 for pred in &clause.predicates {
59 if let WherePredicate::Type(pred) = pred {
60 for bound in &pred.bounds {
61 if matches!(bound, TypeParamBound::TraitSubst(_)) {
62 if let Type::Path(TypePath { qself: None, path }) =
63 &pred.bounded_ty
64 {
65 if let Some(ident) = path.get_ident() {
66 definitive = Some(ident.clone());
67 break;
68 }
69 }
70 }
71 }
72 }
73 }
74 }
75 }
76 let definitive = match definitive {
77 Some(def) => def,
78 None => {
79 return Err(Error::new(
80 Span::call_site(),
81 "no definitive type parameter: require a parameter bound like `T: trait``",
82 ));
83 }
84 };
85
86 Ok(ForDeref {
87 generics,
88 definitive,
89 targets,
90 })
91 }
92 }
93}
94
95fn propegate_attr_to_impl(attr: &syn::Attribute) -> bool {
98 let path = attr.path().to_token_stream().to_string();
99 matches!(path.as_str(), "cfg" | "allow" | "warn" | "deny" | "forbid")
100}
101
102fn has_bound_on_self(gen: &syn::Generics) -> bool {
103 if let Some(ref clause) = gen.where_clause {
104 for pred in clause.predicates.iter() {
105 if let syn::WherePredicate::Type(ref ty) = pred {
106 if let Type::Path(ref bounded) = ty.bounded_ty {
107 if bounded.qself.is_none() && bounded.path.is_ident("Self") {
108 if ty
109 .bounds
110 .iter()
111 .any(|bound| matches!(bound, syn::TypeParamBound::Trait(_)))
112 {
113 return true;
114 }
115 }
116 }
117 }
118 }
122 }
123
124 false
125}
126
127impl ForDeref {
128 pub fn expand(self, item: TokenStream) -> TokenStream {
133 let trait_def = match syn::parse2::<Item>(item) {
134 Ok(Item::Trait(item)) => item,
135 Ok(item) => {
136 emit_error!(item, "expected trait");
137 return TokenStream::new();
138 }
139 Err(err) => return err.into_compile_error(),
140 };
141
142 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
143 enum Bound {
144 None,
145 Deref(bool), ErrorEmitted,
147 }
148 let mut bound = Bound::None;
149
150 let trait_ident = &trait_def.ident;
151 let (_, trait_generics, _) = trait_def.generics.split_for_impl();
152 let trait_ty = quote! { #trait_ident #trait_generics };
153 let ty_generics = self.generics.ty_generics(&trait_def.generics);
154 let (impl_generics, where_clause) =
155 self.generics.impl_generics(&trait_def.generics, &trait_ty);
156
157 let definitive_ty = self.definitive;
158 let definitive = quote! { < #definitive_ty as #trait_ty > };
159
160 let mut impl_items = TokenStream::new();
162 let tokens = &mut impl_items;
163 for item in trait_def.items.into_iter() {
164 match item {
165 TraitItem::Const(item) => {
166 for attr in item.attrs.iter() {
167 if *attr.path() == parse_quote! { cfg } {
168 attr.to_tokens(tokens);
169 }
170 }
171
172 item.const_token.to_tokens(tokens);
173 item.ident.to_tokens(tokens);
174 item.colon_token.to_tokens(tokens);
175 item.ty.to_tokens(tokens);
176
177 Eq::default().to_tokens(tokens);
178 definitive.to_tokens(tokens);
179 PathSep::default().to_tokens(tokens);
180 item.ident.to_tokens(tokens);
181
182 item.semi_token.to_tokens(tokens);
183 }
184 TraitItem::Fn(mut item) => {
185 for attr in item.attrs.iter() {
186 if propegate_attr_to_impl(attr) {
187 attr.to_tokens(tokens);
188 }
189 }
190
191 if has_bound_on_self(&item.sig.generics) {
192 if item.default.is_none() {
197 emit_call_site_error!(
198 "cannot autoimpl trait with Deref";
199 note = item.span() => "method has a bound on Self and no default implementation";
200 );
201 }
202
203 continue;
204 }
205
206 for (i, arg) in item.sig.inputs.iter_mut().enumerate() {
207 if let FnArg::Typed(ref mut ty) = arg {
208 if let Pat::Ident(pat) = &mut *ty.pat {
209 pat.by_ref = None;
211 pat.mutability = None;
212 assert_eq!(pat.subpat, None);
213 } else {
214 let name = format!("arg{i}");
216 let ident = Ident::new(&name, Span::call_site());
217 ty.pat = Box::new(Pat::Ident(syn::PatIdent {
218 attrs: vec![],
219 by_ref: None,
220 mutability: None,
221 ident,
222 subpat: None,
223 }));
224 }
225 }
226 }
227 item.sig.to_tokens(tokens);
228
229 bound = bound.max(match item.sig.inputs.first() {
230 Some(FnArg::Receiver(rec)) => {
231 if rec.reference.is_some() {
232 Bound::Deref(rec.mutability.is_some())
233 } else {
234 emit_call_site_error!(
235 "cannot autoimpl trait with Deref";
236 note = rec.span() => "deref cannot yield `self` by value";
237 );
238 Bound::ErrorEmitted
239 }
240 }
241 Some(FnArg::Typed(ref pat)) => match &*pat.ty {
242 Type::Reference(rf) if rf.elem == parse_quote! { Self } => {
243 Bound::Deref(rf.mutability.is_some())
244 }
245 _ => Bound::None,
246 },
247 _ => Bound::None,
248 });
249
250 let ident = &item.sig.ident;
251 let params = item.sig.inputs.iter().map(|arg| {
252 let mut toks = TokenStream::new();
253 match arg {
254 FnArg::Receiver(arg) => {
255 for attr in &arg.attrs {
256 if propegate_attr_to_impl(&attr) {
257 attr.to_tokens(&mut toks);
258 }
259 }
260 arg.self_token.to_tokens(&mut toks);
261 }
262 FnArg::Typed(arg) => {
263 for attr in &arg.attrs {
264 if propegate_attr_to_impl(&attr) {
265 attr.to_tokens(&mut toks);
266 };
267 }
268
269 arg.pat.to_tokens(&mut toks);
270 }
271 };
272 toks
273 });
274 tokens.append_all(quote! { {
275 #definitive :: #ident ( #(#params),* )
276 } });
277 }
278 TraitItem::Type(item) => {
279 for attr in item.attrs.iter() {
280 if *attr.path() == parse_quote! { cfg } {
281 attr.to_tokens(tokens);
282 }
283 }
284
285 if has_bound_on_self(&item.generics) {
286 emit_call_site_error!(
287 "cannot autoimpl trait with Deref";
288 note = item.span() => "type has a bound on Self";
289 );
290 }
291
292 item.type_token.to_tokens(tokens);
293 item.ident.to_tokens(tokens);
294
295 let (_, ty_generics, where_clause) = item.generics.split_for_impl();
296 ty_generics.to_tokens(tokens);
297
298 Eq::default().to_tokens(tokens);
299 definitive.to_tokens(tokens);
300 PathSep::default().to_tokens(tokens);
301 item.ident.to_tokens(tokens);
302 ty_generics.to_tokens(tokens);
303
304 where_clause.to_tokens(tokens);
305 item.semi_token.to_tokens(tokens);
306 }
307 TraitItem::Macro(item) => {
308 emit_error!(item, "unsupported: macro item in trait");
309 }
310 TraitItem::Verbatim(item) => {
311 emit_error!(item, "unsupported: verbatim item in trait");
312 }
313
314 _ => (),
320 }
321 }
322
323 let mut toks = TokenStream::new();
324 match bound {
325 Bound::None => (),
326 Bound::Deref(is_mut) => {
327 let bound = match is_mut {
329 false => quote! { ::core::ops::Deref },
330 true => quote! { ::core::ops::DerefMut },
331 };
332
333 let target_impls = self.targets.iter().map(|target| {
334 quote! {
335 impl #impl_generics TargetMustImplDeref #ty_generics for #target
336 #where_clause {}
337 }
338 });
339
340 toks.append_all(quote! {
341 #[automatically_derived]
342 const _: () = {
343 trait TargetMustImplDeref #impl_generics: #bound<Target = #definitive_ty>
344 #where_clause {}
345
346 #(#target_impls)*
347 };
348 });
349 }
350 Bound::ErrorEmitted => return toks,
351 }
352
353 for target in self.targets {
354 toks.append_all(quote! {
355 #[automatically_derived]
356 impl #impl_generics #trait_ty for #target #where_clause {
357 #impl_items
358 }
359 });
360 }
361 toks
362 }
363}