Duplicate name checking now takes case insensitivity into account
This commit is contained in:
1 parent
ed83ffc5f3
commit
1fab354030
3 files changed
+70
-27
No files matched your search
@@ -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>,
|
||||||
@@ -23,11 +26,11 @@ impl MacroError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
Reference in new issue
Block a user