1use crate::generics::{clause_to_toks, WhereClause};
9use crate::SimplePath;
10use proc_macro2::{Span, TokenStream as Toks};
11use proc_macro_error2::emit_error;
12use quote::{quote, TokenStreamExt};
13use syn::spanned::Spanned;
14use syn::token::Comma;
15use syn::{
16 parse2, Field, Fields, Ident, Index, Item, ItemEnum, ItemStruct, Member, Path, PathArguments,
17 Token,
18};
19
20mod for_deref;
21mod impl_misc;
22mod impl_using;
23
24pub use for_deref::ForDeref;
25pub use impl_misc::*;
26pub use impl_using::*;
27
28pub const STD_IMPLS: &[&dyn ImplTrait] = &[
30 &ImplClone,
31 &ImplCopy,
32 &ImplDebug,
33 &ImplDefault,
34 &ImplPartialEq,
35 &ImplEq,
36 &ImplPartialOrd,
37 &ImplOrd,
38 &ImplHash,
39 &ImplBorrow,
40 &ImplBorrowMut,
41 &ImplAsRef,
42 &ImplAsMut,
43 &ImplDeref,
44 &ImplDerefMut,
45];
46
47pub trait ImplTrait {
49 fn path(&self) -> SimplePath;
53
54 fn support_path_arguments(&self) -> bool {
56 false
57 }
58
59 fn support_ignore(&self) -> bool {
63 false
64 }
65
66 fn allow_ignore_with(&self) -> Option<SimplePath> {
70 None
71 }
72
73 fn support_using(&self) -> bool {
77 false
78 }
79
80 fn enum_impl(&self, item: &ItemEnum, args: &ImplArgs) -> Result<Toks> {
87 let type_ident = &item.ident;
88 let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl();
89
90 let (path, items) = self.enum_items(item, args)?;
91
92 let wc = clause_to_toks(&args.clause, item_wc, &path);
93
94 Ok(quote! {
95 #[automatically_derived]
96 impl #impl_generics #path for #type_ident #ty_generics #wc {
97 #items
98 }
99 })
100 }
101
102 fn struct_impl(&self, item: &ItemStruct, args: &ImplArgs) -> Result<Toks> {
109 let type_ident = &item.ident;
110 let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl();
111
112 let (path, items) = self.struct_items(item, args)?;
113
114 let wc = clause_to_toks(&args.clause, item_wc, &path);
115
116 Ok(quote! {
117 #[automatically_derived]
118 impl #impl_generics #path for #type_ident #ty_generics #wc {
119 #items
120 }
121 })
122 }
123
124 fn enum_items(&self, item: &ItemEnum, args: &ImplArgs) -> Result<(Toks, Toks)> {
138 let _ = (item, args);
139 Err(Error::CallSite("enum expansion not supported"))
140 }
141
142 fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)>;
154}
155
156#[allow(non_camel_case_types)]
157mod kw {
158 use syn::custom_keyword;
159
160 custom_keyword!(ignore);
161 custom_keyword!(using);
162}
163
164pub enum Attr {
166 ForDeref(ForDeref),
168 ImplTraits(ImplTraits),
170}
171
172pub struct ImplTraits {
174 targets: Vec<Path>,
175 args: ImplArgs,
176}
177
178pub enum Error {
180 RequireUsing,
182 CallSite(&'static str),
184 WithSpan(Span, &'static str),
186 PathArguments(&'static str),
188}
189
190impl Error {
191 pub fn emit(self, target: Span, path_args: Span) {
193 match self {
194 Error::RequireUsing => {
195 emit_error!(target, "target requires argument `using self.FIELD`")
196 }
197 Error::CallSite(msg) => emit_error!(target, msg),
198 Error::WithSpan(span, msg) => emit_error!(span, msg),
199 Error::PathArguments(msg) => emit_error!(path_args, msg),
200 }
201 }
202}
203
204pub type Result<T> = std::result::Result<T, Error>;
206
207mod parsing {
208 use super::*;
209 use syn::parse::{Parse, ParseStream, Result};
210
211 impl Parse for Attr {
212 fn parse(input: ParseStream) -> Result<Self> {
213 let mut empty_or_trailing = true;
214 let mut lookahead = input.lookahead1();
215
216 if lookahead.peek(Token![for]) {
217 return input.call(ForDeref::parse).map(Attr::ForDeref);
218 }
219
220 let mut targets = Vec::new();
221 let mut using = None;
222 let mut ignores = Vec::new();
223 let mut clause = None;
224
225 while !input.is_empty() {
226 if lookahead.peek(Token![where])
227 || lookahead.peek(kw::using)
228 || lookahead.peek(kw::ignore)
229 {
230 break;
231 }
232
233 if empty_or_trailing {
234 if lookahead.peek(Ident) {
235 targets.push(input.parse()?);
236 empty_or_trailing = false;
237 lookahead = input.lookahead1();
238 continue;
239 }
240 } else if input.peek(Comma) {
241 let _ = input.parse::<Comma>()?;
242 empty_or_trailing = true;
243 lookahead = input.lookahead1();
244 continue;
245 }
246 return Err(lookahead.error());
247 }
248
249 while !input.is_empty() {
250 lookahead = input.lookahead1();
251 if clause.is_none() && using.is_none() && lookahead.peek(kw::using) {
252 let _: kw::using = input.parse()?;
253 let _ = input.parse::<Token![self]>()?;
254 let _ = input.parse::<Token![.]>()?;
255 using = Some(input.parse()?);
256 } else if clause.is_none() && ignores.is_empty() && lookahead.peek(kw::ignore) {
257 let _: kw::ignore = input.parse()?;
258 let _ = input.parse::<Token![self]>()?;
259 let _ = input.parse::<Token![.]>()?;
260 ignores.push(input.parse()?);
261 while input.peek(Comma) {
262 let _ = input.parse::<Comma>()?;
263 if input.peek(Token![self]) {
264 let _ = input.parse::<Token![self]>()?;
265 let _ = input.parse::<Token![.]>()?;
266 ignores.push(input.parse()?);
267 continue;
268 }
269 break;
270 }
271 } else if lookahead.peek(Token![where]) {
272 clause = Some(input.parse()?);
274 } else {
275 return Err(lookahead.error());
276 }
277 }
278
279 let args = ImplArgs {
280 path_arguments: PathArguments::None,
281 ignores,
282 using,
283 clause,
284 };
285 Ok(Attr::ImplTraits(ImplTraits { targets, args }))
286 }
287 }
288}
289
290impl ImplTraits {
291 pub fn expand(
298 self,
299 item: Toks,
300 find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
301 ) -> Toks {
302 match parse2::<Item>(item) {
303 Ok(Item::Enum(item)) => self.expand_enum(item, find_impl),
304 Ok(Item::Struct(item)) => self.expand_struct(item, find_impl),
305 Ok(_) => syn::Error::new(
306 Span::call_site(),
307 "#[autoimpl(Trait)] can only be used on enum or struct items",
308 )
309 .into_compile_error(),
310 Err(err) => err.into_compile_error(),
311 }
312 }
313
314 fn expand_enum(
315 self,
316 item: ItemEnum,
317 find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
318 ) -> Toks {
319 let ImplTraits {
320 mut targets,
321 mut args,
322 } = self;
323
324 if !args.ignores.is_empty() {
325 let ignores = args.ignores.iter();
326 let list = quote! { #(#ignores)* };
327 emit_error!(list, "enum expansion does not currently support `ignore`",);
328 return Toks::new();
329 }
330 if let Some(mem) = args.using {
331 emit_error!(mem, "enum expansion does not currently support `using`",);
332 return Toks::new();
333 }
334
335 let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
336 for mut target in targets.drain(..) {
337 let target_span = target.span();
338 let path_args = target
339 .segments
340 .last_mut()
341 .map(|seg| std::mem::take(&mut seg.arguments))
342 .unwrap_or(PathArguments::None);
343 let target_impl = match find_impl(&target) {
344 Some(impl_) => impl_,
345 None => {
346 emit_error!(target, "unsupported trait");
347 return Toks::new();
348 }
349 };
350
351 if !(path_args.is_empty() || target_impl.support_path_arguments()) {
352 emit_error!(
353 target_span,
354 "target {} does not support path arguments",
355 target_impl.path()
356 );
357 }
358
359 impl_targets.push((target.span(), target_impl, path_args));
360 }
361
362 let mut toks = Toks::new();
363
364 for (span, target, path_args) in impl_targets.drain(..) {
365 let path_args_span = path_args.span();
366 args.path_arguments = path_args;
367 match target.enum_impl(&item, &args) {
368 Ok(items) => toks.append_all(items),
369 Err(error) => error.emit(span, path_args_span),
370 }
371 }
372 toks
373 }
374
375 fn expand_struct(
376 self,
377 item: ItemStruct,
378 find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
379 ) -> Toks {
380 let ImplTraits {
381 mut targets,
382 mut args,
383 } = self;
384
385 let mut not_supporting_ignore = vec![];
386 let mut not_supporting_using = vec![];
387
388 let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
389 for mut target in targets.drain(..) {
390 let target_span = target.span();
391 let path_args = target
392 .segments
393 .last_mut()
394 .map(|seg| std::mem::take(&mut seg.arguments))
395 .unwrap_or(PathArguments::None);
396 let target_impl = match find_impl(&target) {
397 Some(impl_) => impl_,
398 None => {
399 emit_error!(target, "unsupported trait");
400 return Toks::new();
401 }
402 };
403
404 if !target_impl.support_ignore() {
405 let except_with = target_impl.allow_ignore_with();
406 not_supporting_ignore.push((target.clone(), except_with));
407 }
408 if !target_impl.support_using() {
409 not_supporting_using.push(target.clone());
410 }
411 if !(path_args.is_empty() || target_impl.support_path_arguments()) {
412 emit_error!(
413 target_span,
414 "target {} does not support path arguments",
415 target_impl.path()
416 );
417 }
418
419 impl_targets.push((target.span(), target_impl, path_args));
420 }
421
422 if !args.ignores.is_empty() {
423 for (target, except_with) in not_supporting_ignore.into_iter() {
424 if let Some(path) = except_with {
425 if impl_targets
426 .iter()
427 .any(|(_, target_impl, _)| path == target_impl.path())
428 {
429 continue;
430 }
431 }
432 emit_error!(target, "target does not support `ignore`",);
433 }
434 }
435 if args.using.is_some() {
436 for target in not_supporting_using.into_iter() {
437 emit_error!(target, "target does not support `using`",);
438 }
439 }
440
441 fn check_is_field(mem: &Member, fields: &Fields) {
442 match (fields, mem) {
443 (Fields::Named(fields), Member::Named(ref ident)) => {
444 if fields
445 .named
446 .iter()
447 .any(|field| field.ident.as_ref() == Some(ident))
448 {
449 return;
450 }
451 }
452 (Fields::Unnamed(fields), Member::Unnamed(index)) => {
453 if (index.index as usize) < fields.unnamed.len() {
454 return;
455 }
456 }
457 _ => (),
458 }
459 emit_error!(mem, "not a struct field");
460 }
461
462 let mut toks = Toks::new();
463 for mem in &args.ignores {
464 check_is_field(mem, &item.fields);
465 }
466 if let Some(mem) = args.using_member() {
467 check_is_field(mem, &item.fields);
468 }
469
470 for (span, target, path_args) in impl_targets.drain(..) {
471 let path_args_span = path_args.span();
472 args.path_arguments = path_args;
473 match target.struct_impl(&item, &args) {
474 Ok(items) => toks.append_all(items),
475 Err(error) => error.emit(span, path_args_span),
476 }
477 }
478 toks
479 }
480}
481
482pub struct ImplArgs {
484 pub path_arguments: PathArguments,
489 pub ignores: Vec<Member>,
491 pub using: Option<Member>,
493 pub clause: Option<WhereClause>,
495}
496
497impl ImplArgs {
498 pub fn ignore(&self, member: &Member) -> bool {
500 self.ignores.contains(member)
501 }
502
503 pub fn ignore_named(&self, ident: &Ident) -> bool {
505 self.ignores.iter().any(|ig| match ig {
506 Member::Named(m) => m == ident,
507 _ => false,
508 })
509 }
510
511 pub fn ignore_unnamed(&self, index: &Index) -> bool {
513 self.ignores.iter().any(|ig| match ig {
514 Member::Unnamed(m) => m == index,
515 _ => false,
516 })
517 }
518
519 pub fn using_member(&self) -> Option<&Member> {
521 self.using.as_ref()
522 }
523
524 pub fn using_field<'b>(&self, fields: &'b Fields) -> Option<&'b Field> {
526 match fields {
527 Fields::Named(fields) => fields.named.iter().find(|field| match self.using {
528 Some(Member::Named(ref ident)) => ident == field.ident.as_ref().unwrap(),
529 _ => false,
530 }),
531 Fields::Unnamed(fields) => {
532 fields
533 .unnamed
534 .iter()
535 .enumerate()
536 .find_map(|(i, field)| match self.using {
537 Some(Member::Unnamed(ref index)) => {
538 (*index == Index::from(i)).then(|| field)
539 }
540 _ => None,
541 })
542 }
543 Fields::Unit => None,
544 }
545 }
546
547 pub fn for_fields<'f>(&self, fields: &'f Fields, f: impl FnMut(Member, &'f Field)) {
549 self.for_fields_iter(fields.iter().enumerate(), f);
550 }
551
552 pub fn for_fields_iter<'f>(
554 &self,
555 fields: impl Iterator<Item = (usize, &'f Field)>,
556 mut f: impl FnMut(Member, &'f Field),
557 ) {
558 for (i, field) in fields {
559 let member = match field.ident.clone() {
560 Some(ident) => Member::Named(ident),
561 None => Member::Unnamed(Index::from(i)),
562 };
563 if !self.ignore(&member) {
564 f(member, field);
565 }
566 }
567 }
568}