summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Geisler <mgeisler@google.com>2024-03-21 15:39:02 +0100
committerMartin Geisler <mgeisler@google.com>2024-03-26 17:44:55 +0100
commit0e02de55baa0987d49ede0a7bb6d9db967053d16 (patch)
tree09e966c6ecf936e39b5f2c9bacd820bc817e8b3a
parent7378c0c9ed0b4bd94f608bc242fa86c6ececb95f (diff)
downloaddarling_core-0e02de55baa0987d49ede0a7bb6d9db967053d16.tar.gz
Import 'darling_core' crateupstream
Request Document: go/android-rust-importing-crates For CL Reviewers: go/android3p#cl-review For Build Team: go/ab-third-party-imports Bug: http://b/328419565 Test: m libdarling_core Change-Id: I3385fa49d5bd1defb7a16145eff7e53d41de6bea
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp20
-rw-r--r--Cargo.lock68
-rw-r--r--Cargo.toml49
-rw-r--r--LICENSE21
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS2
-rw-r--r--cargo_embargo.json9
-rw-r--r--src/ast/data.rs517
-rw-r--r--src/ast/generics.rs192
-rw-r--r--src/ast/mod.rs7
-rw-r--r--src/codegen/attr_extractor.rs110
-rw-r--r--src/codegen/attrs_field.rs107
-rw-r--r--src/codegen/default_expr.rs53
-rw-r--r--src/codegen/error.rs46
-rw-r--r--src/codegen/field.rs267
-rw-r--r--src/codegen/from_attributes_impl.rs111
-rw-r--r--src/codegen/from_derive_impl.rs148
-rw-r--r--src/codegen/from_field.rs114
-rw-r--r--src/codegen/from_meta_impl.rs155
-rw-r--r--src/codegen/from_type_param.rs111
-rw-r--r--src/codegen/from_variant_impl.rs134
-rw-r--r--src/codegen/mod.rs32
-rw-r--r--src/codegen/outer_from_impl.rs61
-rw-r--r--src/codegen/postfix_transform.rs30
-rw-r--r--src/codegen/trait_impl.rs146
-rw-r--r--src/codegen/variant.rs173
-rw-r--r--src/codegen/variant_data.rs105
-rw-r--r--src/derive.rs61
-rw-r--r--src/error/child.rs82
-rw-r--r--src/error/kind.rs229
-rw-r--r--src/error/mod.rs1035
-rw-r--r--src/from_attributes.rs27
-rw-r--r--src/from_derive_input.rs26
-rw-r--r--src/from_field.rs38
-rw-r--r--src/from_generic_param.rs19
-rw-r--r--src/from_generics.rs27
-rw-r--r--src/from_meta.rs1085
-rw-r--r--src/from_type_param.rs32
-rw-r--r--src/from_variant.rs33
-rw-r--r--src/lib.rs42
-rw-r--r--src/macros_private.rs6
-rw-r--r--src/macros_public.rs96
-rw-r--r--src/options/core.rs198
-rw-r--r--src/options/forward_attrs.rs73
-rw-r--r--src/options/from_attributes.rs74
-rw-r--r--src/options/from_derive.rs97
-rw-r--r--src/options/from_field.rs76
-rw-r--r--src/options/from_meta.rs72
-rw-r--r--src/options/from_type_param.rs76
-rw-r--r--src/options/from_variant.rs82
-rw-r--r--src/options/input_field.rs241
-rw-r--r--src/options/input_variant.rs126
-rw-r--r--src/options/mod.rs158
-rw-r--r--src/options/outer_from.rs95
-rw-r--r--src/options/shape.rs259
-rw-r--r--src/usage/generics_ext.rs24
-rw-r--r--src/usage/ident_set.rs8
-rw-r--r--src/usage/lifetimes.rs351
-rw-r--r--src/usage/mod.rs111
-rw-r--r--src/usage/options.rs58
-rw-r--r--src/usage/type_params.rs364
-rw-r--r--src/util/flag.rs99
-rw-r--r--src/util/ident_string.rs157
-rw-r--r--src/util/ignored.rs50
-rw-r--r--src/util/mod.rs24
-rw-r--r--src/util/over_ride.rs151
-rw-r--r--src/util/parse_attribute.rs86
-rw-r--r--src/util/parse_expr.rs86
-rw-r--r--src/util/path_list.rs105
-rw-r--r--src/util/path_to_string.rs36
-rw-r--r--src/util/shape.rs285
-rw-r--r--src/util/spanned_value.rs127
-rw-r--r--src/util/with_original.rs35
75 files changed, 9436 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..5274e79
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "25ce6ecc079c58a18cbecbdcc98bf8d647410edd"
+ },
+ "path_in_vcs": "core"
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..17ce192
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,20 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+// TODO: Add license.
+rust_library_host {
+ name: "libdarling_core",
+ crate_name: "darling_core",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["src/lib.rs"],
+ edition: "2018",
+ rustlibs: [
+ "libfnv",
+ "libident_case",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ compile_multilib: "first",
+}
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..0dcab58
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,68 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "darling_core"
+version = "0.20.8"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2ad8f1d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,49 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "darling_core"
+version = "0.20.8"
+authors = ["Ted Driggs <ted.driggs@outlook.com>"]
+description = """
+Helper crate for proc-macro library for reading attributes into structs when
+implementing custom derives. Use https://crates.io/crates/darling in your code.
+"""
+license = "MIT"
+repository = "https://github.com/TedDriggs/darling"
+
+[dependencies.fnv]
+version = "1.0.7"
+
+[dependencies.ident_case]
+version = "1.0.1"
+
+[dependencies.proc-macro2]
+version = "1.0.37"
+
+[dependencies.quote]
+version = "1.0.18"
+
+[dependencies.strsim]
+version = "0.10.0"
+optional = true
+
+[dependencies.syn]
+version = "2.0.15"
+features = [
+ "full",
+ "extra-traits",
+]
+
+[features]
+diagnostics = []
+suggestions = ["strsim"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0b48ead
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Ted Driggs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..a2848b6
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "darling_core"
+description: "()"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "darling_core"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/darling_core/darling_core-0.20.8.crate"
+ primary_source: true
+ }
+ version: "0.20.8"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2024
+ month: 3
+ day: 6
+ }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..48bea6e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..f47e9e6
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,9 @@
+{
+ "run_cargo": false,
+ "package": {
+ "darling_core": {
+ "device_supported": false,
+ "host_first_multilib": true
+ }
+ }
+}
diff --git a/src/ast/data.rs b/src/ast/data.rs
new file mode 100644
index 0000000..4b91595
--- /dev/null
+++ b/src/ast/data.rs
@@ -0,0 +1,517 @@
+use std::{slice, vec};
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens};
+use syn::ext::IdentExt;
+use syn::parse::Parser;
+use syn::spanned::Spanned;
+use syn::Token;
+
+use crate::usage::{
+ self, IdentRefSet, IdentSet, LifetimeRefSet, LifetimeSet, UsesLifetimes, UsesTypeParams,
+};
+use crate::{Error, FromField, FromVariant, Result};
+
+/// A struct or enum body.
+///
+/// `V` is the type which receives any encountered variants, and `F` receives struct fields.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Data<V, F> {
+ Enum(Vec<V>),
+ Struct(Fields<F>),
+}
+
+impl<V, F> Data<V, F> {
+ /// Creates an empty body of the same shape as the passed-in body.
+ ///
+ /// # Panics
+ /// This function will panic if passed `syn::Data::Union`.
+ pub fn empty_from(src: &syn::Data) -> Self {
+ match *src {
+ syn::Data::Enum(_) => Data::Enum(vec![]),
+ syn::Data::Struct(ref vd) => Data::Struct(Fields::empty_from(&vd.fields)),
+ syn::Data::Union(_) => panic!("Unions are not supported"),
+ }
+ }
+
+ /// Creates an empty body of the same shape as the passed-in body.
+ ///
+ /// `darling` does not support unions; calling this function with a union body will return an error.
+ pub fn try_empty_from(src: &syn::Data) -> Result<Self> {
+ match *src {
+ syn::Data::Enum(_) => Ok(Data::Enum(vec![])),
+ syn::Data::Struct(ref vd) => Ok(Data::Struct(Fields::empty_from(&vd.fields))),
+ // This deliberately doesn't set a span on the error message, as the error is most useful if
+ // applied to the call site of the offending macro. Given that the message is very generic,
+ // putting it on the union keyword ends up being confusing.
+ syn::Data::Union(_) => Err(Error::custom("Unions are not supported")),
+ }
+ }
+
+ /// Creates a new `Data<&'a V, &'a F>` instance from `Data<V, F>`.
+ pub fn as_ref(&self) -> Data<&V, &F> {
+ match *self {
+ Data::Enum(ref variants) => Data::Enum(variants.iter().collect()),
+ Data::Struct(ref data) => Data::Struct(data.as_ref()),
+ }
+ }
+
+ /// Applies a function `V -> U` on enum variants, if this is an enum.
+ pub fn map_enum_variants<T, U>(self, map: T) -> Data<U, F>
+ where
+ T: FnMut(V) -> U,
+ {
+ match self {
+ Data::Enum(v) => Data::Enum(v.into_iter().map(map).collect()),
+ Data::Struct(f) => Data::Struct(f),
+ }
+ }
+
+ /// Applies a function `F -> U` on struct fields, if this is a struct.
+ pub fn map_struct_fields<T, U>(self, map: T) -> Data<V, U>
+ where
+ T: FnMut(F) -> U,
+ {
+ match self {
+ Data::Enum(v) => Data::Enum(v),
+ Data::Struct(f) => Data::Struct(f.map(map)),
+ }
+ }
+
+ /// Applies a function to the `Fields` if this is a struct.
+ pub fn map_struct<T, U>(self, mut map: T) -> Data<V, U>
+ where
+ T: FnMut(Fields<F>) -> Fields<U>,
+ {
+ match self {
+ Data::Enum(v) => Data::Enum(v),
+ Data::Struct(f) => Data::Struct(map(f)),
+ }
+ }
+
+ /// Consumes the `Data`, returning `Fields<F>` if it was a struct.
+ pub fn take_struct(self) -> Option<Fields<F>> {
+ match self {
+ Data::Enum(_) => None,
+ Data::Struct(f) => Some(f),
+ }
+ }
+
+ /// Consumes the `Data`, returning `Vec<V>` if it was an enum.
+ pub fn take_enum(self) -> Option<Vec<V>> {
+ match self {
+ Data::Enum(v) => Some(v),
+ Data::Struct(_) => None,
+ }
+ }
+
+ /// Returns `true` if this instance is `Data::Enum`.
+ pub fn is_enum(&self) -> bool {
+ match *self {
+ Data::Enum(_) => true,
+ Data::Struct(_) => false,
+ }
+ }
+
+ /// Returns `true` if this instance is `Data::Struct`.
+ pub fn is_struct(&self) -> bool {
+ !self.is_enum()
+ }
+}
+
+impl<V: FromVariant, F: FromField> Data<V, F> {
+ /// Attempt to convert from a `syn::Data` instance.
+ pub fn try_from(body: &syn::Data) -> Result<Self> {
+ match *body {
+ syn::Data::Enum(ref data) => {
+ let mut errors = Error::accumulator();
+ let items = data
+ .variants
+ .iter()
+ .filter_map(|v| errors.handle(FromVariant::from_variant(v)))
+ .collect();
+
+ errors.finish_with(Data::Enum(items))
+ }
+ syn::Data::Struct(ref data) => Ok(Data::Struct(Fields::try_from(&data.fields)?)),
+ // This deliberately doesn't set a span on the error message, as the error is most useful if
+ // applied to the call site of the offending macro. Given that the message is very generic,
+ // putting it on the union keyword ends up being confusing.
+ syn::Data::Union(_) => Err(Error::custom("Unions are not supported")),
+ }
+ }
+}
+
+impl<V: UsesTypeParams, F: UsesTypeParams> UsesTypeParams for Data<V, F> {
+ fn uses_type_params<'a>(
+ &self,
+ options: &usage::Options,
+ type_set: &'a IdentSet,
+ ) -> IdentRefSet<'a> {
+ match *self {
+ Data::Struct(ref v) => v.uses_type_params(options, type_set),
+ Data::Enum(ref v) => v.uses_type_params(options, type_set),
+ }
+ }
+}
+
+impl<V: UsesLifetimes, F: UsesLifetimes> UsesLifetimes for Data<V, F> {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &usage::Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
+ Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
+ }
+ }
+}
+
+/// Equivalent to `syn::Fields`, but replaces the AST element with a generic.
+#[derive(Debug, Clone)]
+pub struct Fields<T> {
+ pub style: Style,
+ pub fields: Vec<T>,
+ span: Option<Span>,
+ __nonexhaustive: (),
+}
+
+impl<T> Fields<T> {
+ /// Creates a new [`Fields`] struct.
+ pub fn new(style: Style, fields: Vec<T>) -> Self {
+ Self {
+ style,
+ fields,
+ span: None,
+ __nonexhaustive: (),
+ }
+ }
+
+ /// Adds a [`Span`] to [`Fields`].
+ pub fn with_span(mut self, span: Span) -> Self {
+ if self.span.is_none() {
+ self.span = Some(span);
+ }
+ self
+ }
+
+ pub fn empty_from(vd: &syn::Fields) -> Self {
+ Self::new(vd.into(), Vec::new())
+ }
+
+ /// Splits the `Fields` into its style and fields for further processing.
+ /// Returns an empty `Vec` for `Unit` data.
+ pub fn split(self) -> (Style, Vec<T>) {
+ (self.style, self.fields)
+ }
+
+ /// Returns true if this variant's data makes it a newtype.
+ pub fn is_newtype(&self) -> bool {
+ self.style == Style::Tuple && self.len() == 1
+ }
+
+ pub fn is_unit(&self) -> bool {
+ self.style.is_unit()
+ }
+
+ pub fn is_tuple(&self) -> bool {
+ self.style.is_tuple()
+ }
+
+ pub fn is_struct(&self) -> bool {
+ self.style.is_struct()
+ }
+
+ pub fn as_ref(&self) -> Fields<&T> {
+ Fields {
+ style: self.style,
+ fields: self.fields.iter().collect(),
+ span: self.span,
+ __nonexhaustive: (),
+ }
+ }
+
+ pub fn map<F, U>(self, map: F) -> Fields<U>
+ where
+ F: FnMut(T) -> U,
+ {
+ Fields {
+ style: self.style,
+ fields: self.fields.into_iter().map(map).collect(),
+ span: self.span,
+ __nonexhaustive: (),
+ }
+ }
+
+ pub fn iter(&self) -> slice::Iter<T> {
+ self.fields.iter()
+ }
+
+ /// Returns the number of fields in the structure.
+ pub fn len(&self) -> usize {
+ self.fields.len()
+ }
+
+ /// Returns `true` if the `Fields` contains no fields.
+ pub fn is_empty(&self) -> bool {
+ self.fields.is_empty()
+ }
+}
+
+impl<F: FromField> Fields<F> {
+ pub fn try_from(fields: &syn::Fields) -> Result<Self> {
+ let mut errors = Error::accumulator();
+ let items = {
+ match &fields {
+ syn::Fields::Named(fields) => fields
+ .named
+ .iter()
+ .filter_map(|field| {
+ errors.handle(FromField::from_field(field).map_err(|err| {
+ // There should always be an ident here, since this is a collection
+ // of named fields, but `syn` doesn't prevent someone from manually
+ // constructing an invalid collection so a guard is still warranted.
+ if let Some(ident) = &field.ident {
+ err.at(ident)
+ } else {
+ err
+ }
+ }))
+ })
+ .collect(),
+ syn::Fields::Unnamed(fields) => fields
+ .unnamed
+ .iter()
+ .filter_map(|field| errors.handle(FromField::from_field(field)))
+ .collect(),
+ syn::Fields::Unit => vec![],
+ }
+ };
+
+ errors.finish()?;
+
+ Ok(Self::new(fields.into(), items).with_span(fields.span()))
+ }
+}
+
+impl<T: ToTokens> ToTokens for Fields<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let fields = &self.fields;
+ // An unknown Span should be `Span::call_site()`;
+ // https://docs.rs/syn/1.0.12/syn/spanned/trait.Spanned.html#tymethod.span
+ let span = self.span.unwrap_or_else(Span::call_site);
+
+ match self.style {
+ Style::Struct => {
+ let trailing_comma = {
+ if fields.is_empty() {
+ quote!()
+ } else {
+ quote!(,)
+ }
+ };
+
+ tokens.extend(quote_spanned![span => { #(#fields),* #trailing_comma }]);
+ }
+ Style::Tuple => {
+ tokens.extend(quote_spanned![span => ( #(#fields),* )]);
+ }
+ Style::Unit => {}
+ }
+ }
+}
+
+impl<T: PartialEq> PartialEq for Fields<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.style == other.style && self.fields == other.fields
+ }
+}
+
+impl<T: Eq> Eq for Fields<T> {}
+
+impl<T> IntoIterator for Fields<T> {
+ type Item = T;
+ type IntoIter = vec::IntoIter<T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.fields.into_iter()
+ }
+}
+
+impl<T> From<Style> for Fields<T> {
+ fn from(style: Style) -> Self {
+ Self::new(style, Vec::new())
+ }
+}
+
+impl<T, U: Into<Vec<T>>> From<(Style, U)> for Fields<T> {
+ fn from((style, fields): (Style, U)) -> Self {
+ style.with_fields(fields)
+ }
+}
+
+impl<T: UsesTypeParams> UsesTypeParams for Fields<T> {
+ fn uses_type_params<'a>(
+ &self,
+ options: &usage::Options,
+ type_set: &'a IdentSet,
+ ) -> IdentRefSet<'a> {
+ self.fields.uses_type_params(options, type_set)
+ }
+}
+
+impl<T: UsesLifetimes> UsesLifetimes for Fields<T> {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &usage::Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.fields.uses_lifetimes(options, lifetimes)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Style {
+ Tuple,
+ Struct,
+ Unit,
+}
+
+impl Style {
+ pub fn is_unit(self) -> bool {
+ self == Style::Unit
+ }
+
+ pub fn is_tuple(self) -> bool {
+ self == Style::Tuple
+ }
+
+ pub fn is_struct(self) -> bool {
+ self == Style::Struct
+ }
+
+ /// Creates a new `Fields` of the specified style with the passed-in fields.
+ fn with_fields<T, U: Into<Vec<T>>>(self, fields: U) -> Fields<T> {
+ Fields::new(self, fields.into())
+ }
+}
+
+impl From<syn::Fields> for Style {
+ fn from(vd: syn::Fields) -> Self {
+ (&vd).into()
+ }
+}
+
+impl<'a> From<&'a syn::Fields> for Style {
+ fn from(vd: &syn::Fields) -> Self {
+ match *vd {
+ syn::Fields::Named(_) => Style::Struct,
+ syn::Fields::Unnamed(_) => Style::Tuple,
+ syn::Fields::Unit => Style::Unit,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum NestedMeta {
+ Meta(syn::Meta),
+ Lit(syn::Lit),
+}
+
+impl NestedMeta {
+ pub fn parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>> {
+ syn::punctuated::Punctuated::<NestedMeta, Token![,]>::parse_terminated
+ .parse2(tokens)
+ .map(|punctuated| punctuated.into_iter().collect())
+ }
+}
+
+impl syn::parse::Parse for NestedMeta {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(Token![=])) {
+ input.parse().map(NestedMeta::Lit)
+ } else if input.peek(syn::Ident::peek_any)
+ || input.peek(Token![::]) && input.peek3(syn::Ident::peek_any)
+ {
+ input.parse().map(NestedMeta::Meta)
+ } else {
+ Err(input.error("expected identifier or literal"))
+ }
+ }
+}
+
+impl ToTokens for NestedMeta {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ NestedMeta::Meta(meta) => meta.to_tokens(tokens),
+ NestedMeta::Lit(lit) => lit.to_tokens(tokens),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // it is not possible to directly convert a TokenStream into syn::Fields, so you have
+ // to convert the TokenStream into DeriveInput first and then pass the syn::Fields to
+ // Fields::try_from.
+ fn token_stream_to_fields(input: TokenStream) -> Fields<syn::Field> {
+ Fields::try_from(&{
+ if let syn::Data::Struct(s) = syn::parse2::<syn::DeriveInput>(input).unwrap().data {
+ s.fields
+ } else {
+ panic!();
+ }
+ })
+ .unwrap()
+ }
+
+ #[test]
+ fn test_style_eq() {
+ // `Fields` implements `Eq` manually, so it has to be ensured, that all fields of `Fields`
+ // implement `Eq`, this test would fail, if someone accidentally removed the Eq
+ // implementation from `Style`.
+ struct _AssertEq
+ where
+ Style: Eq;
+ }
+
+ #[test]
+ fn test_fields_to_tokens_struct() {
+ let reference = quote!(
+ {
+ executable: String,
+ args: Vec<String>,
+ env: Vec<String>,
+ index: usize,
+ optional: Option<String>,
+ current_dir: String,
+ }
+ );
+ let input = quote!(
+ struct ExampleTest #reference
+ );
+
+ let fields = token_stream_to_fields(input);
+
+ let mut result = quote!();
+ fields.to_tokens(&mut result);
+ assert_eq!(result.to_string(), reference.to_string());
+ }
+
+ #[test]
+ fn test_fields_to_tokens_tuple() {
+ let reference = quote!((u64, usize, &'a T));
+ let input = quote!(
+ struct ExampleTest #reference;
+ );
+
+ let fields = token_stream_to_fields(input);
+
+ let mut result = quote!();
+ fields.to_tokens(&mut result);
+ assert_eq!(result.to_string(), reference.to_string());
+ }
+}
diff --git a/src/ast/generics.rs b/src/ast/generics.rs
new file mode 100644
index 0000000..d4bf0c9
--- /dev/null
+++ b/src/ast/generics.rs
@@ -0,0 +1,192 @@
+//! Types for working with generics
+
+use std::iter::Iterator;
+use std::slice::Iter;
+
+use crate::{FromGenericParam, FromGenerics, FromTypeParam, Result};
+
+/// Extension trait for `GenericParam` to support getting values by variant.
+///
+/// # Usage
+/// `darling::ast::Generics` needs a way to test its params array in order to iterate over type params.
+/// Rather than require callers to use `darling::ast::GenericParam` in all cases, this trait makes that
+/// polymorphic.
+pub trait GenericParamExt {
+ /// The type this GenericParam uses to represent type params and their bounds
+ type TypeParam;
+ type LifetimeParam;
+ type ConstParam;
+
+ /// If this GenericParam is a type param, get the underlying value.
+ fn as_type_param(&self) -> Option<&Self::TypeParam> {
+ None
+ }
+
+ /// If this GenericParam is a lifetime, get the underlying value.
+ fn as_lifetime_param(&self) -> Option<&Self::LifetimeParam> {
+ None
+ }
+
+ /// If this GenericParam is a const param, get the underlying value.
+ fn as_const_param(&self) -> Option<&Self::ConstParam> {
+ None
+ }
+}
+
+impl GenericParamExt for syn::GenericParam {
+ type TypeParam = syn::TypeParam;
+ type LifetimeParam = syn::LifetimeParam;
+ type ConstParam = syn::ConstParam;
+
+ fn as_type_param(&self) -> Option<&Self::TypeParam> {
+ if let syn::GenericParam::Type(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+
+ fn as_lifetime_param(&self) -> Option<&Self::LifetimeParam> {
+ if let syn::GenericParam::Lifetime(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+
+ fn as_const_param(&self) -> Option<&Self::ConstParam> {
+ if let syn::GenericParam::Const(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+}
+
+impl GenericParamExt for syn::TypeParam {
+ type TypeParam = syn::TypeParam;
+ type LifetimeParam = ();
+ type ConstParam = ();
+
+ fn as_type_param(&self) -> Option<&Self::TypeParam> {
+ Some(self)
+ }
+}
+
+/// A mirror of `syn::GenericParam` which is generic over all its contents.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum GenericParam<T = syn::TypeParam, L = syn::LifetimeParam, C = syn::ConstParam> {
+ Type(T),
+ Lifetime(L),
+ Const(C),
+}
+
+impl<T: FromTypeParam> FromTypeParam for GenericParam<T> {
+ fn from_type_param(type_param: &syn::TypeParam) -> Result<Self> {
+ Ok(GenericParam::Type(FromTypeParam::from_type_param(
+ type_param,
+ )?))
+ }
+}
+
+impl<T: FromTypeParam> FromGenericParam for GenericParam<T> {
+ fn from_generic_param(param: &syn::GenericParam) -> Result<Self> {
+ Ok(match *param {
+ syn::GenericParam::Type(ref ty) => {
+ GenericParam::Type(FromTypeParam::from_type_param(ty)?)
+ }
+ syn::GenericParam::Lifetime(ref val) => GenericParam::Lifetime(val.clone()),
+ syn::GenericParam::Const(ref val) => GenericParam::Const(val.clone()),
+ })
+ }
+}
+
+impl<T, L, C> GenericParamExt for GenericParam<T, L, C> {
+ type TypeParam = T;
+ type LifetimeParam = L;
+ type ConstParam = C;
+
+ fn as_type_param(&self) -> Option<&T> {
+ if let GenericParam::Type(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+
+ fn as_lifetime_param(&self) -> Option<&L> {
+ if let GenericParam::Lifetime(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+
+ fn as_const_param(&self) -> Option<&C> {
+ if let GenericParam::Const(ref val) = *self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+}
+
+/// A mirror of the `syn::Generics` type which can contain arbitrary representations
+/// of params and where clauses.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Generics<P, W = syn::WhereClause> {
+ pub params: Vec<P>,
+ pub where_clause: Option<W>,
+}
+
+impl<P, W> Generics<P, W> {
+ pub fn type_params(&self) -> TypeParams<'_, P> {
+ TypeParams(self.params.iter())
+ }
+}
+
+impl<P: FromGenericParam> FromGenerics for Generics<P> {
+ fn from_generics(generics: &syn::Generics) -> Result<Self> {
+ Ok(Generics {
+ params: generics
+ .params
+ .iter()
+ .map(FromGenericParam::from_generic_param)
+ .collect::<Result<Vec<P>>>()?,
+ where_clause: generics.where_clause.clone(),
+ })
+ }
+}
+
+pub struct TypeParams<'a, P: 'a>(Iter<'a, P>);
+
+impl<'a, P: GenericParamExt> Iterator for TypeParams<'a, P> {
+ type Item = &'a <P as GenericParamExt>::TypeParam;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let next = self.0.next();
+ match next {
+ None => None,
+ Some(v) => match v.as_type_param() {
+ Some(val) => Some(val),
+ None => self.next(),
+ },
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::{GenericParam, Generics};
+ use crate::FromGenerics;
+
+ #[test]
+ fn generics() {
+ let g: syn::Generics = parse_quote!(<T>);
+ let deified: Generics<GenericParam<syn::Ident>> = FromGenerics::from_generics(&g).unwrap();
+ assert!(deified.params.len() == 1);
+ assert!(deified.where_clause.is_none());
+ }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
new file mode 100644
index 0000000..492b26f
--- /dev/null
+++ b/src/ast/mod.rs
@@ -0,0 +1,7 @@
+//! Utility types for working with the AST.
+
+mod data;
+mod generics;
+
+pub use self::data::*;
+pub use self::generics::{GenericParam, GenericParamExt, Generics};
diff --git a/src/codegen/attr_extractor.rs b/src/codegen/attr_extractor.rs
new file mode 100644
index 0000000..612c28f
--- /dev/null
+++ b/src/codegen/attr_extractor.rs
@@ -0,0 +1,110 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+
+use crate::util::PathList;
+
+use super::ForwardAttrs;
+
+/// Infrastructure for generating an attribute extractor.
+pub trait ExtractAttribute {
+ /// A set of mutable declarations for all members of the implementing type.
+ fn local_declarations(&self) -> TokenStream;
+
+ /// Gets the list of attribute names that should be parsed by the extractor.
+ fn attr_names(&self) -> &PathList;
+
+ fn forward_attrs(&self) -> &ForwardAttrs;
+
+ /// Gets the name used by the generated impl to return to the `syn` item passed as input.
+ fn param_name(&self) -> TokenStream;
+
+ /// Get the tokens to access a borrowed list of attributes where extraction will take place.
+ ///
+ /// By default, this will be `&#input.attrs` where `#input` is `self.param_name()`.
+ fn attrs_accessor(&self) -> TokenStream {
+ let input = self.param_name();
+ quote!(&#input.attrs)
+ }
+
+ /// Gets the core from-meta-item loop that should be used on matching attributes.
+ fn core_loop(&self) -> TokenStream;
+
+ /// Generates the main extraction loop.
+ fn extractor(&self) -> TokenStream {
+ let mut declarations = self.local_declarations();
+ self.forward_attrs()
+ .as_declaration()
+ .to_tokens(&mut declarations);
+
+ let will_parse_any = !self.attr_names().is_empty();
+
+ // Forwarding requires both that there be some items we would forward,
+ // and a place that will keep the forwarded items.
+ let will_fwd_any = self.forward_attrs().will_forward_any();
+
+ if !(will_parse_any || will_fwd_any) {
+ return quote! {
+ #declarations
+ };
+ }
+
+ let attrs_accessor = self.attrs_accessor();
+
+ // The block for parsing attributes whose names have been claimed by the target
+ // struct. If no attributes were claimed, this is a pass-through.
+ let parse_handled = if will_parse_any {
+ let attr_names = self.attr_names().to_strings();
+ let core_loop = self.core_loop();
+ quote!(
+ #(#attr_names)|* => {
+ match ::darling::util::parse_attribute_to_meta_list(__attr) {
+ ::darling::export::Ok(__data) => {
+ match ::darling::export::NestedMeta::parse_meta_list(__data.tokens) {
+ ::darling::export::Ok(ref __items) => {
+ if __items.is_empty() {
+ continue;
+ }
+
+ #core_loop
+ }
+ ::darling::export::Err(__err) => {
+ __errors.push(__err.into());
+ }
+ }
+ }
+ // darling was asked to handle this attribute name, but the actual attribute
+ // isn't one that darling can work with. This either indicates a typing error
+ // or some misunderstanding of the meta attribute syntax; in either case, the
+ // caller should get a useful error.
+ ::darling::export::Err(__err) => {
+ __errors.push(__err);
+ }
+ }
+ }
+ )
+ } else {
+ quote!()
+ };
+
+ let fwd_population = self.forward_attrs().as_value_populator();
+
+ // Specifies the behavior for unhandled attributes. They will either be silently ignored or
+ // forwarded to the inner struct for later analysis.
+ let forward_unhandled = self.forward_attrs().as_match_arms();
+
+ quote!(
+ #declarations
+ use ::darling::ToTokens;
+
+ for __attr in #attrs_accessor {
+ // Filter attributes based on name
+ match ::darling::export::ToString::to_string(&__attr.path().clone().into_token_stream()).as_str() {
+ #parse_handled
+ #forward_unhandled
+ }
+ }
+
+ #fwd_population
+ )
+ }
+}
diff --git a/src/codegen/attrs_field.rs b/src/codegen/attrs_field.rs
new file mode 100644
index 0000000..bbd43f8
--- /dev/null
+++ b/src/codegen/attrs_field.rs
@@ -0,0 +1,107 @@
+use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
+use syn::spanned::Spanned;
+
+use crate::options::{AttrsField, ForwardAttrsFilter};
+
+#[derive(Default)]
+pub struct ForwardAttrs<'a> {
+ pub filter: Option<&'a ForwardAttrsFilter>,
+ pub field: Option<&'a AttrsField>,
+}
+
+impl ForwardAttrs<'_> {
+ /// Check if this will forward any attributes; this requires both that
+ /// there be a filter which can match some attributes and a field to receive them.
+ pub fn will_forward_any(&self) -> bool {
+ if let Some(filter) = self.filter {
+ !filter.is_empty() && self.field.is_some()
+ } else {
+ false
+ }
+ }
+
+ /// Get the field declarations to support attribute forwarding
+ pub fn as_declaration(&self) -> Option<Declaration> {
+ self.field.map(Declaration)
+ }
+
+ /// Get the match arms for attribute matching
+ pub fn as_match_arms(&self) -> MatchArms {
+ MatchArms(self)
+ }
+
+ /// Get the statement that will try to transform forwarded attributes into
+ /// the result expected by the receiver field.
+ pub fn as_value_populator(&self) -> Option<ValuePopulator> {
+ self.field.map(ValuePopulator)
+ }
+
+ /// Get the field initializer for use when building the deriving struct.
+ pub fn as_initializer(&self) -> Option<Initializer> {
+ self.field.map(Initializer)
+ }
+}
+
+pub struct Declaration<'a>(pub &'a AttrsField);
+
+impl ToTokens for Declaration<'_> {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ let ident = &self.0.ident;
+ tokens.append_all(quote! {
+ let mut __fwd_attrs: ::darling::export::Vec<::darling::export::syn::Attribute> = vec![];
+ let mut #ident: ::darling::export::Option<_> = None;
+ });
+ }
+}
+
+pub struct ValuePopulator<'a>(pub &'a AttrsField);
+
+impl ToTokens for ValuePopulator<'_> {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ let AttrsField { ident, with } = self.0;
+ let initializer_expr = match with {
+ Some(with) => quote_spanned!(with.span()=> __errors.handle(#with(__fwd_attrs))),
+ None => quote!(::darling::export::Some(__fwd_attrs)),
+ };
+ tokens.append_all(quote!(#ident = #initializer_expr;));
+ }
+}
+
+pub struct Initializer<'a>(pub &'a AttrsField);
+
+impl ToTokens for Initializer<'_> {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ let ident = &self.0.ident;
+ tokens.append_all(quote!(#ident: #ident.expect("Errors were already checked"),));
+ }
+}
+
+pub struct MatchArms<'a>(&'a ForwardAttrs<'a>);
+
+impl ToTokens for MatchArms<'_> {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ if !self.0.will_forward_any() {
+ tokens.append_all(quote!(_ => continue));
+ return;
+ }
+
+ let push_command = quote!(__fwd_attrs.push(__attr.clone()));
+
+ tokens.append_all(
+ match self
+ .0
+ .filter
+ .expect("Can only forward attributes if filter is defined")
+ {
+ ForwardAttrsFilter::All => quote!(_ => #push_command),
+ ForwardAttrsFilter::Only(idents) => {
+ let names = idents.to_strings();
+ quote! {
+ #(#names)|* => #push_command,
+ _ => continue,
+ }
+ }
+ },
+ );
+ }
+}
diff --git a/src/codegen/default_expr.rs b/src/codegen/default_expr.rs
new file mode 100644
index 0000000..fa8b159
--- /dev/null
+++ b/src/codegen/default_expr.rs
@@ -0,0 +1,53 @@
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
+use syn::{spanned::Spanned, Ident, Path};
+
+/// This will be in scope during struct initialization after option parsing.
+const DEFAULT_STRUCT_NAME: &str = "__default";
+
+/// The fallback value for a field or container.
+#[derive(Debug, Clone)]
+pub enum DefaultExpression<'a> {
+ /// Only valid on fields, `Inherit` indicates that the value should be taken from a pre-constructed
+ /// fallback object. The value in the variant is the ident of the field.
+ Inherit(&'a Ident),
+ Explicit(&'a Path),
+ Trait {
+ span: Span,
+ },
+}
+
+impl<'a> DefaultExpression<'a> {
+ pub fn as_declaration(&'a self) -> DefaultDeclaration<'a> {
+ DefaultDeclaration(self)
+ }
+}
+
+impl<'a> ToTokens for DefaultExpression<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append_all(match *self {
+ DefaultExpression::Inherit(ident) => {
+ let dsn = Ident::new(DEFAULT_STRUCT_NAME, ::proc_macro2::Span::call_site());
+ quote!(#dsn.#ident)
+ }
+ DefaultExpression::Explicit(path) => {
+ // Use quote_spanned to properly set the span of the parentheses
+ quote_spanned!(path.span()=>#path())
+ }
+ DefaultExpression::Trait { span } => {
+ quote_spanned!(span=> ::darling::export::Default::default())
+ }
+ });
+ }
+}
+
+/// Used only by containers, this wrapper type generates code to declare the fallback instance.
+pub struct DefaultDeclaration<'a>(&'a DefaultExpression<'a>);
+
+impl<'a> ToTokens for DefaultDeclaration<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let name = Ident::new(DEFAULT_STRUCT_NAME, ::proc_macro2::Span::call_site());
+ let expr = self.0;
+ tokens.append_all(quote!(let #name: Self = #expr;));
+ }
+}
diff --git a/src/codegen/error.rs b/src/codegen/error.rs
new file mode 100644
index 0000000..f93a740
--- /dev/null
+++ b/src/codegen/error.rs
@@ -0,0 +1,46 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+
+/// Declares the local variable into which errors will be accumulated.
+#[derive(Default)]
+pub struct ErrorDeclaration {
+ __hidden: (),
+}
+
+impl ToTokens for ErrorDeclaration {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append_all(quote! {
+ let mut __errors = ::darling::Error::accumulator();
+ })
+ }
+}
+
+/// Returns early if attribute or body parsing has caused any errors.
+#[derive(Default)]
+pub struct ErrorCheck<'a> {
+ location: Option<&'a str>,
+ __hidden: (),
+}
+
+impl<'a> ErrorCheck<'a> {
+ pub fn with_location(location: &'a str) -> Self {
+ ErrorCheck {
+ location: Some(location),
+ __hidden: (),
+ }
+ }
+}
+
+impl<'a> ToTokens for ErrorCheck<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let at_call = if let Some(ref s) = self.location {
+ quote!(.map_err(|e| e.at(#s)))
+ } else {
+ quote!()
+ };
+
+ tokens.append_all(quote! {
+ __errors.finish() #at_call?;
+ })
+ }
+}
diff --git a/src/codegen/field.rs b/src/codegen/field.rs
new file mode 100644
index 0000000..51020a9
--- /dev/null
+++ b/src/codegen/field.rs
@@ -0,0 +1,267 @@
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
+use syn::{spanned::Spanned, Ident, Path, Type};
+
+use crate::codegen::{DefaultExpression, PostfixTransform};
+use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
+
+/// Properties needed to generate code for a field in all the contexts
+/// where one may appear.
+#[derive(Debug, Clone)]
+pub struct Field<'a> {
+ /// The name presented to the user of the library. This will appear
+ /// in error messages and will be looked when parsing names.
+ pub name_in_attr: Cow<'a, String>,
+
+ /// The name presented to the author of the library. This will appear
+ /// in the setters or temporary variables which contain the values.
+ pub ident: &'a Ident,
+
+ /// The type of the field in the input.
+ pub ty: &'a Type,
+ pub default_expression: Option<DefaultExpression<'a>>,
+ pub with_path: Cow<'a, Path>,
+ pub post_transform: Option<&'a PostfixTransform>,
+ pub skip: bool,
+ pub multiple: bool,
+ /// If set, this field will be given all unclaimed meta items and will
+ /// not be exposed as a standard named field.
+ pub flatten: bool,
+}
+
+impl<'a> Field<'a> {
+ /// Get the name of the meta item that should be matched against input and should be used in diagnostics.
+ ///
+ /// This will be `None` if the field is `skip` or `flatten`, as neither kind of field is addressable
+ /// by name from the input meta.
+ pub fn as_name(&'a self) -> Option<&'a str> {
+ if self.skip || self.flatten {
+ None
+ } else {
+ Some(&self.name_in_attr)
+ }
+ }
+
+ pub fn as_declaration(&'a self) -> Declaration<'a> {
+ Declaration(self)
+ }
+
+ pub fn as_flatten_initializer(
+ &'a self,
+ parent_field_names: Vec<&'a str>,
+ ) -> FlattenInitializer<'a> {
+ FlattenInitializer {
+ field: self,
+ parent_field_names,
+ }
+ }
+
+ pub fn as_match(&'a self) -> MatchArm<'a> {
+ MatchArm(self)
+ }
+
+ pub fn as_initializer(&'a self) -> Initializer<'a> {
+ Initializer(self)
+ }
+
+ pub fn as_presence_check(&'a self) -> CheckMissing<'a> {
+ CheckMissing(self)
+ }
+}
+
+impl<'a> UsesTypeParams for Field<'a> {
+ fn uses_type_params<'b>(
+ &self,
+ options: &usage::Options,
+ type_set: &'b IdentSet,
+ ) -> IdentRefSet<'b> {
+ self.ty.uses_type_params(options, type_set)
+ }
+}
+
+/// An individual field during variable declaration in the generated parsing method.
+pub struct Declaration<'a>(&'a Field<'a>);
+
+impl<'a> ToTokens for Declaration<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let field: &Field = self.0;
+ let ident = field.ident;
+ let ty = field.ty;
+
+ tokens.append_all(if field.multiple {
+ // This is NOT mutable, as it will be declared mutable only temporarily.
+ quote!(let mut #ident: #ty = ::darling::export::Default::default();)
+ } else {
+ quote!(let mut #ident: (bool, ::darling::export::Option<#ty>) = (false, None);)
+ });
+
+ // The flatten field additionally needs a place to buffer meta items
+ // until attribute walking is done, so declare that now.
+ //
+ // We expect there can only be one field marked `flatten`, so it shouldn't
+ // be possible for this to shadow another declaration.
+ if field.flatten {
+ tokens.append_all(quote! {
+ let mut __flatten: Vec<::darling::ast::NestedMeta> = vec![];
+ });
+ }
+ }
+}
+
+pub struct FlattenInitializer<'a> {
+ field: &'a Field<'a>,
+ parent_field_names: Vec<&'a str>,
+}
+
+impl<'a> ToTokens for FlattenInitializer<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Self {
+ field,
+ parent_field_names,
+ } = self;
+ let ident = field.ident;
+
+ let add_parent_fields = if parent_field_names.is_empty() {
+ None
+ } else {
+ Some(quote! {
+ .map_err(|e| e.add_sibling_alts_for_unknown_field(&[#(#parent_field_names),*]))
+ })
+ };
+
+ tokens.append_all(quote! {
+ #ident = (true,
+ __errors.handle(
+ ::darling::FromMeta::from_list(&__flatten) #add_parent_fields
+ )
+ );
+ });
+ }
+}
+
+/// Represents an individual field in the match.
+pub struct MatchArm<'a>(&'a Field<'a>);
+
+impl<'a> ToTokens for MatchArm<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let field: &Field = self.0;
+
+ // Skipped and flattened fields cannot be populated by a meta
+ // with their name, so they do not have a match arm.
+ if field.skip || field.flatten {
+ return;
+ }
+
+ let name_str = &field.name_in_attr;
+ let ident = field.ident;
+ let with_path = &field.with_path;
+ let post_transform = field.post_transform.as_ref();
+
+ // Errors include the location of the bad input, so we compute that here.
+ // Fields that take multiple values add the index of the error for convenience,
+ // while single-value fields only expose the name in the input attribute.
+ let location = if field.multiple {
+ // we use the local variable `len` here because location is accessed via
+ // a closure, and the borrow checker gets very unhappy if we try to immutably
+ // borrow `#ident` in that closure when it was declared `mut` outside.
+ quote!(&format!("{}[{}]", #name_str, __len))
+ } else {
+ quote!(#name_str)
+ };
+
+ // Give darling's generated code the span of the `with_path` so that if the target
+ // type doesn't impl FromMeta, darling's immediate user gets a properly-spanned error.
+ //
+ // Within the generated code, add the span immediately on extraction failure, so that it's
+ // as specific as possible.
+ // The behavior of `with_span` makes this safe to do; if the child applied an
+ // even-more-specific span, our attempt here will not overwrite that and will only cost
+ // us one `if` check.
+ let extractor = quote_spanned!(with_path.span()=>#with_path(__inner)#post_transform.map_err(|e| e.with_span(&__inner).at(#location)));
+
+ tokens.append_all(if field.multiple {
+ quote!(
+ #name_str => {
+ // Store the index of the name we're assessing in case we need
+ // it for error reporting.
+ let __len = #ident.len();
+ if let ::darling::export::Some(__val) = __errors.handle(#extractor) {
+ #ident.push(__val)
+ }
+ }
+ )
+ } else {
+ quote!(
+ #name_str => {
+ if !#ident.0 {
+ #ident = (true, __errors.handle(#extractor));
+ } else {
+ __errors.push(::darling::Error::duplicate_field(#name_str).with_span(&__inner));
+ }
+ }
+ )
+ });
+ }
+}
+
+/// Wrapper to generate initialization code for a field.
+pub struct Initializer<'a>(&'a Field<'a>);
+
+impl<'a> ToTokens for Initializer<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let field: &Field = self.0;
+ let ident = field.ident;
+ tokens.append_all(if field.multiple {
+ if let Some(ref expr) = field.default_expression {
+ quote_spanned!(expr.span()=> #ident: if !#ident.is_empty() {
+ #ident
+ } else {
+ #expr
+ })
+ } else {
+ quote!(#ident: #ident)
+ }
+ } else if let Some(ref expr) = field.default_expression {
+ quote_spanned!(expr.span()=> #ident: if let Some(__val) = #ident.1 {
+ __val
+ } else {
+ #expr
+ })
+ } else {
+ quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked"))
+ });
+ }
+}
+
+/// Creates an error if a field has no value and no default.
+pub struct CheckMissing<'a>(&'a Field<'a>);
+
+impl<'a> ToTokens for CheckMissing<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if !self.0.multiple && self.0.default_expression.is_none() {
+ let ident = self.0.ident;
+ let ty = self.0.ty;
+ let name_in_attr = &self.0.name_in_attr;
+
+ // If `ty` does not impl FromMeta, the compiler error should point
+ // at the offending type rather than at the derive-macro call site.
+ let from_none_call =
+ quote_spanned!(ty.span()=> <#ty as ::darling::FromMeta>::from_none());
+
+ tokens.append_all(quote! {
+ if !#ident.0 {
+ match #from_none_call {
+ ::darling::export::Some(__type_fallback) => {
+ #ident.1 = ::darling::export::Some(__type_fallback);
+ }
+ ::darling::export::None => {
+ __errors.push(::darling::Error::missing_field(#name_in_attr))
+ }
+ }
+ }
+ })
+ }
+ }
+}
diff --git a/src/codegen/from_attributes_impl.rs b/src/codegen/from_attributes_impl.rs
new file mode 100644
index 0000000..72da349
--- /dev/null
+++ b/src/codegen/from_attributes_impl.rs
@@ -0,0 +1,111 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+
+use crate::{
+ ast::Data,
+ codegen::{ExtractAttribute, OuterFromImpl, TraitImpl},
+ util::PathList,
+};
+
+use super::ForwardAttrs;
+
+pub struct FromAttributesImpl<'a> {
+ pub base: TraitImpl<'a>,
+ pub attr_names: &'a PathList,
+ pub forward_attrs: ForwardAttrs<'a>,
+}
+
+impl ToTokens for FromAttributesImpl<'_> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let ty_ident = self.base.ident;
+ let input = self.param_name();
+ let post_transform = self.base.post_transform_call();
+
+ if let Data::Struct(ref data) = self.base.data {
+ if data.is_newtype() {
+ self.wrap(
+ quote! {
+ fn from_attributes(#input: &[::darling::export::syn::Attribute]) -> ::darling::Result<Self> {
+ ::darling::export::Ok(
+ #ty_ident(::darling::FromAttributes::from_attributes(#input)?)
+ ) #post_transform
+ }
+ },
+ tokens,
+ );
+
+ return;
+ }
+ }
+
+ let inits = self.base.initializers();
+ let default = self.base.fallback_decl();
+
+ let grab_attrs = self.extractor();
+
+ let declare_errors = self.base.declare_errors();
+ let require_fields = self.base.require_fields();
+ let check_errors = self.base.check_errors();
+
+ self.wrap(
+ quote! {
+ fn from_attributes(#input: &[::darling::export::syn::Attribute]) -> ::darling::Result<Self> {
+ #declare_errors
+
+ #grab_attrs
+
+ #require_fields
+
+ #check_errors
+
+ #default
+
+ ::darling::export::Ok(#ty_ident {
+ #inits
+ }) #post_transform
+ }
+ },
+ tokens,
+ );
+ }
+}
+
+impl<'a> ExtractAttribute for FromAttributesImpl<'a> {
+ fn local_declarations(&self) -> TokenStream {
+ self.base.local_declarations()
+ }
+
+ fn attr_names(&self) -> &PathList {
+ self.attr_names
+ }
+
+ fn forward_attrs(&self) -> &super::ForwardAttrs {
+ &self.forward_attrs
+ }
+
+ fn param_name(&self) -> TokenStream {
+ quote!(__di)
+ }
+
+ fn attrs_accessor(&self) -> TokenStream {
+ self.param_name()
+ }
+
+ fn core_loop(&self) -> TokenStream {
+ self.base.core_loop()
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromAttributesImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromAttributes)
+ }
+
+ fn trait_bound(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/from_derive_impl.rs b/src/codegen/from_derive_impl.rs
new file mode 100644
index 0000000..d64d9e2
--- /dev/null
+++ b/src/codegen/from_derive_impl.rs
@@ -0,0 +1,148 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::Ident;
+
+use crate::{
+ ast::Data,
+ codegen::{ExtractAttribute, OuterFromImpl, TraitImpl},
+ options::DeriveInputShapeSet,
+ util::PathList,
+};
+
+use super::ForwardAttrs;
+
+pub struct FromDeriveInputImpl<'a> {
+ pub ident: Option<&'a Ident>,
+ pub generics: Option<&'a Ident>,
+ pub vis: Option<&'a Ident>,
+ pub data: Option<&'a Ident>,
+ pub base: TraitImpl<'a>,
+ pub attr_names: &'a PathList,
+ pub forward_attrs: ForwardAttrs<'a>,
+ pub from_ident: bool,
+ pub supports: Option<&'a DeriveInputShapeSet>,
+}
+
+impl<'a> ToTokens for FromDeriveInputImpl<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let ty_ident = self.base.ident;
+ let input = self.param_name();
+ let post_transform = self.base.post_transform_call();
+
+ if let Data::Struct(ref data) = self.base.data {
+ if data.is_newtype() {
+ self.wrap(
+ quote!{
+ fn from_derive_input(#input: &::darling::export::syn::DeriveInput) -> ::darling::Result<Self> {
+ ::darling::export::Ok(
+ #ty_ident(::darling::FromDeriveInput::from_derive_input(#input)?)
+ ) #post_transform
+ }
+ },
+ tokens,
+ );
+
+ return;
+ }
+ }
+
+ let passed_ident = self
+ .ident
+ .as_ref()
+ .map(|i| quote!(#i: #input.ident.clone(),));
+ let passed_vis = self.vis.as_ref().map(|i| quote!(#i: #input.vis.clone(),));
+ let passed_generics = self
+ .generics
+ .as_ref()
+ .map(|i| quote!(#i: ::darling::FromGenerics::from_generics(&#input.generics)?,));
+ let passed_attrs = self.forward_attrs.as_initializer();
+ let passed_body = self
+ .data
+ .as_ref()
+ .map(|i| quote!(#i: ::darling::ast::Data::try_from(&#input.data)?,));
+
+ let supports = self.supports.map(|i| {
+ quote! {
+ #i
+ __errors.handle(__validate_body(&#input.data));
+ }
+ });
+
+ let inits = self.base.initializers();
+ let default = if self.from_ident {
+ quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
+ } else {
+ self.base.fallback_decl()
+ };
+
+ let grab_attrs = self.extractor();
+
+ let declare_errors = self.base.declare_errors();
+ let require_fields = self.base.require_fields();
+ let check_errors = self.base.check_errors();
+
+ self.wrap(
+ quote! {
+ fn from_derive_input(#input: &::darling::export::syn::DeriveInput) -> ::darling::Result<Self> {
+ #declare_errors
+
+ #grab_attrs
+
+ #supports
+
+ #require_fields
+
+ #check_errors
+
+ #default
+
+ ::darling::export::Ok(#ty_ident {
+ #passed_ident
+ #passed_generics
+ #passed_vis
+ #passed_attrs
+ #passed_body
+ #inits
+ }) #post_transform
+ }
+ },
+ tokens,
+ );
+ }
+}
+
+impl<'a> ExtractAttribute for FromDeriveInputImpl<'a> {
+ fn attr_names(&self) -> &PathList {
+ self.attr_names
+ }
+
+ fn forward_attrs(&self) -> &ForwardAttrs {
+ &self.forward_attrs
+ }
+
+ fn param_name(&self) -> TokenStream {
+ quote!(__di)
+ }
+
+ fn core_loop(&self) -> TokenStream {
+ self.base.core_loop()
+ }
+
+ fn local_declarations(&self) -> TokenStream {
+ self.base.local_declarations()
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromDeriveInputImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromDeriveInput)
+ }
+
+ fn trait_bound(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/from_field.rs b/src/codegen/from_field.rs
new file mode 100644
index 0000000..5bd7350
--- /dev/null
+++ b/src/codegen/from_field.rs
@@ -0,0 +1,114 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::Ident;
+
+use crate::{
+ codegen::{ExtractAttribute, OuterFromImpl, TraitImpl},
+ util::PathList,
+};
+
+use super::ForwardAttrs;
+
+/// `impl FromField` generator. This is used for parsing an individual
+/// field and its attributes.
+pub struct FromFieldImpl<'a> {
+ pub ident: Option<&'a Ident>,
+ pub vis: Option<&'a Ident>,
+ pub ty: Option<&'a Ident>,
+ pub base: TraitImpl<'a>,
+ pub attr_names: &'a PathList,
+ pub forward_attrs: ForwardAttrs<'a>,
+ pub from_ident: bool,
+}
+
+impl<'a> ToTokens for FromFieldImpl<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let input = self.param_name();
+
+ let error_declaration = self.base.declare_errors();
+ let require_fields = self.base.require_fields();
+ let error_check = self.base.check_errors();
+
+ let initializers = self.base.initializers();
+
+ let default = if self.from_ident {
+ quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
+ } else {
+ self.base.fallback_decl()
+ };
+
+ let passed_ident = self
+ .ident
+ .as_ref()
+ .map(|i| quote!(#i: #input.ident.clone(),));
+ let passed_vis = self.vis.as_ref().map(|i| quote!(#i: #input.vis.clone(),));
+ let passed_ty = self.ty.as_ref().map(|i| quote!(#i: #input.ty.clone(),));
+ let passed_attrs = self.forward_attrs.as_initializer();
+
+ // Determine which attributes to forward (if any).
+ let grab_attrs = self.extractor();
+ let post_transform = self.base.post_transform_call();
+
+ self.wrap(
+ quote! {
+ fn from_field(#input: &::darling::export::syn::Field) -> ::darling::Result<Self> {
+ #error_declaration
+
+ #grab_attrs
+
+ #require_fields
+
+ #error_check
+
+ #default
+
+ ::darling::export::Ok(Self {
+ #passed_ident
+ #passed_ty
+ #passed_vis
+ #passed_attrs
+ #initializers
+ }) #post_transform
+
+ }
+ },
+ tokens,
+ );
+ }
+}
+
+impl<'a> ExtractAttribute for FromFieldImpl<'a> {
+ fn attr_names(&self) -> &PathList {
+ self.attr_names
+ }
+
+ fn forward_attrs(&self) -> &super::ForwardAttrs {
+ &self.forward_attrs
+ }
+
+ fn param_name(&self) -> TokenStream {
+ quote!(__field)
+ }
+
+ fn core_loop(&self) -> TokenStream {
+ self.base.core_loop()
+ }
+
+ fn local_declarations(&self) -> TokenStream {
+ self.base.local_declarations()
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromFieldImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromField)
+ }
+
+ fn trait_bound(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/from_meta_impl.rs b/src/codegen/from_meta_impl.rs
new file mode 100644
index 0000000..cc8f87e
--- /dev/null
+++ b/src/codegen/from_meta_impl.rs
@@ -0,0 +1,155 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+
+use crate::ast::{Data, Fields, Style};
+use crate::codegen::{Field, OuterFromImpl, TraitImpl, Variant};
+
+pub struct FromMetaImpl<'a> {
+ pub base: TraitImpl<'a>,
+}
+
+impl<'a> ToTokens for FromMetaImpl<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let base = &self.base;
+
+ let impl_block = match base.data {
+ // Unit structs allow empty bodies only.
+ Data::Struct(ref vd) if vd.style.is_unit() => {
+ let ty_ident = base.ident;
+ quote!(
+ fn from_word() -> ::darling::Result<Self> {
+ ::darling::export::Ok(#ty_ident)
+ }
+ )
+ }
+
+ // Newtype structs proxy to the sole value they contain.
+ Data::Struct(Fields {
+ ref fields,
+ style: Style::Tuple,
+ ..
+ }) if fields.len() == 1 => {
+ let ty_ident = base.ident;
+ quote!(
+ fn from_meta(__item: &::darling::export::syn::Meta) -> ::darling::Result<Self> {
+ ::darling::FromMeta::from_meta(__item)
+ .map_err(|e| e.with_span(&__item))
+ .map(#ty_ident)
+ }
+ )
+ }
+ Data::Struct(Fields {
+ style: Style::Tuple,
+ ..
+ }) => {
+ panic!("Multi-field tuples are not supported");
+ }
+ Data::Struct(ref data) => {
+ let inits = data.fields.iter().map(Field::as_initializer);
+ let declare_errors = base.declare_errors();
+ let require_fields = base.require_fields();
+ let check_errors = base.check_errors();
+ let decls = base.local_declarations();
+ let core_loop = base.core_loop();
+ let default = base.fallback_decl();
+ let post_transform = base.post_transform_call();
+
+ quote!(
+ fn from_list(__items: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
+
+ #decls
+
+ #declare_errors
+
+ #core_loop
+
+ #require_fields
+
+ #check_errors
+
+ #default
+
+ ::darling::export::Ok(Self {
+ #(#inits),*
+ }) #post_transform
+ }
+ )
+ }
+ Data::Enum(ref variants) => {
+ let unit_arms = variants.iter().map(Variant::as_unit_match_arm);
+
+ let unknown_variant_err = if !variants.is_empty() {
+ let names = variants.iter().map(Variant::as_name);
+ quote! {
+ unknown_field_with_alts(__other, &[#(#names),*])
+ }
+ } else {
+ quote! {
+ unknown_field(__other)
+ }
+ };
+
+ let word_or_err = variants
+ .iter()
+ .find_map(|variant| {
+ if variant.word {
+ let ty_ident = variant.ty_ident;
+ let variant_ident = variant.variant_ident;
+ Some(quote!(::darling::export::Ok(#ty_ident::#variant_ident)))
+ } else {
+ None
+ }
+ })
+ .unwrap_or_else(|| {
+ quote!(::darling::export::Err(
+ ::darling::Error::unsupported_format("word")
+ ))
+ });
+
+ quote!(
+ fn from_list(__outer: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
+ // An enum must have exactly one value inside the parentheses if it's not a unit
+ // match arm.
+ match __outer.len() {
+ 0 => ::darling::export::Err(::darling::Error::too_few_items(1)),
+ 1 => {
+ if let ::darling::export::NestedMeta::Meta(ref __nested) = __outer[0] {
+ match ::darling::util::path_to_string(__nested.path()).as_ref() {
+ #(#variants)*
+ __other => ::darling::export::Err(::darling::Error::#unknown_variant_err.with_span(__nested))
+ }
+ } else {
+ ::darling::export::Err(::darling::Error::unsupported_format("literal"))
+ }
+ }
+ _ => ::darling::export::Err(::darling::Error::too_many_items(1)),
+ }
+ }
+
+ fn from_string(lit: &str) -> ::darling::Result<Self> {
+ match lit {
+ #(#unit_arms)*
+ __other => ::darling::export::Err(::darling::Error::unknown_value(__other))
+ }
+ }
+
+ fn from_word() -> ::darling::Result<Self> {
+ #word_or_err
+ }
+ )
+ }
+ };
+
+ self.wrap(impl_block, tokens);
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromMetaImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/from_type_param.rs b/src/codegen/from_type_param.rs
new file mode 100644
index 0000000..d44e8eb
--- /dev/null
+++ b/src/codegen/from_type_param.rs
@@ -0,0 +1,111 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::Ident;
+
+use crate::codegen::{ExtractAttribute, ForwardAttrs, OuterFromImpl, TraitImpl};
+use crate::util::PathList;
+
+pub struct FromTypeParamImpl<'a> {
+ pub base: TraitImpl<'a>,
+ pub ident: Option<&'a Ident>,
+ pub bounds: Option<&'a Ident>,
+ pub default: Option<&'a Ident>,
+ pub attr_names: &'a PathList,
+ pub forward_attrs: ForwardAttrs<'a>,
+ pub from_ident: bool,
+}
+
+impl<'a> ToTokens for FromTypeParamImpl<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let input = self.param_name();
+
+ let error_declaration = self.base.declare_errors();
+ let grab_attrs = self.extractor();
+ let require_fields = self.base.require_fields();
+ let error_check = self.base.check_errors();
+
+ let default = if self.from_ident {
+ quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
+ } else {
+ self.base.fallback_decl()
+ };
+
+ let passed_ident = self
+ .ident
+ .as_ref()
+ .map(|i| quote!(#i: #input.ident.clone(),));
+ let passed_attrs = self.forward_attrs.as_initializer();
+ let passed_bounds = self
+ .bounds
+ .as_ref()
+ .map(|i| quote!(#i: #input.bounds.clone().into_iter().collect::<Vec<_>>(),));
+ let passed_default = self
+ .default
+ .as_ref()
+ .map(|i| quote!(#i: #input.default.clone(),));
+ let initializers = self.base.initializers();
+
+ let post_transform = self.base.post_transform_call();
+
+ self.wrap(
+ quote! {
+ fn from_type_param(#input: &::darling::export::syn::TypeParam) -> ::darling::Result<Self> {
+ #error_declaration
+
+ #grab_attrs
+
+ #require_fields
+
+ #error_check
+
+ #default
+
+ ::darling::export::Ok(Self {
+ #passed_ident
+ #passed_bounds
+ #passed_default
+ #passed_attrs
+ #initializers
+ }) #post_transform
+ }
+ },
+ tokens,
+ );
+ }
+}
+
+impl<'a> ExtractAttribute for FromTypeParamImpl<'a> {
+ fn attr_names(&self) -> &PathList {
+ self.attr_names
+ }
+
+ fn forward_attrs(&self) -> &ForwardAttrs {
+ &self.forward_attrs
+ }
+
+ fn param_name(&self) -> TokenStream {
+ quote!(__type_param)
+ }
+
+ fn core_loop(&self) -> TokenStream {
+ self.base.core_loop()
+ }
+
+ fn local_declarations(&self) -> TokenStream {
+ self.base.local_declarations()
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromTypeParamImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromTypeParam)
+ }
+
+ fn trait_bound(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/from_variant_impl.rs b/src/codegen/from_variant_impl.rs
new file mode 100644
index 0000000..a835b13
--- /dev/null
+++ b/src/codegen/from_variant_impl.rs
@@ -0,0 +1,134 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::Ident;
+
+use crate::codegen::{ExtractAttribute, ForwardAttrs, OuterFromImpl, TraitImpl};
+use crate::options::DataShape;
+use crate::util::PathList;
+
+pub struct FromVariantImpl<'a> {
+ pub base: TraitImpl<'a>,
+ /// If set, the ident of the field into which the variant ident should be placed.
+ ///
+ /// This is one of `darling`'s "magic fields", which allow a type deriving a `darling`
+ /// trait to get fields from the input `syn` element added to the deriving struct
+ /// automatically.
+ pub ident: Option<&'a Ident>,
+ /// If set, the ident of the field into which the transformed output of the input
+ /// variant's fields should be placed.
+ ///
+ /// This is one of `darling`'s "magic fields".
+ pub fields: Option<&'a Ident>,
+ /// If set, the ident of the field into which the discriminant of the input variant
+ /// should be placed. The receiving field must be an `Option` as not all enums have
+ /// discriminants.
+ ///
+ /// This is one of `darling`'s "magic fields".
+ pub discriminant: Option<&'a Ident>,
+ pub attr_names: &'a PathList,
+ pub forward_attrs: ForwardAttrs<'a>,
+ pub from_ident: bool,
+ pub supports: Option<&'a DataShape>,
+}
+
+impl<'a> ToTokens for FromVariantImpl<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let input = self.param_name();
+ let extractor = self.extractor();
+ let passed_ident = self
+ .ident
+ .as_ref()
+ .map(|i| quote!(#i: #input.ident.clone(),));
+ let passed_discriminant = self
+ .discriminant
+ .as_ref()
+ .map(|i| quote!(#i: #input.discriminant.as_ref().map(|(_, expr)| expr.clone()),));
+ let passed_attrs = self.forward_attrs.as_initializer();
+ let passed_fields = self
+ .fields
+ .as_ref()
+ .map(|i| quote!(#i: ::darling::ast::Fields::try_from(&#input.fields)?,));
+
+ let inits = self.base.initializers();
+ let post_transform = self.base.post_transform_call();
+
+ let default = if self.from_ident {
+ quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
+ } else {
+ self.base.fallback_decl()
+ };
+
+ let supports = self.supports.map(|i| {
+ quote! {
+ __errors.handle(#i.check(&#input.fields));
+ }
+ });
+
+ let error_declaration = self.base.declare_errors();
+ let require_fields = self.base.require_fields();
+ let error_check = self.base.check_errors();
+
+ self.wrap(
+ quote!(
+ fn from_variant(#input: &::darling::export::syn::Variant) -> ::darling::Result<Self> {
+ #error_declaration
+
+ #extractor
+
+ #supports
+
+ #require_fields
+
+ #error_check
+
+ #default
+
+ ::darling::export::Ok(Self {
+ #passed_ident
+ #passed_discriminant
+ #passed_attrs
+ #passed_fields
+ #inits
+ }) #post_transform
+ }
+ ),
+ tokens,
+ );
+ }
+}
+
+impl<'a> ExtractAttribute for FromVariantImpl<'a> {
+ fn local_declarations(&self) -> TokenStream {
+ self.base.local_declarations()
+ }
+
+ fn attr_names(&self) -> &PathList {
+ self.attr_names
+ }
+
+ fn forward_attrs(&self) -> &ForwardAttrs {
+ &self.forward_attrs
+ }
+
+ fn param_name(&self) -> TokenStream {
+ quote!(__variant)
+ }
+
+ fn core_loop(&self) -> TokenStream {
+ self.base.core_loop()
+ }
+}
+
+impl<'a> OuterFromImpl<'a> for FromVariantImpl<'a> {
+ fn trait_path(&self) -> syn::Path {
+ path!(::darling::FromVariant)
+ }
+
+ fn trait_bound(&self) -> syn::Path {
+ path!(::darling::FromMeta)
+ }
+
+ fn base(&'a self) -> &'a TraitImpl<'a> {
+ &self.base
+ }
+}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
new file mode 100644
index 0000000..af999b9
--- /dev/null
+++ b/src/codegen/mod.rs
@@ -0,0 +1,32 @@
+mod attr_extractor;
+mod attrs_field;
+mod default_expr;
+mod error;
+mod field;
+mod from_attributes_impl;
+mod from_derive_impl;
+mod from_field;
+mod from_meta_impl;
+mod from_type_param;
+mod from_variant_impl;
+mod outer_from_impl;
+mod postfix_transform;
+mod trait_impl;
+mod variant;
+mod variant_data;
+
+pub(in crate::codegen) use self::attr_extractor::ExtractAttribute;
+pub use self::attrs_field::ForwardAttrs;
+pub use self::default_expr::DefaultExpression;
+pub use self::field::Field;
+pub use self::from_attributes_impl::FromAttributesImpl;
+pub use self::from_derive_impl::FromDeriveInputImpl;
+pub use self::from_field::FromFieldImpl;
+pub use self::from_meta_impl::FromMetaImpl;
+pub use self::from_type_param::FromTypeParamImpl;
+pub use self::from_variant_impl::FromVariantImpl;
+pub use self::outer_from_impl::OuterFromImpl;
+pub use self::postfix_transform::PostfixTransform;
+pub use self::trait_impl::TraitImpl;
+pub use self::variant::Variant;
+pub use self::variant_data::FieldsGen;
diff --git a/src/codegen/outer_from_impl.rs b/src/codegen/outer_from_impl.rs
new file mode 100644
index 0000000..79c5c68
--- /dev/null
+++ b/src/codegen/outer_from_impl.rs
@@ -0,0 +1,61 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{GenericParam, Generics, Path, TraitBound, TraitBoundModifier, TypeParamBound};
+
+use crate::codegen::TraitImpl;
+use crate::usage::IdentSet;
+
+/// Wrapper for "outer From" traits, such as `FromDeriveInput`, `FromVariant`, and `FromField`.
+pub trait OuterFromImpl<'a> {
+ /// Gets the path of the trait being implemented.
+ fn trait_path(&self) -> Path;
+
+ fn base(&'a self) -> &'a TraitImpl<'a>;
+
+ fn trait_bound(&self) -> Path {
+ self.trait_path()
+ }
+
+ fn wrap<T: ToTokens>(&'a self, body: T, tokens: &mut TokenStream) {
+ let base = self.base();
+ let trayt = self.trait_path();
+ let ty_ident = base.ident;
+ // The type parameters used in non-skipped, non-magic fields.
+ // These must impl `FromMeta` unless they have custom bounds.
+ let used = base.used_type_params();
+ let generics = compute_impl_bounds(self.trait_bound(), base.generics.clone(), &used);
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ tokens.append_all(quote!(
+ #[automatically_derived]
+ impl #impl_generics #trayt for #ty_ident #ty_generics
+ #where_clause
+ {
+ #body
+ }
+ ));
+ }
+}
+
+fn compute_impl_bounds(bound: Path, mut generics: Generics, applies_to: &IdentSet) -> Generics {
+ if generics.params.is_empty() {
+ return generics;
+ }
+
+ let added_bound = TypeParamBound::Trait(TraitBound {
+ paren_token: None,
+ modifier: TraitBoundModifier::None,
+ lifetimes: None,
+ path: bound,
+ });
+
+ for param in generics.params.iter_mut() {
+ if let GenericParam::Type(ref mut typ) = *param {
+ if applies_to.contains(&typ.ident) {
+ typ.bounds.push(added_bound.clone());
+ }
+ }
+ }
+
+ generics
+}
diff --git a/src/codegen/postfix_transform.rs b/src/codegen/postfix_transform.rs
new file mode 100644
index 0000000..e631702
--- /dev/null
+++ b/src/codegen/postfix_transform.rs
@@ -0,0 +1,30 @@
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{Ident, Path};
+
+/// A method invocation applied to a value.
+///
+/// This is used for `map` and `and_then` transforms in derivations.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PostfixTransform {
+ pub(crate) transformer: Ident,
+ pub(crate) function: Path,
+}
+
+impl PostfixTransform {
+ pub fn new(transformer: Ident, function: Path) -> Self {
+ Self {
+ transformer,
+ function,
+ }
+ }
+}
+
+impl ToTokens for PostfixTransform {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ let Self {
+ transformer,
+ function,
+ } = self;
+ tokens.append_all(quote!(.#transformer(#function)))
+ }
+}
diff --git a/src/codegen/trait_impl.rs b/src/codegen/trait_impl.rs
new file mode 100644
index 0000000..2f358db
--- /dev/null
+++ b/src/codegen/trait_impl.rs
@@ -0,0 +1,146 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Generics, Ident, WherePredicate};
+
+use crate::ast::{Data, Fields};
+use crate::codegen::{
+ error::{ErrorCheck, ErrorDeclaration},
+ DefaultExpression, Field, FieldsGen, PostfixTransform, Variant,
+};
+use crate::usage::{CollectTypeParams, IdentSet, Purpose};
+
+#[derive(Debug)]
+pub struct TraitImpl<'a> {
+ pub ident: &'a Ident,
+ pub generics: &'a Generics,
+ pub data: Data<Variant<'a>, Field<'a>>,
+ pub default: Option<DefaultExpression<'a>>,
+ pub post_transform: Option<&'a PostfixTransform>,
+ pub bound: Option<&'a [WherePredicate]>,
+ pub allow_unknown_fields: bool,
+}
+
+impl<'a> TraitImpl<'a> {
+ /// Get all declared type parameters.
+ pub fn declared_type_params(&self) -> IdentSet {
+ self.generics
+ .type_params()
+ .map(|tp| tp.ident.clone())
+ .collect()
+ }
+
+ /// Get the type parameters which are used by non-skipped, non-magic fields.
+ /// These type parameters will have a `FromMeta` bound applied to them in emitted
+ /// code.
+ pub fn used_type_params(&self) -> IdentSet {
+ self.type_params_matching(|f| !f.skip, |v| !v.skip)
+ }
+
+ fn type_params_matching<F, V>(&self, field_filter: F, variant_filter: V) -> IdentSet
+ where
+ F: Fn(&&Field) -> bool,
+ V: Fn(&&Variant) -> bool,
+ {
+ let declared = self.declared_type_params();
+ match self.data {
+ Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared),
+ Data::Enum(ref v) => {
+ v.iter()
+ .filter(variant_filter)
+ .fold(Default::default(), |mut state, variant| {
+ state.extend(self.type_params_in_fields(
+ &variant.data,
+ &field_filter,
+ &declared,
+ ));
+ state
+ })
+ }
+ }
+ }
+
+ /// Get the type parameters of all fields in a set matching some filter
+ fn type_params_in_fields<'b, F>(
+ &'b self,
+ fields: &'b Fields<Field<'a>>,
+ field_filter: F,
+ declared: &IdentSet,
+ ) -> IdentSet
+ where
+ F: Fn(&&'b Field) -> bool,
+ {
+ fields
+ .iter()
+ .filter(field_filter)
+ .collect_type_params_cloned(&Purpose::BoundImpl.into(), declared)
+ }
+}
+
+impl<'a> TraitImpl<'a> {
+ /// Gets the `let` declaration for errors accumulated during parsing.
+ pub fn declare_errors(&self) -> ErrorDeclaration {
+ ErrorDeclaration::default()
+ }
+
+ /// Gets the check which performs an early return if errors occurred during parsing.
+ pub fn check_errors(&self) -> ErrorCheck {
+ ErrorCheck::default()
+ }
+
+ /// Generate local variable declarations for all fields.
+ pub(in crate::codegen) fn local_declarations(&self) -> TokenStream {
+ if let Data::Struct(ref vd) = self.data {
+ let vdr = vd.as_ref().map(Field::as_declaration);
+ let decls = vdr.fields.as_slice();
+ quote!(#(#decls)*)
+ } else {
+ quote!()
+ }
+ }
+
+ pub(in crate::codegen) fn post_transform_call(&self) -> Option<TokenStream> {
+ self.post_transform.map(|pt| quote!(#pt))
+ }
+
+ /// Generate local variable declaration and initialization for instance from which missing fields will be taken.
+ pub(in crate::codegen) fn fallback_decl(&self) -> TokenStream {
+ let default = self.default.as_ref().map(DefaultExpression::as_declaration);
+ quote!(#default)
+ }
+
+ pub fn require_fields(&self) -> TokenStream {
+ if let Data::Struct(ref vd) = self.data {
+ let check_nones = vd.as_ref().map(Field::as_presence_check);
+ let checks = check_nones.fields.as_slice();
+
+ // If a field was marked `flatten`, now is the time to process any unclaimed meta items
+ // and mark the field as having been seen.
+ let flatten_field_init = vd.fields.iter().find(|f| f.flatten).map(|v| {
+ v.as_flatten_initializer(vd.fields.iter().filter_map(Field::as_name).collect())
+ });
+
+ quote! {
+ #flatten_field_init
+ #(#checks)*
+ }
+ } else {
+ quote!()
+ }
+ }
+
+ pub(in crate::codegen) fn initializers(&self) -> TokenStream {
+ self.make_field_ctx().initializers()
+ }
+
+ /// Generate the loop which walks meta items looking for property matches.
+ pub(in crate::codegen) fn core_loop(&self) -> TokenStream {
+ self.make_field_ctx().core_loop()
+ }
+
+ fn make_field_ctx(&'a self) -> FieldsGen<'a> {
+ match self.data {
+ Data::Enum(_) => panic!("Core loop on enums isn't supported"),
+ Data::Struct(ref data) => FieldsGen::new(data, self.allow_unknown_fields),
+ }
+ }
+}
diff --git a/src/codegen/variant.rs b/src/codegen/variant.rs
new file mode 100644
index 0000000..2a55780
--- /dev/null
+++ b/src/codegen/variant.rs
@@ -0,0 +1,173 @@
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::Ident;
+
+use crate::ast::Fields;
+use crate::codegen::error::{ErrorCheck, ErrorDeclaration};
+use crate::codegen::{Field, FieldsGen};
+use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
+
+/// A variant of the enum which is deriving `FromMeta`.
+#[derive(Debug, Clone)]
+pub struct Variant<'a> {
+ /// The name which will appear in code passed to the `FromMeta` input.
+ pub name_in_attr: Cow<'a, String>,
+
+ /// The name of the variant which will be returned for a given `name_in_attr`.
+ pub variant_ident: &'a Ident,
+
+ /// The name of the parent enum type.
+ pub ty_ident: &'a Ident,
+
+ pub data: Fields<Field<'a>>,
+
+ /// Whether or not the variant should be skipped in the generated code.
+ pub skip: bool,
+
+ /// Whether or not the variant should be used to create an instance for
+ /// `FromMeta::from_word`.
+ pub word: bool,
+
+ pub allow_unknown_fields: bool,
+}
+
+impl<'a> Variant<'a> {
+ pub fn as_name(&'a self) -> &'a str {
+ &self.name_in_attr
+ }
+
+ pub fn as_unit_match_arm(&'a self) -> UnitMatchArm<'a> {
+ UnitMatchArm(self)
+ }
+
+ pub fn as_data_match_arm(&'a self) -> DataMatchArm<'a> {
+ DataMatchArm(self)
+ }
+}
+
+impl<'a> UsesTypeParams for Variant<'a> {
+ fn uses_type_params<'b>(
+ &self,
+ options: &usage::Options,
+ type_set: &'b IdentSet,
+ ) -> IdentRefSet<'b> {
+ self.data.uses_type_params(options, type_set)
+ }
+}
+
+impl<'a> ToTokens for Variant<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if self.data.is_unit() {
+ self.as_unit_match_arm().to_tokens(tokens);
+ } else {
+ self.as_data_match_arm().to_tokens(tokens)
+ }
+ }
+}
+
+/// Code generator for an enum variant in a unit match position.
+/// This is placed in generated `from_string` calls for the parent enum.
+/// Value-carrying variants wrapped in this type will emit code to produce an "unsupported format" error.
+pub struct UnitMatchArm<'a>(&'a Variant<'a>);
+
+impl<'a> ToTokens for UnitMatchArm<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let val: &Variant<'a> = self.0;
+
+ if val.skip {
+ return;
+ }
+
+ let name_in_attr = &val.name_in_attr;
+
+ if val.data.is_unit() {
+ let variant_ident = val.variant_ident;
+ let ty_ident = val.ty_ident;
+
+ tokens.append_all(quote!(
+ #name_in_attr => ::darling::export::Ok(#ty_ident::#variant_ident),
+ ));
+ } else {
+ tokens.append_all(quote!(
+ #name_in_attr => ::darling::export::Err(::darling::Error::unsupported_format("literal")),
+ ));
+ }
+ }
+}
+
+/// Code generator for an enum variant in a data-carrying match position.
+/// This is placed in generated `from_list` calls for the parent enum.
+/// Unit variants wrapped in this type will emit code to produce an "unsupported format" error.
+pub struct DataMatchArm<'a>(&'a Variant<'a>);
+
+impl<'a> ToTokens for DataMatchArm<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let val: &Variant<'a> = self.0;
+
+ if val.skip {
+ return;
+ }
+
+ let name_in_attr = &val.name_in_attr;
+ let variant_ident = val.variant_ident;
+ let ty_ident = val.ty_ident;
+
+ if val.data.is_unit() {
+ tokens.append_all(quote!(
+ #name_in_attr => ::darling::export::Err(::darling::Error::unsupported_format("list")),
+ ));
+
+ return;
+ }
+
+ let vdg = FieldsGen::new(&val.data, val.allow_unknown_fields);
+
+ if val.data.is_struct() {
+ let declare_errors = ErrorDeclaration::default();
+ let check_errors = ErrorCheck::with_location(name_in_attr);
+ let require_fields = vdg.require_fields();
+ let decls = vdg.declarations();
+ let core_loop = vdg.core_loop();
+ let inits = vdg.initializers();
+
+ tokens.append_all(quote!(
+ #name_in_attr => {
+ if let ::darling::export::syn::Meta::List(ref __data) = *__nested {
+ let __items = ::darling::export::NestedMeta::parse_meta_list(__data.tokens.clone())?;
+ let __items = &__items;
+
+ #declare_errors
+
+ #decls
+
+ #core_loop
+
+ #require_fields
+
+ #check_errors
+
+ ::darling::export::Ok(#ty_ident::#variant_ident {
+ #inits
+ })
+ } else {
+ ::darling::export::Err(::darling::Error::unsupported_format("non-list"))
+ }
+ }
+ ));
+ } else if val.data.is_newtype() {
+ tokens.append_all(quote!(
+ #name_in_attr => {
+ ::darling::export::Ok(
+ #ty_ident::#variant_ident(
+ ::darling::FromMeta::from_meta(__nested)
+ .map_err(|e| e.at(#name_in_attr))?)
+ )
+ }
+ ));
+ } else {
+ panic!("Match arms aren't supported for tuple variants yet");
+ }
+ }
+}
diff --git a/src/codegen/variant_data.rs b/src/codegen/variant_data.rs
new file mode 100644
index 0000000..9a8bffc
--- /dev/null
+++ b/src/codegen/variant_data.rs
@@ -0,0 +1,105 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+
+use crate::ast::{Fields, Style};
+use crate::codegen::Field;
+
+pub struct FieldsGen<'a> {
+ fields: &'a Fields<Field<'a>>,
+ allow_unknown_fields: bool,
+}
+
+impl<'a> FieldsGen<'a> {
+ pub fn new(fields: &'a Fields<Field<'a>>, allow_unknown_fields: bool) -> Self {
+ Self {
+ fields,
+ allow_unknown_fields,
+ }
+ }
+
+ /// Create declarations for all the fields in the struct.
+ pub(in crate::codegen) fn declarations(&self) -> TokenStream {
+ match *self.fields {
+ Fields {
+ style: Style::Struct,
+ ref fields,
+ ..
+ } => {
+ let vdr = fields.iter().map(Field::as_declaration);
+ quote!(#(#vdr)*)
+ }
+ _ => panic!("FieldsGen doesn't support tuples yet"),
+ }
+ }
+
+ /// Generate the loop which walks meta items looking for property matches.
+ pub(in crate::codegen) fn core_loop(&self) -> TokenStream {
+ let arms = self.fields.as_ref().map(Field::as_match);
+ // If there is a flatten field, buffer the unknown field so it can be passed
+ // to the flatten function with all other unknown fields.
+ let handle_unknown = if self.fields.iter().any(|f| f.flatten) {
+ quote! {
+ __flatten.push(::darling::ast::NestedMeta::Meta(__inner.clone()));
+ }
+ }
+ // If we're allowing unknown fields, then handling one is a no-op.
+ else if self.allow_unknown_fields {
+ quote!()
+ }
+ // Otherwise, we're going to push a new spanned error pointing at the field.
+ else {
+ let mut names = self.fields.iter().filter_map(Field::as_name).peekable();
+ // We can't call `unknown_field_with_alts` with an empty slice, or else it fails to
+ // infer the type of the slice item.
+ let err_fn = if names.peek().is_none() {
+ quote!(unknown_field(__other))
+ } else {
+ quote!(unknown_field_with_alts(__other, &[#(#names),*]))
+ };
+
+ quote! {
+ __errors.push(::darling::Error::#err_fn.with_span(__inner));
+ }
+ };
+ let arms = arms.iter();
+
+ quote!(
+ for __item in __items {
+ match *__item {
+ ::darling::export::NestedMeta::Meta(ref __inner) => {
+ let __name = ::darling::util::path_to_string(__inner.path());
+ match __name.as_str() {
+ #(#arms)*
+ __other => { #handle_unknown }
+ }
+ }
+ ::darling::export::NestedMeta::Lit(ref __inner) => {
+ __errors.push(::darling::Error::unsupported_format("literal")
+ .with_span(__inner));
+ }
+ }
+ }
+ )
+ }
+
+ pub fn require_fields(&self) -> TokenStream {
+ match *self.fields {
+ Fields {
+ style: Style::Struct,
+ ref fields,
+ ..
+ } => {
+ let checks = fields.iter().map(Field::as_presence_check);
+ quote!(#(#checks)*)
+ }
+ _ => panic!("FieldsGen doesn't support tuples for requirement checks"),
+ }
+ }
+
+ pub(in crate::codegen) fn initializers(&self) -> TokenStream {
+ let inits = self.fields.as_ref().map(Field::as_initializer);
+ let inits = inits.iter();
+
+ quote!(#(#inits),*)
+ }
+}
diff --git a/src/derive.rs b/src/derive.rs
new file mode 100644
index 0000000..43674aa
--- /dev/null
+++ b/src/derive.rs
@@ -0,0 +1,61 @@
+//! Functions to derive `darling`'s traits from well-formed input, without directly depending
+//! on `proc_macro`.
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::DeriveInput;
+
+use crate::options;
+
+/// Run an expression which returns a `darling::Result`, then either return the tokenized
+/// representation of the `Ok` value, or the tokens of the compiler errors in the `Err` case.
+macro_rules! emit_impl_or_error {
+ ($e:expr) => {
+ match $e {
+ Ok(val) => val.into_token_stream(),
+ Err(err) => err.write_errors(),
+ }
+ };
+}
+
+/// Create tokens for a `darling::FromMeta` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_meta(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FromMetaOptions::new(input))
+}
+
+/// Create tokens for a `darling::FromAttributes` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_attributes(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FromAttributesOptions::new(input))
+}
+
+/// Create tokens for a `darling::FromDeriveInput` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_derive_input(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FdiOptions::new(input))
+}
+
+/// Create tokens for a `darling::FromField` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_field(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FromFieldOptions::new(input))
+}
+
+/// Create tokens for a `darling::FromTypeParam` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_type_param(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FromTypeParamOptions::new(input))
+}
+
+/// Create tokens for a `darling::FromVariant` impl from a `DeriveInput`. If
+/// the input cannot produce a valid impl, the returned tokens will contain
+/// compile errors instead.
+pub fn from_variant(input: &DeriveInput) -> TokenStream {
+ emit_impl_or_error!(options::FromVariantOptions::new(input))
+}
diff --git a/src/error/child.rs b/src/error/child.rs
new file mode 100644
index 0000000..c0e46f0
--- /dev/null
+++ b/src/error/child.rs
@@ -0,0 +1,82 @@
+use proc_macro2::Span;
+
+/// Exhaustive mirror of [`proc_macro::Level`].
+#[derive(Debug, Clone)]
+pub(in crate::error) enum Level {
+ Error,
+ Warning,
+ Note,
+ Help,
+}
+
+/// Supplemental message for an [`Error`](super::Error) when it's emitted as a `Diagnostic`.
+///
+/// # Example Output
+/// The `note` and `help` lines below come from child diagnostics.
+///
+/// ```text
+/// error: My custom error
+/// --> my_project/my_file.rs:3:5
+/// |
+/// 13 | FooBar { value: String },
+/// | ^^^^^^
+/// |
+/// = note: My note on the macro usage
+/// = help: Try doing this instead
+/// ```
+#[derive(Debug, Clone)]
+pub(in crate::error) struct ChildDiagnostic {
+ level: Level,
+ span: Option<Span>,
+ message: String,
+}
+
+impl ChildDiagnostic {
+ pub(in crate::error) fn new(level: Level, span: Option<Span>, message: String) -> Self {
+ Self {
+ level,
+ span,
+ message,
+ }
+ }
+}
+
+impl ChildDiagnostic {
+ /// Append this child diagnostic to a `Diagnostic`.
+ ///
+ /// # Panics
+ /// This method panics if `self` has a span and is being invoked outside of
+ /// a proc-macro due to the behavior of [`Span::unwrap()`](Span).
+ pub fn append_to(self, diagnostic: proc_macro::Diagnostic) -> proc_macro::Diagnostic {
+ match self.level {
+ Level::Error => {
+ if let Some(span) = self.span {
+ diagnostic.span_error(span.unwrap(), self.message)
+ } else {
+ diagnostic.error(self.message)
+ }
+ }
+ Level::Warning => {
+ if let Some(span) = self.span {
+ diagnostic.span_warning(span.unwrap(), self.message)
+ } else {
+ diagnostic.warning(self.message)
+ }
+ }
+ Level::Note => {
+ if let Some(span) = self.span {
+ diagnostic.span_note(span.unwrap(), self.message)
+ } else {
+ diagnostic.note(self.message)
+ }
+ }
+ Level::Help => {
+ if let Some(span) = self.span {
+ diagnostic.span_help(span.unwrap(), self.message)
+ } else {
+ diagnostic.help(self.message)
+ }
+ }
+ }
+ }
+}
diff --git a/src/error/kind.rs b/src/error/kind.rs
new file mode 100644
index 0000000..1254f67
--- /dev/null
+++ b/src/error/kind.rs
@@ -0,0 +1,229 @@
+use std::fmt;
+
+use crate::error::Error;
+
+type DeriveInputShape = String;
+type FieldName = String;
+type MetaFormat = String;
+
+#[derive(Debug, Clone)]
+// Don't want to publicly commit to ErrorKind supporting equality yet, but
+// not having it makes testing very difficult.
+#[cfg_attr(test, derive(PartialEq))]
+pub(in crate::error) enum ErrorKind {
+ /// An arbitrary error message.
+ Custom(String),
+ DuplicateField(FieldName),
+ MissingField(FieldName),
+ UnsupportedShape {
+ observed: DeriveInputShape,
+ expected: Option<String>,
+ },
+ UnknownField(ErrorUnknownField),
+ UnexpectedFormat(MetaFormat),
+ UnexpectedType(String),
+ UnknownValue(String),
+ TooFewItems(usize),
+ TooManyItems(usize),
+ /// A set of errors.
+ Multiple(Vec<Error>),
+
+ // TODO make this variant take `!` so it can't exist
+ #[doc(hidden)]
+ __NonExhaustive,
+}
+
+impl ErrorKind {
+ pub fn description(&self) -> &str {
+ use self::ErrorKind::*;
+
+ match *self {
+ Custom(ref s) => s,
+ DuplicateField(_) => "Duplicate field",
+ MissingField(_) => "Missing field",
+ UnknownField(_) => "Unexpected field",
+ UnsupportedShape { .. } => "Unsupported shape",
+ UnexpectedFormat(_) => "Unexpected meta-item format",
+ UnexpectedType(_) => "Unexpected type",
+ UnknownValue(_) => "Unknown literal value",
+ TooFewItems(_) => "Too few items",
+ TooManyItems(_) => "Too many items",
+ Multiple(_) => "Multiple errors",
+ __NonExhaustive => unreachable!(),
+ }
+ }
+
+ /// Deeply counts the number of errors this item represents.
+ pub fn len(&self) -> usize {
+ if let ErrorKind::Multiple(ref items) = *self {
+ items.iter().map(Error::len).sum()
+ } else {
+ 1
+ }
+ }
+}
+
+impl fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ErrorKind::*;
+
+ match *self {
+ Custom(ref s) => s.fmt(f),
+ DuplicateField(ref field) => write!(f, "Duplicate field `{}`", field),
+ MissingField(ref field) => write!(f, "Missing field `{}`", field),
+ UnknownField(ref field) => field.fmt(f),
+ UnsupportedShape {
+ ref observed,
+ ref expected,
+ } => {
+ write!(f, "Unsupported shape `{}`", observed)?;
+ if let Some(expected) = &expected {
+ write!(f, ". Expected {}.", expected)?;
+ }
+
+ Ok(())
+ }
+ UnexpectedFormat(ref format) => write!(f, "Unexpected meta-item format `{}`", format),
+ UnexpectedType(ref ty) => write!(f, "Unexpected type `{}`", ty),
+ UnknownValue(ref val) => write!(f, "Unknown literal value `{}`", val),
+ TooFewItems(ref min) => write!(f, "Too few items: Expected at least {}", min),
+ TooManyItems(ref max) => write!(f, "Too many items: Expected no more than {}", max),
+ Multiple(ref items) if items.len() == 1 => items[0].fmt(f),
+ Multiple(ref items) => {
+ write!(f, "Multiple errors: (")?;
+ let mut first = true;
+ for item in items {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+
+ item.fmt(f)?;
+ }
+
+ write!(f, ")")
+ }
+ __NonExhaustive => unreachable!(),
+ }
+ }
+}
+
+impl From<ErrorUnknownField> for ErrorKind {
+ fn from(err: ErrorUnknownField) -> Self {
+ ErrorKind::UnknownField(err)
+ }
+}
+
+/// An error for an unknown field, with a possible "did-you-mean" suggestion to get
+/// the user back on the right track.
+#[derive(Clone, Debug)]
+// Don't want to publicly commit to ErrorKind supporting equality yet, but
+// not having it makes testing very difficult.
+#[cfg_attr(test, derive(PartialEq))]
+pub(in crate::error) struct ErrorUnknownField {
+ name: String,
+ did_you_mean: Option<(f64, String)>,
+}
+
+impl ErrorUnknownField {
+ pub fn new<I: Into<String>>(name: I, did_you_mean: Option<(f64, String)>) -> Self {
+ ErrorUnknownField {
+ name: name.into(),
+ did_you_mean,
+ }
+ }
+
+ pub fn with_alts<'a, T, I>(field: &str, alternates: I) -> Self
+ where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+ {
+ ErrorUnknownField::new(field, did_you_mean(field, alternates))
+ }
+
+ /// Add more alternate field names to the error, updating the `did_you_mean` suggestion
+ /// if a closer match to the unknown field's name is found.
+ pub fn add_alts<'a, T, I>(&mut self, alternates: I)
+ where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+ {
+ if let Some(bna) = did_you_mean(&self.name, alternates) {
+ if let Some(current) = &self.did_you_mean {
+ if bna.0 > current.0 {
+ self.did_you_mean = Some(bna);
+ }
+ } else {
+ self.did_you_mean = Some(bna);
+ }
+ }
+ }
+
+ #[cfg(feature = "diagnostics")]
+ pub fn into_diagnostic(self, span: Option<::proc_macro2::Span>) -> ::proc_macro::Diagnostic {
+ let base = span
+ .unwrap_or_else(::proc_macro2::Span::call_site)
+ .unwrap()
+ .error(self.top_line());
+ match self.did_you_mean {
+ Some((_, alt_name)) => base.help(format!("did you mean `{}`?", alt_name)),
+ None => base,
+ }
+ }
+
+ #[cfg(feature = "diagnostics")]
+ fn top_line(&self) -> String {
+ format!("Unknown field: `{}`", self.name)
+ }
+}
+
+impl From<String> for ErrorUnknownField {
+ fn from(name: String) -> Self {
+ ErrorUnknownField::new(name, None)
+ }
+}
+
+impl<'a> From<&'a str> for ErrorUnknownField {
+ fn from(name: &'a str) -> Self {
+ ErrorUnknownField::new(name, None)
+ }
+}
+
+impl fmt::Display for ErrorUnknownField {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Unknown field: `{}`", self.name)?;
+
+ if let Some((_, ref did_you_mean)) = self.did_you_mean {
+ write!(f, ". Did you mean `{}`?", did_you_mean)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "suggestions")]
+fn did_you_mean<'a, T, I>(field: &str, alternates: I) -> Option<(f64, String)>
+where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+{
+ let mut candidate: Option<(f64, &str)> = None;
+ for pv in alternates {
+ let confidence = ::strsim::jaro_winkler(field, pv.as_ref());
+ if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
+ {
+ candidate = Some((confidence, pv.as_ref()));
+ }
+ }
+ candidate.map(|(score, candidate)| (score, candidate.into()))
+}
+
+#[cfg(not(feature = "suggestions"))]
+fn did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<(f64, String)>
+where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+{
+ None
+}
diff --git a/src/error/mod.rs b/src/error/mod.rs
new file mode 100644
index 0000000..3c22d4c
--- /dev/null
+++ b/src/error/mod.rs
@@ -0,0 +1,1035 @@
+//! The `darling::Error` type, the multiple error `Accumulator`, and their internals.
+//!
+//! Error handling is one of the core values of `darling`; creating great errors is hard and
+//! never the reason that a proc-macro author started writing their crate. As a result, the
+//! `Error` type in `darling` tries to make adding span information, suggestions, and other
+//! help content easy when manually implementing `darling` traits, and automatic when deriving
+//! them.
+
+use proc_macro2::{Span, TokenStream};
+use std::error::Error as StdError;
+use std::fmt;
+use std::iter::{self, Iterator};
+use std::string::ToString;
+use std::vec;
+use syn::spanned::Spanned;
+use syn::{Expr, Lit, LitStr, Path};
+
+#[cfg(feature = "diagnostics")]
+mod child;
+mod kind;
+
+use crate::util::path_to_string;
+
+use self::kind::{ErrorKind, ErrorUnknownField};
+
+/// An alias of `Result` specific to attribute parsing.
+pub type Result<T> = ::std::result::Result<T, Error>;
+
+/// An error encountered during attribute parsing.
+///
+/// Given that most errors darling encounters represent code bugs in dependent crates,
+/// the internal structure of the error is deliberately opaque.
+///
+/// # Usage
+/// Proc-macro expansion happens very infrequently compared to runtime tasks such as
+/// deserialization, and it happens in the context of an expensive compilation taks.
+/// For that reason, darling prefers not to fail on the first error it encounters, instead
+/// doing as much work as it can, accumulating errors into a single report.
+///
+/// As a result, `darling::Error` is more of guaranteed-non-empty error collection
+/// than a single problem. These errors also have some notion of hierarchy, stemming from
+/// the hierarchical nature of darling's input.
+///
+/// These characteristics make for great experiences when using darling-powered crates,
+/// provided crates using darling adhere to some best practices:
+///
+/// 1. Do not attempt to simplify a `darling::Error` into some other error type, such as
+/// `syn::Error`. To surface compile errors, instead use `darling::Error::write_errors`.
+/// This preserves all span information, suggestions, etc. Wrapping a `darling::Error` in
+/// a custom error enum works as-expected and does not force any loss of fidelity.
+/// 2. Do not use early return (e.g. the `?` operator) for custom validations. Instead,
+/// create an [`error::Accumulator`](Accumulator) to collect errors as they are encountered. Then use
+/// [`Accumulator::finish`] to return your validated result; it will give `Ok` if and only if
+/// no errors were encountered. This can create very complex custom validation functions;
+/// in those cases, split independent "validation chains" out into their own functions to
+/// keep the main validator manageable.
+/// 3. Use `darling::Error::custom` to create additional errors as-needed, then call `with_span`
+/// to ensure those errors appear in the right place. Use `darling::util::SpannedValue` to keep
+/// span information around on parsed fields so that custom diagnostics can point to the correct
+/// parts of the input AST.
+#[derive(Debug, Clone)]
+pub struct Error {
+ kind: ErrorKind,
+ locations: Vec<String>,
+ /// The span to highlight in the emitted diagnostic.
+ span: Option<Span>,
+ /// Additional diagnostic messages to show with the error.
+ #[cfg(feature = "diagnostics")]
+ children: Vec<child::ChildDiagnostic>,
+}
+
+/// Error creation functions
+impl Error {
+ pub(in crate::error) fn new(kind: ErrorKind) -> Self {
+ Error {
+ kind,
+ locations: Vec::new(),
+ span: None,
+ #[cfg(feature = "diagnostics")]
+ children: vec![],
+ }
+ }
+
+ /// Creates a new error with a custom message.
+ pub fn custom<T: fmt::Display>(msg: T) -> Self {
+ Error::new(ErrorKind::Custom(msg.to_string()))
+ }
+
+ /// Creates a new error for a field that appears twice in the input.
+ pub fn duplicate_field(name: &str) -> Self {
+ Error::new(ErrorKind::DuplicateField(name.into()))
+ }
+
+ /// Creates a new error for a field that appears twice in the input. Helper to avoid repeating
+ /// the syn::Path to String conversion.
+ pub fn duplicate_field_path(path: &Path) -> Self {
+ Error::duplicate_field(&path_to_string(path))
+ }
+
+ /// Creates a new error for a non-optional field that does not appear in the input.
+ pub fn missing_field(name: &str) -> Self {
+ Error::new(ErrorKind::MissingField(name.into()))
+ }
+
+ /// Creates a new error for a field name that appears in the input but does not correspond
+ /// to a known field.
+ pub fn unknown_field(name: &str) -> Self {
+ Error::new(ErrorKind::UnknownField(name.into()))
+ }
+
+ /// Creates a new error for a field name that appears in the input but does not correspond
+ /// to a known field. Helper to avoid repeating the syn::Path to String conversion.
+ pub fn unknown_field_path(path: &Path) -> Self {
+ Error::unknown_field(&path_to_string(path))
+ }
+
+ /// Creates a new error for a field name that appears in the input but does not correspond to
+ /// a known attribute. The second argument is the list of known attributes; if a similar name
+ /// is found that will be shown in the emitted error message.
+ pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
+ where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+ {
+ Error::new(ErrorUnknownField::with_alts(field, alternates).into())
+ }
+
+ /// Creates a new error for a field name that appears in the input but does not correspond to
+ /// a known attribute. The second argument is the list of known attributes; if a similar name
+ /// is found that will be shown in the emitted error message.
+ pub fn unknown_field_path_with_alts<'a, T, I>(field: &Path, alternates: I) -> Self
+ where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+ {
+ Error::new(ErrorUnknownField::with_alts(&path_to_string(field), alternates).into())
+ }
+
+ /// Creates a new error for a struct or variant that does not adhere to the supported shape.
+ pub fn unsupported_shape(shape: &str) -> Self {
+ Error::new(ErrorKind::UnsupportedShape {
+ observed: shape.into(),
+ expected: None,
+ })
+ }
+
+ pub fn unsupported_shape_with_expected<T: fmt::Display>(shape: &str, expected: &T) -> Self {
+ Error::new(ErrorKind::UnsupportedShape {
+ observed: shape.into(),
+ expected: Some(expected.to_string()),
+ })
+ }
+
+ pub fn unsupported_format(format: &str) -> Self {
+ Error::new(ErrorKind::UnexpectedFormat(format.into()))
+ }
+
+ /// Creates a new error for a field which has an unexpected literal type.
+ pub fn unexpected_type(ty: &str) -> Self {
+ Error::new(ErrorKind::UnexpectedType(ty.into()))
+ }
+
+ pub fn unexpected_expr_type(expr: &Expr) -> Self {
+ Error::unexpected_type(match *expr {
+ Expr::Array(_) => "array",
+ Expr::Assign(_) => "assign",
+ Expr::Async(_) => "async",
+ Expr::Await(_) => "await",
+ Expr::Binary(_) => "binary",
+ Expr::Block(_) => "block",
+ Expr::Break(_) => "break",
+ Expr::Call(_) => "call",
+ Expr::Cast(_) => "cast",
+ Expr::Closure(_) => "closure",
+ Expr::Const(_) => "const",
+ Expr::Continue(_) => "continue",
+ Expr::Field(_) => "field",
+ Expr::ForLoop(_) => "for_loop",
+ Expr::Group(_) => "group",
+ Expr::If(_) => "if",
+ Expr::Index(_) => "index",
+ Expr::Infer(_) => "infer",
+ Expr::Let(_) => "let",
+ Expr::Lit(_) => "lit",
+ Expr::Loop(_) => "loop",
+ Expr::Macro(_) => "macro",
+ Expr::Match(_) => "match",
+ Expr::MethodCall(_) => "method_call",
+ Expr::Paren(_) => "paren",
+ Expr::Path(_) => "path",
+ Expr::Range(_) => "range",
+ Expr::Reference(_) => "reference",
+ Expr::Repeat(_) => "repeat",
+ Expr::Return(_) => "return",
+ Expr::Struct(_) => "struct",
+ Expr::Try(_) => "try",
+ Expr::TryBlock(_) => "try_block",
+ Expr::Tuple(_) => "tuple",
+ Expr::Unary(_) => "unary",
+ Expr::Unsafe(_) => "unsafe",
+ Expr::Verbatim(_) => "verbatim",
+ Expr::While(_) => "while",
+ Expr::Yield(_) => "yield",
+ // non-exhaustive enum
+ _ => "unknown",
+ })
+ .with_span(expr)
+ }
+
+ /// Creates a new error for a field which has an unexpected literal type. This will automatically
+ /// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
+ /// literal value.
+ ///
+ /// # Usage
+ /// This is most frequently used in overrides of the `FromMeta::from_value` method.
+ ///
+ /// ```rust
+ /// # // pretend darling_core is darling so the doc example looks correct.
+ /// # extern crate darling_core as darling;
+ /// # extern crate syn;
+ ///
+ /// use darling::{FromMeta, Error, Result};
+ /// use syn::{Lit, LitStr};
+ ///
+ /// pub struct Foo(String);
+ ///
+ /// impl FromMeta for Foo {
+ /// fn from_value(value: &Lit) -> Result<Self> {
+ /// if let Lit::Str(ref lit_str) = *value {
+ /// Ok(Foo(lit_str.value()))
+ /// } else {
+ /// Err(Error::unexpected_lit_type(value))
+ /// }
+ /// }
+ /// }
+ ///
+ /// # fn main() {}
+ /// ```
+ pub fn unexpected_lit_type(lit: &Lit) -> Self {
+ Error::unexpected_type(match *lit {
+ Lit::Str(_) => "string",
+ Lit::ByteStr(_) => "byte string",
+ Lit::Byte(_) => "byte",
+ Lit::Char(_) => "char",
+ Lit::Int(_) => "int",
+ Lit::Float(_) => "float",
+ Lit::Bool(_) => "bool",
+ Lit::Verbatim(_) => "verbatim",
+ // non-exhaustive enum
+ _ => "unknown",
+ })
+ .with_span(lit)
+ }
+
+ /// Creates a new error for a value which doesn't match a set of expected literals.
+ pub fn unknown_value(value: &str) -> Self {
+ Error::new(ErrorKind::UnknownValue(value.into()))
+ }
+
+ /// Creates a new error for a list which did not get enough items to proceed.
+ pub fn too_few_items(min: usize) -> Self {
+ Error::new(ErrorKind::TooFewItems(min))
+ }
+
+ /// Creates a new error when a list got more items than it supports. The `max` argument
+ /// is the largest number of items the receiver could accept.
+ pub fn too_many_items(max: usize) -> Self {
+ Error::new(ErrorKind::TooManyItems(max))
+ }
+
+ /// Bundle a set of multiple errors into a single `Error` instance.
+ ///
+ /// Usually it will be more convenient to use an [`error::Accumulator`](Accumulator).
+ ///
+ /// # Panics
+ /// This function will panic if `errors.is_empty() == true`.
+ pub fn multiple(mut errors: Vec<Error>) -> Self {
+ match errors.len() {
+ 1 => errors
+ .pop()
+ .expect("Error array of length 1 has a first item"),
+ 0 => panic!("Can't deal with 0 errors"),
+ _ => Error::new(ErrorKind::Multiple(errors)),
+ }
+ }
+
+ /// Creates an error collector, for aggregating multiple errors
+ ///
+ /// See [`Accumulator`] for details.
+ pub fn accumulator() -> Accumulator {
+ Default::default()
+ }
+}
+
+impl Error {
+ /// Create a new error about a literal string that doesn't match a set of known
+ /// or permissible values. This function can be made public if the API proves useful
+ /// beyond impls for `syn` types.
+ pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
+ Error::unknown_value(&value.value()).with_span(value)
+ }
+}
+
+/// Error instance methods
+#[allow(clippy::len_without_is_empty)] // Error can never be empty
+impl Error {
+ /// Check if this error is associated with a span in the token stream.
+ pub fn has_span(&self) -> bool {
+ self.span.is_some()
+ }
+
+ /// Tie a span to the error if none is already present. This is used in `darling::FromMeta`
+ /// and other traits to attach errors to the most specific possible location in the input
+ /// source code.
+ ///
+ /// All `darling`-built impls, either from the crate or from the proc macro, will call this
+ /// when appropriate during parsing, so it should not be necessary to call this unless you have
+ /// overridden:
+ ///
+ /// * `FromMeta::from_meta`
+ /// * `FromMeta::from_nested_meta`
+ /// * `FromMeta::from_value`
+ pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
+ if !self.has_span() {
+ self.span = Some(node.span());
+ }
+
+ self
+ }
+
+ /// Get a span for the error.
+ ///
+ /// # Return Value
+ /// This function will return [`Span::call_site()`](proc_macro2::Span) if [`Self::has_span`] is `false`.
+ /// To get the span only if one has been explicitly set for `self`, instead use [`Error::explicit_span`].
+ pub fn span(&self) -> Span {
+ self.span.unwrap_or_else(Span::call_site)
+ }
+
+ /// Get the span for `self`, if one has been set.
+ pub fn explicit_span(&self) -> Option<Span> {
+ self.span
+ }
+
+ /// Recursively converts a tree of errors to a flattened list.
+ ///
+ /// # Child Diagnostics
+ /// If the `diagnostics` feature is enabled, any child diagnostics on `self`
+ /// will be cloned down to all the errors within `self`.
+ pub fn flatten(self) -> Self {
+ Error::multiple(self.into_vec())
+ }
+
+ fn into_vec(self) -> Vec<Self> {
+ if let ErrorKind::Multiple(errors) = self.kind {
+ let locations = self.locations;
+
+ #[cfg(feature = "diagnostics")]
+ let children = self.children;
+
+ errors
+ .into_iter()
+ .flat_map(|error| {
+ // This is mutated if the diagnostics feature is enabled
+ #[allow(unused_mut)]
+ let mut error = error.prepend_at(locations.clone());
+
+ // Any child diagnostics in `self` are cloned down to all the distinct
+ // errors contained in `self`.
+ #[cfg(feature = "diagnostics")]
+ error.children.extend(children.iter().cloned());
+
+ error.into_vec()
+ })
+ .collect()
+ } else {
+ vec![self]
+ }
+ }
+
+ /// Adds a location to the error, such as a field or variant.
+ /// Locations must be added in reverse order of specificity.
+ pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
+ self.locations.insert(0, location.to_string());
+ self
+ }
+
+ /// Adds a location to the error, such as a field or variant.
+ /// Locations must be added in reverse order of specificity. This is a helper function to avoid
+ /// repeating path to string logic.
+ pub fn at_path(self, path: &Path) -> Self {
+ self.at(path_to_string(path))
+ }
+
+ /// Gets the number of individual errors in this error.
+ ///
+ /// This function never returns `0`, as it's impossible to construct
+ /// a multi-error from an empty `Vec`.
+ pub fn len(&self) -> usize {
+ self.kind.len()
+ }
+
+ /// Consider additional field names as "did you mean" suggestions for
+ /// unknown field errors **if and only if** the caller appears to be operating
+ /// at error's origin (meaning no calls to [`Self::at`] have yet taken place).
+ ///
+ /// # Usage
+ /// `flatten` fields in derived trait implementations rely on this method to offer correct
+ /// "did you mean" suggestions in errors.
+ ///
+ /// Because the `flatten` field receives _all_ unknown fields, if a user mistypes a field name
+ /// that is present on the outer struct but not the flattened struct, they would get an incomplete
+ /// or inferior suggestion unless this method was invoked.
+ pub fn add_sibling_alts_for_unknown_field<'a, T, I>(mut self, alternates: I) -> Self
+ where
+ T: AsRef<str> + 'a,
+ I: IntoIterator<Item = &'a T>,
+ {
+ // The error may have bubbled up before this method was called,
+ // and in those cases adding alternates would be incorrect.
+ if !self.locations.is_empty() {
+ return self;
+ }
+
+ if let ErrorKind::UnknownField(unknown_field) = &mut self.kind {
+ unknown_field.add_alts(alternates);
+ } else if let ErrorKind::Multiple(errors) = self.kind {
+ let alternates = alternates.into_iter().collect::<Vec<_>>();
+ self.kind = ErrorKind::Multiple(
+ errors
+ .into_iter()
+ .map(|err| {
+ err.add_sibling_alts_for_unknown_field(
+ // This clone seems like it shouldn't be necessary.
+ // Attempting to borrow alternates here leads to the following compiler error:
+ //
+ // error: reached the recursion limit while instantiating `darling::Error::add_sibling_alts_for_unknown_field::<'_, &&&&..., ...>`
+ alternates.clone(),
+ )
+ })
+ .collect(),
+ )
+ }
+
+ self
+ }
+
+ /// Adds a location chain to the head of the error's existing locations.
+ fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
+ if !locations.is_empty() {
+ locations.extend(self.locations);
+ self.locations = locations;
+ }
+
+ self
+ }
+
+ /// Gets the location slice.
+ #[cfg(test)]
+ pub(crate) fn location(&self) -> Vec<&str> {
+ self.locations.iter().map(|i| i.as_str()).collect()
+ }
+
+ /// Write this error and any children as compile errors into a `TokenStream` to
+ /// be returned by the proc-macro.
+ ///
+ /// The behavior of this method will be slightly different if the `diagnostics` feature
+ /// is enabled: In that case, the diagnostics will be emitted immediately by this call,
+ /// and an empty `TokenStream` will be returned.
+ ///
+ /// Return these tokens unmodified to avoid disturbing the attached span information.
+ ///
+ /// # Usage
+ /// ```rust,ignore
+ /// // in your proc-macro function
+ /// let opts = match MyOptions::from_derive_input(&ast) {
+ /// Ok(val) => val,
+ /// Err(err) => {
+ /// return err.write_errors();
+ /// }
+ /// }
+ /// ```
+ pub fn write_errors(self) -> TokenStream {
+ #[cfg(feature = "diagnostics")]
+ {
+ self.emit();
+ TokenStream::default()
+ }
+
+ #[cfg(not(feature = "diagnostics"))]
+ {
+ syn::Error::from(self).into_compile_error()
+ }
+ }
+
+ #[cfg(feature = "diagnostics")]
+ fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
+ use proc_macro::{Diagnostic, Level};
+
+ // Delegate to dedicated error formatters when applicable.
+ //
+ // If span information is available, don't include the error property path
+ // since it's redundant and not consistent with native compiler diagnostics.
+ let diagnostic = match self.kind {
+ ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
+ _ => match self.span {
+ Some(span) => span.unwrap().error(self.kind.to_string()),
+ None => Diagnostic::new(Level::Error, self.to_string()),
+ },
+ };
+
+ self.children
+ .into_iter()
+ .fold(diagnostic, |out, child| child.append_to(out))
+ }
+
+ /// Transform this error and its children into a list of compiler diagnostics
+ /// and emit them. If the `Error` has associated span information, the diagnostics
+ /// will identify the correct location in source code automatically.
+ ///
+ /// # Stability
+ /// This is only available on `nightly` until the compiler `proc_macro_diagnostic`
+ /// feature stabilizes. Until then, it may break at any time.
+ #[cfg(feature = "diagnostics")]
+ pub fn emit(self) {
+ for error in self.flatten() {
+ error.single_to_diagnostic().emit()
+ }
+ }
+
+ /// Transform the error into a compiler diagnostic and - if the diagnostic points to
+ /// a specific code location - add a spanned help child diagnostic that points to the
+ /// parent derived trait.
+ ///
+ /// This is experimental and therefore not exposed outside the crate.
+ #[cfg(feature = "diagnostics")]
+ #[allow(dead_code)]
+ fn emit_with_macro_help_span(self) {
+ use proc_macro::Diagnostic;
+
+ for error in self.flatten() {
+ let needs_help = error.has_span();
+ let diagnostic = error.single_to_diagnostic();
+ Diagnostic::emit(if needs_help {
+ diagnostic.span_help(
+ Span::call_site().unwrap(),
+ "Encountered as part of this derive-mode-macro",
+ )
+ } else {
+ diagnostic
+ })
+ }
+ }
+}
+
+#[cfg(feature = "diagnostics")]
+macro_rules! add_child {
+ ($unspanned:ident, $spanned:ident, $level:ident) => {
+ #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
+ #[doc = "# Example"]
+ #[doc = "```rust"]
+ #[doc = "# use darling_core::Error;"]
+ #[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
+ #[doc = "```"]
+ pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
+ self.children.push(child::ChildDiagnostic::new(
+ child::Level::$level,
+ None,
+ message.to_string(),
+ ));
+ self
+ }
+
+ #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
+ #[doc = "# Example"]
+ #[doc = "```rust"]
+ #[doc = "# use darling_core::Error;"]
+ #[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
+ #[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
+ #[doc = "```"]
+ pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
+ self.children.push(child::ChildDiagnostic::new(
+ child::Level::$level,
+ Some(span.span()),
+ message.to_string(),
+ ));
+ self
+ }
+ };
+}
+
+/// Add child diagnostics to the error.
+///
+/// # Example
+///
+/// ## Code
+///
+/// ```rust
+/// # use darling_core::Error;
+/// # let struct_ident = proc_macro2::Span::call_site();
+/// Error::custom("this is a demo")
+/// .with_span(&struct_ident)
+/// .note("we wrote this")
+/// .help("try doing this instead");
+/// ```
+/// ## Output
+///
+/// ```text
+/// error: this is a demo
+/// --> my_project/my_file.rs:3:5
+/// |
+/// 13 | FooBar { value: String },
+/// | ^^^^^^
+/// |
+/// = note: we wrote this
+/// = help: try doing this instead
+/// ```
+#[cfg(feature = "diagnostics")]
+impl Error {
+ add_child!(error, span_error, Error);
+ add_child!(warning, span_warning, Warning);
+ add_child!(note, span_note, Note);
+ add_child!(help, span_help, Help);
+}
+
+impl StdError for Error {
+ fn description(&self) -> &str {
+ self.kind.description()
+ }
+
+ fn cause(&self) -> Option<&dyn StdError> {
+ None
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.kind)?;
+ if !self.locations.is_empty() {
+ write!(f, " at {}", self.locations.join("/"))?;
+ }
+
+ Ok(())
+ }
+}
+
+impl From<syn::Error> for Error {
+ fn from(e: syn::Error) -> Self {
+ // This impl assumes there is nothing but the message and span that needs to be preserved
+ // from the passed-in error. If this changes at some point, a new ErrorKind should be made
+ // to hold the syn::Error, and this impl should preserve it unmodified while setting its own
+ // span to be a copy of the passed-in error.
+ Self {
+ span: Some(e.span()),
+ ..Self::custom(e)
+ }
+ }
+}
+
+impl From<Error> for syn::Error {
+ fn from(e: Error) -> Self {
+ if e.len() == 1 {
+ if let Some(span) = e.explicit_span() {
+ // Don't include the location path if the error has an explicit span,
+ // since it will be redundant and isn't consistent with how rustc
+ // exposes errors.
+ syn::Error::new(span, e.kind)
+ } else {
+ // If the error's span is going to be the macro call site, include
+ // the location information to try and help the user pinpoint the issue.
+ syn::Error::new(e.span(), e)
+ }
+ } else {
+ let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
+ let mut error = syn_errors
+ .next()
+ .expect("darling::Error can never be empty");
+
+ for next_error in syn_errors {
+ error.combine(next_error);
+ }
+
+ error
+ }
+ }
+}
+
+// Don't want to publicly commit to Error supporting equality yet, but
+// not having it makes testing very difficult. Note that spans are not
+// considered for equality since that would break testing in most cases.
+#[cfg(test)]
+impl PartialEq for Error {
+ fn eq(&self, other: &Self) -> bool {
+ self.kind == other.kind && self.locations == other.locations
+ }
+}
+
+#[cfg(test)]
+impl Eq for Error {}
+
+impl IntoIterator for Error {
+ type Item = Error;
+ type IntoIter = IntoIter;
+
+ fn into_iter(self) -> IntoIter {
+ if let ErrorKind::Multiple(errors) = self.kind {
+ IntoIter {
+ inner: IntoIterEnum::Multiple(errors.into_iter()),
+ }
+ } else {
+ IntoIter {
+ inner: IntoIterEnum::Single(iter::once(self)),
+ }
+ }
+ }
+}
+
+enum IntoIterEnum {
+ Single(iter::Once<Error>),
+ Multiple(vec::IntoIter<Error>),
+}
+
+impl Iterator for IntoIterEnum {
+ type Item = Error;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match *self {
+ IntoIterEnum::Single(ref mut content) => content.next(),
+ IntoIterEnum::Multiple(ref mut content) => content.next(),
+ }
+ }
+}
+
+/// An iterator that moves out of an `Error`.
+pub struct IntoIter {
+ inner: IntoIterEnum,
+}
+
+impl Iterator for IntoIter {
+ type Item = Error;
+
+ fn next(&mut self) -> Option<Error> {
+ self.inner.next()
+ }
+}
+
+/// Accumulator for errors, for helping call [`Error::multiple`].
+///
+/// See the docs for [`darling::Error`](Error) for more discussion of error handling with darling.
+///
+/// # Panics
+///
+/// `Accumulator` panics on drop unless [`finish`](Self::finish), [`finish_with`](Self::finish_with),
+/// or [`into_inner`](Self::into_inner) has been called, **even if it contains no errors**.
+/// If you want to discard an `Accumulator` that you know to be empty, use `accumulator.finish().unwrap()`.
+///
+/// # Example
+///
+/// ```
+/// # extern crate darling_core as darling;
+/// # struct Thing;
+/// # struct Output;
+/// # impl Thing { fn validate(self) -> darling::Result<Output> { Ok(Output) } }
+/// fn validate_things(inputs: Vec<Thing>) -> darling::Result<Vec<Output>> {
+/// let mut errors = darling::Error::accumulator();
+///
+/// let outputs = inputs
+/// .into_iter()
+/// .filter_map(|thing| errors.handle_in(|| thing.validate()))
+/// .collect::<Vec<_>>();
+///
+/// errors.finish()?;
+/// Ok(outputs)
+/// }
+/// ```
+#[derive(Debug)]
+#[must_use = "Accumulator will panic on drop if not defused."]
+pub struct Accumulator(Option<Vec<Error>>);
+
+impl Accumulator {
+ /// Runs a closure, returning the successful value as `Some`, or collecting the error
+ ///
+ /// The closure's return type is `darling::Result`, so inside it one can use `?`.
+ pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
+ self.handle(f())
+ }
+
+ /// Handles a possible error.
+ ///
+ /// Returns a successful value as `Some`, or collects the error and returns `None`.
+ pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
+ match result {
+ Ok(y) => Some(y),
+ Err(e) => {
+ self.push(e);
+ None
+ }
+ }
+ }
+
+ /// Stop accumulating errors, producing `Ok` if there are no errors or producing
+ /// an error with all those encountered by the accumulator.
+ pub fn finish(self) -> Result<()> {
+ self.finish_with(())
+ }
+
+ /// Bundles the collected errors if there were any, or returns the success value
+ ///
+ /// Call this at the end of your input processing.
+ ///
+ /// If there were no errors recorded, returns `Ok(success)`.
+ /// Otherwise calls [`Error::multiple`] and returns the result as an `Err`.
+ pub fn finish_with<T>(self, success: T) -> Result<T> {
+ let errors = self.into_inner();
+ if errors.is_empty() {
+ Ok(success)
+ } else {
+ Err(Error::multiple(errors))
+ }
+ }
+
+ fn errors(&mut self) -> &mut Vec<Error> {
+ match &mut self.0 {
+ Some(errors) => errors,
+ None => panic!("darling internal error: Accumulator accessed after defuse"),
+ }
+ }
+
+ /// Returns the accumulated errors as a `Vec`.
+ ///
+ /// This function defuses the drop bomb.
+ #[must_use = "Accumulated errors should be handled or propagated to the caller"]
+ pub fn into_inner(mut self) -> Vec<Error> {
+ match self.0.take() {
+ Some(errors) => errors,
+ None => panic!("darling internal error: Accumulator accessed after defuse"),
+ }
+ }
+
+ /// Add one error to the collection.
+ pub fn push(&mut self, error: Error) {
+ self.errors().push(error)
+ }
+
+ /// Finish the current accumulation, and if there are no errors create a new `Self` so processing may continue.
+ ///
+ /// This is shorthand for:
+ ///
+ /// ```rust,ignore
+ /// errors.finish()?;
+ /// errors = Error::accumulator();
+ /// ```
+ ///
+ /// # Drop Behavior
+ /// This function returns a new [`Accumulator`] in the success case.
+ /// This new accumulator is "armed" and will detonate if dropped without being finished.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # extern crate darling_core as darling;
+ /// # struct Thing;
+ /// # struct Output;
+ /// # impl Thing { fn validate(&self) -> darling::Result<Output> { Ok(Output) } }
+ /// fn validate(lorem_inputs: &[Thing], ipsum_inputs: &[Thing])
+ /// -> darling::Result<(Vec<Output>, Vec<Output>)> {
+ /// let mut errors = darling::Error::accumulator();
+ ///
+ /// let lorems = lorem_inputs.iter().filter_map(|l| {
+ /// errors.handle(l.validate())
+ /// }).collect();
+ ///
+ /// errors = errors.checkpoint()?;
+ ///
+ /// let ipsums = ipsum_inputs.iter().filter_map(|l| {
+ /// errors.handle(l.validate())
+ /// }).collect();
+ ///
+ /// errors.finish_with((lorems, ipsums))
+ /// }
+ /// # validate(&[], &[]).unwrap();
+ /// ```
+ pub fn checkpoint(self) -> Result<Accumulator> {
+ // The doc comment says on success we "return the Accumulator for future use".
+ // Actually, we have consumed it by feeding it to finish so we make a fresh one.
+ // This is OK since by definition of the success path, it was empty on entry.
+ self.finish()?;
+ Ok(Self::default())
+ }
+}
+
+impl Default for Accumulator {
+ fn default() -> Self {
+ Accumulator(Some(vec![]))
+ }
+}
+
+impl Extend<Error> for Accumulator {
+ fn extend<I>(&mut self, iter: I)
+ where
+ I: IntoIterator<Item = Error>,
+ {
+ self.errors().extend(iter)
+ }
+}
+
+impl Drop for Accumulator {
+ fn drop(&mut self) {
+ // don't try to panic if we are currently unwinding a panic
+ // otherwise we end up with an unhelful "thread panicked while panicking. aborting." message
+ if !std::thread::panicking() {
+ if let Some(errors) = &mut self.0 {
+ match errors.len() {
+ 0 => panic!("darling::error::Accumulator dropped without being finished"),
+ error_count => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Error;
+
+ #[test]
+ fn flatten_noop() {
+ let err = Error::duplicate_field("hello").at("world");
+ assert_eq!(err.clone().flatten(), err);
+ }
+
+ #[test]
+ fn flatten_simple() {
+ let err = Error::multiple(vec![
+ Error::unknown_field("hello").at("world"),
+ Error::missing_field("hell_no").at("world"),
+ ])
+ .at("foo")
+ .flatten();
+
+ assert!(err.location().is_empty());
+
+ let mut err_iter = err.into_iter();
+
+ let first = err_iter.next();
+ assert!(first.is_some());
+ assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
+
+ let second = err_iter.next();
+ assert!(second.is_some());
+
+ assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
+
+ assert!(err_iter.next().is_none());
+ }
+
+ #[test]
+ fn len_single() {
+ let err = Error::duplicate_field("hello");
+ assert_eq!(1, err.len());
+ }
+
+ #[test]
+ fn len_multiple() {
+ let err = Error::multiple(vec![
+ Error::duplicate_field("hello"),
+ Error::missing_field("hell_no"),
+ ]);
+ assert_eq!(2, err.len());
+ }
+
+ #[test]
+ fn len_nested() {
+ let err = Error::multiple(vec![
+ Error::duplicate_field("hello"),
+ Error::multiple(vec![
+ Error::duplicate_field("hi"),
+ Error::missing_field("bye"),
+ Error::multiple(vec![Error::duplicate_field("whatsup")]),
+ ]),
+ ]);
+
+ assert_eq!(4, err.len());
+ }
+
+ #[test]
+ fn accum_ok() {
+ let errs = Error::accumulator();
+ assert_eq!("test", errs.finish_with("test").unwrap());
+ }
+
+ #[test]
+ fn accum_errr() {
+ let mut errs = Error::accumulator();
+ errs.push(Error::custom("foo!"));
+ errs.finish().unwrap_err();
+ }
+
+ #[test]
+ fn accum_into_inner() {
+ let mut errs = Error::accumulator();
+ errs.push(Error::custom("foo!"));
+ let errs: Vec<_> = errs.into_inner();
+ assert_eq!(errs.len(), 1);
+ }
+
+ #[test]
+ #[should_panic(expected = "Accumulator dropped")]
+ fn accum_drop_panic() {
+ let _errs = Error::accumulator();
+ }
+
+ #[test]
+ #[should_panic(expected = "2 errors")]
+ fn accum_drop_panic_with_error_count() {
+ let mut errors = Error::accumulator();
+ errors.push(Error::custom("first"));
+ errors.push(Error::custom("second"));
+ }
+
+ #[test]
+ fn accum_checkpoint_error() {
+ let mut errs = Error::accumulator();
+ errs.push(Error::custom("foo!"));
+ errs.checkpoint().unwrap_err();
+ }
+
+ #[test]
+ #[should_panic(expected = "Accumulator dropped")]
+ fn accum_checkpoint_drop_panic() {
+ let mut errs = Error::accumulator();
+ errs = errs.checkpoint().unwrap();
+ let _ = errs;
+ }
+}
diff --git a/src/from_attributes.rs b/src/from_attributes.rs
new file mode 100644
index 0000000..c73f0f2
--- /dev/null
+++ b/src/from_attributes.rs
@@ -0,0 +1,27 @@
+use syn::Attribute;
+
+use crate::Result;
+
+/// Create an instance by parsing a list of attributes.
+///
+/// This trait is useful when dealing with items such as traits on traits and impl blocks,
+/// for which `darling` does not provide dedicated traits.
+pub trait FromAttributes: Sized {
+ /// Create an instance by parsing a list of attributes.
+ ///
+ /// By convention, `FromAttributes` implementations should merge item
+ /// declarations across attributes, so that the following forms are
+ /// equivalent:
+ ///
+ /// ```rust,ignore
+ /// #[derive(Serialize)]
+ /// #[serde(rename_all = "camel_case")]
+ /// #[serde(borrow)]
+ /// pub struct SplitExample {}
+ ///
+ /// #[derive(Serialize)]
+ /// #[serde(borrow, rename_all = "camel_case")]
+ /// pub struct JoinedExample {}
+ /// ```
+ fn from_attributes(attrs: &[Attribute]) -> Result<Self>;
+}
diff --git a/src/from_derive_input.rs b/src/from_derive_input.rs
new file mode 100644
index 0000000..b5bb86f
--- /dev/null
+++ b/src/from_derive_input.rs
@@ -0,0 +1,26 @@
+use syn::DeriveInput;
+
+use crate::Result;
+
+/// Creates an instance by parsing an entire proc-macro `derive` input,
+/// including the, identity, generics, and visibility of the type.
+///
+/// This trait should either be derived or manually implemented by a type
+/// in the proc macro crate which is directly using `darling`. It is unlikely
+/// that these implementations will be reusable across crates.
+pub trait FromDeriveInput: Sized {
+ /// Create an instance from `syn::DeriveInput`, or return an error.
+ fn from_derive_input(input: &DeriveInput) -> Result<Self>;
+}
+
+impl FromDeriveInput for () {
+ fn from_derive_input(_: &DeriveInput) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromDeriveInput for DeriveInput {
+ fn from_derive_input(input: &DeriveInput) -> Result<Self> {
+ Ok(input.clone())
+ }
+}
diff --git a/src/from_field.rs b/src/from_field.rs
new file mode 100644
index 0000000..7ce4e55
--- /dev/null
+++ b/src/from_field.rs
@@ -0,0 +1,38 @@
+use syn::Field;
+
+use crate::Result;
+
+/// Creates an instance by parsing an individual field and its attributes.
+pub trait FromField: Sized {
+ fn from_field(field: &Field) -> Result<Self>;
+}
+
+impl FromField for () {
+ fn from_field(_: &Field) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromField for Field {
+ fn from_field(field: &Field) -> Result<Self> {
+ Ok(field.clone())
+ }
+}
+
+impl FromField for syn::Type {
+ fn from_field(field: &Field) -> Result<Self> {
+ Ok(field.ty.clone())
+ }
+}
+
+impl FromField for syn::Visibility {
+ fn from_field(field: &Field) -> Result<Self> {
+ Ok(field.vis.clone())
+ }
+}
+
+impl FromField for Vec<syn::Attribute> {
+ fn from_field(field: &Field) -> Result<Self> {
+ Ok(field.attrs.clone())
+ }
+}
diff --git a/src/from_generic_param.rs b/src/from_generic_param.rs
new file mode 100644
index 0000000..757ad76
--- /dev/null
+++ b/src/from_generic_param.rs
@@ -0,0 +1,19 @@
+use crate::Result;
+
+/// Creates an instance by parsing a specific `syn::GenericParam`.
+/// This can be a type param, a lifetime, or a const param.
+pub trait FromGenericParam: Sized {
+ fn from_generic_param(param: &syn::GenericParam) -> Result<Self>;
+}
+
+impl FromGenericParam for () {
+ fn from_generic_param(_param: &syn::GenericParam) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromGenericParam for syn::GenericParam {
+ fn from_generic_param(param: &syn::GenericParam) -> Result<Self> {
+ Ok(param.clone())
+ }
+}
diff --git a/src/from_generics.rs b/src/from_generics.rs
new file mode 100644
index 0000000..8f68710
--- /dev/null
+++ b/src/from_generics.rs
@@ -0,0 +1,27 @@
+use syn::Generics;
+
+use crate::Result;
+
+/// Creates an instance by parsing an entire generics declaration, including the
+/// `where` clause.
+pub trait FromGenerics: Sized {
+ fn from_generics(generics: &Generics) -> Result<Self>;
+}
+
+impl FromGenerics for () {
+ fn from_generics(_generics: &Generics) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromGenerics for Generics {
+ fn from_generics(generics: &Generics) -> Result<Self> {
+ Ok(generics.clone())
+ }
+}
+
+impl<T: FromGenerics> FromGenerics for Result<T> {
+ fn from_generics(generics: &Generics) -> Result<Self> {
+ Ok(FromGenerics::from_generics(generics))
+ }
+}
diff --git a/src/from_meta.rs b/src/from_meta.rs
new file mode 100644
index 0000000..7e50e4e
--- /dev/null
+++ b/src/from_meta.rs
@@ -0,0 +1,1085 @@
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::hash_map::HashMap;
+use std::collections::HashSet;
+use std::hash::BuildHasher;
+use std::rc::Rc;
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+
+use syn::{Expr, Lit, Meta};
+
+use crate::ast::NestedMeta;
+use crate::util::path_to_string;
+use crate::{Error, Result};
+
+/// Create an instance from an item in an attribute declaration.
+///
+/// # Implementing `FromMeta`
+/// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct.
+/// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors.
+///
+/// # Provided Implementations
+/// ## bool
+///
+/// * Word with no value specified - becomes `true`.
+/// * As a boolean literal, e.g. `foo = true`.
+/// * As a string literal, e.g. `foo = "true"`.
+///
+/// ## char
+/// * As a char literal, e.g. `foo = '#'`.
+/// * As a string literal consisting of a single character, e.g. `foo = "#"`.
+///
+/// ## String
+/// * As a string literal, e.g. `foo = "hello"`.
+/// * As a raw string literal, e.g. `foo = r#"hello "world""#`.
+///
+/// ## Number
+/// * As a string literal, e.g. `foo = "-25"`.
+/// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks.
+///
+/// ## ()
+/// * Word with no value specified, e.g. `foo`. This is best used with `Option`.
+/// See `darling::util::Flag` for a more strongly-typed alternative.
+///
+/// ## Option
+/// * Any format produces `Some`.
+///
+/// ## `Result<T, darling::Error>`
+/// * Allows for fallible parsing; will populate the target field with the result of the
+/// parse attempt.
+pub trait FromMeta: Sized {
+ fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
+ (match *item {
+ NestedMeta::Lit(ref lit) => Self::from_value(lit),
+ NestedMeta::Meta(ref mi) => Self::from_meta(mi),
+ })
+ .map_err(|e| e.with_span(item))
+ }
+
+ /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
+ /// trait function. This generally should not be overridden by implementers.
+ ///
+ /// # Error Spans
+ /// If this method is overridden and can introduce errors that weren't passed up from
+ /// other `from_meta` calls, the override must call `with_span` on the error using the
+ /// `item` to make sure that the emitted diagnostic points to the correct location in
+ /// source code.
+ fn from_meta(item: &Meta) -> Result<Self> {
+ (match *item {
+ Meta::Path(_) => Self::from_word(),
+ Meta::List(ref value) => {
+ Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..])
+ }
+ Meta::NameValue(ref value) => Self::from_expr(&value.value),
+ })
+ .map_err(|e| e.with_span(item))
+ }
+
+ /// When a field is omitted from a parent meta-item, `from_none` is used to attempt
+ /// recovery before a missing field error is generated.
+ ///
+ /// **Most types should not override this method.** `darling` already allows field-level
+ /// missing-field recovery using `#[darling(default)]` and `#[darling(default = "...")]`,
+ /// and users who add a `String` field to their `FromMeta`-deriving struct would be surprised
+ /// if they get back `""` instead of a missing field error when that field is omitted.
+ ///
+ /// The primary use-case for this is `Option<T>` fields gracefully handlling absence without
+ /// needing `#[darling(default)]`.
+ fn from_none() -> Option<Self> {
+ None
+ }
+
+ /// Create an instance from the presence of the word in the attribute with no
+ /// additional options specified.
+ fn from_word() -> Result<Self> {
+ Err(Error::unsupported_format("word"))
+ }
+
+ /// Create an instance from a list of nested meta items.
+ #[allow(unused_variables)]
+ fn from_list(items: &[NestedMeta]) -> Result<Self> {
+ Err(Error::unsupported_format("list"))
+ }
+
+ /// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`.
+ /// This dispatches to the appropriate method based on the type of literal encountered,
+ /// and generally should not be overridden by implementers.
+ ///
+ /// # Error Spans
+ /// If this method is overridden, the override must make sure to add `value`'s span
+ /// information to the returned error by calling `with_span(value)` on the `Error` instance.
+ fn from_value(value: &Lit) -> Result<Self> {
+ (match *value {
+ Lit::Bool(ref b) => Self::from_bool(b.value),
+ Lit::Str(ref s) => Self::from_string(&s.value()),
+ Lit::Char(ref ch) => Self::from_char(ch.value()),
+ _ => Err(Error::unexpected_lit_type(value)),
+ })
+ .map_err(|e| e.with_span(value))
+ }
+
+ fn from_expr(expr: &Expr) -> Result<Self> {
+ match *expr {
+ Expr::Lit(ref lit) => Self::from_value(&lit.lit),
+ Expr::Group(ref group) => {
+ // syn may generate this invisible group delimiter when the input to the darling
+ // proc macro (specifically, the attributes) are generated by a
+ // macro_rules! (e.g. propagating a macro_rules!'s expr)
+ // Since we want to basically ignore these invisible group delimiters,
+ // we just propagate the call to the inner expression.
+ Self::from_expr(&group.expr)
+ }
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ .map_err(|e| e.with_span(expr))
+ }
+
+ /// Create an instance from a char literal in a value position.
+ #[allow(unused_variables)]
+ fn from_char(value: char) -> Result<Self> {
+ Err(Error::unexpected_type("char"))
+ }
+
+ /// Create an instance from a string literal in a value position.
+ #[allow(unused_variables)]
+ fn from_string(value: &str) -> Result<Self> {
+ Err(Error::unexpected_type("string"))
+ }
+
+ /// Create an instance from a bool literal in a value position.
+ #[allow(unused_variables)]
+ fn from_bool(value: bool) -> Result<Self> {
+ Err(Error::unexpected_type("bool"))
+ }
+}
+
+// FromMeta impls for std and syn types.
+
+impl FromMeta for () {
+ fn from_word() -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromMeta for bool {
+ fn from_word() -> Result<Self> {
+ Ok(true)
+ }
+
+ #[allow(clippy::wrong_self_convention)] // false positive
+ fn from_bool(value: bool) -> Result<Self> {
+ Ok(value)
+ }
+
+ fn from_string(value: &str) -> Result<Self> {
+ value.parse().map_err(|_| Error::unknown_value(value))
+ }
+}
+
+impl FromMeta for AtomicBool {
+ fn from_meta(mi: &Meta) -> Result<Self> {
+ FromMeta::from_meta(mi)
+ .map(AtomicBool::new)
+ .map_err(|e| e.with_span(mi))
+ }
+}
+
+impl FromMeta for char {
+ #[allow(clippy::wrong_self_convention)] // false positive
+ fn from_char(value: char) -> Result<Self> {
+ Ok(value)
+ }
+
+ fn from_string(s: &str) -> Result<Self> {
+ let mut chars = s.chars();
+ let char1 = chars.next();
+ let char2 = chars.next();
+
+ if let (Some(char), None) = (char1, char2) {
+ Ok(char)
+ } else {
+ Err(Error::unexpected_type("string"))
+ }
+ }
+}
+
+impl FromMeta for String {
+ fn from_string(s: &str) -> Result<Self> {
+ Ok(s.to_string())
+ }
+}
+
+impl FromMeta for std::path::PathBuf {
+ fn from_string(s: &str) -> Result<Self> {
+ Ok(s.into())
+ }
+}
+
+/// Generate an impl of `FromMeta` that will accept strings which parse to numbers or
+/// integer literals.
+macro_rules! from_meta_num {
+ ($ty:ident) => {
+ impl FromMeta for $ty {
+ fn from_string(s: &str) -> Result<Self> {
+ s.parse().map_err(|_| Error::unknown_value(s))
+ }
+
+ fn from_value(value: &Lit) -> Result<Self> {
+ (match *value {
+ Lit::Str(ref s) => Self::from_string(&s.value()),
+ Lit::Int(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
+ _ => Err(Error::unexpected_lit_type(value)),
+ })
+ .map_err(|e| e.with_span(value))
+ }
+ }
+ };
+}
+
+from_meta_num!(u8);
+from_meta_num!(u16);
+from_meta_num!(u32);
+from_meta_num!(u64);
+from_meta_num!(u128);
+from_meta_num!(usize);
+from_meta_num!(i8);
+from_meta_num!(i16);
+from_meta_num!(i32);
+from_meta_num!(i64);
+from_meta_num!(i128);
+from_meta_num!(isize);
+
+/// Generate an impl of `FromMeta` that will accept strings which parse to floats or
+/// float literals.
+macro_rules! from_meta_float {
+ ($ty:ident) => {
+ impl FromMeta for $ty {
+ fn from_string(s: &str) -> Result<Self> {
+ s.parse().map_err(|_| Error::unknown_value(s))
+ }
+
+ fn from_value(value: &Lit) -> Result<Self> {
+ (match *value {
+ Lit::Str(ref s) => Self::from_string(&s.value()),
+ Lit::Float(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
+ _ => Err(Error::unexpected_lit_type(value)),
+ })
+ .map_err(|e| e.with_span(value))
+ }
+ }
+ };
+}
+
+from_meta_float!(f32);
+from_meta_float!(f64);
+
+/// Parsing support for punctuated. This attempts to preserve span information
+/// when available, but also supports parsing strings with the call site as the
+/// emitted span.
+impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
+ fn from_value(value: &Lit) -> Result<Self> {
+ if let Lit::Str(ref ident) = *value {
+ ident
+ .parse_with(syn::punctuated::Punctuated::parse_terminated)
+ .map_err(|_| Error::unknown_lit_str_value(ident))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+}
+
+/// Support for arbitrary expressions as values in a meta item.
+///
+/// For backwards-compatibility to versions of `darling` based on `syn` 1,
+/// string literals will be "unwrapped" and their contents will be parsed
+/// as an expression.
+///
+/// See [`util::parse_expr`](crate::util::parse_expr) for functions to provide
+/// alternate parsing modes for this type.
+impl FromMeta for syn::Expr {
+ fn from_expr(expr: &Expr) -> Result<Self> {
+ match expr {
+ Expr::Lit(syn::ExprLit {
+ lit: lit @ syn::Lit::Str(_),
+ ..
+ }) => Self::from_value(lit),
+ Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
+ _ => Ok(expr.clone()),
+ }
+ }
+
+ fn from_string(value: &str) -> Result<Self> {
+ syn::parse_str(value).map_err(|_| Error::unknown_value(value))
+ }
+
+ fn from_value(value: &::syn::Lit) -> Result<Self> {
+ if let ::syn::Lit::Str(ref v) = *value {
+ v.parse::<syn::Expr>()
+ .map_err(|_| Error::unknown_lit_str_value(v))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+}
+
+/// Parser for paths that supports both quote-wrapped and bare values.
+impl FromMeta for syn::Path {
+ fn from_string(value: &str) -> Result<Self> {
+ syn::parse_str(value).map_err(|_| Error::unknown_value(value))
+ }
+
+ fn from_value(value: &::syn::Lit) -> Result<Self> {
+ if let ::syn::Lit::Str(ref v) = *value {
+ v.parse().map_err(|_| Error::unknown_lit_str_value(v))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+
+ fn from_expr(expr: &Expr) -> Result<Self> {
+ match expr {
+ Expr::Lit(lit) => Self::from_value(&lit.lit),
+ Expr::Path(path) => Ok(path.path.clone()),
+ Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ }
+}
+
+impl FromMeta for syn::Ident {
+ fn from_string(value: &str) -> Result<Self> {
+ syn::parse_str(value).map_err(|_| Error::unknown_value(value))
+ }
+
+ fn from_value(value: &syn::Lit) -> Result<Self> {
+ if let syn::Lit::Str(ref v) = *value {
+ v.parse().map_err(|_| Error::unknown_lit_str_value(v))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+
+ fn from_expr(expr: &Expr) -> Result<Self> {
+ match expr {
+ Expr::Lit(lit) => Self::from_value(&lit.lit),
+ // All idents are paths, but not all paths are idents -
+ // the get_ident() method does additional validation to
+ // make sure the path is actually an ident.
+ Expr::Path(path) => match path.path.get_ident() {
+ Some(ident) => Ok(ident.clone()),
+ None => Err(Error::unexpected_expr_type(expr)),
+ },
+ Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ }
+}
+
+/// Adapter for various expression types.
+///
+/// Prior to syn 2.0, darling supported arbitrary expressions as long as they
+/// were wrapped in quotation marks. This was helpful for people writing
+/// libraries that needed expressions, but it now creates an ambiguity when
+/// parsing a meta item.
+///
+/// To address this, the macro supports both formats; if it cannot parse the
+/// item as an expression of the right type and the passed-in expression is
+/// a string literal, it will fall back to parsing the string contents.
+macro_rules! from_syn_expr_type {
+ ($ty:path, $variant:ident) => {
+ impl FromMeta for $ty {
+ fn from_expr(expr: &syn::Expr) -> Result<Self> {
+ match expr {
+ syn::Expr::$variant(body) => Ok(body.clone()),
+ syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
+ syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ }
+
+ fn from_value(value: &::syn::Lit) -> Result<Self> {
+ if let syn::Lit::Str(body) = &value {
+ body.parse::<$ty>()
+ .map_err(|_| Error::unknown_lit_str_value(body))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+ }
+ };
+}
+
+from_syn_expr_type!(syn::ExprArray, Array);
+from_syn_expr_type!(syn::ExprPath, Path);
+
+/// Adapter from `syn::parse::Parse` to `FromMeta` for items that cannot
+/// be expressed in a [`syn::MetaNameValue`].
+///
+/// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values.
+/// Therefore, we use a macro and a lot of impls.
+macro_rules! from_syn_parse {
+ ($ty:path) => {
+ impl FromMeta for $ty {
+ fn from_string(value: &str) -> Result<Self> {
+ syn::parse_str(value).map_err(|_| Error::unknown_value(value))
+ }
+
+ fn from_value(value: &::syn::Lit) -> Result<Self> {
+ if let ::syn::Lit::Str(ref v) = *value {
+ v.parse::<$ty>()
+ .map_err(|_| Error::unknown_lit_str_value(v))
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+ }
+ };
+}
+
+from_syn_parse!(syn::Type);
+from_syn_parse!(syn::TypeArray);
+from_syn_parse!(syn::TypeBareFn);
+from_syn_parse!(syn::TypeGroup);
+from_syn_parse!(syn::TypeImplTrait);
+from_syn_parse!(syn::TypeInfer);
+from_syn_parse!(syn::TypeMacro);
+from_syn_parse!(syn::TypeNever);
+from_syn_parse!(syn::TypeParam);
+from_syn_parse!(syn::TypeParen);
+from_syn_parse!(syn::TypePath);
+from_syn_parse!(syn::TypePtr);
+from_syn_parse!(syn::TypeReference);
+from_syn_parse!(syn::TypeSlice);
+from_syn_parse!(syn::TypeTraitObject);
+from_syn_parse!(syn::TypeTuple);
+from_syn_parse!(syn::Visibility);
+from_syn_parse!(syn::WhereClause);
+
+macro_rules! from_numeric_array {
+ ($ty:ident) => {
+ /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`.
+ impl FromMeta for Vec<$ty> {
+ fn from_expr(expr: &syn::Expr) -> Result<Self> {
+ match expr {
+ syn::Expr::Array(expr_array) => expr_array
+ .elems
+ .iter()
+ .map(|expr| {
+ let unexpected = || {
+ Error::custom("Expected array of unsigned integers").with_span(expr)
+ };
+ match expr {
+ Expr::Lit(lit) => $ty::from_value(&lit.lit),
+ Expr::Group(group) => match &*group.expr {
+ Expr::Lit(lit) => $ty::from_value(&lit.lit),
+ _ => Err(unexpected()),
+ },
+ _ => Err(unexpected()),
+ }
+ })
+ .collect::<Result<Vec<$ty>>>(),
+ syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
+ syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ }
+
+ fn from_value(value: &Lit) -> Result<Self> {
+ let expr_array = syn::ExprArray::from_value(value)?;
+ Self::from_expr(&syn::Expr::Array(expr_array))
+ }
+ }
+ };
+}
+
+from_numeric_array!(u8);
+from_numeric_array!(u16);
+from_numeric_array!(u32);
+from_numeric_array!(u64);
+from_numeric_array!(usize);
+
+impl FromMeta for syn::Lit {
+ fn from_value(value: &Lit) -> Result<Self> {
+ Ok(value.clone())
+ }
+}
+
+macro_rules! from_meta_lit {
+ ($impl_ty:path, $lit_variant:path) => {
+ impl FromMeta for $impl_ty {
+ fn from_value(value: &Lit) -> Result<Self> {
+ if let $lit_variant(ref value) = *value {
+ Ok(value.clone())
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+ }
+
+ impl FromMeta for Vec<$impl_ty> {
+ fn from_list(items: &[NestedMeta]) -> Result<Self> {
+ items
+ .iter()
+ .map(<$impl_ty as FromMeta>::from_nested_meta)
+ .collect()
+ }
+
+ fn from_value(value: &syn::Lit) -> Result<Self> {
+ let expr_array = syn::ExprArray::from_value(value)?;
+ Self::from_expr(&syn::Expr::Array(expr_array))
+ }
+
+ fn from_expr(expr: &syn::Expr) -> Result<Self> {
+ match expr {
+ syn::Expr::Array(expr_array) => expr_array
+ .elems
+ .iter()
+ .map(<$impl_ty as FromMeta>::from_expr)
+ .collect::<Result<Vec<_>>>(),
+ syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
+ syn::Expr::Group(g) => Self::from_expr(&g.expr),
+ _ => Err(Error::unexpected_expr_type(expr)),
+ }
+ }
+ }
+ };
+}
+
+from_meta_lit!(syn::LitInt, Lit::Int);
+from_meta_lit!(syn::LitFloat, Lit::Float);
+from_meta_lit!(syn::LitStr, Lit::Str);
+from_meta_lit!(syn::LitByte, Lit::Byte);
+from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
+from_meta_lit!(syn::LitChar, Lit::Char);
+from_meta_lit!(syn::LitBool, Lit::Bool);
+from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
+
+impl FromMeta for syn::Meta {
+ fn from_meta(value: &syn::Meta) -> Result<Self> {
+ Ok(value.clone())
+ }
+}
+
+impl FromMeta for Vec<syn::WherePredicate> {
+ fn from_string(value: &str) -> Result<Self> {
+ syn::WhereClause::from_string(&format!("where {}", value))
+ .map(|c| c.predicates.into_iter().collect())
+ }
+
+ fn from_value(value: &Lit) -> Result<Self> {
+ if let syn::Lit::Str(s) = value {
+ syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
+ &format!("where {}", s.value()),
+ value.span(),
+ )))
+ .map(|c| c.predicates.into_iter().collect())
+ } else {
+ Err(Error::unexpected_lit_type(value))
+ }
+ }
+}
+
+impl FromMeta for ident_case::RenameRule {
+ fn from_string(value: &str) -> Result<Self> {
+ value.parse().map_err(|_| Error::unknown_value(value))
+ }
+}
+
+impl<T: FromMeta> FromMeta for Option<T> {
+ fn from_none() -> Option<Self> {
+ Some(None)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ FromMeta::from_meta(item).map(Some)
+ }
+}
+
+impl<T: FromMeta> FromMeta for Box<T> {
+ fn from_none() -> Option<Self> {
+ T::from_none().map(Box::new)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ FromMeta::from_meta(item).map(Box::new)
+ }
+}
+
+impl<T: FromMeta> FromMeta for Result<T> {
+ fn from_none() -> Option<Self> {
+ T::from_none().map(Ok)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ Ok(FromMeta::from_meta(item))
+ }
+}
+
+/// Parses the meta-item, and in case of error preserves a copy of the input for
+/// later analysis.
+impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
+ fn from_meta(item: &Meta) -> Result<Self> {
+ T::from_meta(item)
+ .map(Ok)
+ .or_else(|_| Ok(Err(item.clone())))
+ }
+}
+
+impl<T: FromMeta> FromMeta for Rc<T> {
+ fn from_none() -> Option<Self> {
+ T::from_none().map(Rc::new)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ FromMeta::from_meta(item).map(Rc::new)
+ }
+}
+
+impl<T: FromMeta> FromMeta for Arc<T> {
+ fn from_none() -> Option<Self> {
+ T::from_none().map(Arc::new)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ FromMeta::from_meta(item).map(Arc::new)
+ }
+}
+
+impl<T: FromMeta> FromMeta for RefCell<T> {
+ fn from_none() -> Option<Self> {
+ T::from_none().map(RefCell::new)
+ }
+
+ fn from_meta(item: &Meta) -> Result<Self> {
+ FromMeta::from_meta(item).map(RefCell::new)
+ }
+}
+
+/// Trait to convert from a path into an owned key for a map.
+trait KeyFromPath: Sized {
+ fn from_path(path: &syn::Path) -> Result<Self>;
+ fn to_display(&self) -> Cow<'_, str>;
+}
+
+impl KeyFromPath for String {
+ fn from_path(path: &syn::Path) -> Result<Self> {
+ Ok(path_to_string(path))
+ }
+
+ fn to_display(&self) -> Cow<'_, str> {
+ Cow::Borrowed(self)
+ }
+}
+
+impl KeyFromPath for syn::Path {
+ fn from_path(path: &syn::Path) -> Result<Self> {
+ Ok(path.clone())
+ }
+
+ fn to_display(&self) -> Cow<'_, str> {
+ Cow::Owned(path_to_string(self))
+ }
+}
+
+impl KeyFromPath for syn::Ident {
+ fn from_path(path: &syn::Path) -> Result<Self> {
+ if path.segments.len() == 1
+ && path.leading_colon.is_none()
+ && path.segments[0].arguments.is_empty()
+ {
+ Ok(path.segments[0].ident.clone())
+ } else {
+ Err(Error::custom("Key must be an identifier").with_span(path))
+ }
+ }
+
+ fn to_display(&self) -> Cow<'_, str> {
+ Cow::Owned(self.to_string())
+ }
+}
+
+macro_rules! hash_map {
+ ($key:ty) => {
+ impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
+ fn from_list(nested: &[NestedMeta]) -> Result<Self> {
+ // Convert the nested meta items into a sequence of (path, value result) result tuples.
+ // An outer Err means no (key, value) structured could be found, while an Err in the
+ // second position of the tuple means that value was rejected by FromMeta.
+ //
+ // We defer key conversion into $key so that we don't lose span information in the case
+ // of String keys; we'll need it for good duplicate key errors later.
+ let pairs = nested
+ .iter()
+ .map(|item| -> Result<(&syn::Path, Result<V>)> {
+ match *item {
+ NestedMeta::Meta(ref inner) => {
+ let path = inner.path();
+ Ok((
+ path,
+ FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
+ ))
+ }
+ NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
+ }
+ });
+
+ let mut errors = Error::accumulator();
+ // We need to track seen keys separately from the final map, since a seen key with an
+ // Err value won't go into the final map but should trigger a duplicate field error.
+ //
+ // This is a set of $key rather than Path to avoid the possibility that a key type
+ // parses two paths of different values to the same key value.
+ let mut seen_keys = HashSet::with_capacity(nested.len());
+
+ // The map to return in the Ok case. Its size will always be exactly nested.len(),
+ // since otherwise ≥1 field had a problem and the entire map is dropped immediately
+ // when the function returns `Err`.
+ let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
+
+ for item in pairs {
+ if let Some((path, value)) = errors.handle(item) {
+ let key: $key = match KeyFromPath::from_path(path) {
+ Ok(k) => k,
+ Err(e) => {
+ errors.push(e);
+
+ // Surface value errors even under invalid keys
+ errors.handle(value);
+
+ continue;
+ }
+ };
+
+ let already_seen = seen_keys.contains(&key);
+
+ if already_seen {
+ errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
+ }
+
+ match value {
+ Ok(_) if already_seen => {}
+ Ok(val) => {
+ map.insert(key.clone(), val);
+ }
+ Err(e) => {
+ errors.push(e);
+ }
+ }
+
+ seen_keys.insert(key);
+ }
+ }
+
+ errors.finish_with(map)
+ }
+ }
+ };
+}
+
+// This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility
+// with 0.12.x, while still sharing the same impl.
+hash_map!(String);
+hash_map!(syn::Ident);
+hash_map!(syn::Path);
+
+/// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input,
+/// it should not be considered by the parsing.
+#[cfg(test)]
+mod tests {
+ use proc_macro2::TokenStream;
+ use quote::quote;
+ use syn::parse_quote;
+
+ use crate::{Error, FromMeta, Result};
+
+ /// parse a string as a syn::Meta instance.
+ fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
+ let attribute: syn::Attribute = parse_quote!(#[#tokens]);
+ Ok(attribute.meta)
+ }
+
+ #[track_caller]
+ fn fm<T: FromMeta>(tokens: TokenStream) -> T {
+ FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
+ .expect("Tests should pass valid input")
+ }
+
+ #[test]
+ fn unit_succeeds() {
+ fm::<()>(quote!(ignore));
+ }
+
+ #[test]
+ #[allow(clippy::bool_assert_comparison)]
+ fn bool_succeeds() {
+ // word format
+ assert_eq!(fm::<bool>(quote!(ignore)), true);
+
+ // bool literal
+ assert_eq!(fm::<bool>(quote!(ignore = true)), true);
+ assert_eq!(fm::<bool>(quote!(ignore = false)), false);
+
+ // string literals
+ assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
+ assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
+ }
+
+ #[test]
+ fn char_succeeds() {
+ // char literal
+ assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
+
+ // string literal
+ assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
+ }
+
+ #[test]
+ fn string_succeeds() {
+ // cooked form
+ assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
+
+ // raw form
+ assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
+ }
+
+ #[test]
+ fn pathbuf_succeeds() {
+ assert_eq!(
+ fm::<std::path::PathBuf>(quote!(ignore = r#"C:\"#)),
+ std::path::PathBuf::from(r#"C:\"#)
+ );
+ }
+
+ #[test]
+ #[allow(clippy::float_cmp)] // we want exact equality
+ fn number_succeeds() {
+ assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
+ assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
+ assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
+ }
+
+ #[test]
+ fn int_without_quotes() {
+ assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
+ assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
+ assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
+
+ // Check that we aren't tripped up by incorrect suffixes
+ assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
+ }
+
+ #[test]
+ fn negative_int_without_quotes() {
+ assert_eq!(fm::<i8>(quote!(ignore = -2)), -2i8);
+ assert_eq!(fm::<i32>(quote!(ignore = -255)), -255i32);
+ }
+
+ #[test]
+ #[allow(clippy::float_cmp)] // we want exact equality
+ fn float_without_quotes() {
+ assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
+ assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
+ assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
+ }
+
+ #[test]
+ fn meta_succeeds() {
+ use syn::Meta;
+
+ assert_eq!(
+ fm::<Meta>(quote!(hello(world, today))),
+ pm(quote!(hello(world, today))).unwrap()
+ );
+ }
+
+ #[test]
+ fn hash_map_succeeds() {
+ use std::collections::HashMap;
+
+ let comparison = {
+ let mut c = HashMap::new();
+ c.insert("hello".to_string(), true);
+ c.insert("world".to_string(), false);
+ c.insert("there".to_string(), true);
+ c
+ };
+
+ assert_eq!(
+ fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
+ comparison
+ );
+ }
+
+ /// Check that a `HashMap` cannot have duplicate keys, and that the generated error
+ /// is assigned a span to correctly target the diagnostic message.
+ #[test]
+ fn hash_map_duplicate() {
+ use std::collections::HashMap;
+
+ let err: Result<HashMap<String, bool>> =
+ FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
+
+ let err = err.expect_err("Duplicate keys in HashMap should error");
+
+ assert!(err.has_span());
+ assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
+ }
+
+ #[test]
+ fn hash_map_multiple_errors() {
+ use std::collections::HashMap;
+
+ let err = HashMap::<String, bool>::from_meta(
+ &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
+ )
+ .expect_err("Duplicates and bad values should error");
+
+ assert_eq!(err.len(), 3);
+ let errors = err.into_iter().collect::<Vec<_>>();
+ assert!(errors[0].has_span());
+ assert!(errors[1].has_span());
+ assert!(errors[2].has_span());
+ }
+
+ #[test]
+ fn hash_map_ident_succeeds() {
+ use std::collections::HashMap;
+ use syn::parse_quote;
+
+ let comparison = {
+ let mut c = HashMap::<syn::Ident, bool>::new();
+ c.insert(parse_quote!(first), true);
+ c.insert(parse_quote!(second), false);
+ c
+ };
+
+ assert_eq!(
+ fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
+ comparison
+ );
+ }
+
+ #[test]
+ fn hash_map_ident_rejects_non_idents() {
+ use std::collections::HashMap;
+
+ let err: Result<HashMap<syn::Ident, bool>> =
+ FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
+
+ err.unwrap_err();
+ }
+
+ #[test]
+ fn hash_map_path_succeeds() {
+ use std::collections::HashMap;
+ use syn::parse_quote;
+
+ let comparison = {
+ let mut c = HashMap::<syn::Path, bool>::new();
+ c.insert(parse_quote!(first), true);
+ c.insert(parse_quote!(the::second), false);
+ c
+ };
+
+ assert_eq!(
+ fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
+ comparison
+ );
+ }
+
+ /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
+ /// and will accurately preserve the inner contents.
+ #[test]
+ fn darling_result_succeeds() {
+ fm::<Result<()>>(quote!(ignore)).unwrap();
+ fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
+ }
+
+ /// Test punctuated
+ #[test]
+ fn test_punctuated() {
+ fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
+ ignore = "a: u8, b: Type"
+ ));
+ fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
+ }
+
+ #[test]
+ fn test_expr_array() {
+ fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
+ fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
+ }
+
+ #[test]
+ fn test_expr() {
+ fm::<syn::Expr>(quote!(ignore = "x + y"));
+ fm::<syn::Expr>(quote!(ignore = "an_object.method_call()"));
+ fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }"));
+ }
+
+ #[test]
+ fn test_expr_without_quotes() {
+ fm::<syn::Expr>(quote!(ignore = x + y));
+ fm::<syn::Expr>(quote!(ignore = an_object.method_call()));
+ fm::<syn::Expr>(quote!(
+ ignore = {
+ a_statement();
+ in_a_block
+ }
+ ));
+ }
+
+ #[test]
+ fn test_expr_path() {
+ fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace"));
+ fm::<syn::ExprPath>(quote!(ignore = "x"));
+ fm::<syn::ExprPath>(quote!(ignore = "example::<Test>"));
+ }
+
+ #[test]
+ fn test_expr_path_without_quotes() {
+ fm::<syn::ExprPath>(quote!(ignore = std::mem::replace));
+ fm::<syn::ExprPath>(quote!(ignore = x));
+ fm::<syn::ExprPath>(quote!(ignore = example::<Test>));
+ }
+
+ #[test]
+ fn test_path_without_quotes() {
+ fm::<syn::Path>(quote!(ignore = std::mem::replace));
+ fm::<syn::Path>(quote!(ignore = x));
+ fm::<syn::Path>(quote!(ignore = example::<Test>));
+ }
+
+ #[test]
+ fn test_number_array() {
+ assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]);
+ assert_eq!(
+ fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
+ vec![0x20, 0xffff]
+ );
+ assert_eq!(
+ fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
+ vec![0x30, 0xffffffff]
+ );
+ assert_eq!(
+ fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
+ vec![0x40, 0xffffffffffffffff]
+ );
+ assert_eq!(
+ fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
+ vec![0x50, 0xffffffff]
+ );
+ }
+
+ #[test]
+ fn test_lit_array() {
+ fm::<Vec<syn::LitStr>>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
+ fm::<Vec<syn::LitStr>>(quote!(ignore = ["Hello World", "Test Array"]));
+ fm::<Vec<syn::LitChar>>(quote!(ignore = "['a', 'b', 'c']"));
+ fm::<Vec<syn::LitBool>>(quote!(ignore = "[true]"));
+ fm::<Vec<syn::LitStr>>(quote!(ignore = "[]"));
+ fm::<Vec<syn::LitStr>>(quote!(ignore = []));
+ fm::<Vec<syn::LitBool>>(quote!(ignore = [true, false]));
+ }
+}
diff --git a/src/from_type_param.rs b/src/from_type_param.rs
new file mode 100644
index 0000000..bea9796
--- /dev/null
+++ b/src/from_type_param.rs
@@ -0,0 +1,32 @@
+use syn::TypeParam;
+
+use crate::Result;
+
+/// Creates an instance by parsing an individual type_param and its attributes.
+pub trait FromTypeParam: Sized {
+ fn from_type_param(type_param: &TypeParam) -> Result<Self>;
+}
+
+impl FromTypeParam for () {
+ fn from_type_param(_: &TypeParam) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromTypeParam for TypeParam {
+ fn from_type_param(type_param: &TypeParam) -> Result<Self> {
+ Ok(type_param.clone())
+ }
+}
+
+impl FromTypeParam for Vec<syn::Attribute> {
+ fn from_type_param(type_param: &TypeParam) -> Result<Self> {
+ Ok(type_param.attrs.clone())
+ }
+}
+
+impl FromTypeParam for syn::Ident {
+ fn from_type_param(type_param: &TypeParam) -> Result<Self> {
+ Ok(type_param.ident.clone())
+ }
+}
diff --git a/src/from_variant.rs b/src/from_variant.rs
new file mode 100644
index 0000000..50bf635
--- /dev/null
+++ b/src/from_variant.rs
@@ -0,0 +1,33 @@
+use syn::Variant;
+
+use crate::Result;
+
+/// Creates an instance from a specified `syn::Variant`.
+pub trait FromVariant: Sized {
+ /// Create an instance from `syn::Variant`, or return an error.
+ fn from_variant(variant: &Variant) -> Result<Self>;
+}
+
+impl FromVariant for () {
+ fn from_variant(_: &Variant) -> Result<Self> {
+ Ok(())
+ }
+}
+
+impl FromVariant for Variant {
+ fn from_variant(variant: &Variant) -> Result<Self> {
+ Ok(variant.clone())
+ }
+}
+
+impl FromVariant for syn::Ident {
+ fn from_variant(variant: &Variant) -> Result<Self> {
+ Ok(variant.ident.clone())
+ }
+}
+
+impl FromVariant for Vec<syn::Attribute> {
+ fn from_variant(variant: &Variant) -> Result<Self> {
+ Ok(variant.attrs.clone())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..bf2050c
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,42 @@
+#![recursion_limit = "256"]
+#![cfg_attr(feature = "diagnostics", feature(proc_macro_diagnostic))]
+
+#[cfg(feature = "diagnostics")]
+extern crate proc_macro;
+
+#[macro_use]
+mod macros_private;
+#[macro_use]
+mod macros_public;
+
+pub mod ast;
+pub(crate) mod codegen;
+pub mod derive;
+pub mod error;
+mod from_attributes;
+mod from_derive_input;
+mod from_field;
+mod from_generic_param;
+mod from_generics;
+mod from_meta;
+mod from_type_param;
+mod from_variant;
+pub(crate) mod options;
+pub mod usage;
+pub mod util;
+
+pub use self::error::{Error, Result};
+pub use self::from_attributes::FromAttributes;
+pub use self::from_derive_input::FromDeriveInput;
+pub use self::from_field::FromField;
+pub use self::from_generic_param::FromGenericParam;
+pub use self::from_generics::FromGenerics;
+pub use self::from_meta::FromMeta;
+pub use self::from_type_param::FromTypeParam;
+pub use self::from_variant::FromVariant;
+
+// Re-exports
+#[doc(hidden)]
+pub use quote::ToTokens;
+#[doc(hidden)]
+pub use syn;
diff --git a/src/macros_private.rs b/src/macros_private.rs
new file mode 100644
index 0000000..61edf36
--- /dev/null
+++ b/src/macros_private.rs
@@ -0,0 +1,6 @@
+macro_rules! path {
+ ($($path:tt)+) => {
+ ::syn::parse_quote!($($path)+)
+ //stringify!($($path)+).parse().unwrap()
+ };
+}
diff --git a/src/macros_public.rs b/src/macros_public.rs
new file mode 100644
index 0000000..c264fcc
--- /dev/null
+++ b/src/macros_public.rs
@@ -0,0 +1,96 @@
+//! Macros that should be exported from both `darling_core` and `darling`.
+//! Note that these are **sym-linked** into the main code, and so cannot declare on items that are exported differently
+//! in `darling_core` vs. `darling`.
+
+/// Generator for `UsesTypeParam` impls that unions the used type parameters of the selected fields.
+///
+/// # Usage
+/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
+/// fields for the rest of its arguments.
+///
+/// The type of each passed-in field must implement `UsesTypeParams`, or the resulting code won't compile.
+///
+/// ```rust
+/// # extern crate syn;
+/// # use darling_core::uses_type_params;
+/// #
+/// struct MyField {
+/// ty: syn::Type,
+/// }
+///
+/// uses_type_params!(MyField, ty);
+///
+/// fn main() {
+/// // no test run
+/// }
+/// ```
+///
+/// `darling` cannot derive this trait automatically, as it doesn't know which information extracted from
+/// proc-macro input is meant to constitute "using" the type parameter, but crate consumers should
+/// implement it by hand or using the macro.
+#[macro_export]
+macro_rules! uses_type_params {
+ ($impl_type:ty, $accessor:ident) => {
+ impl $crate::usage::UsesTypeParams for $impl_type {
+ fn uses_type_params<'gen>(
+ &self,
+ options: &$crate::usage::Options,
+ type_set: &'gen $crate::usage::IdentSet
+ ) -> $crate::usage::IdentRefSet<'gen> {
+ self.$accessor.uses_type_params(options, type_set)
+ }
+ }
+ };
+ ($impl_type:ty, $first:ident, $($field:ident),+) => {
+ impl $crate::usage::UsesTypeParams for $impl_type {
+ fn uses_type_params<'gen>(
+ &self,
+ options: &$crate::usage::Options,
+ type_set: &'gen $crate::usage::IdentSet
+ ) -> $crate::usage::IdentRefSet<'gen> {
+ let mut hits = self.$first.uses_type_params(options, type_set);
+ $(
+ hits.extend(self.$field.uses_type_params(options, type_set));
+ )*
+ hits
+ }
+ }
+ };
+}
+
+/// Generator for `UsesLifetimes` impls that unions the used lifetimes of the selected fields.
+///
+/// # Usage
+/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
+/// fields for the rest of its arguments.
+///
+/// The type of each passed-in field must implement `UsesLifetimes`, or the resulting code won't compile.
+#[macro_export]
+macro_rules! uses_lifetimes {
+ ($impl_type:ty, $accessor:ident) => {
+ impl $crate::usage::UsesLifetimes for $impl_type {
+ fn uses_lifetimes<'gen>(
+ &self,
+ options: &$crate::usage::Options,
+ type_set: &'gen $crate::usage::LifetimeSet
+ ) -> $crate::usage::LifetimeRefSet<'gen> {
+ self.$accessor.uses_lifetimes(options, type_set)
+ }
+ }
+ };
+ ($impl_type:ty, $first:ident, $($field:ident),+) => {
+ impl $crate::usage::UsesLifetimes for $impl_type {
+ fn uses_lifetimes<'gen>(
+ &self,
+ options: &$crate::usage::Options,
+ type_set: &'gen $crate::usage::LifetimeSet
+ ) -> $crate::usage::LifetimeRefSet<'gen> {
+ let mut hits = self.$first.uses_lifetimes(options, type_set);
+ $(
+ hits.extend(self.$field.uses_lifetimes(options, type_set));
+ )*
+ hits
+ }
+ }
+ };
+}
diff --git a/src/options/core.rs b/src/options/core.rs
new file mode 100644
index 0000000..de8bdb0
--- /dev/null
+++ b/src/options/core.rs
@@ -0,0 +1,198 @@
+use ident_case::RenameRule;
+
+use crate::ast::{Data, Fields, Style};
+use crate::codegen;
+use crate::codegen::PostfixTransform;
+use crate::error::Accumulator;
+use crate::options::{DefaultExpression, InputField, InputVariant, ParseAttribute, ParseData};
+use crate::{Error, FromMeta, Result};
+
+/// A struct or enum which should have `FromMeta` or `FromDeriveInput` implementations
+/// generated.
+#[derive(Debug, Clone)]
+pub struct Core {
+ /// The type identifier.
+ pub ident: syn::Ident,
+
+ /// The type's generics. If the type does not use any generics, this will
+ /// be an empty instance.
+ pub generics: syn::Generics,
+
+ /// Controls whether missing properties should cause errors or should be filled by
+ /// the result of a function call. This can be overridden at the field level.
+ pub default: Option<DefaultExpression>,
+
+ /// The rule that should be used to rename all fields/variants in the container.
+ pub rename_rule: RenameRule,
+
+ /// A transform which will be called on `darling::Result<Self>`. It must either be
+ /// an `FnOnce(T) -> T` when `map` is used, or `FnOnce(T) -> darling::Result<T>` when
+ /// `and_then` is used.
+ ///
+ /// `map` and `and_then` are mutually-exclusive to avoid confusion about the order in
+ /// which the two are applied.
+ pub post_transform: Option<codegen::PostfixTransform>,
+
+ /// The body of the _deriving_ type.
+ pub data: Data<InputVariant, InputField>,
+
+ /// The custom bound to apply to the generated impl
+ pub bound: Option<Vec<syn::WherePredicate>>,
+
+ /// Whether or not unknown fields should produce an error at compilation time.
+ pub allow_unknown_fields: Option<bool>,
+}
+
+impl Core {
+ /// Partially initializes `Core` by reading the identity, generics, and body shape.
+ pub fn start(di: &syn::DeriveInput) -> Result<Self> {
+ Ok(Core {
+ ident: di.ident.clone(),
+ generics: di.generics.clone(),
+ data: Data::try_empty_from(&di.data)?,
+ default: Default::default(),
+ // See https://github.com/TedDriggs/darling/issues/10: We default to snake_case
+ // for enums to help authors produce more idiomatic APIs.
+ rename_rule: if let syn::Data::Enum(_) = di.data {
+ RenameRule::SnakeCase
+ } else {
+ Default::default()
+ },
+ post_transform: Default::default(),
+ bound: Default::default(),
+ allow_unknown_fields: Default::default(),
+ })
+ }
+
+ fn as_codegen_default(&self) -> Option<codegen::DefaultExpression<'_>> {
+ self.default.as_ref().map(|expr| match *expr {
+ DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
+ DefaultExpression::Inherit => {
+ // It should be impossible for any input to get here,
+ // so panic rather than returning an error or pretending
+ // everything is fine.
+ panic!("DefaultExpression::Inherit is not valid at container level")
+ }
+ DefaultExpression::Trait { span } => codegen::DefaultExpression::Trait { span },
+ })
+ }
+}
+
+impl ParseAttribute for Core {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ let path = mi.path();
+
+ if path.is_ident("default") {
+ if self.default.is_some() {
+ return Err(Error::duplicate_field("default").with_span(mi));
+ }
+
+ self.default = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("rename_all") {
+ // WARNING: This may have been set based on body shape previously,
+ // so an overwrite may be permissible.
+ self.rename_rule = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("map") || path.is_ident("and_then") {
+ // This unwrap is safe because we just called is_ident above
+ let transformer = path.get_ident().unwrap().clone();
+
+ if let Some(post_transform) = &self.post_transform {
+ if transformer == post_transform.transformer {
+ return Err(Error::duplicate_field(&transformer.to_string()).with_span(mi));
+ } else {
+ return Err(Error::custom(format!(
+ "Options `{}` and `{}` are mutually exclusive",
+ transformer, post_transform.transformer
+ ))
+ .with_span(mi));
+ }
+ }
+
+ self.post_transform =
+ Some(PostfixTransform::new(transformer, FromMeta::from_meta(mi)?));
+ } else if path.is_ident("bound") {
+ self.bound = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("allow_unknown_fields") {
+ if self.allow_unknown_fields.is_some() {
+ return Err(Error::duplicate_field("allow_unknown_fields").with_span(mi));
+ }
+
+ self.allow_unknown_fields = FromMeta::from_meta(mi)?;
+ } else {
+ return Err(Error::unknown_field_path(path).with_span(mi));
+ }
+
+ Ok(())
+ }
+}
+
+impl ParseData for Core {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ let v = InputVariant::from_variant(variant, Some(self))?;
+
+ match self.data {
+ Data::Enum(ref mut variants) => {
+ variants.push(v);
+ Ok(())
+ }
+ Data::Struct(_) => panic!("Core::parse_variant should never be called for a struct"),
+ }
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ let f = InputField::from_field(field, Some(self))?;
+
+ match self.data {
+ Data::Struct(Fields {
+ style: Style::Unit, ..
+ }) => panic!("Core::parse_field should not be called on unit"),
+ Data::Struct(Fields { ref mut fields, .. }) => {
+ fields.push(f);
+ Ok(())
+ }
+ Data::Enum(_) => panic!("Core::parse_field should never be called for an enum"),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut Accumulator) {
+ if let Data::Struct(fields) = &self.data {
+ let flatten_targets: Vec<_> = fields
+ .iter()
+ .filter_map(|field| {
+ if field.flatten.is_present() {
+ Some(field.flatten)
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ if flatten_targets.len() > 1 {
+ for flatten in flatten_targets {
+ errors.push(
+ Error::custom("`#[darling(flatten)]` can only be applied to one field")
+ .with_span(&flatten.span()),
+ );
+ }
+ }
+ }
+ }
+}
+
+impl<'a> From<&'a Core> for codegen::TraitImpl<'a> {
+ fn from(v: &'a Core) -> Self {
+ codegen::TraitImpl {
+ ident: &v.ident,
+ generics: &v.generics,
+ data: v
+ .data
+ .as_ref()
+ .map_struct_fields(InputField::as_codegen_field)
+ .map_enum_variants(|variant| variant.as_codegen_variant(&v.ident)),
+ default: v.as_codegen_default(),
+ post_transform: v.post_transform.as_ref(),
+ bound: v.bound.as_deref(),
+ allow_unknown_fields: v.allow_unknown_fields.unwrap_or_default(),
+ }
+ }
+}
diff --git a/src/options/forward_attrs.rs b/src/options/forward_attrs.rs
new file mode 100644
index 0000000..abe2d85
--- /dev/null
+++ b/src/options/forward_attrs.rs
@@ -0,0 +1,73 @@
+use proc_macro2::Ident;
+use syn::Path;
+
+use crate::ast::NestedMeta;
+use crate::util::PathList;
+use crate::{Error, FromField, FromMeta, Result};
+
+use super::ParseAttribute;
+
+/// The `attrs` magic field and attributes that influence its behavior.
+#[derive(Debug, Clone)]
+pub struct AttrsField {
+ /// The ident of the field that will receive the forwarded attributes.
+ pub ident: Ident,
+ /// Path of the function that will be called to convert the `Vec` of
+ /// forwarded attributes into the type expected by the field in `ident`.
+ pub with: Option<Path>,
+}
+
+impl FromField for AttrsField {
+ fn from_field(field: &syn::Field) -> crate::Result<Self> {
+ let result = Self {
+ ident: field.ident.clone().ok_or_else(|| {
+ Error::custom("attributes receiver must be named field").with_span(field)
+ })?,
+ with: None,
+ };
+
+ result.parse_attributes(&field.attrs)
+ }
+}
+
+impl ParseAttribute for AttrsField {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> crate::Result<()> {
+ if mi.path().is_ident("with") {
+ if self.with.is_some() {
+ return Err(Error::duplicate_field_path(mi.path()).with_span(mi));
+ }
+
+ self.with = FromMeta::from_meta(mi)?;
+ Ok(())
+ } else {
+ Err(Error::unknown_field_path_with_alts(mi.path(), &["with"]).with_span(mi))
+ }
+ }
+}
+
+/// A rule about which attributes to forward to the generated struct.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ForwardAttrsFilter {
+ All,
+ Only(PathList),
+}
+
+impl ForwardAttrsFilter {
+ /// Returns `true` if this will not forward any attributes.
+ pub fn is_empty(&self) -> bool {
+ match *self {
+ ForwardAttrsFilter::All => false,
+ ForwardAttrsFilter::Only(ref list) => list.is_empty(),
+ }
+ }
+}
+
+impl FromMeta for ForwardAttrsFilter {
+ fn from_word() -> Result<Self> {
+ Ok(ForwardAttrsFilter::All)
+ }
+
+ fn from_list(nested: &[NestedMeta]) -> Result<Self> {
+ Ok(ForwardAttrsFilter::Only(PathList::from_list(nested)?))
+ }
+}
diff --git a/src/options/from_attributes.rs b/src/options/from_attributes.rs
new file mode 100644
index 0000000..dc11b29
--- /dev/null
+++ b/src/options/from_attributes.rs
@@ -0,0 +1,74 @@
+use quote::ToTokens;
+
+use crate::{ast::Data, codegen::FromAttributesImpl, Error, Result};
+
+use super::{OuterFrom, ParseAttribute, ParseData};
+
+/// Receiver for derived `FromAttributes` impls.
+pub struct FromAttributesOptions {
+ // Note: FromAttributes has no behaviors beyond those common
+ // to all the `OuterFrom` traits.
+ pub base: OuterFrom,
+}
+
+impl FromAttributesOptions {
+ pub fn new(di: &syn::DeriveInput) -> Result<Self> {
+ let opts = (Self {
+ base: OuterFrom::start(di)?,
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)?;
+
+ if !opts.is_newtype() && opts.base.attr_names.is_empty() {
+ Err(Error::custom(
+ "FromAttributes without attributes collects nothing",
+ ))
+ } else {
+ Ok(opts)
+ }
+ }
+
+ fn is_newtype(&self) -> bool {
+ if let Data::Struct(ref data) = self.base.container.data {
+ data.is_newtype()
+ } else {
+ false
+ }
+ }
+}
+
+impl ParseAttribute for FromAttributesOptions {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ self.base.parse_nested(mi)
+ }
+}
+
+impl ParseData for FromAttributesOptions {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ self.base.parse_variant(variant)
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ self.base.parse_field(field)
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.base.validate_body(errors);
+ }
+}
+
+impl<'a> From<&'a FromAttributesOptions> for FromAttributesImpl<'a> {
+ fn from(v: &'a FromAttributesOptions) -> Self {
+ FromAttributesImpl {
+ base: (&v.base.container).into(),
+ attr_names: &v.base.attr_names,
+ forward_attrs: Default::default(),
+ }
+ }
+}
+
+impl ToTokens for FromAttributesOptions {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ FromAttributesImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/from_derive.rs b/src/options/from_derive.rs
new file mode 100644
index 0000000..eb27641
--- /dev/null
+++ b/src/options/from_derive.rs
@@ -0,0 +1,97 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Ident;
+
+use crate::codegen::FromDeriveInputImpl;
+use crate::options::{DeriveInputShapeSet, OuterFrom, ParseAttribute, ParseData};
+use crate::{FromMeta, Result};
+
+#[derive(Debug)]
+pub struct FdiOptions {
+ pub base: OuterFrom,
+
+ /// The field on the target struct which should receive the type visibility, if any.
+ pub vis: Option<Ident>,
+
+ /// The field on the target struct which should receive the type generics, if any.
+ pub generics: Option<Ident>,
+
+ pub data: Option<Ident>,
+
+ pub supports: Option<DeriveInputShapeSet>,
+}
+
+impl FdiOptions {
+ pub fn new(di: &syn::DeriveInput) -> Result<Self> {
+ (FdiOptions {
+ base: OuterFrom::start(di)?,
+ vis: Default::default(),
+ generics: Default::default(),
+ data: Default::default(),
+ supports: Default::default(),
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)
+ }
+}
+
+impl ParseAttribute for FdiOptions {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ if mi.path().is_ident("supports") {
+ self.supports = FromMeta::from_meta(mi)?;
+ Ok(())
+ } else {
+ self.base.parse_nested(mi)
+ }
+ }
+}
+
+impl ParseData for FdiOptions {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ self.base.parse_variant(variant)
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
+ Some("vis") => {
+ self.vis = field.ident.clone();
+ Ok(())
+ }
+ Some("data") => {
+ self.data = field.ident.clone();
+ Ok(())
+ }
+ Some("generics") => {
+ self.generics = field.ident.clone();
+ Ok(())
+ }
+ _ => self.base.parse_field(field),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.base.validate_body(errors);
+ }
+}
+
+impl<'a> From<&'a FdiOptions> for FromDeriveInputImpl<'a> {
+ fn from(v: &'a FdiOptions) -> Self {
+ FromDeriveInputImpl {
+ base: (&v.base.container).into(),
+ attr_names: &v.base.attr_names,
+ from_ident: v.base.from_ident,
+ ident: v.base.ident.as_ref(),
+ vis: v.vis.as_ref(),
+ data: v.data.as_ref(),
+ generics: v.generics.as_ref(),
+ forward_attrs: v.base.as_forward_attrs(),
+ supports: v.supports.as_ref(),
+ }
+ }
+}
+
+impl ToTokens for FdiOptions {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ FromDeriveInputImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/from_field.rs b/src/options/from_field.rs
new file mode 100644
index 0000000..6dcd0f6
--- /dev/null
+++ b/src/options/from_field.rs
@@ -0,0 +1,76 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Ident;
+
+use crate::codegen::FromFieldImpl;
+use crate::options::{OuterFrom, ParseAttribute, ParseData};
+use crate::Result;
+
+#[derive(Debug)]
+pub struct FromFieldOptions {
+ pub base: OuterFrom,
+ pub vis: Option<Ident>,
+ pub ty: Option<Ident>,
+}
+
+impl FromFieldOptions {
+ pub fn new(di: &syn::DeriveInput) -> Result<Self> {
+ (FromFieldOptions {
+ base: OuterFrom::start(di)?,
+ vis: Default::default(),
+ ty: Default::default(),
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)
+ }
+}
+
+impl ParseAttribute for FromFieldOptions {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ self.base.parse_nested(mi)
+ }
+}
+
+impl ParseData for FromFieldOptions {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ self.base.parse_variant(variant)
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
+ Some("vis") => {
+ self.vis = field.ident.clone();
+ Ok(())
+ }
+ Some("ty") => {
+ self.ty = field.ident.clone();
+ Ok(())
+ }
+ _ => self.base.parse_field(field),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.base.validate_body(errors);
+ }
+}
+
+impl<'a> From<&'a FromFieldOptions> for FromFieldImpl<'a> {
+ fn from(v: &'a FromFieldOptions) -> Self {
+ FromFieldImpl {
+ ident: v.base.ident.as_ref(),
+ vis: v.vis.as_ref(),
+ ty: v.ty.as_ref(),
+ base: (&v.base.container).into(),
+ attr_names: &v.base.attr_names,
+ forward_attrs: v.base.as_forward_attrs(),
+ from_ident: v.base.from_ident,
+ }
+ }
+}
+
+impl ToTokens for FromFieldOptions {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ FromFieldImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/from_meta.rs b/src/options/from_meta.rs
new file mode 100644
index 0000000..cc527e8
--- /dev/null
+++ b/src/options/from_meta.rs
@@ -0,0 +1,72 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+
+use crate::ast::Data;
+use crate::codegen::FromMetaImpl;
+use crate::error::Accumulator;
+use crate::options::{Core, ParseAttribute, ParseData};
+use crate::{Error, Result};
+
+pub struct FromMetaOptions {
+ base: Core,
+}
+
+impl FromMetaOptions {
+ pub fn new(di: &syn::DeriveInput) -> Result<Self> {
+ (FromMetaOptions {
+ base: Core::start(di)?,
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)
+ }
+}
+
+impl ParseAttribute for FromMetaOptions {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ self.base.parse_nested(mi)
+ }
+}
+
+impl ParseData for FromMetaOptions {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ self.base.parse_variant(variant)
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ self.base.parse_field(field)
+ }
+
+ fn validate_body(&self, errors: &mut Accumulator) {
+ self.base.validate_body(errors);
+
+ if let Data::Enum(ref data) = self.base.data {
+ // Adds errors for duplicate `#[darling(word)]` annotations across all variants.
+ let word_variants: Vec<_> = data
+ .iter()
+ .filter_map(|variant| variant.word.as_ref())
+ .collect();
+ if word_variants.len() > 1 {
+ for word in word_variants {
+ errors.push(
+ Error::custom("`#[darling(word)]` can only be applied to one variant")
+ .with_span(&word.span()),
+ );
+ }
+ }
+ }
+ }
+}
+
+impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> {
+ fn from(v: &'a FromMetaOptions) -> Self {
+ FromMetaImpl {
+ base: (&v.base).into(),
+ }
+ }
+}
+
+impl ToTokens for FromMetaOptions {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ FromMetaImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/from_type_param.rs b/src/options/from_type_param.rs
new file mode 100644
index 0000000..e14de99
--- /dev/null
+++ b/src/options/from_type_param.rs
@@ -0,0 +1,76 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Ident;
+
+use crate::codegen::FromTypeParamImpl;
+use crate::options::{OuterFrom, ParseAttribute, ParseData};
+use crate::Result;
+
+#[derive(Debug)]
+pub struct FromTypeParamOptions {
+ pub base: OuterFrom,
+ pub bounds: Option<Ident>,
+ pub default: Option<Ident>,
+}
+
+impl FromTypeParamOptions {
+ pub fn new(di: &syn::DeriveInput) -> Result<Self> {
+ (FromTypeParamOptions {
+ base: OuterFrom::start(di)?,
+ bounds: None,
+ default: None,
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)
+ }
+}
+
+impl ParseAttribute for FromTypeParamOptions {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ self.base.parse_nested(mi)
+ }
+}
+
+impl ParseData for FromTypeParamOptions {
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ self.base.parse_variant(variant)
+ }
+
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
+ Some("bounds") => {
+ self.bounds = field.ident.clone();
+ Ok(())
+ }
+ Some("default") => {
+ self.default = field.ident.clone();
+ Ok(())
+ }
+ _ => self.base.parse_field(field),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.base.validate_body(errors);
+ }
+}
+
+impl<'a> From<&'a FromTypeParamOptions> for FromTypeParamImpl<'a> {
+ fn from(v: &'a FromTypeParamOptions) -> Self {
+ FromTypeParamImpl {
+ base: (&v.base.container).into(),
+ ident: v.base.ident.as_ref(),
+ bounds: v.bounds.as_ref(),
+ default: v.default.as_ref(),
+ attr_names: &v.base.attr_names,
+ forward_attrs: v.base.as_forward_attrs(),
+ from_ident: v.base.from_ident,
+ }
+ }
+}
+
+impl ToTokens for FromTypeParamOptions {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ FromTypeParamImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/from_variant.rs b/src/options/from_variant.rs
new file mode 100644
index 0000000..c3223c5
--- /dev/null
+++ b/src/options/from_variant.rs
@@ -0,0 +1,82 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::{DeriveInput, Field, Ident, Meta};
+
+use crate::codegen::FromVariantImpl;
+use crate::options::{DataShape, OuterFrom, ParseAttribute, ParseData};
+use crate::{FromMeta, Result};
+
+#[derive(Debug, Clone)]
+pub struct FromVariantOptions {
+ pub base: OuterFrom,
+ /// The field on the deriving struct into which the discriminant expression
+ /// should be placed by the derived `FromVariant` impl.
+ pub discriminant: Option<Ident>,
+ pub fields: Option<Ident>,
+ pub supports: Option<DataShape>,
+}
+
+impl FromVariantOptions {
+ pub fn new(di: &DeriveInput) -> Result<Self> {
+ (FromVariantOptions {
+ base: OuterFrom::start(di)?,
+ discriminant: Default::default(),
+ fields: Default::default(),
+ supports: Default::default(),
+ })
+ .parse_attributes(&di.attrs)?
+ .parse_body(&di.data)
+ }
+}
+
+impl<'a> From<&'a FromVariantOptions> for FromVariantImpl<'a> {
+ fn from(v: &'a FromVariantOptions) -> Self {
+ FromVariantImpl {
+ base: (&v.base.container).into(),
+ ident: v.base.ident.as_ref(),
+ discriminant: v.discriminant.as_ref(),
+ fields: v.fields.as_ref(),
+ attr_names: &v.base.attr_names,
+ forward_attrs: v.base.as_forward_attrs(),
+ from_ident: v.base.from_ident,
+ supports: v.supports.as_ref(),
+ }
+ }
+}
+
+impl ParseAttribute for FromVariantOptions {
+ fn parse_nested(&mut self, mi: &Meta) -> Result<()> {
+ if mi.path().is_ident("supports") {
+ self.supports = FromMeta::from_meta(mi)?;
+ Ok(())
+ } else {
+ self.base.parse_nested(mi)
+ }
+ }
+}
+
+impl ParseData for FromVariantOptions {
+ fn parse_field(&mut self, field: &Field) -> Result<()> {
+ match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
+ Some("discriminant") => {
+ self.discriminant = field.ident.clone();
+ Ok(())
+ }
+ Some("fields") => {
+ self.fields = field.ident.clone();
+ Ok(())
+ }
+ _ => self.base.parse_field(field),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.base.validate_body(errors);
+ }
+}
+
+impl ToTokens for FromVariantOptions {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ FromVariantImpl::from(self).to_tokens(tokens)
+ }
+}
diff --git a/src/options/input_field.rs b/src/options/input_field.rs
new file mode 100644
index 0000000..9b688d8
--- /dev/null
+++ b/src/options/input_field.rs
@@ -0,0 +1,241 @@
+use std::borrow::Cow;
+
+use syn::{parse_quote_spanned, spanned::Spanned};
+
+use crate::codegen;
+use crate::options::{Core, DefaultExpression, ParseAttribute};
+use crate::util::{Flag, SpannedValue};
+use crate::{Error, FromMeta, Result};
+
+#[derive(Debug, Clone)]
+pub struct InputField {
+ pub ident: syn::Ident,
+ pub attr_name: Option<String>,
+ pub ty: syn::Type,
+ pub default: Option<DefaultExpression>,
+ pub with: Option<syn::Path>,
+
+ /// If `true`, generated code will not look for this field in the input meta item,
+ /// instead always falling back to either `InputField::default` or `Default::default`.
+ pub skip: Option<SpannedValue<bool>>,
+ pub post_transform: Option<codegen::PostfixTransform>,
+ pub multiple: Option<bool>,
+ pub flatten: Flag,
+}
+
+impl InputField {
+ /// Generate a view into this field that can be used for code generation.
+ pub fn as_codegen_field(&self) -> codegen::Field<'_> {
+ codegen::Field {
+ ident: &self.ident,
+ name_in_attr: self
+ .attr_name
+ .as_ref()
+ .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
+ ty: &self.ty,
+ default_expression: self.as_codegen_default(),
+ with_path: self.with.as_ref().map_or_else(
+ || {
+ Cow::Owned(
+ parse_quote_spanned!(self.ty.span()=> ::darling::FromMeta::from_meta),
+ )
+ },
+ Cow::Borrowed,
+ ),
+ skip: *self.skip.unwrap_or_default(),
+ post_transform: self.post_transform.as_ref(),
+ multiple: self.multiple.unwrap_or_default(),
+ flatten: self.flatten.is_present(),
+ }
+ }
+
+ /// Generate a codegen::DefaultExpression for this field. This requires the field name
+ /// in the `Inherit` case.
+ fn as_codegen_default(&self) -> Option<codegen::DefaultExpression<'_>> {
+ self.default.as_ref().map(|expr| match *expr {
+ DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
+ DefaultExpression::Inherit => codegen::DefaultExpression::Inherit(&self.ident),
+ DefaultExpression::Trait { span } => codegen::DefaultExpression::Trait { span },
+ })
+ }
+
+ fn new(ident: syn::Ident, ty: syn::Type) -> Self {
+ InputField {
+ ident,
+ ty,
+ attr_name: None,
+ default: None,
+ with: None,
+ skip: None,
+ post_transform: Default::default(),
+ multiple: None,
+ flatten: Default::default(),
+ }
+ }
+
+ pub fn from_field(f: &syn::Field, parent: Option<&Core>) -> Result<Self> {
+ let ident = f
+ .ident
+ .clone()
+ .unwrap_or_else(|| syn::Ident::new("__unnamed", ::proc_macro2::Span::call_site()));
+ let ty = f.ty.clone();
+ let base = Self::new(ident, ty).parse_attributes(&f.attrs)?;
+
+ Ok(if let Some(container) = parent {
+ base.with_inherited(container)
+ } else {
+ base
+ })
+ }
+
+ /// Apply inherited settings from the container. This is done _after_ parsing
+ /// to ensure deference to explicit field-level settings.
+ fn with_inherited(mut self, parent: &Core) -> Self {
+ // explicit renamings take precedence over rename rules on the container,
+ // but in the absence of an explicit name we apply the rule.
+ if self.attr_name.is_none() {
+ self.attr_name = Some(parent.rename_rule.apply_to_field(self.ident.to_string()));
+ }
+
+ // Determine the default expression for this field, based on three pieces of information:
+ // 1. Will we look for this field in the attribute?
+ // 1. Is there a locally-defined default?
+ // 1. Did the parent define a default?
+ self.default = match (&self.skip, self.default.is_some(), parent.default.is_some()) {
+ // If we have a default, use it.
+ (_, true, _) => self.default,
+
+ // If there isn't an explicit default but the struct sets a default, we'll
+ // inherit from that.
+ (_, false, true) => Some(DefaultExpression::Inherit),
+
+ // If we're skipping the field and no defaults have been expressed then we should
+ // use the ::darling::export::Default trait, and set the span to the skip keyword
+ // so that an error caused by the skipped field's type not implementing `Default`
+ // will correctly identify why darling is trying to use `Default`.
+ (Some(v), false, false) if **v => Some(DefaultExpression::Trait { span: v.span() }),
+
+ // If we don't have or need a default, then leave it blank.
+ (_, false, false) => None,
+ };
+
+ self
+ }
+}
+
+impl ParseAttribute for InputField {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ let path = mi.path();
+
+ if path.is_ident("rename") {
+ if self.attr_name.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.attr_name = FromMeta::from_meta(mi)?;
+
+ if self.flatten.is_present() {
+ return Err(
+ Error::custom("`flatten` and `rename` cannot be used together").with_span(mi),
+ );
+ }
+ } else if path.is_ident("default") {
+ if self.default.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+ self.default = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("with") {
+ if self.with.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.with = Some(FromMeta::from_meta(mi)?);
+
+ if self.flatten.is_present() {
+ return Err(
+ Error::custom("`flatten` and `with` cannot be used together").with_span(mi),
+ );
+ }
+ } else if path.is_ident("skip") {
+ if self.skip.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.skip = FromMeta::from_meta(mi)?;
+
+ if self.skip.map(|v| *v).unwrap_or_default() && self.flatten.is_present() {
+ return Err(
+ Error::custom("`flatten` and `skip` cannot be used together").with_span(mi),
+ );
+ }
+ } else if path.is_ident("map") || path.is_ident("and_then") {
+ let transformer = path.get_ident().unwrap().clone();
+ if let Some(post_transform) = &self.post_transform {
+ if transformer == post_transform.transformer {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ } else {
+ return Err(Error::custom(format!(
+ "Options `{}` and `{}` are mutually exclusive",
+ transformer, post_transform.transformer
+ ))
+ .with_span(mi));
+ }
+ }
+
+ self.post_transform = Some(codegen::PostfixTransform::new(
+ transformer,
+ FromMeta::from_meta(mi)?,
+ ));
+ } else if path.is_ident("multiple") {
+ if self.multiple.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.multiple = FromMeta::from_meta(mi)?;
+
+ if self.multiple == Some(true) && self.flatten.is_present() {
+ return Err(
+ Error::custom("`flatten` and `multiple` cannot be used together").with_span(mi),
+ );
+ }
+ } else if path.is_ident("flatten") {
+ if self.flatten.is_present() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.flatten = FromMeta::from_meta(mi)?;
+
+ let mut conflicts = Error::accumulator();
+
+ if self.multiple == Some(true) {
+ conflicts.push(
+ Error::custom("`flatten` and `multiple` cannot be used together").with_span(mi),
+ );
+ }
+
+ if self.attr_name.is_some() {
+ conflicts.push(
+ Error::custom("`flatten` and `rename` cannot be used together").with_span(mi),
+ );
+ }
+
+ if self.with.is_some() {
+ conflicts.push(
+ Error::custom("`flatten` and `with` cannot be used together").with_span(mi),
+ );
+ }
+
+ if self.skip.map(|v| *v).unwrap_or_default() {
+ conflicts.push(
+ Error::custom("`flatten` and `skip` cannot be used together").with_span(mi),
+ );
+ }
+
+ conflicts.finish()?;
+ } else {
+ return Err(Error::unknown_field_path(path).with_span(mi));
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/options/input_variant.rs b/src/options/input_variant.rs
new file mode 100644
index 0000000..05b9e27
--- /dev/null
+++ b/src/options/input_variant.rs
@@ -0,0 +1,126 @@
+use std::borrow::Cow;
+
+use crate::ast::Fields;
+use crate::codegen;
+use crate::options::{Core, InputField, ParseAttribute};
+use crate::util::SpannedValue;
+use crate::{Error, FromMeta, Result};
+
+#[derive(Debug, Clone)]
+pub struct InputVariant {
+ ident: syn::Ident,
+ attr_name: Option<String>,
+ data: Fields<InputField>,
+ skip: Option<bool>,
+ /// Whether or not the variant should be used to create an instance for
+ /// `FromMeta::from_word`.
+ pub word: Option<SpannedValue<bool>>,
+ /// Whether or not unknown fields are acceptable in this
+ allow_unknown_fields: Option<bool>,
+}
+
+impl InputVariant {
+ pub fn as_codegen_variant<'a>(&'a self, ty_ident: &'a syn::Ident) -> codegen::Variant<'a> {
+ codegen::Variant {
+ ty_ident,
+ variant_ident: &self.ident,
+ name_in_attr: self
+ .attr_name
+ .as_ref()
+ .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
+ data: self.data.as_ref().map(InputField::as_codegen_field),
+ skip: self.skip.unwrap_or_default(),
+ word: *self.word.unwrap_or_default(),
+ allow_unknown_fields: self.allow_unknown_fields.unwrap_or_default(),
+ }
+ }
+
+ pub fn from_variant(v: &syn::Variant, parent: Option<&Core>) -> Result<Self> {
+ let mut starter = (InputVariant {
+ ident: v.ident.clone(),
+ attr_name: Default::default(),
+ data: Fields::empty_from(&v.fields),
+ skip: Default::default(),
+ word: Default::default(),
+ allow_unknown_fields: None,
+ })
+ .parse_attributes(&v.attrs)?;
+
+ starter.data.fields = match v.fields {
+ syn::Fields::Unit => vec![],
+ syn::Fields::Unnamed(ref fields) => {
+ let mut items = Vec::with_capacity(fields.unnamed.len());
+ for item in &fields.unnamed {
+ items.push(InputField::from_field(item, parent)?);
+ }
+
+ items
+ }
+ syn::Fields::Named(ref fields) => {
+ let mut items = Vec::with_capacity(fields.named.len());
+ for item in &fields.named {
+ items.push(InputField::from_field(item, parent)?);
+ }
+
+ items
+ }
+ };
+
+ Ok(if let Some(p) = parent {
+ starter.with_inherited(p)
+ } else {
+ starter
+ })
+ }
+
+ fn with_inherited(mut self, parent: &Core) -> Self {
+ if self.attr_name.is_none() {
+ self.attr_name = Some(parent.rename_rule.apply_to_variant(self.ident.to_string()));
+ }
+
+ if self.allow_unknown_fields.is_none() {
+ self.allow_unknown_fields = Some(parent.allow_unknown_fields.unwrap_or_default());
+ }
+
+ self
+ }
+}
+
+impl ParseAttribute for InputVariant {
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
+ let path = mi.path();
+ if path.is_ident("rename") {
+ if self.attr_name.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.attr_name = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("skip") {
+ if self.skip.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ self.skip = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("word") {
+ if self.word.is_some() {
+ return Err(Error::duplicate_field_path(path).with_span(mi));
+ }
+
+ if !self.data.is_unit() {
+ let note = "`#[darling(word)]` can only be applied to a unit variant";
+ #[cfg(feature = "diagnostics")]
+ let error = Error::unknown_field_path(path).note(note);
+ #[cfg(not(feature = "diagnostics"))]
+ let error = Error::custom(format!("Unexpected field: `word`. {}", note));
+
+ return Err(error.with_span(mi));
+ }
+
+ self.word = FromMeta::from_meta(mi)?;
+ } else {
+ return Err(Error::unknown_field_path(path).with_span(mi));
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/options/mod.rs b/src/options/mod.rs
new file mode 100644
index 0000000..c00e0f1
--- /dev/null
+++ b/src/options/mod.rs
@@ -0,0 +1,158 @@
+use proc_macro2::Span;
+use syn::{parse_quote, spanned::Spanned};
+
+use crate::ast::NestedMeta;
+use crate::error::Accumulator;
+use crate::{Error, FromMeta, Result};
+
+mod core;
+mod forward_attrs;
+mod from_attributes;
+mod from_derive;
+mod from_field;
+mod from_meta;
+mod from_type_param;
+mod from_variant;
+mod input_field;
+mod input_variant;
+mod outer_from;
+mod shape;
+
+pub use self::core::Core;
+pub use self::forward_attrs::{AttrsField, ForwardAttrsFilter};
+pub use self::from_attributes::FromAttributesOptions;
+pub use self::from_derive::FdiOptions;
+pub use self::from_field::FromFieldOptions;
+pub use self::from_meta::FromMetaOptions;
+pub use self::from_type_param::FromTypeParamOptions;
+pub use self::from_variant::FromVariantOptions;
+pub use self::input_field::InputField;
+pub use self::input_variant::InputVariant;
+pub use self::outer_from::OuterFrom;
+pub use self::shape::{DataShape, DeriveInputShapeSet};
+
+/// A default/fallback expression encountered in attributes during parsing.
+#[derive(Debug, Clone)]
+pub enum DefaultExpression {
+ /// The value should be taken from the `default` instance of the containing struct.
+ /// This is not valid in container options.
+ Inherit,
+ Explicit(syn::Path),
+ Trait {
+ /// The input span that is responsible for the use of `Default::default`.
+ span: Span,
+ },
+}
+
+#[doc(hidden)]
+impl FromMeta for DefaultExpression {
+ // Note: This cannot use `from_word` as it needs to capture the span
+ // in the `Meta::Path` case.
+ fn from_meta(item: &syn::Meta) -> Result<Self> {
+ match item {
+ syn::Meta::Path(_) => Ok(DefaultExpression::Trait { span: item.span() }),
+ syn::Meta::List(nm) => Err(Error::unsupported_format("list").with_span(nm)),
+ syn::Meta::NameValue(nv) => Self::from_expr(&nv.value),
+ }
+ }
+
+ fn from_expr(expr: &syn::Expr) -> Result<Self> {
+ syn::Path::from_expr(expr).map(DefaultExpression::Explicit)
+ }
+
+ fn from_value(value: &syn::Lit) -> Result<Self> {
+ syn::Path::from_value(value).map(DefaultExpression::Explicit)
+ }
+}
+
+/// Middleware for extracting attribute values. Implementers are expected to override
+/// `parse_nested` so they can apply individual items to themselves, while `parse_attributes`
+/// is responsible for looping through distinct outer attributes and collecting errors.
+pub trait ParseAttribute: Sized {
+ fn parse_attributes(mut self, attrs: &[syn::Attribute]) -> Result<Self> {
+ let mut errors = Error::accumulator();
+ for attr in attrs {
+ if attr.meta.path() == &parse_quote!(darling) {
+ errors.handle(parse_attr(attr, &mut self));
+ }
+ }
+
+ errors.finish_with(self)
+ }
+
+ /// Read a meta-item, and apply its values to the current instance.
+ fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()>;
+}
+
+fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Result<()> {
+ let mut errors = Error::accumulator();
+ match &attr.meta {
+ syn::Meta::List(data) => {
+ for item in NestedMeta::parse_meta_list(data.tokens.clone())? {
+ if let NestedMeta::Meta(ref mi) = item {
+ errors.handle(target.parse_nested(mi));
+ } else {
+ panic!("Wasn't able to parse: `{:?}`", item);
+ }
+ }
+
+ errors.finish()
+ }
+ item => panic!("Wasn't able to parse: `{:?}`", item),
+ }
+}
+
+/// Middleware for extracting values from the body of the derive input. Implementers are
+/// expected to override `parse_field` or `parse_variant` as appropriate for their use-case,
+/// while `parse_body` dispatches to the appropriate methods and handles error collection.
+pub trait ParseData: Sized {
+ fn parse_body(mut self, body: &syn::Data) -> Result<Self> {
+ use syn::{Data, Fields};
+
+ let mut errors = Error::accumulator();
+
+ match *body {
+ Data::Struct(ref data) => match data.fields {
+ Fields::Unit => {}
+ Fields::Named(ref fields) => {
+ for field in &fields.named {
+ errors.handle(self.parse_field(field));
+ }
+ }
+ Fields::Unnamed(ref fields) => {
+ for field in &fields.unnamed {
+ errors.handle(self.parse_field(field));
+ }
+ }
+ },
+ Data::Enum(ref data) => {
+ for variant in &data.variants {
+ errors.handle(self.parse_variant(variant));
+ }
+ }
+ Data::Union(_) => unreachable!(),
+ };
+
+ self.validate_body(&mut errors);
+
+ errors.finish_with(self)
+ }
+
+ /// Apply the next found variant to the object, returning an error
+ /// if parsing goes wrong.
+ fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
+ Err(Error::unsupported_format("enum variant").with_span(variant))
+ }
+
+ /// Apply the next found struct field to the object, returning an error
+ /// if parsing goes wrong.
+ fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
+ Err(Error::unsupported_format("struct field").with_span(field))
+ }
+
+ /// Perform validation checks that require data from more than one field or variant.
+ /// The default implementation does no validations.
+ /// Implementors can override this method as appropriate for their use-case.
+ #[allow(unused_variables)]
+ fn validate_body(&self, errors: &mut Accumulator) {}
+}
diff --git a/src/options/outer_from.rs b/src/options/outer_from.rs
new file mode 100644
index 0000000..da97b47
--- /dev/null
+++ b/src/options/outer_from.rs
@@ -0,0 +1,95 @@
+use syn::spanned::Spanned;
+use syn::{Field, Ident, Meta};
+
+use crate::codegen::ForwardAttrs;
+use crate::options::{
+ AttrsField, Core, DefaultExpression, ForwardAttrsFilter, ParseAttribute, ParseData,
+};
+use crate::util::PathList;
+use crate::{FromField, FromMeta, Result};
+
+/// Reusable base for `FromDeriveInput`, `FromVariant`, `FromField`, and other top-level
+/// `From*` traits.
+#[derive(Debug, Clone)]
+pub struct OuterFrom {
+ /// The field on the target struct which should receive the type identifier, if any.
+ pub ident: Option<Ident>,
+
+ /// The field on the target struct which should receive the type attributes, if any.
+ pub attrs: Option<AttrsField>,
+
+ pub container: Core,
+
+ /// The attribute names that should be searched.
+ pub attr_names: PathList,
+
+ /// The attribute names that should be forwarded. The presence of the word with no additional
+ /// filtering will cause _all_ attributes to be cloned and exposed to the struct after parsing.
+ pub forward_attrs: Option<ForwardAttrsFilter>,
+
+ /// Whether or not the container can be made through conversion from the type `Ident`.
+ pub from_ident: bool,
+}
+
+impl OuterFrom {
+ pub fn start(di: &syn::DeriveInput) -> Result<Self> {
+ Ok(OuterFrom {
+ container: Core::start(di)?,
+ attrs: Default::default(),
+ ident: Default::default(),
+ attr_names: Default::default(),
+ forward_attrs: Default::default(),
+ from_ident: Default::default(),
+ })
+ }
+
+ pub fn as_forward_attrs(&self) -> ForwardAttrs {
+ ForwardAttrs {
+ field: self.attrs.as_ref(),
+ filter: self.forward_attrs.as_ref(),
+ }
+ }
+}
+
+impl ParseAttribute for OuterFrom {
+ fn parse_nested(&mut self, mi: &Meta) -> Result<()> {
+ let path = mi.path();
+ if path.is_ident("attributes") {
+ self.attr_names = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("forward_attrs") {
+ self.forward_attrs = FromMeta::from_meta(mi)?;
+ } else if path.is_ident("from_ident") {
+ // HACK: Declaring that a default is present will cause fields to
+ // generate correct code, but control flow isn't that obvious.
+ self.container.default = Some(DefaultExpression::Trait {
+ // Use the span of the `from_ident` keyword so that errors in generated code
+ // caused by this will point back to the correct location.
+ span: path.span(),
+ });
+ self.from_ident = true;
+ } else {
+ return self.container.parse_nested(mi);
+ }
+ Ok(())
+ }
+}
+
+impl ParseData for OuterFrom {
+ fn parse_field(&mut self, field: &Field) -> Result<()> {
+ match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
+ Some("ident") => {
+ self.ident = field.ident.clone();
+ Ok(())
+ }
+ Some("attrs") => {
+ self.attrs = AttrsField::from_field(field).map(Some)?;
+ Ok(())
+ }
+ _ => self.container.parse_field(field),
+ }
+ }
+
+ fn validate_body(&self, errors: &mut crate::error::Accumulator) {
+ self.container.validate_body(errors);
+ }
+}
diff --git a/src/options/shape.rs b/src/options/shape.rs
new file mode 100644
index 0000000..3e35a23
--- /dev/null
+++ b/src/options/shape.rs
@@ -0,0 +1,259 @@
+//! Types for "shape" validation. This allows types deriving `FromDeriveInput` etc. to declare
+//! that they only work on - for example - structs with named fields, or newtype enum variants.
+
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{parse_quote, Meta};
+
+use crate::ast::NestedMeta;
+use crate::{Error, FromMeta, Result};
+
+/// Receiver struct for shape validation. Shape validation allows a deriving type
+/// to declare that it only accepts - for example - named structs, or newtype enum
+/// variants.
+///
+/// ```rust,ignore
+/// #[ignore(any, struct_named, enum_newtype)]
+/// ```
+#[derive(Debug, Clone)]
+pub struct DeriveInputShapeSet {
+ enum_values: DataShape,
+ struct_values: DataShape,
+ any: bool,
+}
+
+impl Default for DeriveInputShapeSet {
+ fn default() -> Self {
+ DeriveInputShapeSet {
+ enum_values: DataShape::new("enum_"),
+ struct_values: DataShape::new("struct_"),
+ any: Default::default(),
+ }
+ }
+}
+
+impl FromMeta for DeriveInputShapeSet {
+ fn from_list(items: &[NestedMeta]) -> Result<Self> {
+ let mut new = DeriveInputShapeSet::default();
+ for item in items {
+ if let NestedMeta::Meta(Meta::Path(ref path)) = *item {
+ let ident = &path.segments.first().unwrap().ident;
+ let word = ident.to_string();
+ if word == "any" {
+ new.any = true;
+ } else if word.starts_with("enum_") {
+ new.enum_values
+ .set_word(&word)
+ .map_err(|e| e.with_span(&ident))?;
+ } else if word.starts_with("struct_") {
+ new.struct_values
+ .set_word(&word)
+ .map_err(|e| e.with_span(&ident))?;
+ } else {
+ return Err(Error::unknown_value(&word).with_span(&ident));
+ }
+ } else {
+ return Err(Error::unsupported_format("non-word").with_span(item));
+ }
+ }
+
+ Ok(new)
+ }
+}
+
+impl ToTokens for DeriveInputShapeSet {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let fn_body = if self.any {
+ quote!(::darling::export::Ok(()))
+ } else {
+ let en = &self.enum_values;
+ let st = &self.struct_values;
+
+ quote! {
+ {
+ let struct_check = #st;
+ let enum_check = #en;
+
+ match *__body {
+ ::darling::export::syn::Data::Enum(ref data) => {
+ if enum_check.is_empty() {
+ return ::darling::export::Err(
+ ::darling::Error::unsupported_shape_with_expected("enum", &format!("struct with {}", struct_check))
+ );
+ }
+
+ let mut variant_errors = ::darling::Error::accumulator();
+ for variant in &data.variants {
+ variant_errors.handle(enum_check.check(variant));
+ }
+
+ variant_errors.finish()
+ }
+ ::darling::export::syn::Data::Struct(ref struct_data) => {
+ if struct_check.is_empty() {
+ return ::darling::export::Err(
+ ::darling::Error::unsupported_shape_with_expected("struct", &format!("enum with {}", enum_check))
+ );
+ }
+
+ struct_check.check(struct_data)
+ }
+ ::darling::export::syn::Data::Union(_) => unreachable!(),
+ }
+ }
+ }
+ };
+
+ tokens.append_all(quote! {
+ #[allow(unused_variables)]
+ fn __validate_body(__body: &::darling::export::syn::Data) -> ::darling::Result<()> {
+ #fn_body
+ }
+ });
+ }
+}
+
+/// Receiver for shape information within a struct or enum context. See `Shape` for more information
+/// on valid uses of shape validation.
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+pub struct DataShape {
+ /// The kind of shape being described. This can be `struct_` or `enum_`.
+ prefix: &'static str,
+ newtype: bool,
+ named: bool,
+ tuple: bool,
+ unit: bool,
+ any: bool,
+}
+
+impl DataShape {
+ fn new(prefix: &'static str) -> Self {
+ DataShape {
+ prefix,
+ ..Default::default()
+ }
+ }
+
+ fn set_word(&mut self, word: &str) -> Result<()> {
+ match word.trim_start_matches(self.prefix) {
+ "newtype" => {
+ self.newtype = true;
+ Ok(())
+ }
+ "named" => {
+ self.named = true;
+ Ok(())
+ }
+ "tuple" => {
+ self.tuple = true;
+ Ok(())
+ }
+ "unit" => {
+ self.unit = true;
+ Ok(())
+ }
+ "any" => {
+ self.any = true;
+ Ok(())
+ }
+ _ => Err(Error::unknown_value(word)),
+ }
+ }
+}
+
+impl FromMeta for DataShape {
+ fn from_list(items: &[NestedMeta]) -> Result<Self> {
+ let mut errors = Error::accumulator();
+ let mut new = DataShape::default();
+
+ for item in items {
+ if let NestedMeta::Meta(Meta::Path(ref path)) = *item {
+ errors.handle(new.set_word(&path.segments.first().unwrap().ident.to_string()));
+ } else {
+ errors.push(Error::unsupported_format("non-word").with_span(item));
+ }
+ }
+
+ errors.finish_with(new)
+ }
+}
+
+impl ToTokens for DataShape {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Self {
+ any,
+ named,
+ tuple,
+ unit,
+ newtype,
+ ..
+ } = *self;
+
+ let shape_path: syn::Path = parse_quote!(::darling::util::Shape);
+
+ let mut shapes = vec![];
+ if any || named {
+ shapes.push(quote!(#shape_path::Named));
+ }
+
+ if any || tuple {
+ shapes.push(quote!(#shape_path::Tuple));
+ }
+
+ if any || newtype {
+ shapes.push(quote!(#shape_path::Newtype));
+ }
+
+ if any || unit {
+ shapes.push(quote!(#shape_path::Unit));
+ }
+
+ tokens.append_all(quote! {
+ ::darling::util::ShapeSet::new(vec![#(#shapes),*])
+ });
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use proc_macro2::TokenStream;
+ use quote::quote;
+ use syn::parse_quote;
+
+ use super::DeriveInputShapeSet;
+ use crate::FromMeta;
+
+ /// parse a string as a syn::Meta instance.
+ fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
+ let attribute: syn::Attribute = parse_quote!(#[#tokens]);
+ Ok(attribute.meta)
+ }
+
+ fn fm<T: FromMeta>(tokens: TokenStream) -> T {
+ FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
+ .expect("Tests should pass valid input")
+ }
+
+ #[test]
+ fn supports_any() {
+ let decl = fm::<DeriveInputShapeSet>(quote!(ignore(any)));
+ assert!(decl.any);
+ }
+
+ #[test]
+ fn supports_struct() {
+ let decl = fm::<DeriveInputShapeSet>(quote!(ignore(struct_any, struct_newtype)));
+ assert!(decl.struct_values.any);
+ assert!(decl.struct_values.newtype);
+ }
+
+ #[test]
+ fn supports_mixed() {
+ let decl =
+ fm::<DeriveInputShapeSet>(quote!(ignore(struct_newtype, enum_newtype, enum_tuple)));
+ assert!(decl.struct_values.newtype);
+ assert!(decl.enum_values.newtype);
+ assert!(decl.enum_values.tuple);
+ assert!(!decl.struct_values.any);
+ }
+}
diff --git a/src/usage/generics_ext.rs b/src/usage/generics_ext.rs
new file mode 100644
index 0000000..74e5142
--- /dev/null
+++ b/src/usage/generics_ext.rs
@@ -0,0 +1,24 @@
+use syn::Generics;
+
+use crate::usage::{IdentSet, LifetimeSet};
+
+/// Extension trait for pulling specific generics data from a generics AST representation.
+pub trait GenericsExt {
+ /// Get the set of all lifetimes declared by the syntax element.
+ /// This does not look for usage of the lifetime; see `UsesLifetimes` for that.
+ fn declared_lifetimes(&self) -> LifetimeSet;
+
+ /// Get the set of all type parameters declared by the syntax element.
+ /// This does not look for usage of the type parameter; see `UsesTypeParams` for that.
+ fn declared_type_params(&self) -> IdentSet;
+}
+
+impl GenericsExt for Generics {
+ fn declared_lifetimes(&self) -> LifetimeSet {
+ self.lifetimes().map(|lt| lt.lifetime.clone()).collect()
+ }
+
+ fn declared_type_params(&self) -> IdentSet {
+ self.type_params().map(|tp| tp.ident.clone()).collect()
+ }
+}
diff --git a/src/usage/ident_set.rs b/src/usage/ident_set.rs
new file mode 100644
index 0000000..e38d332
--- /dev/null
+++ b/src/usage/ident_set.rs
@@ -0,0 +1,8 @@
+use fnv::FnvHashSet;
+use syn::Ident;
+
+/// A set of idents.
+pub type IdentSet = FnvHashSet<Ident>;
+
+/// A set of references to idents.
+pub type IdentRefSet<'a> = FnvHashSet<&'a Ident>;
diff --git a/src/usage/lifetimes.rs b/src/usage/lifetimes.rs
new file mode 100644
index 0000000..b7124aa
--- /dev/null
+++ b/src/usage/lifetimes.rs
@@ -0,0 +1,351 @@
+use fnv::FnvHashSet;
+use syn::punctuated::Punctuated;
+use syn::{Lifetime, Type};
+
+use crate::usage::Options;
+
+/// A set of lifetimes.
+pub type LifetimeSet = FnvHashSet<Lifetime>;
+
+/// A set of references to lifetimes.
+pub type LifetimeRefSet<'a> = FnvHashSet<&'a Lifetime>;
+
+/// Searcher for finding lifetimes in a syntax tree.
+/// This can be used to determine which lifetimes must be emitted in generated code.
+pub trait UsesLifetimes {
+ /// Returns the subset of the queried lifetimes that are used by the implementing syntax element.
+ ///
+ /// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
+ /// predicates are not detected.
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a>;
+
+ /// Find all used lifetimes, then clone them and return that set.
+ fn uses_lifetimes_cloned(&self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
+ self.uses_lifetimes(options, lifetimes)
+ .into_iter()
+ .cloned()
+ .collect()
+ }
+}
+
+/// Searcher for finding lifetimes in an iterator.
+///
+/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
+/// of lifetimes.
+pub trait CollectLifetimes {
+ /// Consume an iterator, accumulating all lifetimes in the elements which occur in `lifetimes`.
+ fn collect_lifetimes<'a>(
+ self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a>;
+
+ /// Consume an iterator using `collect_lifetimes`, then clone all found lifetimes and return that set.
+ fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet;
+}
+
+impl<'i, I, T> CollectLifetimes for T
+where
+ T: IntoIterator<Item = &'i I>,
+ I: 'i + UsesLifetimes,
+{
+ fn collect_lifetimes<'a>(
+ self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.into_iter()
+ .fold(Default::default(), |mut state, value| {
+ state.extend(value.uses_lifetimes(options, lifetimes));
+ state
+ })
+ }
+
+ fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
+ self.collect_lifetimes(options, lifetimes)
+ .into_iter()
+ .cloned()
+ .collect()
+ }
+}
+
+impl<T: UsesLifetimes> UsesLifetimes for Vec<T> {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.collect_lifetimes(options, lifetimes)
+ }
+}
+
+impl<T: UsesLifetimes, U> UsesLifetimes for Punctuated<T, U> {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.collect_lifetimes(options, lifetimes)
+ }
+}
+
+impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.as_ref()
+ .map(|v| v.uses_lifetimes(options, lifetimes))
+ .unwrap_or_default()
+ }
+}
+
+impl UsesLifetimes for Lifetime {
+ fn uses_lifetimes<'a>(&self, _: &Options, lifetimes: &'a LifetimeSet) -> LifetimeRefSet<'a> {
+ lifetimes.iter().filter(|lt| *lt == self).collect()
+ }
+}
+
+uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
+uses_lifetimes!(syn::AssocType, ty);
+uses_lifetimes!(syn::BareFnArg, ty);
+uses_lifetimes!(syn::BoundLifetimes, lifetimes);
+uses_lifetimes!(syn::ConstParam, ty);
+uses_lifetimes!(syn::Constraint, bounds);
+uses_lifetimes!(syn::DataEnum, variants);
+uses_lifetimes!(syn::DataStruct, fields);
+uses_lifetimes!(syn::DataUnion, fields);
+uses_lifetimes!(syn::Field, ty);
+uses_lifetimes!(syn::FieldsNamed, named);
+uses_lifetimes!(syn::LifetimeParam, lifetime, bounds);
+uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
+uses_lifetimes!(syn::Path, segments);
+uses_lifetimes!(syn::PathSegment, arguments);
+uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
+uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
+uses_lifetimes!(syn::QSelf, ty);
+uses_lifetimes!(syn::TraitBound, path, lifetimes);
+uses_lifetimes!(syn::TypeArray, elem);
+uses_lifetimes!(syn::TypeBareFn, inputs, output);
+uses_lifetimes!(syn::TypeGroup, elem);
+uses_lifetimes!(syn::TypeImplTrait, bounds);
+uses_lifetimes!(syn::TypeParam, bounds);
+uses_lifetimes!(syn::TypeParen, elem);
+uses_lifetimes!(syn::TypePtr, elem);
+uses_lifetimes!(syn::TypeReference, lifetime, elem);
+uses_lifetimes!(syn::TypeSlice, elem);
+uses_lifetimes!(syn::TypeTuple, elems);
+uses_lifetimes!(syn::TypeTraitObject, bounds);
+uses_lifetimes!(syn::Variant, fields);
+
+impl UsesLifetimes for syn::Data {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::Data::Union(ref v) => v.uses_lifetimes(options, lifetimes),
+ }
+ }
+}
+
+impl UsesLifetimes for Type {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ Type::Slice(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Array(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Ptr(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Reference(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::BareFn(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Tuple(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Path(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Paren(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Group(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::TraitObject(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::ImplTrait(ref v) => v.uses_lifetimes(options, lifetimes),
+ Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
+ Default::default()
+ }
+ _ => panic!("Unknown syn::Type: {:?}", self),
+ }
+ }
+}
+
+impl UsesLifetimes for syn::Fields {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ self.collect_lifetimes(options, lifetimes)
+ }
+}
+
+impl UsesLifetimes for syn::TypePath {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ let mut hits = self.path.uses_lifetimes(options, lifetimes);
+
+ if options.include_type_path_qself() {
+ hits.extend(self.qself.uses_lifetimes(options, lifetimes));
+ }
+
+ hits
+ }
+}
+
+impl UsesLifetimes for syn::ReturnType {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ if let syn::ReturnType::Type(_, ref ty) = *self {
+ ty.uses_lifetimes(options, lifetimes)
+ } else {
+ Default::default()
+ }
+ }
+}
+
+impl UsesLifetimes for syn::PathArguments {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::PathArguments::None => Default::default(),
+ syn::PathArguments::AngleBracketed(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::PathArguments::Parenthesized(ref v) => v.uses_lifetimes(options, lifetimes),
+ }
+ }
+}
+
+impl UsesLifetimes for syn::WherePredicate {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::WherePredicate::Type(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::WherePredicate::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::WherePredicate: {:?}", self),
+ }
+ }
+}
+
+impl UsesLifetimes for syn::GenericArgument {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::GenericArgument::Type(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericArgument::AssocType(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericArgument::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericArgument::Constraint(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericArgument::AssocConst(_) | syn::GenericArgument::Const(_) => {
+ Default::default()
+ }
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::GenericArgument: {:?}", self),
+ }
+ }
+}
+
+impl UsesLifetimes for syn::GenericParam {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::GenericParam::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericParam::Type(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::GenericParam::Const(ref v) => v.uses_lifetimes(options, lifetimes),
+ }
+ }
+}
+
+impl UsesLifetimes for syn::TypeParamBound {
+ fn uses_lifetimes<'a>(
+ &self,
+ options: &Options,
+ lifetimes: &'a LifetimeSet,
+ ) -> LifetimeRefSet<'a> {
+ match *self {
+ syn::TypeParamBound::Trait(ref v) => v.uses_lifetimes(options, lifetimes),
+ syn::TypeParamBound::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::TypeParamBound: {:?}", self),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use proc_macro2::Span;
+ use syn::{parse_quote, DeriveInput};
+
+ use super::UsesLifetimes;
+ use crate::usage::GenericsExt;
+ use crate::usage::Purpose::*;
+
+ #[test]
+ fn struct_named() {
+ let input: DeriveInput = parse_quote! {
+ struct Foo<'a, 'b: 'a> {
+ parent: &'b Bar,
+ child: &'a Baz,
+ }
+ };
+ let omitted = syn::Lifetime::new("'c", Span::call_site());
+
+ let lifetimes = {
+ let mut lt = input.generics.declared_lifetimes();
+ lt.insert(omitted);
+ lt
+ };
+
+ let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
+ assert_eq!(matches.len(), 2);
+ }
+
+ #[test]
+ fn qself() {
+ let input: DeriveInput = parse_quote! {
+ struct Foo<'a, 'b: 'a> {
+ parent: &'b Bar,
+ child: <Bar<'a> as MyIterator>::Item,
+ }
+ };
+ let lifetimes = input.generics.declared_lifetimes();
+ let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
+ assert_eq!(matches.len(), 1);
+
+ let decl_matches = input.data.uses_lifetimes(&Declare.into(), &lifetimes);
+ assert_eq!(decl_matches.len(), 2);
+ }
+}
diff --git a/src/usage/mod.rs b/src/usage/mod.rs
new file mode 100644
index 0000000..fe8f12f
--- /dev/null
+++ b/src/usage/mod.rs
@@ -0,0 +1,111 @@
+//! Traits and types used for tracking the usage of generic parameters through a proc-macro input.
+//!
+//! When generating trait impls, libraries often want to automatically figure out which type parameters
+//! are used in which fields, and then emit bounds that will produce the most permissive compilable
+//! code.
+//!
+//! # Usage
+//!
+//! ## Example 1: Filtering
+//! This example accepts a proc-macro input, then finds all lifetimes and type parameters used
+//! by private fields.
+//!
+//! ```rust
+//! # extern crate darling_core;
+//! # extern crate syn;
+//! #
+//! # // in real-world usage, import from `darling`
+//! # use darling_core::usage::{self, CollectLifetimes, CollectTypeParams, GenericsExt, Purpose};
+//! # use syn::{Data, DeriveInput, GenericParam, Generics, Visibility};
+//! #
+//! # #[allow(dead_code)]
+//! fn process(input: &DeriveInput) -> Generics {
+//! let type_params = input.generics.declared_type_params();
+//! let lifetimes = input.generics.declared_lifetimes();
+//!
+//! let mut ret_generics = input.generics.clone();
+//!
+//! if let Data::Struct(ref body) = input.data {
+//! let internal_fields = body
+//! .fields
+//! .iter()
+//! .filter(|field| field.vis == Visibility::Inherited)
+//! .collect::<Vec<_>>();
+//!
+//! let int_type_params = internal_fields
+//! .collect_type_params(&Purpose::BoundImpl.into(), &type_params);
+//!
+//! // We could reuse the vec from above, but here we'll instead
+//! // directly consume the chained iterator.
+//! let int_lifetimes = body
+//! .fields
+//! .iter()
+//! .filter(|field| field.vis == Visibility::Inherited)
+//! .collect_lifetimes(&Purpose::BoundImpl.into(), &lifetimes);
+//!
+//!
+//! ret_generics.params = ret_generics
+//! .params
+//! .into_iter()
+//! .filter(|gp| {
+//! match *gp {
+//! GenericParam::Type(ref ty) => int_type_params.contains(&ty.ident),
+//! GenericParam::Lifetime(ref lt) => int_lifetimes.contains(&lt.lifetime),
+//! _ => true,
+//! }
+//! })
+//! .collect();
+//! }
+//!
+//! ret_generics
+//! }
+//!
+//! # fn main() {}
+//! ```
+//!
+//! ## Example 2: Integrating with `FromDeriveInput`
+//! It is possible to use `darling`'s magic fields feature in tandem with the `usage` feature set.
+//! While there is no custom derive for `UsesTypeParams` or `UsesLifetimes`, there are macros to
+//! generate impls.
+//!
+//! ```rust,ignore
+//! #![allow(dead_code)]
+//!
+//! #[derive(FromField)]
+//! #[darling(attributes(speak))]
+//! struct SpeakerField {
+//! ident: Option<syn::Ident>,
+//! ty: syn::Type,
+//! #[darling(default)]
+//! volume: Option<u32>,
+//! }
+//!
+//! uses_type_params!(SpeakerField, ty);
+//! uses_lifetimes!(SpeakerField, ty);
+//!
+//! #[derive(FromDeriveInput)]
+//! struct SpeakerOptions {
+//! generics: syn::Generics,
+//! data: darling::ast::Data<darling::util::Ignored, SpeakerField>,
+//! }
+//! ```
+//!
+//! At this point, you are able to call `uses_type_params` on `SpeakerOptions.data`, or any filtered
+//! view of it. `darling` internally uses this in conjunction with the `skip` meta-item to determine
+//! which type parameters don't require the `FromMeta` bound in generated impls.
+//!
+//! **Note:** If you are performing operations referencing generic params in meta-items parsed by `darling`,
+//! you should determine if those impact the emitted code and wire up `UsesTypeParams` accordingly for
+//! your field/variant.
+
+mod generics_ext;
+mod ident_set;
+mod lifetimes;
+mod options;
+mod type_params;
+
+pub use self::generics_ext::GenericsExt;
+pub use self::ident_set::{IdentRefSet, IdentSet};
+pub use self::lifetimes::{CollectLifetimes, LifetimeRefSet, LifetimeSet, UsesLifetimes};
+pub use self::options::{Options, Purpose};
+pub use self::type_params::{CollectTypeParams, UsesTypeParams};
diff --git a/src/usage/options.rs b/src/usage/options.rs
new file mode 100644
index 0000000..f154881
--- /dev/null
+++ b/src/usage/options.rs
@@ -0,0 +1,58 @@
+/// The goal of tracing generic parameter usage.
+///
+/// Not all uses of type parameters imply a need to add bounds to a generated trait impl.
+/// For example, a field of type `<Vec<T> as a::b::Trait>::Associated` does not need a
+/// `where T: Serialize` bound in `serde`.
+/// However, a proc macro that is attempting to generate a helper struct _would_ need to
+/// know about this usage, or else the generated code would reference an unknown type `T`
+/// and fail to compile.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Purpose {
+ /// The tracing is being used to generate an `impl` block.
+ ///
+ /// Uses such as `syn::TypePath.qself` will _not_ be returned.
+ BoundImpl,
+ /// The tracing is being used to generate a new struct or enum.
+ ///
+ /// All uses will be returned.
+ Declare,
+}
+
+/// Control struct for searching type parameters.
+///
+/// This acts as the search context, preserving information that might have been
+/// kept on a visitor in a different implementation.
+/// Trait implementers are required to pass this through on any invocations they make.
+///
+/// # Usage
+/// For extensibility, `Options` hides all of its fields from consumers.
+/// To create an instance, use the `From<Purpose>` trait implementation:
+///
+/// ```rust
+/// # use darling_core::usage::{Options, Purpose};
+/// let opts: Options = Purpose::BoundImpl.into();
+/// assert!(!opts.include_type_path_qself());
+/// ```
+#[derive(Debug, Clone)]
+pub struct Options {
+ purpose: Purpose,
+ #[doc(hidden)]
+ __nonexhaustive: (),
+}
+
+impl From<Purpose> for Options {
+ fn from(purpose: Purpose) -> Self {
+ Self {
+ purpose,
+ __nonexhaustive: (),
+ }
+ }
+}
+
+impl Options {
+ /// Returns `true` if the implementer of `UseTypeParams` should search
+ /// `<___ as ...>::...` when looking for type parameter uses.
+ pub fn include_type_path_qself(&self) -> bool {
+ self.purpose == Purpose::Declare
+ }
+}
diff --git a/src/usage/type_params.rs b/src/usage/type_params.rs
new file mode 100644
index 0000000..d4e7e79
--- /dev/null
+++ b/src/usage/type_params.rs
@@ -0,0 +1,364 @@
+use syn::punctuated::Punctuated;
+use syn::{Ident, Type};
+
+use crate::usage::{IdentRefSet, IdentSet, Options};
+
+/// Searcher for finding type params in a syntax tree.
+/// This can be used to determine if a given type parameter needs to be bounded in a generated impl.
+pub trait UsesTypeParams {
+ /// Returns the subset of the queried type parameters that are used by the implementing syntax element.
+ ///
+ /// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
+ /// predicates are not detected.
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
+
+ /// Find all type params using `uses_type_params`, then clone the found values and return the set.
+ fn uses_type_params_cloned(&self, options: &Options, type_set: &IdentSet) -> IdentSet {
+ self.uses_type_params(options, type_set)
+ .into_iter()
+ .cloned()
+ .collect()
+ }
+}
+
+/// Searcher for finding type params in an iterator.
+///
+/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
+/// of type parameter idents.
+pub trait CollectTypeParams {
+ /// Consume an iterator, accumulating all type parameters in the elements which occur in `type_set`.
+ fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
+
+ /// Consume an iterator using `collect_type_params`, then clone all found type params and return that set.
+ fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet;
+}
+
+impl<'i, T, I> CollectTypeParams for T
+where
+ T: IntoIterator<Item = &'i I>,
+ I: 'i + UsesTypeParams,
+{
+ fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ self.into_iter().fold(
+ IdentRefSet::with_capacity_and_hasher(type_set.len(), Default::default()),
+ |state, value| union_in_place(state, value.uses_type_params(options, type_set)),
+ )
+ }
+
+ fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet {
+ self.collect_type_params(options, type_set)
+ .into_iter()
+ .cloned()
+ .collect()
+ }
+}
+
+/// Insert the contents of `right` into `left`.
+fn union_in_place<'a>(mut left: IdentRefSet<'a>, right: IdentRefSet<'a>) -> IdentRefSet<'a> {
+ left.extend(right);
+
+ left
+}
+
+impl UsesTypeParams for () {
+ fn uses_type_params<'a>(&self, _options: &Options, _type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ Default::default()
+ }
+}
+
+impl<T: UsesTypeParams> UsesTypeParams for Option<T> {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ self.as_ref()
+ .map(|v| v.uses_type_params(options, type_set))
+ .unwrap_or_default()
+ }
+}
+
+impl<T: UsesTypeParams> UsesTypeParams for Vec<T> {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ self.collect_type_params(options, type_set)
+ }
+}
+
+impl<T: UsesTypeParams, U> UsesTypeParams for Punctuated<T, U> {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ self.collect_type_params(options, type_set)
+ }
+}
+
+uses_type_params!(syn::AngleBracketedGenericArguments, args);
+uses_type_params!(syn::AssocType, ty);
+uses_type_params!(syn::BareFnArg, ty);
+uses_type_params!(syn::Constraint, bounds);
+uses_type_params!(syn::DataEnum, variants);
+uses_type_params!(syn::DataStruct, fields);
+uses_type_params!(syn::DataUnion, fields);
+uses_type_params!(syn::Field, ty);
+uses_type_params!(syn::FieldsNamed, named);
+uses_type_params!(syn::ParenthesizedGenericArguments, inputs, output);
+uses_type_params!(syn::PredicateType, bounded_ty, bounds);
+uses_type_params!(syn::QSelf, ty);
+uses_type_params!(syn::TraitBound, path);
+uses_type_params!(syn::TypeArray, elem);
+uses_type_params!(syn::TypeBareFn, inputs, output);
+uses_type_params!(syn::TypeGroup, elem);
+uses_type_params!(syn::TypeImplTrait, bounds);
+uses_type_params!(syn::TypeParen, elem);
+uses_type_params!(syn::TypePtr, elem);
+uses_type_params!(syn::TypeReference, elem);
+uses_type_params!(syn::TypeSlice, elem);
+uses_type_params!(syn::TypeTuple, elems);
+uses_type_params!(syn::TypeTraitObject, bounds);
+uses_type_params!(syn::Variant, fields);
+
+impl UsesTypeParams for syn::Data {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ syn::Data::Struct(ref v) => v.uses_type_params(options, type_set),
+ syn::Data::Enum(ref v) => v.uses_type_params(options, type_set),
+ syn::Data::Union(ref v) => v.uses_type_params(options, type_set),
+ }
+ }
+}
+
+impl UsesTypeParams for syn::Fields {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ self.collect_type_params(options, type_set)
+ }
+}
+
+/// Check if an Ident exactly matches one of the sought-after type parameters.
+impl UsesTypeParams for Ident {
+ fn uses_type_params<'a>(&self, _options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ type_set.iter().filter(|v| *v == self).collect()
+ }
+}
+
+impl UsesTypeParams for syn::ReturnType {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ if let syn::ReturnType::Type(_, ref ty) = *self {
+ ty.uses_type_params(options, type_set)
+ } else {
+ Default::default()
+ }
+ }
+}
+
+impl UsesTypeParams for Type {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ Type::Slice(ref v) => v.uses_type_params(options, type_set),
+ Type::Array(ref v) => v.uses_type_params(options, type_set),
+ Type::Ptr(ref v) => v.uses_type_params(options, type_set),
+ Type::Reference(ref v) => v.uses_type_params(options, type_set),
+ Type::BareFn(ref v) => v.uses_type_params(options, type_set),
+ Type::Tuple(ref v) => v.uses_type_params(options, type_set),
+ Type::Path(ref v) => v.uses_type_params(options, type_set),
+ Type::Paren(ref v) => v.uses_type_params(options, type_set),
+ Type::Group(ref v) => v.uses_type_params(options, type_set),
+ Type::TraitObject(ref v) => v.uses_type_params(options, type_set),
+ Type::ImplTrait(ref v) => v.uses_type_params(options, type_set),
+ Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
+ Default::default()
+ }
+ _ => panic!("Unknown syn::Type: {:?}", self),
+ }
+ }
+}
+
+impl UsesTypeParams for syn::TypePath {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ let hits = self.path.uses_type_params(options, type_set);
+
+ if options.include_type_path_qself() {
+ union_in_place(hits, self.qself.uses_type_params(options, type_set))
+ } else {
+ hits
+ }
+ }
+}
+
+impl UsesTypeParams for syn::Path {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ // Not sure if this is even possible, but a path with no segments definitely
+ // can't use type parameters.
+ if self.segments.is_empty() {
+ return Default::default();
+ }
+
+ // A path segment ident can only match if it is not global and it is the first segment
+ // in the path.
+ let ident_hits = if self.leading_colon.is_none() {
+ self.segments[0].ident.uses_type_params(options, type_set)
+ } else {
+ Default::default()
+ };
+
+ // Merge ident hit, if any, with all hits from path arguments
+ self.segments.iter().fold(ident_hits, |state, segment| {
+ union_in_place(state, segment.arguments.uses_type_params(options, type_set))
+ })
+ }
+}
+
+impl UsesTypeParams for syn::PathArguments {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ syn::PathArguments::None => Default::default(),
+ syn::PathArguments::AngleBracketed(ref v) => v.uses_type_params(options, type_set),
+ syn::PathArguments::Parenthesized(ref v) => v.uses_type_params(options, type_set),
+ }
+ }
+}
+
+impl UsesTypeParams for syn::WherePredicate {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ syn::WherePredicate::Lifetime(_) => Default::default(),
+ syn::WherePredicate::Type(ref v) => v.uses_type_params(options, type_set),
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::WherePredicate: {:?}", self),
+ }
+ }
+}
+
+impl UsesTypeParams for syn::GenericArgument {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ syn::GenericArgument::Type(ref v) => v.uses_type_params(options, type_set),
+ syn::GenericArgument::AssocType(ref v) => v.uses_type_params(options, type_set),
+ syn::GenericArgument::Constraint(ref v) => v.uses_type_params(options, type_set),
+ syn::GenericArgument::AssocConst(_)
+ | syn::GenericArgument::Const(_)
+ | syn::GenericArgument::Lifetime(_) => Default::default(),
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::GenericArgument: {:?}", self),
+ }
+ }
+}
+
+impl UsesTypeParams for syn::TypeParamBound {
+ fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
+ match *self {
+ syn::TypeParamBound::Trait(ref v) => v.uses_type_params(options, type_set),
+ syn::TypeParamBound::Lifetime(_) => Default::default(),
+ // non-exhaustive enum
+ // TODO: replace panic with failible function
+ _ => panic!("Unknown syn::TypeParamBound: {:?}", self),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use proc_macro2::Span;
+ use syn::{parse_quote, DeriveInput, Ident};
+
+ use super::UsesTypeParams;
+ use crate::usage::IdentSet;
+ use crate::usage::Purpose::*;
+
+ fn ident_set(idents: Vec<&str>) -> IdentSet {
+ idents
+ .into_iter()
+ .map(|s| Ident::new(s, Span::call_site()))
+ .collect()
+ }
+
+ #[test]
+ fn finds_simple() {
+ let input: DeriveInput = parse_quote! { struct Foo<T, U>(T, i32, A, U); };
+ let generics = ident_set(vec!["T", "U", "X"]);
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+ assert_eq!(matches.len(), 2);
+ assert!(matches.contains::<Ident>(&parse_quote!(T)));
+ assert!(matches.contains::<Ident>(&parse_quote!(U)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(X)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(A)));
+ }
+
+ #[test]
+ fn finds_named() {
+ let input: DeriveInput = parse_quote! {
+ struct Foo<T, U = usize> {
+ bar: T,
+ world: U,
+ }
+ };
+
+ let generics = ident_set(vec!["T", "U", "X"]);
+
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+
+ assert_eq!(matches.len(), 2);
+ assert!(matches.contains::<Ident>(&parse_quote!(T)));
+ assert!(matches.contains::<Ident>(&parse_quote!(U)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(X)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(A)));
+ }
+
+ #[test]
+ fn finds_as_type_arg() {
+ let input: DeriveInput = parse_quote! {
+ struct Foo<T, U> {
+ bar: T,
+ world: Vec<U>,
+ }
+ };
+
+ let generics = ident_set(vec!["T", "U", "X"]);
+
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+
+ assert_eq!(matches.len(), 2);
+ assert!(matches.contains::<Ident>(&parse_quote!(T)));
+ assert!(matches.contains::<Ident>(&parse_quote!(U)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(X)));
+ assert!(!matches.contains::<Ident>(&parse_quote!(A)));
+ }
+
+ #[test]
+ fn associated_type() {
+ let input: DeriveInput =
+ parse_quote! { struct Foo<'a, T> where T: Iterator { peek: T::Item } };
+ let generics = ident_set(vec!["T", "INTO"]);
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+ assert_eq!(matches.len(), 1);
+ }
+
+ #[test]
+ fn box_fn_output() {
+ let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn() -> T>); };
+ let generics = ident_set(vec!["T"]);
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+ assert_eq!(matches.len(), 1);
+ assert!(matches.contains::<Ident>(&parse_quote!(T)));
+ }
+
+ #[test]
+ fn box_fn_input() {
+ let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn(&T) -> ()>); };
+ let generics = ident_set(vec!["T"]);
+ let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+ assert_eq!(matches.len(), 1);
+ assert!(matches.contains::<Ident>(&parse_quote!(T)));
+ }
+
+ /// Test that `syn::TypePath` is correctly honoring the different modes a
+ /// search can execute in.
+ #[test]
+ fn qself_vec() {
+ let input: DeriveInput =
+ parse_quote! { struct Foo<T>(<Vec<T> as a::b::Trait>::AssociatedItem); };
+ let generics = ident_set(vec!["T", "U"]);
+
+ let bound_matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
+ assert_eq!(bound_matches.len(), 0);
+
+ let declare_matches = input.data.uses_type_params(&Declare.into(), &generics);
+ assert_eq!(declare_matches.len(), 1);
+ assert!(declare_matches.contains::<Ident>(&parse_quote!(T)));
+ }
+}
diff --git a/src/util/flag.rs b/src/util/flag.rs
new file mode 100644
index 0000000..73cb738
--- /dev/null
+++ b/src/util/flag.rs
@@ -0,0 +1,99 @@
+use proc_macro2::Span;
+use syn::{spanned::Spanned, Meta};
+
+use crate::{FromMeta, Result};
+
+/// A meta-item that can be present as a word - with no value - or absent.
+///
+/// # Defaulting
+/// Like `Option`, `Flag` does not require `#[darling(default)]` to be optional.
+/// If the caller does not include the property, then an absent `Flag` will be included
+/// in the receiver struct.
+///
+/// # Spans
+/// `Flag` keeps the span where its word was seen.
+/// This enables attaching custom error messages to the word, such as in the case of two
+/// conflicting flags being present.
+///
+/// # Example
+/// ```ignore
+/// #[derive(FromMeta)]
+/// #[darling(and_then = Self::not_both)]
+/// struct Demo {
+/// flag_a: Flag,
+/// flag_b: Flag,
+/// }
+///
+/// impl Demo {
+/// fn not_both(self) -> Result<Self> {
+/// if self.flag_a.is_present() && self.flag_b.is_present() {
+/// Err(Error::custom("Cannot set flag_a and flag_b").with_span(&self.flag_b.span()))
+/// } else {
+/// Ok(self)
+/// }
+/// }
+/// }
+/// ```
+///
+/// The above struct would then produce the following error.
+///
+/// ```ignore
+/// #[example(flag_a, flag_b)]
+/// // ^^^^^^ Cannot set flag_a and flag_b
+/// ```
+#[derive(Debug, Clone, Copy, Default)]
+pub struct Flag(Option<Span>);
+
+impl Flag {
+ /// Creates a new `Flag` which corresponds to the presence of a value.
+ pub fn present() -> Self {
+ Flag(Some(Span::call_site()))
+ }
+
+ /// Check if the flag is present.
+ pub fn is_present(&self) -> bool {
+ self.0.is_some()
+ }
+
+ #[deprecated(since = "0.14.0", note = "Use Flag::is_present")]
+ pub fn is_some(&self) -> bool {
+ self.is_present()
+ }
+
+ /// Get the span of the flag, or [`Span::call_site`] if the flag was not present.
+ pub fn span(&self) -> Span {
+ self.0.unwrap_or_else(Span::call_site)
+ }
+}
+
+impl FromMeta for Flag {
+ fn from_none() -> Option<Self> {
+ Some(Flag(None))
+ }
+
+ fn from_meta(mi: &syn::Meta) -> Result<Self> {
+ if let Meta::Path(p) = mi {
+ Ok(Flag(Some(p.span())))
+ } else {
+ // The implementation for () will produce an error for all non-path meta items;
+ // call it to make sure the span behaviors and error messages are the same.
+ Err(<()>::from_meta(mi).unwrap_err())
+ }
+ }
+}
+
+impl From<Flag> for bool {
+ fn from(flag: Flag) -> Self {
+ flag.is_present()
+ }
+}
+
+impl From<bool> for Flag {
+ fn from(v: bool) -> Self {
+ if v {
+ Flag::present()
+ } else {
+ Flag(None)
+ }
+ }
+}
diff --git a/src/util/ident_string.rs b/src/util/ident_string.rs
new file mode 100644
index 0000000..937d1a6
--- /dev/null
+++ b/src/util/ident_string.rs
@@ -0,0 +1,157 @@
+use std::fmt;
+use std::hash::{Hash, Hasher};
+
+use proc_macro2::{Span, TokenStream};
+use quote::ToTokens;
+use syn::{Ident, Meta};
+
+use crate::{FromMeta, Result};
+
+/// A wrapper for an `Ident` which also keeps the value as a string.
+///
+/// This struct can be used to perform string comparisons and operations.
+#[derive(Clone, PartialOrd, Ord)]
+pub struct IdentString {
+ ident: Ident,
+ string: String,
+}
+
+impl IdentString {
+ /// Create a new `IdentString`.
+ pub fn new(ident: Ident) -> Self {
+ IdentString {
+ string: ident.to_string(),
+ ident,
+ }
+ }
+
+ /// Get the ident as a `proc_macro2::Ident`.
+ pub fn as_ident(&self) -> &Ident {
+ &self.ident
+ }
+
+ /// Get the ident as a string.
+ pub fn as_str(&self) -> &str {
+ &self.string
+ }
+
+ /// Get the location of this `Ident` in source.
+ pub fn span(&self) -> Span {
+ self.ident.span()
+ }
+
+ /// Apply some transform to the ident's string representation.
+ ///
+ /// # Panics
+ /// This will panic if the transform produces an invalid ident.
+ pub fn map<F, S>(self, map_fn: F) -> Self
+ where
+ F: FnOnce(String) -> S,
+ S: AsRef<str>,
+ {
+ let span = self.span();
+ let string = map_fn(self.string);
+ Ident::new(string.as_ref(), span).into()
+ }
+}
+
+impl AsRef<Ident> for IdentString {
+ fn as_ref(&self) -> &Ident {
+ self.as_ident()
+ }
+}
+
+impl AsRef<str> for IdentString {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl From<Ident> for IdentString {
+ fn from(ident: Ident) -> Self {
+ IdentString::new(ident)
+ }
+}
+
+impl From<IdentString> for Ident {
+ fn from(v: IdentString) -> Ident {
+ v.ident
+ }
+}
+
+impl From<IdentString> for String {
+ fn from(v: IdentString) -> String {
+ v.string
+ }
+}
+
+impl Eq for IdentString {}
+
+impl PartialEq for IdentString {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.ident == rhs.ident
+ }
+}
+
+impl PartialEq<String> for IdentString {
+ fn eq(&self, rhs: &String) -> bool {
+ self.as_str() == rhs
+ }
+}
+
+impl<'a> PartialEq<&'a str> for IdentString {
+ fn eq(&self, rhs: &&str) -> bool {
+ self.as_str() == *rhs
+ }
+}
+
+impl Hash for IdentString {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.ident.hash(state);
+ }
+}
+
+impl ToTokens for IdentString {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.ident.to_tokens(tokens);
+ }
+}
+
+impl fmt::Debug for IdentString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self.ident)
+ }
+}
+
+impl fmt::Display for IdentString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.ident)
+ }
+}
+
+impl FromMeta for IdentString {
+ fn from_meta(item: &Meta) -> Result<Self> {
+ Ident::from_meta(item).map(IdentString::from)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::IdentString;
+
+ #[test]
+ fn convert() {
+ let i_str = IdentString::new(parse_quote!(t));
+ assert_eq!(i_str.as_str(), "t");
+ }
+
+ #[test]
+ fn map_transform() {
+ let i = IdentString::new(parse_quote!(my));
+ let after = i.map(|v| format!("var_{}", v));
+ assert_eq!(after, "var_my");
+ assert_eq!(after, String::from("var_my"));
+ }
+}
diff --git a/src/util/ignored.rs b/src/util/ignored.rs
new file mode 100644
index 0000000..b74acfd
--- /dev/null
+++ b/src/util/ignored.rs
@@ -0,0 +1,50 @@
+use crate::{
+ usage::{self, UsesLifetimes, UsesTypeParams},
+ FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
+ FromVariant, Result,
+};
+
+/// An efficient way of discarding data from a syntax element.
+///
+/// All syntax elements will be successfully read into
+/// the `Ignored` struct, with all properties discarded.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
+pub struct Ignored;
+
+macro_rules! ignored {
+ ($trayt:ident, $method:ident, $syn:path) => {
+ impl $trayt for Ignored {
+ fn $method(_: &$syn) -> Result<Self> {
+ Ok(Ignored)
+ }
+ }
+ };
+}
+
+ignored!(FromGenericParam, from_generic_param, syn::GenericParam);
+ignored!(FromGenerics, from_generics, syn::Generics);
+ignored!(FromTypeParam, from_type_param, syn::TypeParam);
+ignored!(FromMeta, from_meta, syn::Meta);
+ignored!(FromDeriveInput, from_derive_input, syn::DeriveInput);
+ignored!(FromField, from_field, syn::Field);
+ignored!(FromVariant, from_variant, syn::Variant);
+
+impl UsesTypeParams for Ignored {
+ fn uses_type_params<'a>(
+ &self,
+ _opts: &usage::Options,
+ _: &'a usage::IdentSet,
+ ) -> usage::IdentRefSet<'a> {
+ Default::default()
+ }
+}
+
+impl UsesLifetimes for Ignored {
+ fn uses_lifetimes<'a>(
+ &self,
+ _opts: &usage::Options,
+ _: &'a usage::LifetimeSet,
+ ) -> usage::LifetimeRefSet<'a> {
+ Default::default()
+ }
+}
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
index 0000000..cf761ed
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1,24 @@
+//! Utility types for attribute parsing.
+
+mod flag;
+mod ident_string;
+mod ignored;
+mod over_ride;
+mod parse_attribute;
+pub mod parse_expr;
+mod path_list;
+mod path_to_string;
+mod shape;
+mod spanned_value;
+mod with_original;
+
+pub use self::flag::Flag;
+pub use self::ident_string::IdentString;
+pub use self::ignored::Ignored;
+pub use self::over_ride::Override;
+pub use self::parse_attribute::parse_attribute_to_meta_list;
+pub use self::path_list::PathList;
+pub use self::path_to_string::path_to_string;
+pub use self::shape::{AsShape, Shape, ShapeSet};
+pub use self::spanned_value::SpannedValue;
+pub use self::with_original::WithOriginal;
diff --git a/src/util/over_ride.rs b/src/util/over_ride.rs
new file mode 100644
index 0000000..0b52dd8
--- /dev/null
+++ b/src/util/over_ride.rs
@@ -0,0 +1,151 @@
+use std::fmt;
+
+use syn::Lit;
+
+use crate::ast::NestedMeta;
+use crate::{FromMeta, Result};
+
+use self::Override::*;
+
+/// A value which can inherit a default value or have an explicit value specified.
+///
+/// # Usage
+/// This type is meant for attributes like `default` in `darling`, which can take the following forms:
+///
+/// * `#[darling(default)]`
+/// * `#[darling(default="path::to::fn")]`
+///
+/// In a struct collecting input for this attribute, that would be written as:
+///
+/// ```rust,ignore
+/// use darling::{util::Override, FromField};
+/// #[derive(FromField)]
+/// #[darling(attributes(darling))]
+/// pub struct Options {
+/// default: Option<Override<syn::Path>>,
+/// }
+///
+/// impl Options {
+/// fn hydrate(self) -> Option<syn::Path> {
+/// self.default.map(|ov| ov.unwrap_or(syn::parse_path("::Default::default").unwrap()))
+/// }
+/// }
+/// ```
+///
+/// The `word` format (with no associated value), would produce `Override::Inherit`, while a list
+/// or value format would produce `Override::Explicit`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Override<T> {
+ /// Inherit the eventual value from an external source.
+ Inherit,
+
+ /// Explicitly set the value.
+ Explicit(T),
+}
+
+impl<T> Override<T> {
+ /// Converts from `Override<T>` to `Override<&T>`.
+ ///
+ /// Produces a new `Override`, containing a reference into the original, leaving the original in place.
+ pub fn as_ref(&self) -> Override<&T> {
+ match *self {
+ Inherit => Inherit,
+ Explicit(ref val) => Explicit(val),
+ }
+ }
+
+ /// Converts from `Override<T>` to `Override<&mut T>`.
+ ///
+ /// Produces a new `Override`, containing a mutable reference into the original.
+ pub fn as_mut(&mut self) -> Override<&mut T> {
+ match *self {
+ Inherit => Inherit,
+ Explicit(ref mut val) => Explicit(val),
+ }
+ }
+
+ /// Returns `true` if the override is an `Explicit` value.
+ pub fn is_explicit(&self) -> bool {
+ match *self {
+ Inherit => false,
+ Explicit(_) => true,
+ }
+ }
+
+ /// Converts from `Override<T>` to `Option<T>`.
+ pub fn explicit(self) -> Option<T> {
+ match self {
+ Inherit => None,
+ Explicit(val) => Some(val),
+ }
+ }
+
+ /// Unwraps an override, yielding the content of an `Explicit`. Otherwise, it returns `optb`.
+ pub fn unwrap_or(self, optb: T) -> T {
+ match self {
+ Inherit => optb,
+ Explicit(val) => val,
+ }
+ }
+
+ /// Unwraps an override, yielding the content of an `Explicit`. Otherwise, it calls `op`.
+ pub fn unwrap_or_else<F>(self, op: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ match self {
+ Inherit => op(),
+ Explicit(val) => val,
+ }
+ }
+}
+
+impl<T: Default> Override<T> {
+ /// Returns the contained value or the default value of `T`.
+ pub fn unwrap_or_default(self) -> T {
+ match self {
+ Inherit => Default::default(),
+ Explicit(val) => val,
+ }
+ }
+}
+
+impl<T> Default for Override<T> {
+ fn default() -> Self {
+ Inherit
+ }
+}
+
+impl<T> From<Option<T>> for Override<T> {
+ fn from(v: Option<T>) -> Self {
+ match v {
+ None => Inherit,
+ Some(val) => Explicit(val),
+ }
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for Override<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Inherit => write!(f, "Inherit"),
+ Explicit(ref val) => write!(f, "Explicit `{}`", val),
+ }
+ }
+}
+
+/// Parses a `Meta`. A bare word will produce `Override::Inherit`, while
+/// any value will be forwarded to `T::from_meta`.
+impl<T: FromMeta> FromMeta for Override<T> {
+ fn from_word() -> Result<Self> {
+ Ok(Inherit)
+ }
+
+ fn from_list(items: &[NestedMeta]) -> Result<Self> {
+ Ok(Explicit(FromMeta::from_list(items)?))
+ }
+
+ fn from_value(lit: &Lit) -> Result<Self> {
+ Ok(Explicit(FromMeta::from_value(lit)?))
+ }
+}
diff --git a/src/util/parse_attribute.rs b/src/util/parse_attribute.rs
new file mode 100644
index 0000000..747c30d
--- /dev/null
+++ b/src/util/parse_attribute.rs
@@ -0,0 +1,86 @@
+use crate::{Error, Result};
+use std::fmt;
+use syn::punctuated::Pair;
+use syn::spanned::Spanned;
+use syn::{token, Attribute, Meta, MetaList, Path};
+
+/// Try to parse an attribute into a meta list. Path-type meta values are accepted and returned
+/// as empty lists with their passed-in path. Name-value meta values and non-meta attributes
+/// will cause errors to be returned.
+pub fn parse_attribute_to_meta_list(attr: &Attribute) -> Result<MetaList> {
+ match &attr.meta {
+ Meta::List(list) => Ok(list.clone()),
+ Meta::NameValue(nv) => Err(Error::custom(format!(
+ "Name-value arguments are not supported. Use #[{}(...)]",
+ DisplayPath(&nv.path)
+ ))
+ .with_span(&nv)),
+ Meta::Path(path) => Ok(MetaList {
+ path: path.clone(),
+ delimiter: syn::MacroDelimiter::Paren(token::Paren {
+ span: {
+ let mut group = proc_macro2::Group::new(
+ proc_macro2::Delimiter::None,
+ proc_macro2::TokenStream::new(),
+ );
+ group.set_span(attr.span());
+ group.delim_span()
+ },
+ }),
+ tokens: Default::default(),
+ }),
+ }
+}
+
+struct DisplayPath<'a>(&'a Path);
+
+impl fmt::Display for DisplayPath<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let path = self.0;
+ if path.leading_colon.is_some() {
+ write!(f, "::")?;
+ }
+ for segment in path.segments.pairs() {
+ match segment {
+ Pair::Punctuated(segment, _) => write!(f, "{}::", segment.ident)?,
+ Pair::End(segment) => segment.ident.fmt(f)?,
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::parse_attribute_to_meta_list;
+ use crate::ast::NestedMeta;
+ use syn::spanned::Spanned;
+ use syn::{parse_quote, Ident};
+
+ #[test]
+ fn parse_list() {
+ let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar(baz = 4)])).unwrap();
+ let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
+ assert_eq!(nested_meta.len(), 1);
+ }
+
+ #[test]
+ fn parse_path_returns_empty_list() {
+ let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar])).unwrap();
+ let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
+ assert!(meta.path.is_ident(&Ident::new("bar", meta.path.span())));
+ assert!(nested_meta.is_empty());
+ }
+
+ #[test]
+ fn parse_name_value_returns_error() {
+ parse_attribute_to_meta_list(&parse_quote!(#[bar = 4])).unwrap_err();
+ }
+
+ #[test]
+ fn parse_name_value_error_includes_example() {
+ let err = parse_attribute_to_meta_list(&parse_quote!(#[bar = 4])).unwrap_err();
+ assert!(err.to_string().contains("#[bar(...)]"));
+ }
+}
diff --git a/src/util/parse_expr.rs b/src/util/parse_expr.rs
new file mode 100644
index 0000000..fcc96a0
--- /dev/null
+++ b/src/util/parse_expr.rs
@@ -0,0 +1,86 @@
+//! Functions to use with `#[darling(with = "...")]` that control how quoted values
+//! in [`Meta`] instances are parsed into [`Expr`] fields.
+//!
+//! Version 1 of syn did not permit expressions on the right-hand side of the `=` in a
+//! [`MetaNameValue`](syn::MetaNameValue), so darling accepted string literals and then
+//! parsed their contents as expressions.
+//! Passing a string literal in this version would have required the use of a raw string
+//! to add quotation marks inside the literal.
+//!
+//! Version 2 of syn removes the requirement that the right-hand side be a literal.
+//! For most types, such as [`Path`](syn::Path), the [`FromMeta`] impl can accept the
+//! version without quotation marks without causing ambiguity; a path cannot start and
+//! end with quotation marks, so removal is automatic.
+//!
+//! [`Expr`] is the one type where this ambiguity is new and unavoidable. To address this,
+//! this module provides different functions for different expected behaviors.
+
+use syn::{Expr, Meta};
+
+use crate::{Error, FromMeta};
+
+/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the emitted
+/// expression will be a string literal.
+pub fn preserve_str_literal(meta: &Meta) -> crate::Result<Expr> {
+ match meta {
+ Meta::Path(_) => Err(Error::unsupported_format("path").with_span(meta)),
+ Meta::List(_) => Err(Error::unsupported_format("list").with_span(meta)),
+ Meta::NameValue(nv) => Ok(nv.value.clone()),
+ }
+}
+
+/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the string's
+/// contents will be parsed as an expression and emitted.
+pub fn parse_str_literal(meta: &Meta) -> crate::Result<Expr> {
+ match meta {
+ Meta::Path(_) => Err(Error::unsupported_format("path").with_span(meta)),
+ Meta::List(_) => Err(Error::unsupported_format("list").with_span(meta)),
+ Meta::NameValue(nv) => {
+ if let Expr::Lit(expr_lit) = &nv.value {
+ Expr::from_value(&expr_lit.lit)
+ } else {
+ Ok(nv.value.clone())
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::*;
+
+ macro_rules! meta {
+ ($body:expr) => {
+ {
+ let attr: ::syn::Attribute = ::syn::parse_quote!(#[ignore = $body]);
+ attr.meta
+ }
+ };
+ }
+
+ #[test]
+ fn preserve_str() {
+ assert_eq!(
+ preserve_str_literal(&meta!("World")).unwrap(),
+ parse_quote!("World")
+ );
+ }
+
+ #[test]
+ fn preserve_binary_exp() {
+ assert_eq!(
+ preserve_str_literal(&meta!("World" + 5)).unwrap(),
+ parse_quote!("World" + 5)
+ )
+ }
+
+ #[test]
+ fn parse_ident() {
+ assert_eq!(
+ parse_str_literal(&meta!("world")).unwrap(),
+ parse_quote!(world)
+ )
+ }
+}
diff --git a/src/util/path_list.rs b/src/util/path_list.rs
new file mode 100644
index 0000000..bea25fd
--- /dev/null
+++ b/src/util/path_list.rs
@@ -0,0 +1,105 @@
+use std::ops::Deref;
+
+use syn::{Meta, Path};
+
+use crate::ast::NestedMeta;
+use crate::{Error, FromMeta, Result};
+
+use super::path_to_string;
+
+/// A list of `syn::Path` instances. This type is used to extract a list of paths from an
+/// attribute.
+///
+/// # Usage
+/// An `PathList` field on a struct implementing `FromMeta` will turn `#[builder(derive(serde::Debug, Clone))]` into:
+///
+/// ```rust,ignore
+/// StructOptions {
+/// derive: PathList(vec![syn::Path::new("serde::Debug"), syn::Path::new("Clone")])
+/// }
+/// ```
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct PathList(Vec<Path>);
+
+impl PathList {
+ /// Create a new list.
+ pub fn new<T: Into<Path>>(vals: Vec<T>) -> Self {
+ PathList(vals.into_iter().map(T::into).collect())
+ }
+
+ /// Create a new `Vec` containing the string representation of each path.
+ pub fn to_strings(&self) -> Vec<String> {
+ self.0.iter().map(path_to_string).collect()
+ }
+}
+
+impl Deref for PathList {
+ type Target = Vec<Path>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Vec<Path>> for PathList {
+ fn from(v: Vec<Path>) -> Self {
+ PathList(v)
+ }
+}
+
+impl FromMeta for PathList {
+ fn from_list(v: &[NestedMeta]) -> Result<Self> {
+ let mut paths = Vec::with_capacity(v.len());
+ for nmi in v {
+ if let NestedMeta::Meta(Meta::Path(ref path)) = *nmi {
+ paths.push(path.clone());
+ } else {
+ return Err(Error::unexpected_type("non-word").with_span(nmi));
+ }
+ }
+
+ Ok(PathList(paths))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::PathList;
+ use crate::FromMeta;
+ use proc_macro2::TokenStream;
+ use quote::quote;
+ use syn::{parse_quote, Attribute, Meta};
+
+ /// parse a string as a syn::Meta instance.
+ fn pm(tokens: TokenStream) -> ::std::result::Result<Meta, String> {
+ let attribute: Attribute = parse_quote!(#[#tokens]);
+ Ok(attribute.meta)
+ }
+
+ fn fm<T: FromMeta>(tokens: TokenStream) -> T {
+ FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
+ .expect("Tests should pass valid input")
+ }
+
+ #[test]
+ fn succeeds() {
+ let paths = fm::<PathList>(quote!(ignore(Debug, Clone, Eq)));
+ assert_eq!(
+ paths.to_strings(),
+ vec![
+ String::from("Debug"),
+ String::from("Clone"),
+ String::from("Eq")
+ ]
+ );
+ }
+
+ /// Check that the parser rejects non-word members of the list, and that the error
+ /// has an associated span.
+ #[test]
+ fn fails_non_word() {
+ let input = PathList::from_meta(&pm(quote!(ignore(Debug, Clone = false))).unwrap());
+ let err = input.unwrap_err();
+ assert!(err.has_span());
+ }
+}
diff --git a/src/util/path_to_string.rs b/src/util/path_to_string.rs
new file mode 100644
index 0000000..0e0ad99
--- /dev/null
+++ b/src/util/path_to_string.rs
@@ -0,0 +1,36 @@
+/// Transform Rust paths to a readable and comparable string.
+///
+/// # Limitations
+/// * Leading colons are ignored.
+/// * Angle brackets and `as` elements are ignored.
+///
+/// # Example
+/// ```rust
+/// # use darling_core::util::path_to_string;
+/// # use syn::parse_quote;
+/// assert_eq!(path_to_string(&parse_quote!(a::b)), "a::b");
+/// ```
+pub fn path_to_string(path: &syn::Path) -> String {
+ path.segments
+ .iter()
+ .map(|s| s.ident.to_string())
+ .collect::<Vec<String>>()
+ .join("::")
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::path_to_string;
+
+ #[test]
+ fn simple_ident() {
+ assert_eq!(path_to_string(&parse_quote!(a)), "a");
+ }
+
+ #[test]
+ fn simple_path() {
+ assert_eq!(path_to_string(&parse_quote!(a::b)), "a::b");
+ }
+}
diff --git a/src/util/shape.rs b/src/util/shape.rs
new file mode 100644
index 0000000..5a90891
--- /dev/null
+++ b/src/util/shape.rs
@@ -0,0 +1,285 @@
+use std::{fmt, iter::FromIterator};
+
+use crate::ast;
+
+/// Get the "shape" of a fields container, such as a struct or variant.
+pub trait AsShape {
+ /// Get the "shape" of a fields container.
+ fn as_shape(&self) -> Shape;
+}
+
+impl<T> AsShape for ast::Fields<T> {
+ fn as_shape(&self) -> Shape {
+ match self.style {
+ ast::Style::Tuple if self.fields.len() == 1 => Shape::Newtype,
+ ast::Style::Tuple => Shape::Tuple,
+ ast::Style::Struct => Shape::Named,
+ ast::Style::Unit => Shape::Unit,
+ }
+ }
+}
+
+impl AsShape for syn::Fields {
+ fn as_shape(&self) -> Shape {
+ match self {
+ syn::Fields::Named(fields) => fields.as_shape(),
+ syn::Fields::Unnamed(fields) => fields.as_shape(),
+ syn::Fields::Unit => Shape::Unit,
+ }
+ }
+}
+
+impl AsShape for syn::FieldsNamed {
+ fn as_shape(&self) -> Shape {
+ Shape::Named
+ }
+}
+
+impl AsShape for syn::FieldsUnnamed {
+ fn as_shape(&self) -> Shape {
+ if self.unnamed.len() == 1 {
+ Shape::Newtype
+ } else {
+ Shape::Tuple
+ }
+ }
+}
+
+impl AsShape for syn::DataStruct {
+ fn as_shape(&self) -> Shape {
+ self.fields.as_shape()
+ }
+}
+
+impl AsShape for syn::Variant {
+ fn as_shape(&self) -> Shape {
+ self.fields.as_shape()
+ }
+}
+
+/// Description of how fields in a struct or variant are syntactically laid out.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Shape {
+ /// A set of named fields, e.g. `{ field: String }`.
+ Named,
+ /// A list of unnamed fields, e.g. `(String, u64)`.
+ Tuple,
+ /// No fields, e.g. `struct Example;`
+ Unit,
+ /// A special case of [`Tuple`](Shape#variant.Tuple) with exactly one field, e.g. `(String)`.
+ Newtype,
+}
+
+impl Shape {
+ pub fn description(&self) -> &'static str {
+ match self {
+ Shape::Named => "named fields",
+ Shape::Tuple => "unnamed fields",
+ Shape::Unit => "no fields",
+ Shape::Newtype => "one unnamed field",
+ }
+ }
+}
+
+impl fmt::Display for Shape {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.description())
+ }
+}
+
+impl AsShape for Shape {
+ fn as_shape(&self) -> Shape {
+ *self
+ }
+}
+
+/// A set of [`Shape`] values, which correctly handles the relationship between
+/// [newtype](Shape#variant.Newtype) and [tuple](Shape#variant.Tuple) shapes.
+///
+/// # Example
+/// ```rust
+/// # use darling_core::util::{Shape, ShapeSet};
+/// let shape_set = ShapeSet::new(vec![Shape::Tuple]);
+///
+/// // This is correct, because all newtypes are single-field tuples.
+/// assert!(shape_set.contains(&Shape::Newtype));
+/// ```
+#[derive(Debug, Clone, Default)]
+pub struct ShapeSet {
+ newtype: bool,
+ named: bool,
+ tuple: bool,
+ unit: bool,
+}
+
+impl ShapeSet {
+ /// Create a new `ShapeSet` which includes the specified items.
+ ///
+ /// # Exampe
+ /// ```rust
+ /// # use darling_core::util::{Shape, ShapeSet};
+ /// let shape_set = ShapeSet::new(vec![Shape::Named, Shape::Newtype]);
+ /// assert!(shape_set.contains(&Shape::Newtype));
+ /// ```
+ pub fn new(items: impl IntoIterator<Item = Shape>) -> Self {
+ items.into_iter().collect()
+ }
+
+ /// Insert all possible shapes into the set.
+ ///
+ /// This is equivalent to calling [`insert`](ShapeSet#method.insert) with every value of [`Shape`].
+ ///
+ /// # Example
+ /// ```rust
+ /// # use darling_core::util::{Shape, ShapeSet};
+ /// let mut shape_set = ShapeSet::default();
+ /// shape_set.insert_all();
+ /// assert!(shape_set.contains(&Shape::Named));
+ /// ```
+ pub fn insert_all(&mut self) {
+ self.insert(Shape::Named);
+ self.insert(Shape::Newtype);
+ self.insert(Shape::Tuple);
+ self.insert(Shape::Unit);
+ }
+
+ /// Insert a shape into the set, so that the set will match that shape
+ pub fn insert(&mut self, shape: Shape) {
+ match shape {
+ Shape::Named => self.named = true,
+ Shape::Tuple => self.tuple = true,
+ Shape::Unit => self.unit = true,
+ Shape::Newtype => self.newtype = true,
+ }
+ }
+
+ /// Whether this set is empty.
+ pub fn is_empty(&self) -> bool {
+ !self.named && !self.newtype && !self.tuple && !self.unit
+ }
+
+ fn contains_shape(&self, shape: Shape) -> bool {
+ match shape {
+ Shape::Named => self.named,
+ Shape::Tuple => self.tuple,
+ Shape::Unit => self.unit,
+ Shape::Newtype => self.newtype || self.tuple,
+ }
+ }
+
+ /// Check if a fields container's shape is in this set.
+ pub fn contains(&self, fields: &impl AsShape) -> bool {
+ self.contains_shape(fields.as_shape())
+ }
+
+ /// Check if a field container's shape is in this set of shapes, and produce
+ /// an [`Error`](crate::Error) if it does not.
+ pub fn check(&self, fields: &impl AsShape) -> crate::Result<()> {
+ let shape = fields.as_shape();
+
+ if self.contains_shape(shape) {
+ Ok(())
+ } else {
+ Err(crate::Error::unsupported_shape_with_expected(
+ shape.description(),
+ self,
+ ))
+ }
+ }
+
+ fn to_vec(&self) -> Vec<Shape> {
+ let mut shapes = Vec::with_capacity(3);
+
+ if self.named {
+ shapes.push(Shape::Named);
+ }
+
+ if self.tuple || self.newtype {
+ shapes.push(if self.tuple {
+ Shape::Tuple
+ } else {
+ Shape::Newtype
+ });
+ }
+
+ if self.unit {
+ shapes.push(Shape::Unit)
+ }
+
+ shapes
+ }
+}
+
+impl fmt::Display for ShapeSet {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let shapes = self.to_vec();
+
+ match shapes.len() {
+ 0 => write!(f, "nothing"),
+ 1 => write!(f, "{}", shapes[0]),
+ 2 => write!(f, "{} or {}", shapes[0], shapes[1]),
+ 3 => write!(f, "{}, {}, or {}", shapes[0], shapes[1], shapes[2]),
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl FromIterator<Shape> for ShapeSet {
+ fn from_iter<T: IntoIterator<Item = Shape>>(iter: T) -> Self {
+ let mut output = ShapeSet::default();
+ for shape in iter.into_iter() {
+ output.insert(shape);
+ }
+
+ output
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use syn::parse_quote;
+
+ use super::*;
+
+ #[test]
+ fn any_accepts_anything() {
+ let mut filter = ShapeSet::default();
+ filter.insert_all();
+ let unit_struct: syn::DeriveInput = syn::parse_quote! {
+ struct Example;
+ };
+ if let syn::Data::Struct(data) = unit_struct.data {
+ assert!(filter.contains(&data));
+ } else {
+ panic!("Struct not parsed as struct");
+ };
+ }
+
+ #[test]
+ fn tuple_accepts_newtype() {
+ let filter = ShapeSet::new(vec![Shape::Tuple]);
+ let newtype_struct: syn::DeriveInput = parse_quote! {
+ struct Example(String);
+ };
+
+ if let syn::Data::Struct(data) = newtype_struct.data {
+ assert!(filter.contains(&data));
+ } else {
+ panic!("Struct not parsed as struct");
+ };
+ }
+
+ #[test]
+ fn newtype_rejects_tuple() {
+ let filter = ShapeSet::new(vec![Shape::Newtype]);
+ let tuple_struct: syn::DeriveInput = parse_quote! {
+ struct Example(String, u64);
+ };
+
+ if let syn::Data::Struct(data) = tuple_struct.data {
+ assert!(!filter.contains(&data));
+ } else {
+ panic!("Struct not parsed as struct");
+ };
+ }
+}
diff --git a/src/util/spanned_value.rs b/src/util/spanned_value.rs
new file mode 100644
index 0000000..68dcc38
--- /dev/null
+++ b/src/util/spanned_value.rs
@@ -0,0 +1,127 @@
+use proc_macro2::Span;
+use std::ops::{Deref, DerefMut};
+use syn::spanned::Spanned;
+
+use crate::{
+ FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
+ FromVariant, Result,
+};
+
+/// A value and an associated position in source code. The main use case for this is
+/// to preserve position information to emit warnings from proc macros. You can use
+/// a `SpannedValue<T>` as a field in any struct that implements or derives any of
+/// `darling`'s core traits.
+///
+/// To access the underlying value, use the struct's `Deref` implementation.
+///
+/// # Defaulting
+/// This type is meant to be used in conjunction with attribute-extracted options,
+/// but the user may not always explicitly set those options in their source code.
+/// In this case, using `Default::default()` will create an instance which points
+/// to `Span::call_site()`.
+#[derive(Debug, Clone, Copy)]
+pub struct SpannedValue<T> {
+ value: T,
+ span: Span,
+}
+
+impl<T> SpannedValue<T> {
+ pub fn new(value: T, span: Span) -> Self {
+ SpannedValue { value, span }
+ }
+
+ /// Get the source code location referenced by this struct.
+ pub fn span(&self) -> Span {
+ self.span
+ }
+
+ /// Apply a mapping function to a reference to the spanned value.
+ pub fn map_ref<U>(&self, map_fn: impl FnOnce(&T) -> U) -> SpannedValue<U> {
+ SpannedValue::new(map_fn(&self.value), self.span)
+ }
+}
+
+impl<T: Default> Default for SpannedValue<T> {
+ fn default() -> Self {
+ SpannedValue::new(Default::default(), Span::call_site())
+ }
+}
+
+impl<T> Deref for SpannedValue<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.value
+ }
+}
+
+impl<T> DerefMut for SpannedValue<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.value
+ }
+}
+
+impl<T> AsRef<T> for SpannedValue<T> {
+ fn as_ref(&self) -> &T {
+ &self.value
+ }
+}
+
+macro_rules! spanned {
+ ($trayt:ident, $method:ident, $syn:path) => {
+ impl<T: $trayt> $trayt for SpannedValue<T> {
+ fn $method(value: &$syn) -> Result<Self> {
+ Ok(SpannedValue::new(
+ $trayt::$method(value).map_err(|e| e.with_span(value))?,
+ value.span(),
+ ))
+ }
+ }
+ };
+}
+
+impl<T: FromMeta> FromMeta for SpannedValue<T> {
+ fn from_meta(item: &syn::Meta) -> Result<Self> {
+ let value = T::from_meta(item).map_err(|e| e.with_span(item))?;
+ let span = match item {
+ // Example: `#[darling(skip)]` as SpannedValue<bool>
+ // should have the span pointing to the word `skip`.
+ syn::Meta::Path(path) => path.span(),
+ // Example: `#[darling(attributes(Value))]` as a SpannedValue<Vec<String>>
+ // should have the span pointing to the list contents.
+ syn::Meta::List(list) => list.tokens.span(),
+ // Example: `#[darling(skip = true)]` as SpannedValue<bool>
+ // should have the span pointing to the word `true`.
+ syn::Meta::NameValue(nv) => nv.value.span(),
+ };
+
+ Ok(Self::new(value, span))
+ }
+}
+
+spanned!(FromGenericParam, from_generic_param, syn::GenericParam);
+spanned!(FromGenerics, from_generics, syn::Generics);
+spanned!(FromTypeParam, from_type_param, syn::TypeParam);
+spanned!(FromDeriveInput, from_derive_input, syn::DeriveInput);
+spanned!(FromField, from_field, syn::Field);
+spanned!(FromVariant, from_variant, syn::Variant);
+
+impl<T: Spanned> From<T> for SpannedValue<T> {
+ fn from(value: T) -> Self {
+ let span = value.span();
+ SpannedValue::new(value, span)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use proc_macro2::Span;
+
+ /// Make sure that `SpannedValue` can be seamlessly used as its underlying type.
+ #[test]
+ fn deref() {
+ let test = SpannedValue::new("hello", Span::call_site());
+ assert_eq!("hello", test.trim());
+ }
+}
diff --git a/src/util/with_original.rs b/src/util/with_original.rs
new file mode 100644
index 0000000..5aa82f8
--- /dev/null
+++ b/src/util/with_original.rs
@@ -0,0 +1,35 @@
+use crate::{
+ FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
+ FromVariant, Result,
+};
+
+/// A container to parse some syntax and retain access to the original.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WithOriginal<T, O> {
+ pub parsed: T,
+ pub original: O,
+}
+
+impl<T, O> WithOriginal<T, O> {
+ pub fn new(parsed: T, original: O) -> Self {
+ WithOriginal { parsed, original }
+ }
+}
+
+macro_rules! with_original {
+ ($trayt:ident, $func:ident, $syn:path) => {
+ impl<T: $trayt> $trayt for WithOriginal<T, $syn> {
+ fn $func(value: &$syn) -> Result<Self> {
+ Ok(WithOriginal::new($trayt::$func(value)?, value.clone()))
+ }
+ }
+ };
+}
+
+with_original!(FromDeriveInput, from_derive_input, syn::DeriveInput);
+with_original!(FromField, from_field, syn::Field);
+with_original!(FromGenerics, from_generics, syn::Generics);
+with_original!(FromGenericParam, from_generic_param, syn::GenericParam);
+with_original!(FromMeta, from_meta, syn::Meta);
+with_original!(FromTypeParam, from_type_param, syn::TypeParam);
+with_original!(FromVariant, from_variant, syn::Variant);