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(item) => {
306 emit_error!(item, "expected struct");
307 Toks::new()
308 }
309 Err(err) => err.into_compile_error(),
310 }
311 }
312
313 fn expand_enum(
314 self,
315 item: ItemEnum,
316 find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
317 ) -> Toks {
318 let ImplTraits {
319 mut targets,
320 mut args,
321 } = self;
322
323 if !args.ignores.is_empty() {
324 let ignores = args.ignores.iter();
325 let list = quote! { #(#ignores)* };
326 emit_error!(list, "enum expansion does not currently support `ignore`",);
327 return Toks::new();
328 }
329 if let Some(mem) = args.using {
330 emit_error!(mem, "enum expansion does not currently support `using`",);
331 return Toks::new();
332 }
333
334 let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
335 for mut target in targets.drain(..) {
336 let target_span = target.span();
337 let path_args = target
338 .segments
339 .last_mut()
340 .map(|seg| std::mem::take(&mut seg.arguments))
341 .unwrap_or(PathArguments::None);
342 let target_impl = match find_impl(&target) {
343 Some(impl_) => impl_,
344 None => {
345 emit_error!(target, "unsupported trait");
346 return Toks::new();
347 }
348 };
349
350 if !(path_args.is_empty() || target_impl.support_path_arguments()) {
351 emit_error!(
352 target_span,
353 "target {} does not support path arguments",
354 target_impl.path()
355 );
356 }
357
358 impl_targets.push((target.span(), target_impl, path_args));
359 }
360
361 let mut toks = Toks::new();
362
363 for (span, target, path_args) in impl_targets.drain(..) {
364 let path_args_span = path_args.span();
365 args.path_arguments = path_args;
366 match target.enum_impl(&item, &args) {
367 Ok(items) => toks.append_all(items),
368 Err(error) => error.emit(span, path_args_span),
369 }
370 }
371 toks
372 }
373
374 fn expand_struct(
375 self,
376 item: ItemStruct,
377 find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
378 ) -> Toks {
379 let ImplTraits {
380 mut targets,
381 mut args,
382 } = self;
383
384 let mut not_supporting_ignore = vec![];
385 let mut not_supporting_using = vec![];
386
387 let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
388 for mut target in targets.drain(..) {
389 let target_span = target.span();
390 let path_args = target
391 .segments
392 .last_mut()
393 .map(|seg| std::mem::take(&mut seg.arguments))
394 .unwrap_or(PathArguments::None);
395 let target_impl = match find_impl(&target) {
396 Some(impl_) => impl_,
397 None => {
398 emit_error!(target, "unsupported trait");
399 return Toks::new();
400 }
401 };
402
403 if !target_impl.support_ignore() {
404 let except_with = target_impl.allow_ignore_with();
405 not_supporting_ignore.push((target.clone(), except_with));
406 }
407 if !target_impl.support_using() {
408 not_supporting_using.push(target.clone());
409 }
410 if !(path_args.is_empty() || target_impl.support_path_arguments()) {
411 emit_error!(
412 target_span,
413 "target {} does not support path arguments",
414 target_impl.path()
415 );
416 }
417
418 impl_targets.push((target.span(), target_impl, path_args));
419 }
420
421 if !args.ignores.is_empty() {
422 for (target, except_with) in not_supporting_ignore.into_iter() {
423 if let Some(path) = except_with {
424 if impl_targets
425 .iter()
426 .any(|(_, target_impl, _)| path == target_impl.path())
427 {
428 continue;
429 }
430 }
431 emit_error!(target, "target does not support `ignore`",);
432 }
433 }
434 if args.using.is_some() {
435 for target in not_supporting_using.into_iter() {
436 emit_error!(target, "target does not support `using`",);
437 }
438 }
439
440 fn check_is_field(mem: &Member, fields: &Fields) {
441 match (fields, mem) {
442 (Fields::Named(fields), Member::Named(ref ident)) => {
443 if fields
444 .named
445 .iter()
446 .any(|field| field.ident.as_ref() == Some(ident))
447 {
448 return;
449 }
450 }
451 (Fields::Unnamed(fields), Member::Unnamed(index)) => {
452 if (index.index as usize) < fields.unnamed.len() {
453 return;
454 }
455 }
456 _ => (),
457 }
458 emit_error!(mem, "not a struct field");
459 }
460
461 let mut toks = Toks::new();
462 for mem in &args.ignores {
463 check_is_field(mem, &item.fields);
464 }
465 if let Some(mem) = args.using_member() {
466 check_is_field(mem, &item.fields);
467 }
468
469 for (span, target, path_args) in impl_targets.drain(..) {
470 let path_args_span = path_args.span();
471 args.path_arguments = path_args;
472 match target.struct_impl(&item, &args) {
473 Ok(items) => toks.append_all(items),
474 Err(error) => error.emit(span, path_args_span),
475 }
476 }
477 toks
478 }
479}
480
481pub struct ImplArgs {
483 pub path_arguments: PathArguments,
488 pub ignores: Vec<Member>,
490 pub using: Option<Member>,
492 pub clause: Option<WhereClause>,
494}
495
496impl ImplArgs {
497 pub fn ignore(&self, member: &Member) -> bool {
499 self.ignores.iter().any(|ig| *ig == *member)
500 }
501
502 pub fn ignore_named(&self, ident: &Ident) -> bool {
504 self.ignores.iter().any(|ig| match ig {
505 Member::Named(m) => m == ident,
506 _ => false,
507 })
508 }
509
510 pub fn ignore_unnamed(&self, index: &Index) -> bool {
512 self.ignores.iter().any(|ig| match ig {
513 Member::Unnamed(m) => m == index,
514 _ => false,
515 })
516 }
517
518 pub fn using_member(&self) -> Option<&Member> {
520 self.using.as_ref()
521 }
522
523 pub fn using_field<'b>(&self, fields: &'b Fields) -> Option<&'b Field> {
525 match fields {
526 Fields::Named(fields) => fields.named.iter().find(|field| match self.using {
527 Some(Member::Named(ref ident)) => ident == field.ident.as_ref().unwrap(),
528 _ => false,
529 }),
530 Fields::Unnamed(fields) => {
531 fields
532 .unnamed
533 .iter()
534 .enumerate()
535 .find_map(|(i, field)| match self.using {
536 Some(Member::Unnamed(ref index)) => {
537 (*index == Index::from(i)).then(|| field)
538 }
539 _ => None,
540 })
541 }
542 Fields::Unit => None,
543 }
544 }
545
546 pub fn for_fields<'f>(&self, fields: &'f Fields, f: impl FnMut(Member, &'f Field)) {
548 self.for_fields_iter(fields.iter().enumerate(), f);
549 }
550
551 pub fn for_fields_iter<'f>(
553 &self,
554 fields: impl Iterator<Item = (usize, &'f Field)>,
555 mut f: impl FnMut(Member, &'f Field),
556 ) {
557 for (i, field) in fields {
558 let member = match field.ident.clone() {
559 Some(ident) => Member::Named(ident),
560 None => Member::Unnamed(Index::from(i)),
561 };
562 if !self.ignore(&member) {
563 f(member, field);
564 }
565 }
566 }
567}