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