Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions tasks/ast_tools/src/derives/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use quote::quote;
use syn::{parse_str, Type};

use crate::{
schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, VariantDef},
schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, VariantDef, Visibility},
utils::number_lit,
Result,
};
Expand Down Expand Up @@ -192,7 +192,8 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
};
}

let mut gen = StructSerializerGenerator::new(!struct_def.estree.no_type, schema);
let krate = struct_def.file(schema).krate();
let mut gen = StructSerializerGenerator::new(!struct_def.estree.no_type, krate, schema);
gen.generate_stmts_for_struct(struct_def, &quote!(self));

let type_field = if gen.add_type_field {
Expand Down Expand Up @@ -232,14 +233,16 @@ struct StructSerializerGenerator<'s> {
/// `true` if a `type` field should be added.
/// `false` one already exists (or if `#[estree(no_type)]` attr on struct).
add_type_field: bool,
/// Crate in which the `Serialize` impl for the type will be generated
krate: &'s str,
/// Schema
schema: &'s Schema,
}

impl<'s> StructSerializerGenerator<'s> {
/// Create new [`StructSerializerGenerator`].
fn new(add_type_field: bool, schema: &'s Schema) -> Self {
Self { stmts: quote!(), add_type_field, schema }
fn new(add_type_field: bool, krate: &'s str, schema: &'s Schema) -> Self {
Self { stmts: quote!(), add_type_field, krate, schema }
}

/// Generate code to serialize all fields in a struct.
Expand All @@ -263,20 +266,26 @@ impl<'s> StructSerializerGenerator<'s> {
let field_name_ident = field.ident();

if should_flatten_field(field, self.schema) {
match field.type_def(self.schema) {
TypeDef::Struct(inner_struct_def) => {
self.generate_stmts_for_struct(
inner_struct_def,
&quote!(#self_path.#field_name_ident),
);
}
TypeDef::Enum(_) => {
self.stmts.extend(quote! {
#self_path.#field_name_ident.serialize(FlatMapSerializer(&mut map))?;
});
}
_ => panic!("Cannot flatten a field which is not a struct or enum"),
if can_flatten_field_inline(field, self.krate, self.schema) {
let inner_struct_def = field.type_def(self.schema).as_struct().unwrap();
self.generate_stmts_for_struct(
inner_struct_def,
&quote!(#self_path.#field_name_ident),
);
return;
}

let field_type = field.type_def(self.schema);
assert!(
field_type.is_struct() || field_type.is_enum(),
"Cannot flatten a field which is not a struct or enum: `{}::{}`",
struct_def.name(),
field_type.name(),
);

self.stmts.extend(quote! {
#self_path.#field_name_ident.serialize(FlatMapSerializer(&mut map))?;
});
return;
}

Expand Down Expand Up @@ -364,6 +373,30 @@ pub fn should_flatten_field(field: &FieldDef, schema: &Schema) -> bool {
}
}

/// Get if struct field can be flattened inline.
///
/// If the field's type is an enum, then it can't.
///
/// If the field's type is a struct, then usually it can.
/// But it can't in the case where that type is defined in a different crate from where
/// the `Serialize` impl will be generated, and one of the flattened fields is not public.
pub fn can_flatten_field_inline(field: &FieldDef, krate: &str, schema: &Schema) -> bool {
let field_type = field.type_def(schema);
let TypeDef::Struct(struct_def) = field_type else { return false };

struct_def.fields.iter().all(|field| {
if should_skip_field(field, schema) {
true
} else {
match field.visibility {
Visibility::Public => true,
Visibility::Restricted => struct_def.file(schema).krate() == krate,
Visibility::Private => false,
}
}
})
}

/// Get value of a fieldless enum variant.
///
/// Value is determined by:
Expand Down
1 change: 0 additions & 1 deletion tasks/ast_tools/src/schema/defs/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ impl StructDef {
}

/// Get the [`File`] which this struct is defined in.
#[expect(dead_code)]
pub fn file<'s>(&self, schema: &'s Schema) -> &'s File {
&schema.files[self.file_id]
}
Expand Down
2 changes: 0 additions & 2 deletions tasks/ast_tools/src/schema/defs/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ impl Def for TypeDef {

/// `is_*` / `as_*` / `as_*_mut` methods.
impl TypeDef {
#[expect(dead_code)]
pub fn is_struct(&self) -> bool {
matches!(self, Self::Struct(_))
}
Expand All @@ -125,7 +124,6 @@ impl TypeDef {
}
}

#[expect(dead_code)]
pub fn is_enum(&self) -> bool {
matches!(self, Self::Enum(_))
}
Expand Down
Loading