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