1 use std::borrow::Cow; 2 3 use proc_macro2::TokenStream; 4 use quote::{quote, quote_spanned, ToTokens, TokenStreamExt}; 5 use syn::{spanned::Spanned, Ident, Path, Type}; 6 7 use crate::codegen::{DefaultExpression, PostfixTransform}; 8 use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams}; 9 10 /// Properties needed to generate code for a field in all the contexts 11 /// where one may appear. 12 #[derive(Debug, Clone)] 13 pub struct Field<'a> { 14 /// The name presented to the user of the library. This will appear 15 /// in error messages and will be looked when parsing names. 16 pub name_in_attr: Cow<'a, String>, 17 18 /// The name presented to the author of the library. This will appear 19 /// in the setters or temporary variables which contain the values. 20 pub ident: &'a Ident, 21 22 /// The type of the field in the input. 23 pub ty: &'a Type, 24 pub default_expression: Option<DefaultExpression<'a>>, 25 pub with_path: Cow<'a, Path>, 26 pub post_transform: Option<&'a PostfixTransform>, 27 pub skip: bool, 28 pub multiple: bool, 29 /// If set, this field will be given all unclaimed meta items and will 30 /// not be exposed as a standard named field. 31 pub flatten: bool, 32 } 33 34 impl<'a> Field<'a> { 35 /// Get the name of the meta item that should be matched against input and should be used in diagnostics. 36 /// 37 /// This will be `None` if the field is `skip` or `flatten`, as neither kind of field is addressable 38 /// by name from the input meta. as_name(&'a self) -> Option<&'a str>39 pub fn as_name(&'a self) -> Option<&'a str> { 40 if self.skip || self.flatten { 41 None 42 } else { 43 Some(&self.name_in_attr) 44 } 45 } 46 as_declaration(&'a self) -> Declaration<'a>47 pub fn as_declaration(&'a self) -> Declaration<'a> { 48 Declaration(self) 49 } 50 as_flatten_initializer( &'a self, parent_field_names: Vec<&'a str>, ) -> FlattenInitializer<'a>51 pub fn as_flatten_initializer( 52 &'a self, 53 parent_field_names: Vec<&'a str>, 54 ) -> FlattenInitializer<'a> { 55 FlattenInitializer { 56 field: self, 57 parent_field_names, 58 } 59 } 60 as_match(&'a self) -> MatchArm<'a>61 pub fn as_match(&'a self) -> MatchArm<'a> { 62 MatchArm(self) 63 } 64 as_initializer(&'a self) -> Initializer<'a>65 pub fn as_initializer(&'a self) -> Initializer<'a> { 66 Initializer(self) 67 } 68 as_presence_check(&'a self) -> CheckMissing<'a>69 pub fn as_presence_check(&'a self) -> CheckMissing<'a> { 70 CheckMissing(self) 71 } 72 } 73 74 impl<'a> UsesTypeParams for Field<'a> { uses_type_params<'b>( &self, options: &usage::Options, type_set: &'b IdentSet, ) -> IdentRefSet<'b>75 fn uses_type_params<'b>( 76 &self, 77 options: &usage::Options, 78 type_set: &'b IdentSet, 79 ) -> IdentRefSet<'b> { 80 self.ty.uses_type_params(options, type_set) 81 } 82 } 83 84 /// An individual field during variable declaration in the generated parsing method. 85 pub struct Declaration<'a>(&'a Field<'a>); 86 87 impl<'a> ToTokens for Declaration<'a> { to_tokens(&self, tokens: &mut TokenStream)88 fn to_tokens(&self, tokens: &mut TokenStream) { 89 let field = self.0; 90 let ident = field.ident; 91 let ty = field.ty; 92 93 tokens.append_all(if field.multiple { 94 // This is NOT mutable, as it will be declared mutable only temporarily. 95 quote!(let mut #ident: #ty = ::darling::export::Default::default();) 96 } else { 97 quote!(let mut #ident: (bool, ::darling::export::Option<#ty>) = (false, None);) 98 }); 99 100 // The flatten field additionally needs a place to buffer meta items 101 // until attribute walking is done, so declare that now. 102 // 103 // We expect there can only be one field marked `flatten`, so it shouldn't 104 // be possible for this to shadow another declaration. 105 if field.flatten { 106 tokens.append_all(quote! { 107 let mut __flatten: Vec<::darling::ast::NestedMeta> = vec![]; 108 }); 109 } 110 } 111 } 112 113 pub struct FlattenInitializer<'a> { 114 field: &'a Field<'a>, 115 parent_field_names: Vec<&'a str>, 116 } 117 118 impl<'a> ToTokens for FlattenInitializer<'a> { to_tokens(&self, tokens: &mut TokenStream)119 fn to_tokens(&self, tokens: &mut TokenStream) { 120 let Self { 121 field, 122 parent_field_names, 123 } = self; 124 let ident = field.ident; 125 126 let add_parent_fields = if parent_field_names.is_empty() { 127 None 128 } else { 129 Some(quote! { 130 .map_err(|e| e.add_sibling_alts_for_unknown_field(&[#(#parent_field_names),*])) 131 }) 132 }; 133 134 tokens.append_all(quote! { 135 #ident = (true, 136 __errors.handle( 137 ::darling::FromMeta::from_list(&__flatten) #add_parent_fields 138 ) 139 ); 140 }); 141 } 142 } 143 144 /// Represents an individual field in the match. 145 pub struct MatchArm<'a>(&'a Field<'a>); 146 147 impl<'a> ToTokens for MatchArm<'a> { to_tokens(&self, tokens: &mut TokenStream)148 fn to_tokens(&self, tokens: &mut TokenStream) { 149 let field = self.0; 150 151 // Skipped and flattened fields cannot be populated by a meta 152 // with their name, so they do not have a match arm. 153 if field.skip || field.flatten { 154 return; 155 } 156 157 let name_str = &field.name_in_attr; 158 let ident = field.ident; 159 let with_path = &field.with_path; 160 let post_transform = field.post_transform.as_ref(); 161 162 // Errors include the location of the bad input, so we compute that here. 163 // Fields that take multiple values add the index of the error for convenience, 164 // while single-value fields only expose the name in the input attribute. 165 let location = if field.multiple { 166 // we use the local variable `len` here because location is accessed via 167 // a closure, and the borrow checker gets very unhappy if we try to immutably 168 // borrow `#ident` in that closure when it was declared `mut` outside. 169 quote!(&format!("{}[{}]", #name_str, __len)) 170 } else { 171 quote!(#name_str) 172 }; 173 174 // Give darling's generated code the span of the `with_path` so that if the target 175 // type doesn't impl FromMeta, darling's immediate user gets a properly-spanned error. 176 // 177 // Within the generated code, add the span immediately on extraction failure, so that it's 178 // as specific as possible. 179 // The behavior of `with_span` makes this safe to do; if the child applied an 180 // even-more-specific span, our attempt here will not overwrite that and will only cost 181 // us one `if` check. 182 let extractor = quote_spanned!(with_path.span()=>#with_path(__inner)#post_transform.map_err(|e| e.with_span(&__inner).at(#location))); 183 184 tokens.append_all(if field.multiple { 185 quote!( 186 #name_str => { 187 // Store the index of the name we're assessing in case we need 188 // it for error reporting. 189 let __len = #ident.len(); 190 if let ::darling::export::Some(__val) = __errors.handle(#extractor) { 191 #ident.push(__val) 192 } 193 } 194 ) 195 } else { 196 quote!( 197 #name_str => { 198 if !#ident.0 { 199 #ident = (true, __errors.handle(#extractor)); 200 } else { 201 __errors.push(::darling::Error::duplicate_field(#name_str).with_span(&__inner)); 202 } 203 } 204 ) 205 }); 206 } 207 } 208 209 /// Wrapper to generate initialization code for a field. 210 pub struct Initializer<'a>(&'a Field<'a>); 211 212 impl<'a> ToTokens for Initializer<'a> { to_tokens(&self, tokens: &mut TokenStream)213 fn to_tokens(&self, tokens: &mut TokenStream) { 214 let field = self.0; 215 let ident = field.ident; 216 tokens.append_all(if field.multiple { 217 if let Some(ref expr) = field.default_expression { 218 quote_spanned!(expr.span()=> #ident: if !#ident.is_empty() { 219 #ident 220 } else { 221 #expr 222 }) 223 } else { 224 quote!(#ident: #ident) 225 } 226 } else if let Some(ref expr) = field.default_expression { 227 quote_spanned!(expr.span()=> #ident: if let Some(__val) = #ident.1 { 228 __val 229 } else { 230 #expr 231 }) 232 } else { 233 quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked")) 234 }); 235 } 236 } 237 238 /// Creates an error if a field has no value and no default. 239 pub struct CheckMissing<'a>(&'a Field<'a>); 240 241 impl<'a> ToTokens for CheckMissing<'a> { to_tokens(&self, tokens: &mut TokenStream)242 fn to_tokens(&self, tokens: &mut TokenStream) { 243 if !self.0.multiple && self.0.default_expression.is_none() { 244 let ident = self.0.ident; 245 let ty = self.0.ty; 246 let name_in_attr = &self.0.name_in_attr; 247 248 // If `ty` does not impl FromMeta, the compiler error should point 249 // at the offending type rather than at the derive-macro call site. 250 let from_none_call = 251 quote_spanned!(ty.span()=> <#ty as ::darling::FromMeta>::from_none()); 252 253 tokens.append_all(quote! { 254 if !#ident.0 { 255 match #from_none_call { 256 ::darling::export::Some(__type_fallback) => { 257 #ident.1 = ::darling::export::Some(__type_fallback); 258 } 259 ::darling::export::None => { 260 __errors.push(::darling::Error::missing_field(#name_in_attr)) 261 } 262 } 263 } 264 }) 265 } 266 } 267 } 268