1 use proc_macro2::TokenStream;
2 use quote::{format_ident, quote, ToTokens};
3 use syn::{
4     parse::{Parse, ParseStream},
5     spanned::Spanned,
6 };
7 
8 pub(crate) enum MemberOrString {
9     Member(syn::Member),
10     String(syn::LitStr),
11 }
12 
13 impl ToTokens for MemberOrString {
to_tokens(&self, tokens: &mut TokenStream)14     fn to_tokens(&self, tokens: &mut TokenStream) {
15         use MemberOrString::*;
16         match self {
17             Member(member) => member.to_tokens(tokens),
18             String(string) => string.to_tokens(tokens),
19         }
20     }
21 }
22 
23 impl Parse for MemberOrString {
parse(input: ParseStream) -> syn::Result<Self>24     fn parse(input: ParseStream) -> syn::Result<Self> {
25         let lookahead = input.lookahead1();
26         if lookahead.peek(syn::Ident) || lookahead.peek(syn::LitInt) {
27             Ok(MemberOrString::Member(input.parse()?))
28         } else if lookahead.peek(syn::LitStr) {
29             Ok(MemberOrString::String(input.parse()?))
30         } else {
31             Err(syn::Error::new(
32                 input.span(),
33                 "Expected a string or a field reference.",
34             ))
35         }
36     }
37 }
38 
39 use crate::{
40     diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
41     forward::WhichFn,
42 };
43 
gen_all_variants_with( variants: &[DiagnosticDef], which_fn: WhichFn, mut f: impl FnMut(&syn::Ident, &syn::Fields, &DiagnosticConcreteArgs) -> Option<TokenStream>, ) -> Option<TokenStream>44 pub(crate) fn gen_all_variants_with(
45     variants: &[DiagnosticDef],
46     which_fn: WhichFn,
47     mut f: impl FnMut(&syn::Ident, &syn::Fields, &DiagnosticConcreteArgs) -> Option<TokenStream>,
48 ) -> Option<TokenStream> {
49     let pairs = variants
50         .iter()
51         .filter_map(|def| {
52             def.args
53                 .forward_or_override_enum(&def.ident, which_fn, |concrete| {
54                     f(&def.ident, &def.fields, concrete)
55                 })
56         })
57         .collect::<Vec<_>>();
58     if pairs.is_empty() {
59         return None;
60     }
61     let signature = which_fn.signature();
62     let catchall = which_fn.catchall_arm();
63     Some(quote! {
64         #signature {
65             #[allow(unused_variables, deprecated)]
66             match self {
67                 #(#pairs)*
68                 #catchall
69             }
70         }
71     })
72 }
73 
74 use crate::fmt::Display;
75 use std::collections::HashSet;
76 
gen_unused_pat(fields: &syn::Fields) -> TokenStream77 pub(crate) fn gen_unused_pat(fields: &syn::Fields) -> TokenStream {
78     match fields {
79         syn::Fields::Named(_) => quote! { { .. } },
80         syn::Fields::Unnamed(_) => quote! { ( .. ) },
81         syn::Fields::Unit => quote! {},
82     }
83 }
84 
85 /// Goes in the slot `let Self #pat = self;` or `match self { Self #pat => ...
86 /// }`.
gen_fields_pat(fields: &syn::Fields) -> TokenStream87 fn gen_fields_pat(fields: &syn::Fields) -> TokenStream {
88     let member_idents = fields.iter().enumerate().map(|(i, field)| {
89         field
90             .ident
91             .as_ref()
92             .cloned()
93             .unwrap_or_else(|| format_ident!("_{}", i))
94     });
95     match fields {
96         syn::Fields::Named(_) => quote! {
97             { #(#member_idents),* }
98         },
99         syn::Fields::Unnamed(_) => quote! {
100             ( #(#member_idents),* )
101         },
102         syn::Fields::Unit => quote! {},
103     }
104 }
105 
106 /// The returned tokens go in the slot `let Self #pat = self;` or `match self {
107 /// Self #pat => ... }`. The members can be passed to
108 /// `Display::expand_shorthand[_cloned]`.
display_pat_members(fields: &syn::Fields) -> (TokenStream, HashSet<syn::Member>)109 pub(crate) fn display_pat_members(fields: &syn::Fields) -> (TokenStream, HashSet<syn::Member>) {
110     let pat = gen_fields_pat(fields);
111     let members: HashSet<syn::Member> = fields
112         .iter()
113         .enumerate()
114         .map(|(i, field)| {
115             if let Some(ident) = field.ident.as_ref().cloned() {
116                 syn::Member::Named(ident)
117             } else {
118                 syn::Member::Unnamed(syn::Index {
119                     index: i as u32,
120                     span: field.span(),
121                 })
122             }
123         })
124         .collect();
125     (pat, members)
126 }
127 
128 impl Display {
129     /// Returns `(fmt, args)` which must be passed to some kind of format macro
130     /// without tokens in between, i.e. `format!(#fmt #args)`.
expand_shorthand_cloned( &self, members: &HashSet<syn::Member>, ) -> (syn::LitStr, TokenStream)131     pub(crate) fn expand_shorthand_cloned(
132         &self,
133         members: &HashSet<syn::Member>,
134     ) -> (syn::LitStr, TokenStream) {
135         let mut display = self.clone();
136         display.expand_shorthand(members);
137         let Display { fmt, args, .. } = display;
138         (fmt, args)
139     }
140 }
141