diff options
Diffstat (limited to 'third_party/rust/enumset_derive')
-rw-r--r-- | third_party/rust/enumset_derive/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/enumset_derive/Cargo.toml | 44 | ||||
-rw-r--r-- | third_party/rust/enumset_derive/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/enumset_derive/LICENSE-MIT | 26 | ||||
-rw-r--r-- | third_party/rust/enumset_derive/README.md | 1 | ||||
-rw-r--r-- | third_party/rust/enumset_derive/src/lib.rs | 591 |
6 files changed, 864 insertions, 0 deletions
diff --git a/third_party/rust/enumset_derive/.cargo-checksum.json b/third_party/rust/enumset_derive/.cargo-checksum.json new file mode 100644 index 0000000000..0361c00770 --- /dev/null +++ b/third_party/rust/enumset_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"2de22e952501bb15d5b13fef8ead3f53d6a40af1c1a284d332bb4a090ba59a3c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"79b4502d93c23afe2765054a80d03716d4934eb260cdfbe8c401898df3aa5a8f","README.md":"d45f82ad73b39aad91f85d0688bc6a94402293c40c295d0cab7b2a9b7225677b","src/lib.rs":"149dbabe0beb802bb41a560cd03f95c4992600ac07fdf57a3b1610a557bc10f4"},"package":"03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"}
\ No newline at end of file diff --git a/third_party/rust/enumset_derive/Cargo.toml b/third_party/rust/enumset_derive/Cargo.toml new file mode 100644 index 0000000000..d95f7ea654 --- /dev/null +++ b/third_party/rust/enumset_derive/Cargo.toml @@ -0,0 +1,44 @@ +# 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 = "enumset_derive" +version = "0.6.1" +authors = ["Alissa Rao <lymia@lymiahugs.com>"] +description = "An internal helper crate for enumset. Not public API." +documentation = "https://lymia.moe/doc/enumset/enumset/" +readme = "README.md" +license = "MIT/Apache-2.0" +repository = "https://github.com/Lymia/enumset" + +[lib] +proc-macro = true + +[dependencies.darling] +version = "0.14" +default-features = false + +[dependencies.proc-macro-crate] +version = "1" +optional = true + +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "1" + +[features] +serde = [] diff --git a/third_party/rust/enumset_derive/LICENSE-APACHE b/third_party/rust/enumset_derive/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/enumset_derive/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/enumset_derive/LICENSE-MIT b/third_party/rust/enumset_derive/LICENSE-MIT new file mode 100644 index 0000000000..58450b341a --- /dev/null +++ b/third_party/rust/enumset_derive/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2017-2020 Alissa Rao <lymiahugs@gmail.com> + +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/enumset_derive/README.md b/third_party/rust/enumset_derive/README.md new file mode 100644 index 0000000000..ed526ab882 --- /dev/null +++ b/third_party/rust/enumset_derive/README.md @@ -0,0 +1 @@ +An internal helper crate for [enumset](https://github.com/Lymia/enumset). Not public API. diff --git a/third_party/rust/enumset_derive/src/lib.rs b/third_party/rust/enumset_derive/src/lib.rs new file mode 100644 index 0000000000..88ac367e0a --- /dev/null +++ b/third_party/rust/enumset_derive/src/lib.rs @@ -0,0 +1,591 @@ +#![recursion_limit = "256"] + +extern crate proc_macro; + +use darling::*; +use proc_macro::TokenStream; +use proc_macro2::{Literal, Span, TokenStream as SynTokenStream}; +use quote::*; +use std::{collections::HashSet, fmt::Display}; +use syn::spanned::Spanned; +use syn::{Error, Result, *}; + +/// Helper function for emitting compile errors. +fn error<T>(span: Span, message: impl Display) -> Result<T> { + Err(Error::new(span, message)) +} + +/// Decodes the custom attributes for our custom derive. +#[derive(FromDeriveInput, Default)] +#[darling(attributes(enumset), default)] +struct EnumsetAttrs { + no_ops: bool, + no_super_impls: bool, + #[darling(default)] + repr: Option<String>, + serialize_as_list: bool, + serialize_deny_unknown: bool, + #[darling(default)] + serialize_repr: Option<String>, + #[darling(default)] + crate_name: Option<String>, +} + +/// An variant in the enum set type. +struct EnumSetValue { + /// The name of the variant. + name: Ident, + /// The discriminant of the variant. + variant_repr: u32, +} + +/// Stores information about the enum set type. +#[allow(dead_code)] +struct EnumSetInfo { + /// The name of the enum. + name: Ident, + /// The crate name to use. + crate_name: Option<Ident>, + /// The numeric type to represent the `EnumSet` as in memory. + explicit_mem_repr: Option<Ident>, + /// The numeric type to serialize the enum as. + explicit_serde_repr: Option<Ident>, + /// Whether the underlying repr of the enum supports negative values. + has_signed_repr: bool, + /// Whether the underlying repr of the enum supports values higher than 2^32. + has_large_repr: bool, + /// A list of variants in the enum. + variants: Vec<EnumSetValue>, + + /// The highest encountered variant discriminant. + max_discrim: u32, + /// The current variant discriminant. Used to track, e.g. `A=10,B,C`. + cur_discrim: u32, + /// A list of variant names that are already in use. + used_variant_names: HashSet<String>, + /// A list of variant discriminants that are already in use. + used_discriminants: HashSet<u32>, + + /// Avoid generating operator overloads on the enum type. + no_ops: bool, + /// Avoid generating implementations for `Clone`, `Copy`, `Eq`, and `PartialEq`. + no_super_impls: bool, + /// Serialize the enum as a list. + serialize_as_list: bool, + /// Disallow unknown bits while deserializing the enum. + serialize_deny_unknown: bool, +} +impl EnumSetInfo { + fn new(input: &DeriveInput, attrs: EnumsetAttrs) -> EnumSetInfo { + EnumSetInfo { + name: input.ident.clone(), + crate_name: attrs.crate_name.map(|x| Ident::new(&x, Span::call_site())), + explicit_mem_repr: attrs.repr.map(|x| Ident::new(&x, Span::call_site())), + explicit_serde_repr: attrs + .serialize_repr + .map(|x| Ident::new(&x, Span::call_site())), + has_signed_repr: false, + has_large_repr: false, + variants: Vec::new(), + max_discrim: 0, + cur_discrim: 0, + used_variant_names: HashSet::new(), + used_discriminants: HashSet::new(), + no_ops: attrs.no_ops, + no_super_impls: attrs.no_super_impls, + serialize_as_list: attrs.serialize_as_list, + serialize_deny_unknown: attrs.serialize_deny_unknown, + } + } + + /// Sets an explicit repr for the enumset. + fn push_explicit_repr(&mut self, attr_span: Span, repr: &str) -> Result<()> { + // Check whether the repr is supported, and if so, set some flags for better error + // messages later on. + match repr { + "Rust" | "C" | "u8" | "u16" | "u32" => Ok(()), + "usize" | "u64" | "u128" => { + self.has_large_repr = true; + Ok(()) + } + "i8" | "i16" | "i32" => { + self.has_signed_repr = true; + Ok(()) + } + "isize" | "i64" | "i128" => { + self.has_signed_repr = true; + self.has_large_repr = true; + Ok(()) + } + _ => error(attr_span, "Unsupported repr."), + } + } + /// Adds a variant to the enumset. + fn push_variant(&mut self, variant: &Variant) -> Result<()> { + if self.used_variant_names.contains(&variant.ident.to_string()) { + error(variant.span(), "Duplicated variant name.") + } else if let Fields::Unit = variant.fields { + // Parse the discriminant. + if let Some((_, expr)) = &variant.discriminant { + let discriminant_fail_message = format!( + "Enum set discriminants must be `u32`s.{}", + if self.has_signed_repr || self.has_large_repr { + format!( + " ({} discrimiants are still unsupported with reprs that allow them.)", + if self.has_large_repr { + "larger" + } else if self.has_signed_repr { + "negative" + } else { + "larger or negative" + } + ) + } else { + String::new() + }, + ); + if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = expr { + match i.base10_parse() { + Ok(val) => self.cur_discrim = val, + Err(_) => error(expr.span(), &discriminant_fail_message)?, + } + } else { + error(variant.span(), &discriminant_fail_message)?; + } + } + + // Validate the discriminant. + let discriminant = self.cur_discrim; + if discriminant >= 128 { + let message = if self.variants.len() <= 127 { + "`#[derive(EnumSetType)]` currently only supports discriminants up to 127." + } else { + "`#[derive(EnumSetType)]` currently only supports enums up to 128 variants." + }; + error(variant.span(), message)?; + } + if self.used_discriminants.contains(&discriminant) { + error(variant.span(), "Duplicated enum discriminant.")?; + } + + // Add the variant to the info. + self.cur_discrim += 1; + if discriminant > self.max_discrim { + self.max_discrim = discriminant; + } + self.variants + .push(EnumSetValue { name: variant.ident.clone(), variant_repr: discriminant }); + self.used_variant_names.insert(variant.ident.to_string()); + self.used_discriminants.insert(discriminant); + + Ok(()) + } else { + error(variant.span(), "`#[derive(EnumSetType)]` can only be used on fieldless enums.") + } + } + /// Validate the enumset type. + fn validate(&self) -> Result<()> { + fn do_check(ty: &str, max_discrim: u32, what: &str) -> Result<()> { + let is_overflowed = match ty { + "u8" => max_discrim >= 8, + "u16" => max_discrim >= 16, + "u32" => max_discrim >= 32, + "u64" => max_discrim >= 64, + "u128" => max_discrim >= 128, + _ => error( + Span::call_site(), + format!( + "Only `u8`, `u16`, `u32`, `u64` and `u128` are supported for {}.", + what + ), + )?, + }; + if is_overflowed { + error(Span::call_site(), format!("{} cannot be smaller than bitset.", what))?; + } + Ok(()) + } + + // Check if all bits of the bitset can fit in the serialization representation. + if let Some(explicit_serde_repr) = &self.explicit_serde_repr { + do_check(&explicit_serde_repr.to_string(), self.max_discrim, "serialize_repr")?; + } + + // Check if all bits of the bitset can fit in the memory representation, if one was given. + if let Some(explicit_mem_repr) = &self.explicit_mem_repr { + do_check(&explicit_mem_repr.to_string(), self.max_discrim, "repr")?; + } + Ok(()) + } + + /// Computes the underlying type used to store the enumset. + fn enumset_repr(&self) -> SynTokenStream { + if let Some(explicit_mem_repr) = &self.explicit_mem_repr { + explicit_mem_repr.to_token_stream() + } else if self.max_discrim <= 7 { + quote! { u8 } + } else if self.max_discrim <= 15 { + quote! { u16 } + } else if self.max_discrim <= 31 { + quote! { u32 } + } else if self.max_discrim <= 63 { + quote! { u64 } + } else if self.max_discrim <= 127 { + quote! { u128 } + } else { + panic!("max_variant > 127?") + } + } + /// Computes the underlying type used to serialize the enumset. + #[cfg(feature = "serde")] + fn serde_repr(&self) -> SynTokenStream { + if let Some(serde_repr) = &self.explicit_serde_repr { + quote! { #serde_repr } + } else { + self.enumset_repr() + } + } + + /// Returns a bitmask of all variants in the set. + fn all_variants(&self) -> u128 { + let mut accum = 0u128; + for variant in &self.variants { + assert!(variant.variant_repr <= 127); + accum |= 1u128 << variant.variant_repr as u128; + } + accum + } +} + +/// Generates the actual `EnumSetType` impl. +fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { + let name = &info.name; + + let enumset = match &info.crate_name { + Some(crate_name) => quote!(::#crate_name), + None => { + #[cfg(feature = "proc-macro-crate")] + { + use proc_macro_crate::FoundCrate; + + let crate_name = proc_macro_crate::crate_name("enumset"); + match crate_name { + Ok(FoundCrate::Name(name)) => { + let ident = Ident::new(&name, Span::call_site()); + quote!(::#ident) + } + _ => quote!(::enumset), + } + } + + #[cfg(not(feature = "proc-macro-crate"))] + { + quote!(::enumset) + } + } + }; + let typed_enumset = quote!(#enumset::EnumSet<#name>); + let core = quote!(#enumset::__internal::core_export); + + let repr = info.enumset_repr(); + let all_variants = Literal::u128_unsuffixed(info.all_variants()); + + let ops = if info.no_ops { + quote! {} + } else { + quote! { + impl <O : Into<#typed_enumset>> #core::ops::Sub<O> for #name { + type Output = #typed_enumset; + fn sub(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) - other.into() + } + } + impl <O : Into<#typed_enumset>> #core::ops::BitAnd<O> for #name { + type Output = #typed_enumset; + fn bitand(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) & other.into() + } + } + impl <O : Into<#typed_enumset>> #core::ops::BitOr<O> for #name { + type Output = #typed_enumset; + fn bitor(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) | other.into() + } + } + impl <O : Into<#typed_enumset>> #core::ops::BitXor<O> for #name { + type Output = #typed_enumset; + fn bitxor(self, other: O) -> Self::Output { + #enumset::EnumSet::only(self) ^ other.into() + } + } + impl #core::ops::Not for #name { + type Output = #typed_enumset; + fn not(self) -> Self::Output { + !#enumset::EnumSet::only(self) + } + } + impl #core::cmp::PartialEq<#typed_enumset> for #name { + fn eq(&self, other: &#typed_enumset) -> bool { + #enumset::EnumSet::only(*self) == *other + } + } + } + }; + + #[cfg(feature = "serde")] + let serde = quote!(#enumset::__internal::serde); + + #[cfg(feature = "serde")] + let serde_ops = if info.serialize_as_list { + let expecting_str = format!("a list of {}", name); + quote! { + fn serialize<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + use #serde::ser::SerializeSeq; + let mut seq = ser.serialize_seq(#core::prelude::v1::Some(set.len()))?; + for bit in set { + seq.serialize_element(&bit)?; + } + seq.end() + } + fn deserialize<'de, D: #serde::Deserializer<'de>>( + de: D, + ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { + struct Visitor; + impl <'de> #serde::de::Visitor<'de> for Visitor { + type Value = #enumset::EnumSet<#name>; + fn expecting( + &self, formatter: &mut #core::fmt::Formatter, + ) -> #core::fmt::Result { + write!(formatter, #expecting_str) + } + fn visit_seq<A>( + mut self, mut seq: A, + ) -> #core::result::Result<Self::Value, A::Error> where + A: #serde::de::SeqAccess<'de> + { + let mut accum = #enumset::EnumSet::<#name>::new(); + while let #core::prelude::v1::Some(val) = seq.next_element::<#name>()? { + accum |= val; + } + #core::prelude::v1::Ok(accum) + } + } + de.deserialize_seq(Visitor) + } + } + } else { + let serialize_repr = info.serde_repr(); + let check_unknown = if info.serialize_deny_unknown { + quote! { + if value & !#all_variants != 0 { + use #serde::de::Error; + return #core::prelude::v1::Err( + D::Error::custom("enumset contains unknown bits") + ) + } + } + } else { + quote! {} + }; + quote! { + fn serialize<S: #serde::Serializer>( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result<S::Ok, S::Error> { + #serde::Serialize::serialize(&(set.__priv_repr as #serialize_repr), ser) + } + fn deserialize<'de, D: #serde::Deserializer<'de>>( + de: D, + ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { + let value = <#serialize_repr as #serde::Deserialize>::deserialize(de)?; + #check_unknown + #core::prelude::v1::Ok(#enumset::EnumSet { + __priv_repr: (value & #all_variants) as #repr, + }) + } + } + }; + + #[cfg(not(feature = "serde"))] + let serde_ops = quote! {}; + + let is_uninhabited = info.variants.is_empty(); + let is_zst = info.variants.len() == 1; + let into_impl = if is_uninhabited { + quote! { + fn enum_into_u32(self) -> u32 { + panic!(concat!(stringify!(#name), " is uninhabited.")) + } + unsafe fn enum_from_u32(val: u32) -> Self { + panic!(concat!(stringify!(#name), " is uninhabited.")) + } + } + } else if is_zst { + let variant = &info.variants[0].name; + quote! { + fn enum_into_u32(self) -> u32 { + self as u32 + } + unsafe fn enum_from_u32(val: u32) -> Self { + #name::#variant + } + } + } else { + let variant_name: Vec<_> = info.variants.iter().map(|x| &x.name).collect(); + let variant_value: Vec<_> = info.variants.iter().map(|x| x.variant_repr).collect(); + + let const_field: Vec<_> = ["IS_U8", "IS_U16", "IS_U32", "IS_U64", "IS_U128"] + .iter() + .map(|x| Ident::new(x, Span::call_site())) + .collect(); + let int_type: Vec<_> = ["u8", "u16", "u32", "u64", "u128"] + .iter() + .map(|x| Ident::new(x, Span::call_site())) + .collect(); + + quote! { + fn enum_into_u32(self) -> u32 { + self as u32 + } + unsafe fn enum_from_u32(val: u32) -> Self { + // We put these in const fields so the branches they guard aren't generated even + // on -O0 + #(const #const_field: bool = + #core::mem::size_of::<#name>() == #core::mem::size_of::<#int_type>();)* + match val { + // Every valid variant value has an explicit branch. If they get optimized out, + // great. If the representation has changed somehow, and they don't, oh well, + // there's still no UB. + #(#variant_value => #name::#variant_name,)* + // Helps hint to the LLVM that this is a transmute. Note that this branch is + // still unreachable. + #(x if #const_field => { + let x = x as #int_type; + *(&x as *const _ as *const #name) + })* + // Default case. Sometimes causes LLVM to generate a table instead of a simple + // transmute, but, oh well. + _ => #core::hint::unreachable_unchecked(), + } + } + } + }; + + let eq_impl = if is_uninhabited { + quote!(panic!(concat!(stringify!(#name), " is uninhabited."))) + } else { + quote!((*self as u32) == (*other as u32)) + }; + + // used in the enum_set! macro `const fn`s. + let self_as_repr_mask = if is_uninhabited { + quote! { 0 } // impossible anyway + } else { + quote! { 1 << self as #repr } + }; + + let super_impls = if info.no_super_impls { + quote! {} + } else { + quote! { + impl #core::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + #eq_impl + } + } + impl #core::cmp::Eq for #name { } + #[allow(clippy::expl_impl_clone_on_copy)] + impl #core::clone::Clone for #name { + fn clone(&self) -> Self { + *self + } + } + impl #core::marker::Copy for #name { } + } + }; + + let impl_with_repr = if info.explicit_mem_repr.is_some() { + quote! { + unsafe impl #enumset::EnumSetTypeWithRepr for #name { + type Repr = #repr; + } + } + } else { + quote! {} + }; + + quote! { + unsafe impl #enumset::__internal::EnumSetTypePrivate for #name { + type Repr = #repr; + const ALL_BITS: Self::Repr = #all_variants; + #into_impl + #serde_ops + } + + unsafe impl #enumset::EnumSetType for #name { } + + #impl_with_repr + #super_impls + + impl #name { + /// Creates a new enumset with only this variant. + #[deprecated(note = "This method is an internal implementation detail generated by \ + the `enumset` crate's procedural macro. It should not be used \ + directly. Use `EnumSet::only` instead.")] + #[doc(hidden)] + pub const fn __impl_enumset_internal__const_only(self) -> #enumset::EnumSet<#name> { + #enumset::EnumSet { __priv_repr: #self_as_repr_mask } + } + + /// Creates a new enumset with this variant added. + #[deprecated(note = "This method is an internal implementation detail generated by \ + the `enumset` crate's procedural macro. It should not be used \ + directly. Use the `|` operator instead.")] + #[doc(hidden)] + pub const fn __impl_enumset_internal__const_merge( + self, chain: #enumset::EnumSet<#name>, + ) -> #enumset::EnumSet<#name> { + #enumset::EnumSet { __priv_repr: chain.__priv_repr | #self_as_repr_mask } + } + } + + #ops + } +} + +#[proc_macro_derive(EnumSetType, attributes(enumset))] +pub fn derive_enum_set_type(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) { + Ok(attrs) => attrs, + Err(e) => return e.write_errors().into(), + }; + match derive_enum_set_type_0(input, attrs) { + Ok(v) => v, + Err(e) => e.to_compile_error().into(), + } +} +fn derive_enum_set_type_0(input: DeriveInput, attrs: EnumsetAttrs) -> Result<TokenStream> { + if !input.generics.params.is_empty() { + error( + input.generics.span(), + "`#[derive(EnumSetType)]` cannot be used on enums with type parameters.", + ) + } else if let Data::Enum(data) = &input.data { + let mut info = EnumSetInfo::new(&input, attrs); + for attr in &input.attrs { + if attr.path.is_ident(&Ident::new("repr", Span::call_site())) { + let meta: Ident = attr.parse_args()?; + info.push_explicit_repr(attr.span(), meta.to_string().as_str())?; + } + } + for variant in &data.variants { + info.push_variant(variant)?; + } + info.validate()?; + Ok(enum_set_type_impl(info).into()) + } else { + error(input.span(), "`#[derive(EnumSetType)]` may only be used on enums") + } +} |