use proc_macro2::TokenStream;
use quote::format_ident;
use std::{collections::hash_map::DefaultHasher, hash::Hasher, iter, mem};
#[cfg(feature = "type_analysis")]
use syn::Type;
use syn::{
parse::{Parse, ParseStream},
parse_quote, Attribute, Error, Expr, Ident, ItemEnum, Macro, Path, Result, Token,
};
use super::visitor::{Dummy, Visitor};
use crate::utils::{expr_call, path, replace_expr, unit, VisitedNode};
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum VisitMode {
Default,
Return( usize),
Try,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum VisitLastMode {
Default,
Never,
}
#[derive(Default)]
struct Diagnostic {
message: Option<Error>,
}
pub(super) const DEFAULT_MARKER: &str = "marker";
pub(super) struct Context {
builder: Builder,
pub(super) marker: String,
pub(super) markers: Vec<String>,
root: bool,
pub(super) other_attr: bool,
pub(super) visit_mode: VisitMode,
pub(super) visit_last_mode: VisitLastMode,
pub(super) span: TokenStream,
diagnostic: Diagnostic,
pub(super) args: Vec<Path>,
#[cfg(feature = "type_analysis")]
traits: Vec<Path>,
}
impl Context {
fn new(
span: TokenStream,
args: TokenStream,
root: bool,
mut markers: Vec<String>,
diagnostic: Diagnostic,
) -> Result<Self> {
let Args { args, marker } = syn::parse2(args)?;
let marker = if let Some(marker) = marker {
let marker_string = marker.to_string();
if markers.contains(&marker_string) {
return Err(error!(
marker,
"A custom marker name is specified that duplicated the name already used in the parent scope",
));
}
marker_string
} else {
DEFAULT_MARKER.to_string()
};
markers.push(marker.clone());
Ok(Self {
builder: Builder::new(&span),
marker,
markers,
root,
other_attr: false,
visit_mode: VisitMode::Default,
visit_last_mode: VisitLastMode::Default,
span,
diagnostic,
args,
#[cfg(feature = "type_analysis")]
traits: Vec::new(),
})
}
pub(super) fn root(span: TokenStream, args: TokenStream) -> Result<Self> {
Self::new(span, args, true, Vec::new(), Diagnostic::default())
}
pub(super) fn make_child(&mut self, span: TokenStream, args: TokenStream) -> Result<Self> {
Self::new(
span,
args,
false,
mem::replace(&mut self.markers, Vec::new()),
mem::replace(&mut self.diagnostic, Diagnostic::default()),
)
}
pub(super) fn join_child(&mut self, mut child: Self) {
debug_assert!(self.markers.is_empty());
child.markers.pop();
self.markers = child.markers;
if let Some(message) = child.diagnostic.message {
self.error(message);
}
}
pub(super) fn error(&mut self, message: Error) {
match &mut self.diagnostic.message {
Some(base) => base.combine(message),
None => self.diagnostic.message = Some(message),
}
}
pub(super) fn compile_error(&self) -> Option<TokenStream> {
self.diagnostic.message.as_ref().map(Error::to_compile_error)
}
pub(super) fn has_error(&self) -> bool {
self.diagnostic.message.is_some()
}
pub(super) fn visit_last(&self) -> bool {
self.visit_last_mode != VisitLastMode::Never && self.visit_mode != VisitMode::Try
}
pub(super) fn is_dummy(&self) -> bool {
#[cfg(not(feature = "type_analysis"))]
{
self.args.is_empty()
}
#[cfg(feature = "type_analysis")]
{
self.args.is_empty() && self.traits.is_empty()
}
}
#[cfg(feature = "type_analysis")]
pub(super) fn variant_is_empty(&self) -> bool {
self.builder.variants.is_empty()
}
pub(super) fn is_marker_expr(&self, expr: &Expr) -> bool {
match expr {
Expr::Macro(expr) => self.is_marker_macro(&expr.mac),
_ => false,
}
}
pub(super) fn is_marker_macro(&self, mac: &Macro) -> bool {
let exact = self.is_marker_macro_exact(mac);
if exact || self.root {
return exact;
}
self.markers.iter().any(|marker| mac.path.is_ident(marker))
}
pub(super) fn is_marker_macro_exact(&self, mac: &Macro) -> bool {
mac.path.is_ident(&self.marker)
}
pub(super) fn next_expr(&mut self, expr: Expr) -> Expr {
self.next_expr_with_attrs(Vec::new(), expr)
}
pub(super) fn next_expr_with_attrs(&mut self, attrs: Vec<Attribute>, expr: Expr) -> Expr {
self.builder.next_expr(attrs, expr)
}
pub(super) fn replace_boxed_expr(&mut self, expr: &mut Option<Box<Expr>>) {
replace_expr(&mut **expr.get_or_insert_with(|| Box::new(unit())), |expr| {
if self.is_marker_expr(&expr) {
expr
} else {
self.next_expr(expr)
}
});
}
pub(super) fn visitor(&mut self, node: &mut impl VisitedNode) {
node.visited(&mut Visitor::new(self));
}
pub(super) fn dummy(&mut self, node: &mut impl VisitedNode) {
#[cfg(not(feature = "type_analysis"))]
debug_assert!(self.is_dummy());
#[cfg(feature = "type_analysis")]
debug_assert!(self.args.is_empty());
node.visited(&mut Dummy::new(self));
}
pub(super) fn build(&mut self, f: impl FnOnce(ItemEnum)) -> Result<()> {
fn err(cx: &Context) -> Error {
let (msg1, msg2) = match cx.visit_last_mode {
VisitLastMode::Default => {
("branches or marker macros in total", "branch or marker macro")
}
VisitLastMode::Never => ("marker macros", "marker macro"),
};
error!(
cx.span,
"`#[auto_enum]` is required two or more {}, there is {} {} in this statement",
msg1,
if cx.builder.variants.is_empty() { "no" } else { "only one" },
msg2
)
}
if !self.has_error() {
match self.builder.variants.len() {
1 => return Err(err(self)),
0 if !self.other_attr => return Err(err(self)),
_ => {}
}
}
if !self.builder.variants.is_empty() {
#[cfg(not(feature = "type_analysis"))]
{
f(self.builder.build(&self.args, &[]));
}
#[cfg(feature = "type_analysis")]
{
f(self.builder.build(&self.args, &self.traits));
}
}
Ok(())
}
#[cfg(feature = "type_analysis")]
pub(super) fn collect_impl_trait(&mut self, ty: &mut Type) -> bool {
super::type_analysis::collect_impl_trait(&self.args, &mut self.traits, ty)
}
}
mod kw {
syn::custom_keyword!(marker);
}
#[allow(dead_code)]
struct Args {
args: Vec<Path>,
marker: Option<Ident>,
}
impl Parse for Args {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut args = Vec::new();
let mut marker = None;
while !input.is_empty() {
if input.peek(kw::marker) && input.peek2(Token![=]) {
let i: kw::marker = input.parse()?;
let _: Token![=] = input.parse()?;
let ident: Ident = input.parse()?;
if marker.replace(ident).is_some() {
return Err(error!(i, "duplicate `marker` argument"));
}
} else {
args.push(input.parse()?);
}
if input.is_empty() {
break;
}
let _: Token![,] = input.parse()?;
}
Ok(Self { args, marker })
}
}
struct Builder {
ident: Ident,
variants: Vec<Ident>,
}
impl Builder {
fn new(input: &TokenStream) -> Self {
Self { ident: format_ident!("__Enum{}", hash(input)), variants: Vec::new() }
}
fn next_expr(&mut self, attrs: Vec<Attribute>, expr: Expr) -> Expr {
let variant = format_ident!("__Variant{}", self.variants.len());
let path =
path(iter::once(self.ident.clone().into()).chain(iter::once(variant.clone().into())));
self.variants.push(variant);
expr_call(attrs, path, expr)
}
fn build(&self, args: &[Path], traits: &[Path]) -> ItemEnum {
let derive = args.iter().chain(traits);
let ident = &self.ident;
let ty_generics = &self.variants;
let variants = &self.variants;
let fields = &self.variants;
parse_quote! {
#[allow(non_camel_case_types)]
#[::auto_enums::enum_derive(#(#derive),*)]
enum #ident<#(#ty_generics),*> {
#(#variants(#fields),)*
}
}
}
}
fn hash(input: &TokenStream) -> u64 {
let mut hasher = DefaultHasher::new();
hasher.write(input.to_string().as_bytes());
hasher.finish()
}