Duplicate name checking now takes case insensitivity into account

rename
Pantonshire 5 years ago
parent ed83ffc5f3
commit 1fab354030

@ -1,11 +1,12 @@
use std::collections::HashSet; use std::collections::HashSet;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{DataEnum, Fields}; use syn::{DataEnum, Fields};
use syn::spanned::Spanned; use syn::spanned::Spanned;
use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER}; use crate::{CASE_INSENSITIVE, CRATE_ATTR, IGNORE, NAME, OTHER};
use crate::TokenStream2;
use crate::attribute::{Dict, Value}; use crate::attribute::{Dict, Value};
use crate::error::{MacroError, MacroResult}; use crate::error::{MacroError, MacroResult};
@ -47,10 +48,10 @@ impl<'a> Variant<'a> {
enum_ident: &Ident, enum_ident: &Ident,
named_fn: &F, named_fn: &F,
other_fn: &G other_fn: &G
) -> MacroResult<Option<(TokenStream, TokenStream)>> ) -> MacroResult<Option<(TokenStream2, TokenStream2)>>
where where
F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream>, F: Fn(&Variant, &Ident, &str) -> MacroResult<TokenStream2>,
G: Fn(&Variant, &Ident, TokenStream) -> MacroResult<TokenStream> G: Fn(&Variant, &Ident, TokenStream2) -> MacroResult<TokenStream2>
{ {
let variant_ident = &self.data.ident; let variant_ident = &self.data.ident;
@ -84,6 +85,8 @@ impl<'a> Variant<'a> {
pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> { pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
let mut variants = Vec::with_capacity(data.variants.len()); let mut variants = Vec::with_capacity(data.variants.len());
let mut taken_names = HashSet::new(); let mut taken_names = HashSet::new();
let mut taken_insensitive_names = HashSet::new();
let mut taken_sensitive_names = HashSet::new();
let mut other_variant = false; let mut other_variant = false;
for variant in data.variants.iter() { for variant in data.variants.iter() {
@ -167,6 +170,23 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
)); ));
} }
taken_names.insert(name.clone());
// Extra duplicate checking for case-insensitive names
let lowercase_name = name.to_lowercase();
if taken_insensitive_names.contains(&lowercase_name) || (case_insensitive && taken_sensitive_names.contains(&lowercase_name)) {
return Err(MacroError::new(
format!("duplicate name \"{}\"", name),
name_span,
));
}
if case_insensitive {
taken_insensitive_names.insert(lowercase_name);
} else {
taken_sensitive_names.insert(lowercase_name);
}
// Return an error if the variant has any fields // Return an error if the variant has any fields
if variant.fields.len() != 0 { if variant.fields.len() != 0 {
let variant_ident = variant.ident.to_string(); let variant_ident = variant.ident.to_string();
@ -189,8 +209,6 @@ pub(crate) fn parse_enum(data: &DataEnum) -> MacroResult<Enum> {
Fields::Unit => VariantConstructor::None, Fields::Unit => VariantConstructor::None,
}; };
taken_names.insert(name.clone());
Variant { Variant {
data: variant, data: variant,
v_type: VariantType::Named { name, constructor, case_insensitive }, v_type: VariantType::Named { name, constructor, case_insensitive },

@ -1,12 +1,15 @@
use proc_macro::TokenStream;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt;
use std::error; use std::error;
use std::fmt;
use std::result; use std::result;
use proc_macro2::Span; use proc_macro2::Span;
use quote::quote_spanned; use quote::quote_spanned;
use syn::Error; use syn::Error;
use crate::TokenStream2;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct MacroError { pub(crate) struct MacroError {
pub(crate) message: Cow<'static, str>, pub(crate) message: Cow<'static, str>,
@ -16,18 +19,18 @@ pub(crate) struct MacroError {
pub(crate) type MacroResult<T> = result::Result<T, MacroError>; pub(crate) type MacroResult<T> = result::Result<T, MacroError>;
impl MacroError { impl MacroError {
pub(crate) fn new<T>(message: T, span: Span) -> Self where T : Into<Cow<'static, str>> { pub(crate) fn new<T>(message: T, span: Span) -> Self where T: Into<Cow<'static, str>> {
MacroError { MacroError {
message: message.into(), message: message.into(),
span, span,
} }
} }
pub(crate) fn to_token_stream(&self) -> proc_macro::TokenStream { pub(crate) fn to_token_stream(&self) -> TokenStream {
self.to_token_stream2().into() self.to_token_stream2().into()
} }
pub(crate) fn to_token_stream2(&self) -> proc_macro2::TokenStream { pub(crate) fn to_token_stream2(&self) -> TokenStream2 {
let message = &self.message; let message = &self.message;
quote_spanned! { quote_spanned! {
self.span => ::std::compile_error!(#message); self.span => ::std::compile_error!(#message);
@ -41,13 +44,13 @@ impl From<syn::Error> for MacroError {
} }
} }
impl From<MacroError> for proc_macro::TokenStream { impl From<MacroError> for TokenStream {
fn from(err: MacroError) -> Self { fn from(err: MacroError) -> Self {
err.to_token_stream() err.to_token_stream()
} }
} }
impl From<MacroError> for proc_macro2::TokenStream { impl From<MacroError> for TokenStream2 {
fn from(err: MacroError) -> Self { fn from(err: MacroError) -> Self {
err.to_token_stream2() err.to_token_stream2()
} }
@ -63,7 +66,7 @@ impl error::Error for MacroError {}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ValueTypeError { pub(crate) struct ValueTypeError {
pub(crate) message: Cow<'static, str> pub(crate) message: Cow<'static, str>,
} }
pub(crate) type ValueTypeResult<T> = result::Result<T, ValueTypeError>; pub(crate) type ValueTypeResult<T> = result::Result<T, ValueTypeError>;

@ -9,7 +9,7 @@ use syn::{Data, DataEnum, DeriveInput};
use error::{MacroError, MacroResult}; use error::{MacroError, MacroResult};
use crate::enums::Variant; use crate::enums::{Variant, VariantType};
use proc_macro2::Ident; use proc_macro2::Ident;
mod enums; mod enums;
@ -55,15 +55,11 @@ fn derive_scribe<F, G, E>(
let enum_ident = &input.ident; let enum_ident = &input.ident;
let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len()); let mut match_arms = Vec::with_capacity(parsed_enum.variants.len());
let mut match_results = Vec::with_capacity(parsed_enum.variants.len());
for variant in parsed_enum.variants.iter() { for variant in parsed_enum.variants.iter() {
match variant.match_variant(enum_ident, &named_fn, &other_fn) { match variant.match_variant(enum_ident, &named_fn, &other_fn) {
Ok(Some((pattern, result))) => { Ok(Some((pattern, result))) => match_arms.push(quote! { #pattern => #result }),
match_patterns.push(pattern);
match_results.push(result);
}
Ok(None) => return ignore_err_fn(variant, enum_ident).into(), Ok(None) => return ignore_err_fn(variant, enum_ident).into(),
Err(err) => return err.into() Err(err) => return err.into()
} }
@ -73,7 +69,7 @@ fn derive_scribe<F, G, E>(
impl #trait_ident for #enum_ident { impl #trait_ident for #enum_ident {
fn scribe(&self) -> #trait_return_type { fn scribe(&self) -> #trait_return_type {
match self { match self {
#(#match_patterns => #match_results,)* #(#match_arms,)*
} }
} }
} }
@ -101,15 +97,11 @@ fn derive_try_scribe<F, G>(
let enum_ident = &input.ident; let enum_ident = &input.ident;
let mut ignore_variant = false; let mut ignore_variant = false;
let mut match_patterns = Vec::with_capacity(parsed_enum.variants.len()); let mut match_arms = Vec::with_capacity(parsed_enum.variants.len());
let mut match_results = Vec::with_capacity(parsed_enum.variants.len());
for variant in parsed_enum.variants.iter() { for variant in parsed_enum.variants.iter() {
match variant.match_variant(enum_ident, &named_fn, &other_fn) { match variant.match_variant(enum_ident, &named_fn, &other_fn) {
Ok(Some((pattern, result))) => { Ok(Some((pattern, result))) => match_arms.push(quote! { #pattern => #result }),
match_patterns.push(pattern);
match_results.push(result);
}
Ok(None) => ignore_variant = true, Ok(None) => ignore_variant = true,
Err(err) => return err.into() Err(err) => return err.into()
} }
@ -125,7 +117,7 @@ fn derive_try_scribe<F, G>(
impl #trait_ident for #enum_ident { impl #trait_ident for #enum_ident {
fn try_scribe(&self) -> #trait_return_type { fn try_scribe(&self) -> #trait_return_type {
match self { match self {
#(#match_patterns => #match_results,)* #(#match_arms,)*
#ignore_arm #ignore_arm
} }
} }
@ -290,6 +282,36 @@ pub fn derive_try_scribe_cow_str(input: TokenStream) -> TokenStream {
) )
} }
#[proc_macro_derive(Unscribe, attributes(enumscribe))]
pub fn derive_unscribe(input: TokenStream) -> TokenStream {
// let input: DeriveInput = syn::parse(input)
// .expect("failed to parse input");
//
// let enum_data = proc_try!(get_enum_data(&input));
// let parsed_enum = proc_try!(enums::parse_enum(enum_data));
//
// let enum_ident = &input.ident;
//
// let mut other_arm = None;
// let mut case_sensitive_arms = Vec::new();
// let mut case_insensitive_arms = Vec::new();
//
// for variant in parsed_enum.variants.iter() {
// match variant.v_type {
// VariantType::Ignore => {}
// VariantType::Named { .. } => {}
// VariantType::Other { .. } => {}
// }
// }
todo!()
}
#[proc_macro_derive(TryUnscribe, attributes(enumscribe))]
pub fn derive_try_unscribe(input: TokenStream) -> TokenStream {
todo!()
}
fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> { fn get_enum_data(input: &DeriveInput) -> MacroResult<&DataEnum> {
let enum_data = match &input.data { let enum_data = match &input.data {
Data::Enum(enum_data) => enum_data, Data::Enum(enum_data) => enum_data,

Loading…
Cancel
Save