Skip to content

Commit

Permalink
Generalize codegen support for mixed "derive"/"full" enums
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Oct 20, 2024
1 parent 9ddac65 commit dd44a26
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 15 deletions.
10 changes: 6 additions & 4 deletions codegen/src/clone.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{cfg, file, lookup};
use crate::{cfg, file, full, lookup};
use anyhow::Result;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
Expand All @@ -13,6 +13,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
match &node.data {
Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
Data::Enum(variants) => {
let mixed_derive_full = full::is_mixed_derive_full_enum(defs, node);
let arms = variants.iter().map(|(variant_name, fields)| {
let variant = Ident::new(variant_name, Span::call_site());
if fields.is_empty() {
Expand All @@ -28,9 +29,10 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
pats.push(pat);
}
let mut cfg = None;
if node.ident == "Expr" {
if mixed_derive_full {
if let Type::Syn(ty) = &fields[0] {
if !lookup::node(defs, ty).features.any.contains("derive") {
let features = &lookup::node(defs, ty).features;
if features.any.contains("full") && !features.any.contains("derive") {
cfg = Some(quote!(#[cfg(feature = "full")]));
}
}
Expand All @@ -41,7 +43,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
}
}
});
let nonexhaustive = if node.ident == "Expr" {
let nonexhaustive = if mixed_derive_full {
Some(quote! {
#[cfg(not(feature = "full"))]
_ => unreachable!(),
Expand Down
10 changes: 6 additions & 4 deletions codegen/src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::cfg::{self, DocCfg};
use crate::{file, lookup};
use crate::{file, full, lookup};
use anyhow::Result;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
Expand Down Expand Up @@ -54,6 +54,7 @@ fn expand_impl_body(
Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
Data::Enum(variants) => {
assert!(!is_syntax_tree_variant);
let mixed_derive_full = full::is_mixed_derive_full_enum(defs, node);
let arms = variants.iter().map(|(variant_name, fields)| {
let variant = Ident::new(variant_name, Span::call_site());
if fields.is_empty() {
Expand All @@ -62,9 +63,10 @@ fn expand_impl_body(
}
} else {
let mut cfg = None;
if node.ident == "Expr" {
if mixed_derive_full {
if let Type::Syn(ty) = &fields[0] {
if !lookup::node(defs, ty).features.any.contains("derive") {
let features = &lookup::node(defs, ty).features;
if features.any.contains("full") && !features.any.contains("derive") {
cfg = Some(quote!(#[cfg(feature = "full")]));
}
}
Expand All @@ -89,7 +91,7 @@ fn expand_impl_body(
}
}
});
let nonexhaustive = if node.ident == "Expr" {
let nonexhaustive = if mixed_derive_full {
Some(quote! {
#[cfg(not(feature = "full"))]
_ => unreachable!(),
Expand Down
8 changes: 5 additions & 3 deletions codegen/src/eq.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{cfg, file, lookup};
use crate::{cfg, file, full, lookup};
use anyhow::Result;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
Expand All @@ -23,6 +23,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
match &node.data {
Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
Data::Enum(variants) => {
let mixed_derive_full = full::is_mixed_derive_full_enum(defs, node);
let arms = variants.iter().map(|(variant_name, fields)| {
let variant = Ident::new(variant_name, Span::call_site());
if fields.is_empty() {
Expand Down Expand Up @@ -57,9 +58,10 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
comparisons.push(quote!(true));
}
let mut cfg = None;
if node.ident == "Expr" {
if mixed_derive_full {
if let Type::Syn(ty) = &fields[0] {
if !lookup::node(defs, ty).features.any.contains("derive") {
let features = &lookup::node(defs, ty).features;
if features.any.contains("full") && !features.any.contains("derive") {
cfg = Some(quote!(#[cfg(feature = "full")]));
}
}
Expand Down
70 changes: 70 additions & 0 deletions codegen/src/full.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::lookup;
use proc_macro2::TokenStream;
use quote::quote;
use syn_codegen::{Data, Definitions, Node, Type};

pub fn get_macro() -> TokenStream {
quote! {
Expand All @@ -18,3 +20,71 @@ pub fn get_macro() -> TokenStream {
}
}
}

/// Syntax tree enum that has some variants enabled in "derive" mode and the
/// rest enabled in "full" mode.
pub fn is_mixed_derive_full_enum(defs: &Definitions, node: &Node) -> bool {
if !(node.features.any.contains("derive") && node.features.any.contains("full")) {
return false;
}

let variants = match &node.data {
Data::Enum(variants) => variants,
Data::Private | Data::Struct(_) => return false,
};

let mut has_derive = false;
let mut has_full = false;
for fields in variants.values() {
match classify_variant(defs, fields) {
VariantAvailability::Derive => has_derive = true,
VariantAvailability::Full => has_full = true,
VariantAvailability::Other => {}
}
}
has_derive && has_full
}

enum VariantAvailability {
Derive,
Full,
Other,
}

fn classify_variant(defs: &Definitions, fields: &[Type]) -> VariantAvailability {
let mut has_derive = false;
let mut has_full = false;
for field in fields {
for_each_syn_type(field, &mut |ty| {
let node = lookup::node(defs, ty);
let derive = node.features.any.contains("derive");
let full = node.features.any.contains("full");
match (derive, full) {
(false, false) => {}
(false, true) => has_full = true,
(true, false | true) => has_derive = true,
}
});
}
if has_full {
VariantAvailability::Full
} else if has_derive {
VariantAvailability::Derive
} else {
VariantAvailability::Other
}
}

fn for_each_syn_type(ty: &Type, f: &mut dyn FnMut(&str)) {
match ty {
Type::Syn(ty) => f(ty),
Type::Std(_) | Type::Ext(_) | Type::Token(_) | Type::Group(_) => {}
Type::Punctuated(punctuated) => for_each_syn_type(&punctuated.element, f),
Type::Option(ty) | Type::Box(ty) | Type::Vec(ty) => for_each_syn_type(ty, f),
Type::Tuple(elements) => {
for ty in elements {
for_each_syn_type(ty, f);
}
}
}
}
11 changes: 7 additions & 4 deletions codegen/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{cfg, file, lookup};
use crate::{cfg, file, full, lookup};
use anyhow::Result;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
Expand All @@ -23,6 +23,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
match &node.data {
Data::Enum(variants) if variants.is_empty() => quote!(match *self {}),
Data::Enum(variants) => {
let mixed_derive_full = full::is_mixed_derive_full_enum(defs, node);
let arms = variants
.iter()
.enumerate()
Expand Down Expand Up @@ -60,9 +61,11 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
pats.push(var);
}
let mut cfg = None;
if node.ident == "Expr" {
if mixed_derive_full {
if let Type::Syn(ty) = &fields[0] {
if !lookup::node(defs, ty).features.any.contains("derive") {
let features = &lookup::node(defs, ty).features;
if features.any.contains("full") && !features.any.contains("derive")
{
cfg = Some(quote!(#[cfg(feature = "full")]));
}
}
Expand All @@ -76,7 +79,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
}
}
});
let nonexhaustive = if node.ident == "Expr" {
let nonexhaustive = if mixed_derive_full {
Some(quote! {
#[cfg(not(feature = "full"))]
_ => unreachable!(),
Expand Down

0 comments on commit dd44a26

Please sign in to comment.