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_call_site_warning, 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 } else if !cfg!(feature = "allow-trait-autoimpl-with-sized-fn-bound") {
202 emit_call_site_warning!(
204 "autoimpl on trait that has a method with Self: Sized bound";
205 note = item.span() => "method impl uses default implementation, not deref";
206 );
207 }
208
209 continue;
210 }
211
212 for (i, arg) in item.sig.inputs.iter_mut().enumerate() {
213 if let FnArg::Typed(ref mut ty) = arg {
214 if let Pat::Ident(pat) = &mut *ty.pat {
215 pat.by_ref = None;
217 pat.mutability = None;
218 assert_eq!(pat.subpat, None);
219 } else {
220 let name = format!("arg{i}");
222 let ident = Ident::new(&name, Span::call_site());
223 ty.pat = Box::new(Pat::Ident(syn::PatIdent {
224 attrs: vec![],
225 by_ref: None,
226 mutability: None,
227 ident,
228 subpat: None,
229 }));
230 }
231 }
232 }
233 item.sig.to_tokens(tokens);
234
235 bound = bound.max(match item.sig.inputs.first() {
236 Some(FnArg::Receiver(rec)) => {
237 if rec.reference.is_some() {
238 Bound::Deref(rec.mutability.is_some())
239 } else {
240 emit_call_site_error!(
241 "cannot autoimpl trait with Deref";
242 note = rec.span() => "deref cannot yield `self` by value";
243 );
244 Bound::ErrorEmitted
245 }
246 }
247 Some(FnArg::Typed(ref pat)) => match &*pat.ty {
248 Type::Reference(rf) if rf.elem == parse_quote! { Self } => {
249 Bound::Deref(rf.mutability.is_some())
250 }
251 _ => Bound::None,
252 },
253 _ => Bound::None,
254 });
255
256 let ident = &item.sig.ident;
257 let params = item.sig.inputs.iter().map(|arg| {
258 let mut toks = TokenStream::new();
259 match arg {
260 FnArg::Receiver(arg) => {
261 for attr in &arg.attrs {
262 if propegate_attr_to_impl(&attr) {
263 attr.to_tokens(&mut toks);
264 }
265 }
266 arg.self_token.to_tokens(&mut toks);
267 }
268 FnArg::Typed(arg) => {
269 for attr in &arg.attrs {
270 if propegate_attr_to_impl(&attr) {
271 attr.to_tokens(&mut toks);
272 };
273 }
274
275 arg.pat.to_tokens(&mut toks);
276 }
277 };
278 toks
279 });
280 tokens.append_all(quote! { {
281 #definitive :: #ident ( #(#params),* )
282 } });
283 }
284 TraitItem::Type(item) => {
285 for attr in item.attrs.iter() {
286 if *attr.path() == parse_quote! { cfg } {
287 attr.to_tokens(tokens);
288 }
289 }
290
291 if has_bound_on_self(&item.generics) {
292 emit_call_site_error!(
293 "cannot autoimpl trait with Deref";
294 note = item.span() => "type has a bound on Self";
295 );
296 }
297
298 item.type_token.to_tokens(tokens);
299 item.ident.to_tokens(tokens);
300
301 let (_, ty_generics, where_clause) = item.generics.split_for_impl();
302 ty_generics.to_tokens(tokens);
303
304 Eq::default().to_tokens(tokens);
305 definitive.to_tokens(tokens);
306 PathSep::default().to_tokens(tokens);
307 item.ident.to_tokens(tokens);
308 ty_generics.to_tokens(tokens);
309
310 where_clause.to_tokens(tokens);
311 item.semi_token.to_tokens(tokens);
312 }
313 TraitItem::Macro(item) => {
314 emit_error!(item, "unsupported: macro item in trait");
315 }
316 TraitItem::Verbatim(item) => {
317 emit_error!(item, "unsupported: verbatim item in trait");
318 }
319
320 _ => (),
326 }
327 }
328
329 let mut toks = TokenStream::new();
330 match bound {
331 Bound::None => (),
332 Bound::Deref(is_mut) => {
333 let bound = match is_mut {
335 false => quote! { ::core::ops::Deref },
336 true => quote! { ::core::ops::DerefMut },
337 };
338
339 let target_impls = self.targets.iter().map(|target| {
340 quote! {
341 impl #impl_generics TargetMustImplDeref #ty_generics for #target
342 #where_clause {}
343 }
344 });
345
346 toks.append_all(quote! {
347 #[automatically_derived]
348 const _: () = {
349 trait TargetMustImplDeref #impl_generics: #bound<Target = #definitive_ty>
350 #where_clause {}
351
352 #(#target_impls)*
353 };
354 });
355 }
356 Bound::ErrorEmitted => return toks,
357 }
358
359 for target in self.targets {
360 toks.append_all(quote! {
361 #[automatically_derived]
362 impl #impl_generics #trait_ty for #target #where_clause {
363 #impl_items
364 }
365 });
366 }
367 toks
368 }
369}