diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/darling_core | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/darling_core')
67 files changed, 8559 insertions, 0 deletions
diff --git a/third_party/rust/darling_core/.cargo-checksum.json b/third_party/rust/darling_core/.cargo-checksum.json new file mode 100644 index 0000000000..e973cd4a86 --- /dev/null +++ b/third_party/rust/darling_core/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"fd1dc1c69d5e143e6f498d09cb99fc98b3d4e303518cd8813d097fc13fca7103","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","src/ast/data.rs":"0ad320af548cbf3c5e86c933c80f2b4ca9748ba915b5a289ab95ddb658ca259d","src/ast/generics.rs":"5a9c30068cd39c59c42c1bb494a1fe6680714798f10ca952abc8b0dabd9152ad","src/ast/mod.rs":"58a58eeb1db7682e5994732cc8eb04088f6ca662805460ef443cf089e5c24e2c","src/codegen/attr_extractor.rs":"4eccbdd6fc6ef8df144171473768297deae27acdef6cfb6cdc1b553d44a867c4","src/codegen/default_expr.rs":"5486c8dc264e3bb8be2a871f3258f56ed2812ae39b1eb9f0d16006605407027f","src/codegen/error.rs":"4ef086317234a161bec493413ba38d842cbbecd0b9610dad0a25fe8883c75d3b","src/codegen/field.rs":"40d3f61507440ee62bb1c2098aca39be84ec74e2cecbd11340e925bd68a3cbfa","src/codegen/from_attributes_impl.rs":"f404e32f68bca0097157a23a2d098365d66ba0c3fad6cfdf10ec50d367916415","src/codegen/from_derive_impl.rs":"0f62e61566202f224bbfb8d251141ca64ace5022c5e85e312739da08f3ef51e2","src/codegen/from_field.rs":"10f18dfdae9e9cc295121585862b2f30ed2f7f1124675fe39ac0dd7921ede62a","src/codegen/from_meta_impl.rs":"2b0f8cafdb90a5766f7ceba1c75c5c86a5190a68cbe6897b522ec1c058352acc","src/codegen/from_type_param.rs":"d301fabd5b0024b1267a6c7887f6214fb86783fd4c0bb9a15867d5f3f58fc4b8","src/codegen/from_variant_impl.rs":"e2cc68ad5d0a45989830d144760d55b3fb62297c5aea097fa085dabb22e3897f","src/codegen/mod.rs":"77b194876541e8ced86efebb534fc8a4f70c06d92c8b493aa90ef936bcc14273","src/codegen/outer_from_impl.rs":"c6ef8aad7f9c817005e236b3bebf56319455f578b109bdb47b23c0a1dc1149f5","src/codegen/postfix_transform.rs":"41c84a239c15671e5a9cb8ce9fbb1b907f20aebe30d5dea4f04f004e3aa584c1","src/codegen/trait_impl.rs":"22d307450c56abf29dc2fdf1ced8de2220cc5f544f5297452e829a30093e906e","src/codegen/variant.rs":"473357c628992ab9103863a1afaa331cf91395537016ac1698de35c1eec7e2c8","src/codegen/variant_data.rs":"e45eb2aaffaa8864c7f9e1590aeec5a43e8a05001db1b25e8b27740dafe290b9","src/derive.rs":"d35f35233eea28bc66bf6f738358415d650741560ca56f8deaee9465b3e88b2a","src/error/child.rs":"a6cc6e21b3a424150a764b2b9a8ffbc525722ccb4c372e197ded5a39021439fc","src/error/kind.rs":"862fb877500ceb95e89d5b53bef987a0836e8754bdcc5c30d074c0f53e94d715","src/error/mod.rs":"0fd60ac9ee6159db4a7163ee647e091421ff2445e1c835206755a947f7e8ce3f","src/from_attributes.rs":"9c98a34eccdb66526711793d6d00bd5216de1d0e9d3755133996242a27efe272","src/from_derive_input.rs":"a60aba72ebd4deddd6bb9919995652f90aafdf88f6109c00c1a1c4459e439049","src/from_field.rs":"1e22b7750843b6a018fe75ae1510fb9252343be99ab02d2093881421e87d867d","src/from_generic_param.rs":"bdabc80f49212501e6e3574c7e6c03b4e453d0fe241ac46d465a5f004381f4c9","src/from_generics.rs":"b0a156179b2d1d88467183649afc97405403b01a7fdae39e4df8392f6b9f500c","src/from_meta.rs":"74031f5648b283bd5677feb5b7c8cb4b6a587ce6b641c2768ed98d1d85d10329","src/from_type_param.rs":"0c8930a42e1f0720429aa968f97f938e207460232dd2169cbf8efec12e7c6f5a","src/from_variant.rs":"dd27ffef65ceea0e68809afde7df82d3751cdedc248773062637aa8c146da6e7","src/lib.rs":"b066b0747dcec5150d6962a80850a8fb7690b24700f62537893ebb2d4c88a4a8","src/macros_private.rs":"4373c36e75d516c340fc9b13c52b471d823fff9997cb7faeeb5f93b11d40d0b4","src/macros_public.rs":"7d2ce0c5026227ef7854db11d7a885ad891255438b2e49bbdfda56fa2f92feec","src/options/core.rs":"e0e0cd859bad35904ab65cc0797a17685ac688da019b95c36158199167c97ae3","src/options/forward_attrs.rs":"eb737f961009bf4e00665ced2d51eea826a56ad595eb7c085a7410f6d73c7e74","src/options/from_attributes.rs":"f73c4dc53cced30e60812a4247f3252ea0630618b1d162058cce0bd1666c43c9","src/options/from_derive.rs":"8627df7b97b34c4cc72ae0e269fe752ffea4f8cd9378838c5f8f8bffe1e324aa","src/options/from_field.rs":"3ea3f3409ecfc1984d97c27948dea1fdf040f3b9d7f731043acd766eaaf0546c","src/options/from_meta.rs":"1a543110dc4ab2e716a914bafced9e1f4035794613117708fc9f37a1036bcf75","src/options/from_type_param.rs":"add1a1dbacba4618628aa1150c76dc7b4e9d43b06cdfd731b8b7a143ecac0db8","src/options/from_variant.rs":"86c3b99d965a38c5d8f2562f267d9dfa740988164f77903f81977527091b4596","src/options/input_field.rs":"93000e7705a0a5f0c2ed3354d6585be1987f0a1ef297bb82ffd140440e26808a","src/options/input_variant.rs":"1ebc013018abb07670f13c71d5c819d079a4e824235ede43fb568239efcbf1f7","src/options/mod.rs":"8efa94917174d881dca6a5b106fc0f11f2e1d652ac97df7d2f6dd2fa3bee1a59","src/options/outer_from.rs":"d55ed99a769a4b10f4251c9d795aec289d278a96d6cff0f49a03c9b1fffc5f0f","src/options/shape.rs":"4e676e0fce9c0cec625fcb391897dfc6b7cfc338912b76ff72cd6179affb836d","src/usage/generics_ext.rs":"b2d15e2ff75c47f43f3ce305b2108e800dd3d89e3a21fadf82bda455837b8090","src/usage/ident_set.rs":"30edb2f0a599284967e3c6b579da31e5f9b15f3dd67bc9a82d6335eb44133df0","src/usage/lifetimes.rs":"f3dcb3fab5370c07ddb19fa04079f8da1f035d671930d987ab3980f77ae7f3cc","src/usage/mod.rs":"e49adadfa8ffed27299d5bbf3c33e68e2c8c7411f89aef655f77e158be9dd642","src/usage/options.rs":"0491c995aad0d55783b24cce8a4e40f0f4435988c54ce2ded34763ac9b199fcf","src/usage/type_params.rs":"e009419b102ad56878706d411b44cb89f5a497e8d0abbb6769bcd2d4af53c90b","src/util/flag.rs":"24ba4d5d9e125d7aac0f6a3e8197739d1b610465be65d40df76bfc7f5d7d1fa2","src/util/ident_string.rs":"2a2d32f9ba3a5b46ddcf430891d62f47d0a65782b592cccbfd0e88bb1361cbc9","src/util/ignored.rs":"099c015023b9be0870fb1c4c44ad00f87789ea13da25e2ca952132443e497214","src/util/mod.rs":"f8214b2e5fb114092e9e83875168449bccc26967637b13a21cbff835681ad256","src/util/over_ride.rs":"3f2f22173d7f86b1575174c706a81733fa279026638632a30b4be031e2d52d12","src/util/parse_attribute.rs":"82f897215865475ab3fc1ea2036e5bb29e02cee677aa2f05949b3abb4c3d814a","src/util/path_list.rs":"939d0035b09ef7b361e654271f0eafde93be8208f6fb9a1caef9a3ee9e7e337d","src/util/path_to_string.rs":"c093354f91ab45e0be3c50d2d4356408f415c910f1569fb9275c5087110736f6","src/util/shape.rs":"dda17aa8a063b5023ae1f055bec2859f984ec32633f6cc55938a442252f07204","src/util/spanned_value.rs":"3a7efefb4085a52d8483ad29b8535633342c8c029354da9a41124ff9c44823ea","src/util/with_original.rs":"56ea20c03d07ebe1a8bc0ed220258f6cee5a3cf7192c0e07601fafda99404062"},"package":"ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"}
\ No newline at end of file diff --git a/third_party/rust/darling_core/Cargo.toml b/third_party/rust/darling_core/Cargo.toml new file mode 100644 index 0000000000..b9e557acb1 --- /dev/null +++ b/third_party/rust/darling_core/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.1" +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/third_party/rust/darling_core/LICENSE b/third_party/rust/darling_core/LICENSE new file mode 100644 index 0000000000..0b48eadc9d --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/ast/data.rs b/third_party/rust/darling_core/src/ast/data.rs new file mode 100644 index 0000000000..4b9159518d --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/ast/generics.rs b/third_party/rust/darling_core/src/ast/generics.rs new file mode 100644 index 0000000000..d4bf0c9b92 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/ast/mod.rs b/third_party/rust/darling_core/src/ast/mod.rs new file mode 100644 index 0000000000..492b26f182 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/codegen/attr_extractor.rs b/third_party/rust/darling_core/src/codegen/attr_extractor.rs new file mode 100644 index 0000000000..4afb174b3b --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/attr_extractor.rs @@ -0,0 +1,121 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::options::ForwardAttrs; +use crate::util::PathList; + +/// 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 forwarded_attrs(&self) -> Option<&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 declarations = self.local_declarations(); + + let will_parse_any = !self.attr_names().is_empty(); + let will_fwd_any = self + .forwarded_attrs() + .map(|fa| !fa.is_empty()) + .unwrap_or_default(); + + 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!() + }; + + // Specifies the behavior for unhandled attributes. They will either be silently ignored or + // forwarded to the inner struct for later analysis. + let forward_unhandled = if will_fwd_any { + forwards_to_local(self.forwarded_attrs().unwrap()) + } else { + quote!(_ => continue) + }; + + quote!( + #declarations + use ::darling::ToTokens; + let mut __fwd_attrs: ::darling::export::Vec<::darling::export::syn::Attribute> = vec![]; + + 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 + } + } + ) + } +} + +fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream { + let push_command = quote!(__fwd_attrs.push(__attr.clone())); + match *behavior { + ForwardAttrs::All => quote!(_ => #push_command), + ForwardAttrs::Only(ref idents) => { + let names = idents.to_strings(); + quote!( + #(#names)|* => #push_command, + _ => continue, + ) + } + } +} diff --git a/third_party/rust/darling_core/src/codegen/default_expr.rs b/third_party/rust/darling_core/src/codegen/default_expr.rs new file mode 100644 index 0000000000..fa8b159897 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/codegen/error.rs b/third_party/rust/darling_core/src/codegen/error.rs new file mode 100644 index 0000000000..f93a7404e0 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/codegen/field.rs b/third_party/rust/darling_core/src/codegen/field.rs new file mode 100644 index 0000000000..03467f0094 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/field.rs @@ -0,0 +1,199 @@ +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, +} + +impl<'a> Field<'a> { + pub fn as_name(&'a self) -> &'a str { + &self.name_in_attr + } + + pub fn as_declaration(&'a self) -> Declaration<'a> { + Declaration(self) + } + + 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);) + }); + } +} + +/// 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; + if !field.skip { + 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/third_party/rust/darling_core/src/codegen/from_attributes_impl.rs b/third_party/rust/darling_core/src/codegen/from_attributes_impl.rs new file mode 100644 index 0000000000..49bbf02c0a --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/from_attributes_impl.rs @@ -0,0 +1,109 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; + +use crate::{ + ast::Data, + codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}, + options::ForwardAttrs, + util::PathList, +}; + +pub struct FromAttributesImpl<'a> { + pub base: TraitImpl<'a>, + pub attr_names: &'a PathList, +} + +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 forwarded_attrs(&self) -> Option<&ForwardAttrs> { + None + } + + 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/third_party/rust/darling_core/src/codegen/from_derive_impl.rs b/third_party/rust/darling_core/src/codegen/from_derive_impl.rs new file mode 100644 index 0000000000..1c1df63d95 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/from_derive_impl.rs @@ -0,0 +1,147 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::Ident; + +use crate::{ + ast::Data, + codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}, + options::{DeriveInputShapeSet, ForwardAttrs}, + util::PathList, +}; + +pub struct FromDeriveInputImpl<'a> { + pub ident: Option<&'a Ident>, + pub generics: Option<&'a Ident>, + pub vis: Option<&'a Ident>, + pub attrs: Option<&'a Ident>, + pub data: Option<&'a Ident>, + pub base: TraitImpl<'a>, + pub attr_names: &'a PathList, + pub forward_attrs: Option<&'a ForwardAttrs>, + 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.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,)); + 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 forwarded_attrs(&self) -> Option<&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/third_party/rust/darling_core/src/codegen/from_field.rs b/third_party/rust/darling_core/src/codegen/from_field.rs new file mode 100644 index 0000000000..74f340607c --- /dev/null +++ b/third_party/rust/darling_core/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}, + options::ForwardAttrs, + util::PathList, +}; + +/// `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 attrs: Option<&'a Ident>, + pub base: TraitImpl<'a>, + pub attr_names: &'a PathList, + pub forward_attrs: Option<&'a ForwardAttrs>, + 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.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,)); + + // 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 forwarded_attrs(&self) -> Option<&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/third_party/rust/darling_core/src/codegen/from_meta_impl.rs b/third_party/rust/darling_core/src/codegen/from_meta_impl.rs new file mode 100644 index 0000000000..5a65c5d7f6 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/from_meta_impl.rs @@ -0,0 +1,135 @@ +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 struct_arms = variants.iter().map(Variant::as_data_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) + } + }; + + 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() { + #(#struct_arms)* + __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)) + } + } + ) + } + }; + + 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/third_party/rust/darling_core/src/codegen/from_type_param.rs b/third_party/rust/darling_core/src/codegen/from_type_param.rs new file mode 100644 index 0000000000..1cce0cad4e --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/from_type_param.rs @@ -0,0 +1,113 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::Ident; + +use crate::codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}; +use crate::options::ForwardAttrs; +use crate::util::PathList; + +pub struct FromTypeParamImpl<'a> { + pub base: TraitImpl<'a>, + pub ident: Option<&'a Ident>, + pub attrs: Option<&'a Ident>, + pub bounds: Option<&'a Ident>, + pub default: Option<&'a Ident>, + pub attr_names: &'a PathList, + pub forward_attrs: Option<&'a ForwardAttrs>, + 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.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,)); + 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 forwarded_attrs(&self) -> Option<&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/third_party/rust/darling_core/src/codegen/from_variant_impl.rs b/third_party/rust/darling_core/src/codegen/from_variant_impl.rs new file mode 100644 index 0000000000..1598e6f2b0 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/from_variant_impl.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::Ident; + +use crate::codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}; +use crate::options::{DataShape, ForwardAttrs}; +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 forwarded attributes of the input + /// variant should be placed. + /// + /// This is one of `darling`'s "magic fields". + pub attrs: 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: Option<&'a ForwardAttrs>, + 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.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,)); + 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 forwarded_attrs(&self) -> Option<&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/third_party/rust/darling_core/src/codegen/mod.rs b/third_party/rust/darling_core/src/codegen/mod.rs new file mode 100644 index 0000000000..d2a1546e05 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/mod.rs @@ -0,0 +1,30 @@ +mod attr_extractor; +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::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/third_party/rust/darling_core/src/codegen/outer_from_impl.rs b/third_party/rust/darling_core/src/codegen/outer_from_impl.rs new file mode 100644 index 0000000000..c2939a6f13 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/outer_from_impl.rs @@ -0,0 +1,60 @@ +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!( + 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/third_party/rust/darling_core/src/codegen/postfix_transform.rs b/third_party/rust/darling_core/src/codegen/postfix_transform.rs new file mode 100644 index 0000000000..e631702cae --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/codegen/trait_impl.rs b/third_party/rust/darling_core/src/codegen/trait_impl.rs new file mode 100644 index 0000000000..0bc7f7eb20 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/trait_impl.rs @@ -0,0 +1,136 @@ +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(); + quote!(#(#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/third_party/rust/darling_core/src/codegen/variant.rs b/third_party/rust/darling_core/src/codegen/variant.rs new file mode 100644 index 0000000000..ccbabad6a9 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/variant.rs @@ -0,0 +1,159 @@ +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, + + 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) + } +} + +/// 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/third_party/rust/darling_core/src/codegen/variant_data.rs b/third_party/rust/darling_core/src/codegen/variant_data.rs new file mode 100644 index 0000000000..7c0063ea01 --- /dev/null +++ b/third_party/rust/darling_core/src/codegen/variant_data.rs @@ -0,0 +1,99 @@ +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 we're allowing unknown fields, then handling one is a no-op. + // Otherwise, we're going to push a new spanned error pointing at the field. + let handle_unknown = if self.allow_unknown_fields { + quote!() + } else { + // 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 arms.is_empty() { + quote!(unknown_field(__other)) + } else { + let names = self.fields.as_ref().map(Field::as_name); + let names = names.iter(); + 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/third_party/rust/darling_core/src/derive.rs b/third_party/rust/darling_core/src/derive.rs new file mode 100644 index 0000000000..43674aae6b --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/error/child.rs b/third_party/rust/darling_core/src/error/child.rs new file mode 100644 index 0000000000..c0e46f06c7 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/error/kind.rs b/third_party/rust/darling_core/src/error/kind.rs new file mode 100644 index 0000000000..2b4eda6765 --- /dev/null +++ b/third_party/rust/darling_core/src/error/kind.rs @@ -0,0 +1,211 @@ +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, Eq))] +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 literal 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 literal 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, Eq))] +pub(in crate::error) struct ErrorUnknownField { + name: String, + did_you_mean: Option<String>, +} + +impl ErrorUnknownField { + pub fn new<I: Into<String>>(name: I, did_you_mean: Option<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)) + } + + #[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<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(|(_, candidate)| candidate.into()) +} + +#[cfg(not(feature = "suggestions"))] +fn did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<String> +where + T: AsRef<str> + 'a, + I: IntoIterator<Item = &'a T>, +{ + None +} diff --git a/third_party/rust/darling_core/src/error/mod.rs b/third_party/rust/darling_core/src/error/mod.rs new file mode 100644 index 0000000000..6f2c045417 --- /dev/null +++ b/third_party/rust/darling_core/src/error/mod.rs @@ -0,0 +1,979 @@ +//! 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 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() + } + + /// 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 std::mem::replace(&mut self.0, None) { + 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/third_party/rust/darling_core/src/from_attributes.rs b/third_party/rust/darling_core/src/from_attributes.rs new file mode 100644 index 0000000000..c73f0f2cf4 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_derive_input.rs b/third_party/rust/darling_core/src/from_derive_input.rs new file mode 100644 index 0000000000..b5bb86f37e --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_field.rs b/third_party/rust/darling_core/src/from_field.rs new file mode 100644 index 0000000000..7ce4e55ca2 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_generic_param.rs b/third_party/rust/darling_core/src/from_generic_param.rs new file mode 100644 index 0000000000..757ad768a8 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_generics.rs b/third_party/rust/darling_core/src/from_generics.rs new file mode 100644 index 0000000000..8f687106bb --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_meta.rs b/third_party/rust/darling_core/src/from_meta.rs new file mode 100644 index 0000000000..4c1ce2459c --- /dev/null +++ b/third_party/rust/darling_core/src/from_meta.rs @@ -0,0 +1,931 @@ +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) => 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()) + } +} + +/// 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!(usize); +from_meta_num!(i8); +from_meta_num!(i16); +from_meta_num!(i32); +from_meta_num!(i64); +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. +impl FromMeta for syn::Expr { + fn from_expr(expr: &Expr) -> Result<Self> { + if let syn::Expr::Lit(expr_lit) = expr { + if let syn::Lit::Str(_) = &expr_lit.lit { + return Self::from_value(&expr_lit.lit); + } + } + + 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)) + } + } +} + +/// 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> { + if let syn::Expr::$variant(body) = expr { + Ok(body.clone()) + } else if let syn::Expr::Lit(expr_lit) = expr { + Self::from_value(&expr_lit.lit) + } else { + 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`. +/// +/// 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::Ident); +from_syn_parse!(syn::Path); +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> { + if let syn::Expr::Array(expr_array) = expr { + let v = expr_array + .elems + .iter() + .map(|expr| match expr { + Expr::Lit(lit) => $ty::from_value(&lit.lit), + _ => Err(Error::custom("Expected array of unsigned integers") + .with_span(expr)), + }) + .collect::<Result<Vec<$ty>>>(); + v + } else if let syn::Expr::Lit(expr_lit) = expr { + Self::from_value(&expr_lit.lit) + } else { + 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)) + } + } + } + }; +} + +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] + #[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] + #[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_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_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] + ); + } +} diff --git a/third_party/rust/darling_core/src/from_type_param.rs b/third_party/rust/darling_core/src/from_type_param.rs new file mode 100644 index 0000000000..bea9796c26 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/from_variant.rs b/third_party/rust/darling_core/src/from_variant.rs new file mode 100644 index 0000000000..50bf6359b6 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/lib.rs b/third_party/rust/darling_core/src/lib.rs new file mode 100644 index 0000000000..bf2050c646 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/macros_private.rs b/third_party/rust/darling_core/src/macros_private.rs new file mode 100644 index 0000000000..61edf360a1 --- /dev/null +++ b/third_party/rust/darling_core/src/macros_private.rs @@ -0,0 +1,6 @@ +macro_rules! path { + ($($path:tt)+) => { + ::syn::parse_quote!($($path)+) + //stringify!($($path)+).parse().unwrap() + }; +} diff --git a/third_party/rust/darling_core/src/macros_public.rs b/third_party/rust/darling_core/src/macros_public.rs new file mode 100644 index 0000000000..c264fcc2a8 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/options/core.rs b/third_party/rust/darling_core/src/options/core.rs new file mode 100644 index 0000000000..cc05f2184b --- /dev/null +++ b/third_party/rust/darling_core/src/options/core.rs @@ -0,0 +1,173 @@ +use ident_case::RenameRule; + +use crate::ast::{Data, Fields, Style}; +use crate::codegen; +use crate::codegen::PostfixTransform; +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"), + } + } +} + +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/third_party/rust/darling_core/src/options/forward_attrs.rs b/third_party/rust/darling_core/src/options/forward_attrs.rs new file mode 100644 index 0000000000..ac9f4e1a84 --- /dev/null +++ b/third_party/rust/darling_core/src/options/forward_attrs.rs @@ -0,0 +1,30 @@ +use crate::ast::NestedMeta; +use crate::util::PathList; +use crate::{FromMeta, Result}; + +/// A rule about which attributes to forward to the generated struct. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ForwardAttrs { + All, + Only(PathList), +} + +impl ForwardAttrs { + /// Returns `true` if this will not forward any attributes. + pub fn is_empty(&self) -> bool { + match *self { + ForwardAttrs::All => false, + ForwardAttrs::Only(ref list) => list.is_empty(), + } + } +} + +impl FromMeta for ForwardAttrs { + fn from_word() -> Result<Self> { + Ok(ForwardAttrs::All) + } + + fn from_list(nested: &[NestedMeta]) -> Result<Self> { + Ok(ForwardAttrs::Only(PathList::from_list(nested)?)) + } +} diff --git a/third_party/rust/darling_core/src/options/from_attributes.rs b/third_party/rust/darling_core/src/options/from_attributes.rs new file mode 100644 index 0000000000..0a133d1b24 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_attributes.rs @@ -0,0 +1,69 @@ +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) + } +} + +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, + } + } +} + +impl ToTokens for FromAttributesOptions { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + FromAttributesImpl::from(self).to_tokens(tokens) + } +} diff --git a/third_party/rust/darling_core/src/options/from_derive.rs b/third_party/rust/darling_core/src/options/from_derive.rs new file mode 100644 index 0000000000..71a3c3cb93 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_derive.rs @@ -0,0 +1,94 @@ +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), + } + } +} + +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(), + attrs: v.base.attrs.as_ref(), + forward_attrs: v.base.forward_attrs.as_ref(), + 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/third_party/rust/darling_core/src/options/from_field.rs b/third_party/rust/darling_core/src/options/from_field.rs new file mode 100644 index 0000000000..75e5fed2b1 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_field.rs @@ -0,0 +1,73 @@ +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), + } + } +} + +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(), + attrs: v.base.attrs.as_ref(), + base: (&v.base.container).into(), + attr_names: &v.base.attr_names, + forward_attrs: v.base.forward_attrs.as_ref(), + 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/third_party/rust/darling_core/src/options/from_meta.rs b/third_party/rust/darling_core/src/options/from_meta.rs new file mode 100644 index 0000000000..18c2bfa676 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_meta.rs @@ -0,0 +1,50 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; + +use crate::codegen::FromMetaImpl; +use crate::options::{Core, ParseAttribute, ParseData}; +use crate::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) + } +} + +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/third_party/rust/darling_core/src/options/from_type_param.rs b/third_party/rust/darling_core/src/options/from_type_param.rs new file mode 100644 index 0000000000..80178ff7e2 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_type_param.rs @@ -0,0 +1,73 @@ +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), + } + } +} + +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(), + attrs: v.base.attrs.as_ref(), + bounds: v.bounds.as_ref(), + default: v.default.as_ref(), + attr_names: &v.base.attr_names, + forward_attrs: v.base.forward_attrs.as_ref(), + 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/third_party/rust/darling_core/src/options/from_variant.rs b/third_party/rust/darling_core/src/options/from_variant.rs new file mode 100644 index 0000000000..ad761ade12 --- /dev/null +++ b/third_party/rust/darling_core/src/options/from_variant.rs @@ -0,0 +1,79 @@ +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(), + attrs: v.base.attrs.as_ref(), + attr_names: &v.base.attr_names, + forward_attrs: v.base.forward_attrs.as_ref(), + 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), + } + } +} + +impl ToTokens for FromVariantOptions { + fn to_tokens(&self, tokens: &mut TokenStream) { + FromVariantImpl::from(self).to_tokens(tokens) + } +} diff --git a/third_party/rust/darling_core/src/options/input_field.rs b/third_party/rust/darling_core/src/options/input_field.rs new file mode 100644 index 0000000000..27fe805f98 --- /dev/null +++ b/third_party/rust/darling_core/src/options/input_field.rs @@ -0,0 +1,180 @@ +use std::borrow::Cow; + +use syn::{parse_quote_spanned, spanned::Spanned}; + +use crate::codegen; +use crate::options::{Core, DefaultExpression, ParseAttribute}; +use crate::util::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>, +} + +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(), + } + } + + /// 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, + } + } + + 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)?; + } 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)?); + } 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("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)?; + } else { + return Err(Error::unknown_field_path(path).with_span(mi)); + } + + Ok(()) + } +} diff --git a/third_party/rust/darling_core/src/options/input_variant.rs b/third_party/rust/darling_core/src/options/input_variant.rs new file mode 100644 index 0000000000..5fab28ffac --- /dev/null +++ b/third_party/rust/darling_core/src/options/input_variant.rs @@ -0,0 +1,104 @@ +use std::borrow::Cow; + +use crate::ast::Fields; +use crate::codegen; +use crate::options::{Core, InputField, ParseAttribute}; +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 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(), + 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(), + 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 { + return Err(Error::unknown_field_path(path).with_span(mi)); + } + + Ok(()) + } +} diff --git a/third_party/rust/darling_core/src/options/mod.rs b/third_party/rust/darling_core/src/options/mod.rs new file mode 100644 index 0000000000..066aa1c1b7 --- /dev/null +++ b/third_party/rust/darling_core/src/options/mod.rs @@ -0,0 +1,145 @@ +use proc_macro2::Span; +use syn::{parse_quote, spanned::Spanned}; + +use crate::ast::NestedMeta; +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::ForwardAttrs; +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_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!(), + }; + + 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)) + } +} diff --git a/third_party/rust/darling_core/src/options/outer_from.rs b/third_party/rust/darling_core/src/options/outer_from.rs new file mode 100644 index 0000000000..492b0cd695 --- /dev/null +++ b/third_party/rust/darling_core/src/options/outer_from.rs @@ -0,0 +1,81 @@ +use syn::spanned::Spanned; +use syn::{Field, Ident, Meta}; + +use crate::options::{Core, DefaultExpression, ForwardAttrs, ParseAttribute, ParseData}; +use crate::util::PathList; +use crate::{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<Ident>, + + 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<ForwardAttrs>, + + /// 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(), + }) + } +} + +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 = field.ident.clone(); + Ok(()) + } + _ => self.container.parse_field(field), + } + } +} diff --git a/third_party/rust/darling_core/src/options/shape.rs b/third_party/rust/darling_core/src/options/shape.rs new file mode 100644 index 0000000000..3e35a236b5 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/usage/generics_ext.rs b/third_party/rust/darling_core/src/usage/generics_ext.rs new file mode 100644 index 0000000000..74e514215c --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/usage/ident_set.rs b/third_party/rust/darling_core/src/usage/ident_set.rs new file mode 100644 index 0000000000..e38d332dd0 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/usage/lifetimes.rs b/third_party/rust/darling_core/src/usage/lifetimes.rs new file mode 100644 index 0000000000..b7124aa53a --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/usage/mod.rs b/third_party/rust/darling_core/src/usage/mod.rs new file mode 100644 index 0000000000..fe8f12fb98 --- /dev/null +++ b/third_party/rust/darling_core/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(<.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/third_party/rust/darling_core/src/usage/options.rs b/third_party/rust/darling_core/src/usage/options.rs new file mode 100644 index 0000000000..f154881be9 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/usage/type_params.rs b/third_party/rust/darling_core/src/usage/type_params.rs new file mode 100644 index 0000000000..d4e7e79900 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/flag.rs b/third_party/rust/darling_core/src/util/flag.rs new file mode 100644 index 0000000000..66f2adc87b --- /dev/null +++ b/third_party/rust/darling_core/src/util/flag.rs @@ -0,0 +1,94 @@ +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)) +/// } 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() + } +} + +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/third_party/rust/darling_core/src/util/ident_string.rs b/third_party/rust/darling_core/src/util/ident_string.rs new file mode 100644 index 0000000000..937d1a6c49 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/ignored.rs b/third_party/rust/darling_core/src/util/ignored.rs new file mode 100644 index 0000000000..b74acfdda8 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/mod.rs b/third_party/rust/darling_core/src/util/mod.rs new file mode 100644 index 0000000000..ddc240b7be --- /dev/null +++ b/third_party/rust/darling_core/src/util/mod.rs @@ -0,0 +1,23 @@ +//! Utility types for attribute parsing. + +mod flag; +mod ident_string; +mod ignored; +mod over_ride; +mod parse_attribute; +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/third_party/rust/darling_core/src/util/over_ride.rs b/third_party/rust/darling_core/src/util/over_ride.rs new file mode 100644 index 0000000000..3de6ed53bf --- /dev/null +++ b/third_party/rust/darling_core/src/util/over_ride.rs @@ -0,0 +1,148 @@ +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 { + self.unwrap_or_else(Default::default) + } +} + +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/third_party/rust/darling_core/src/util/parse_attribute.rs b/third_party/rust/darling_core/src/util/parse_attribute.rs new file mode 100644 index 0000000000..747c30dee2 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/path_list.rs b/third_party/rust/darling_core/src/util/path_list.rs new file mode 100644 index 0000000000..bea25fd5f5 --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/path_to_string.rs b/third_party/rust/darling_core/src/util/path_to_string.rs new file mode 100644 index 0000000000..0e0ad99c7a --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/shape.rs b/third_party/rust/darling_core/src/util/shape.rs new file mode 100644 index 0000000000..5a90891bfc --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/spanned_value.rs b/third_party/rust/darling_core/src/util/spanned_value.rs new file mode 100644 index 0000000000..68dcc3801d --- /dev/null +++ b/third_party/rust/darling_core/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/third_party/rust/darling_core/src/util/with_original.rs b/third_party/rust/darling_core/src/util/with_original.rs new file mode 100644 index 0000000000..5aa82f850b --- /dev/null +++ b/third_party/rust/darling_core/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); |