From 1690a57ed32e860e928c4d25eb8110f56d042773 Mon Sep 17 00:00:00 2001 From: rzvxa <3788964+rzvxa@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:14:31 +0000 Subject: [PATCH] refactor(ast_codegen): move away from `RType` in generators. (#4682) This PR introduces `EarlyCtx` and `LateCtx` in place of the old `CodegenCtx`, Early passes operate at the AST level while generators and other late passes operate on the schema. It will also replace the confusing `RType` name with something more idiomatic ~~(open for suggestions, I haven't found a good name yet)~~ I've named it `AstType` and dropped the `R` prefix for `REnum` and `RStruct`. There are some qualities of life improvements too, Things like `to_type_elide` can be used to simplify the code. Related to #4442 (and can potentially mark it "close as fixed"). --- tasks/ast_codegen/src/defs.rs | 170 ---------- .../src/generators/assert_layouts.rs | 21 +- .../ast_codegen/src/generators/ast_builder.rs | 303 +++++++----------- tasks/ast_codegen/src/generators/ast_kind.rs | 73 ++--- .../src/generators/impl_get_span.rs | 47 ++- tasks/ast_codegen/src/generators/mod.rs | 9 +- tasks/ast_codegen/src/generators/visit.rs | 181 +++++------ tasks/ast_codegen/src/main.rs | 116 ++++--- tasks/ast_codegen/src/markers.rs | 7 +- tasks/ast_codegen/src/passes/build_schema.rs | 18 -- tasks/ast_codegen/src/passes/calc_layout.rs | 42 +-- tasks/ast_codegen/src/passes/linker.rs | 10 +- tasks/ast_codegen/src/passes/mod.rs | 19 +- .../src/{schema.rs => rust_ast.rs} | 145 ++++----- tasks/ast_codegen/src/schema/defs.rs | 168 ++++++++++ tasks/ast_codegen/src/schema/get_generics.rs | 38 +++ tasks/ast_codegen/src/schema/get_ident.rs | 28 ++ tasks/ast_codegen/src/schema/mod.rs | 266 +++++++++++++++ tasks/ast_codegen/src/schema/to_type.rs | 58 ++++ tasks/ast_codegen/src/util.rs | 52 +-- 20 files changed, 1034 insertions(+), 737 deletions(-) delete mode 100644 tasks/ast_codegen/src/defs.rs delete mode 100644 tasks/ast_codegen/src/passes/build_schema.rs rename tasks/ast_codegen/src/{schema.rs => rust_ast.rs} (74%) create mode 100644 tasks/ast_codegen/src/schema/defs.rs create mode 100644 tasks/ast_codegen/src/schema/get_generics.rs create mode 100644 tasks/ast_codegen/src/schema/get_ident.rs create mode 100644 tasks/ast_codegen/src/schema/mod.rs create mode 100644 tasks/ast_codegen/src/schema/to_type.rs diff --git a/tasks/ast_codegen/src/defs.rs b/tasks/ast_codegen/src/defs.rs deleted file mode 100644 index b03f9b8b12edf..0000000000000 --- a/tasks/ast_codegen/src/defs.rs +++ /dev/null @@ -1,170 +0,0 @@ -use super::{REnum, RStruct, RType}; -use crate::{layout::KnownLayout, schema::Inherit, util::TypeExt, TypeName}; -use quote::ToTokens; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub enum TypeDef { - Struct(StructDef), - Enum(EnumDef), -} - -impl TypeDef { - pub fn name(&self) -> &String { - match self { - Self::Struct(it) => &it.name, - Self::Enum(it) => &it.name, - } - } -} - -#[derive(Debug, Serialize)] -pub struct StructDef { - pub name: TypeName, - pub fields: Vec, - pub has_lifetime: bool, - pub size_64: usize, - pub align_64: usize, - pub offsets_64: Option>, - pub size_32: usize, - pub align_32: usize, - pub offsets_32: Option>, -} - -#[derive(Debug, Serialize)] -pub struct EnumDef { - pub name: TypeName, - pub variants: Vec, - /// For `@inherits` inherited enum variants - pub inherits: Vec, - pub has_lifetime: bool, - pub size_64: usize, - pub align_64: usize, - pub offsets_64: Option>, - pub size_32: usize, - pub align_32: usize, - pub offsets_32: Option>, -} - -#[derive(Debug, Serialize)] -pub struct EnumVariantDef { - pub name: TypeName, - pub fields: Vec, - pub discriminant: Option, -} - -#[derive(Debug, Serialize)] -pub struct EnumInheritDef { - pub super_name: String, - pub variants: Vec, -} - -#[derive(Debug, Serialize)] -pub struct FieldDef { - /// `None` if unnamed - pub name: Option, - pub r#type: TypeName, -} - -impl From<&RType> for Option { - fn from(rtype: &RType) -> Self { - match rtype { - RType::Enum(it) => Some(TypeDef::Enum(it.into())), - RType::Struct(it) => Some(TypeDef::Struct(it.into())), - _ => None, - } - } -} - -impl From<&REnum> for EnumDef { - fn from(it @ REnum { item, meta }: &REnum) -> Self { - let (size_64, align_64, offsets_64) = meta - .layout_64 - .clone() - .layout() - .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); - let (size_32, align_32, offsets_32) = meta - .layout_32 - .clone() - .layout() - .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); - Self { - name: it.ident().to_string(), - variants: item.variants.iter().map(Into::into).collect(), - inherits: meta.inherits.iter().map(Into::into).collect(), - has_lifetime: item.generics.lifetimes().count() > 0, - - size_64, - align_64, - offsets_64, - size_32, - align_32, - offsets_32, - } - } -} - -impl From<&RStruct> for StructDef { - fn from(it @ RStruct { item, meta }: &RStruct) -> Self { - let (size_64, align_64, offsets_64) = meta - .layout_64 - .clone() - .layout() - .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); - let (size_32, align_32, offsets_32) = meta - .layout_32 - .clone() - .layout() - .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); - Self { - name: it.ident().to_string(), - fields: item.fields.iter().map(Into::into).collect(), - has_lifetime: item.generics.lifetimes().count() > 0, - - size_64, - align_64, - offsets_64, - size_32, - align_32, - offsets_32, - } - } -} - -impl From<&syn::Variant> for EnumVariantDef { - fn from(variant: &syn::Variant) -> Self { - Self { - name: variant.ident.to_string(), - discriminant: variant.discriminant.as_ref().map(|(_, disc)| match disc { - syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit), .. }) => { - lit.base10_parse().expect("invalid base10 enum discriminant") - } - _ => panic!("invalid enum discriminant"), - }), - fields: variant.fields.iter().map(Into::into).collect(), - } - } -} - -impl From<&Inherit> for EnumInheritDef { - fn from(inherit: &Inherit) -> Self { - match inherit { - Inherit::Linked { super_, variants } => Self { - super_name: super_.get_ident().as_ident().unwrap().to_string(), - variants: variants.iter().map(Into::into).collect(), - }, - Inherit::Unlinked(_) => { - panic!("`Unlinked` inherits can't be converted to a valid `EnumInheritDef`!") - } - } - } -} - -impl From<&syn::Field> for FieldDef { - fn from(field: &syn::Field) -> Self { - Self { - name: field.ident.as_ref().map(ToString::to_string), - r#type: field.ty.to_token_stream().to_string().replace(' ', ""), - } - } -} diff --git a/tasks/ast_codegen/src/generators/assert_layouts.rs b/tasks/ast_codegen/src/generators/assert_layouts.rs index c078075aed46a..7f739bf4189cf 100644 --- a/tasks/ast_codegen/src/generators/assert_layouts.rs +++ b/tasks/ast_codegen/src/generators/assert_layouts.rs @@ -1,10 +1,11 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{PathArguments, Type}; +use syn::Type; use crate::{ - defs::{FieldDef, TypeDef}, - output, CodegenCtx, Generator, GeneratorOutput, + output, + schema::{FieldDef, ToType, TypeDef}, + Generator, GeneratorOutput, LateCtx, }; use super::{define_generator, generated_header}; @@ -18,23 +19,13 @@ impl Generator for AssertLayouts { stringify!(AssertLayouts) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { let (assertions_64, assertions_32) = ctx .schema - .borrow() .definitions .iter() .map(|def| { - let typ = - ctx.find(def.name()).and_then(|ty| ty.borrow().as_type()).map(|mut ty| { - if let Type::Path(ty) = &mut ty { - if let Some(seg) = ty.path.segments.first_mut() { - seg.arguments = PathArguments::None; - } - } - ty - }); - let typ = typ.unwrap(); + let typ = def.to_type_elide(); assert_type(&typ, def) }) .collect::<(Vec, Vec)>(); diff --git a/tasks/ast_codegen/src/generators/ast_builder.rs b/tasks/ast_codegen/src/generators/ast_builder.rs index 0b3e94d8f9535..e715cc6d808f8 100644 --- a/tasks/ast_codegen/src/generators/ast_builder.rs +++ b/tasks/ast_codegen/src/generators/ast_builder.rs @@ -6,18 +6,16 @@ use itertools::Itertools; use lazy_static::lazy_static; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; -use syn::{ - parse_quote, punctuated::Punctuated, AngleBracketedGenericArguments, Attribute, Expr, - GenericArgument, Ident, Lit, Meta, MetaNameValue, PathArguments, Token, Type, TypePath, - Variant, -}; +use syn::{parse_quote, Ident, Type}; +use crate::schema::{ + EnumDef, FieldDef, GetIdent, InheritDef, StructDef, ToType, TypeDef, TypeName, VariantDef, +}; use crate::{ generators::generated_header, output, - schema::{Inherit, REnum, RStruct, RType}, - util::{TypeAnalyzeResult, TypeExt, TypeIdentResult, TypeWrapper}, - CodegenCtx, Generator, GeneratorOutput, TypeRef, + util::{TypeAnalysis, TypeWrapper}, + Generator, GeneratorOutput, LateCtx, }; use super::define_generator; @@ -31,13 +29,13 @@ impl Generator for AstBuilderGenerator { stringify!(AstBuilderGenerator) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { let fns = ctx - .ty_table + .schema + .definitions .iter() - .filter(|it| it.borrow().visitable()) - .map(|it| (it, ctx)) - .filter_map(|(it, ctx)| generate_builder_fn(it, ctx)) + .filter(|it| it.visitable()) + .map(|it| generate_builder_fn(it, ctx)) .collect_vec(); let header = generated_header!(); @@ -99,49 +97,40 @@ fn enum_builder_name(enum_name: String, var_name: String) -> Ident { format_ident!("{}_{}", fn_ident_name(enum_name), fn_ident_name(var_name)) } -fn struct_builder_name(struct_: &RStruct) -> Ident { +fn struct_builder_name(struct_: &StructDef) -> Ident { static RUST_KEYWORDS: [&str; 1] = ["super"]; - let mut ident = fn_ident_name(struct_.ident().to_string()); + let mut ident = fn_ident_name(struct_.name.as_str()); if RUST_KEYWORDS.contains(&ident.as_str()) { ident.push('_'); } format_ident!("{ident}") } -fn generate_builder_fn(ty: &TypeRef, ctx: &CodegenCtx) -> Option { - match &*ty.borrow() { - RType::Enum(it) => Some(generate_enum_builder_fn(it, ctx)), - RType::Struct(it) => Some(generate_struct_builder_fn(it, ctx)), - _ => None, +fn generate_builder_fn(def: &TypeDef, ctx: &LateCtx) -> TokenStream { + match def { + TypeDef::Enum(def) => generate_enum_builder_fn(def, ctx), + TypeDef::Struct(def) => generate_struct_builder_fn(def, ctx), } } -fn generate_enum_builder_fn(ty: &REnum, ctx: &CodegenCtx) -> TokenStream { - let variants_fns = ty - .item - .variants - .iter() - .filter(|it| !it.attrs.iter().any(|it| it.path().is_ident("inherit"))) - .map(|it| generate_enum_variant_builder_fn(ty, it, ctx)); +fn generate_enum_builder_fn(def: &EnumDef, ctx: &LateCtx) -> TokenStream { + let variants_fns = def.variants.iter().map(|it| generate_enum_variant_builder_fn(def, it, ctx)); - let inherits_fns = ty.meta.inherits.iter().map(|it| { - let Inherit::Linked { super_, variants } = it else { panic!("Unresolved inheritance!") }; - generate_enum_inherit_builder_fn(ty, super_, variants, ctx) - }); + let inherits_fns = def.inherits.iter().map(|it| generate_enum_inherit_builder_fn(def, it, ctx)); variants_fns.chain(inherits_fns).collect() } fn generate_enum_inherit_builder_fn( - enum_: &REnum, - super_type: &Type, - _: &Punctuated, - _: &CodegenCtx, + enum_: &EnumDef, + inherit: &InheritDef, + _: &LateCtx, ) -> TokenStream { let enum_ident = enum_.ident(); - let enum_as_type = enum_.as_type(); + let enum_as_type = enum_.to_type(); + let super_type = inherit.super_.to_type(); let fn_name = - enum_builder_name(enum_ident.to_string(), super_type.get_ident().inner_ident().to_string()); + enum_builder_name(enum_ident.to_string(), inherit.super_.name().inner_name().to_string()); quote! { endl!(); @@ -154,34 +143,35 @@ fn generate_enum_inherit_builder_fn( /// Create a builder function for an enum variant (e.g. for `Expression::Binary`) fn generate_enum_variant_builder_fn( - enum_: &REnum, - variant: &Variant, - ctx: &CodegenCtx, + enum_: &EnumDef, + variant: &VariantDef, + ctx: &LateCtx, ) -> TokenStream { assert_eq!(variant.fields.len(), 1); let enum_ident = enum_.ident(); - let enum_type = &enum_.as_type(); - let var_ident = &variant.ident; - let var_type = &variant.fields.iter().next().expect("we have already asserted this one!").ty; - let fn_name = - enum_builder_name(enum_ident.to_string(), var_type.get_ident().inner_ident().to_string()); - let ty = ctx.find(&var_type.get_ident().inner_ident().to_string()).expect("type not found!"); + let enum_type = &enum_.to_type(); + let var_ident = &variant.ident(); + let var_type = &variant.fields.first().expect("we have already asserted this one!").typ; + let var_type_name = &var_type.name(); + let fn_name = enum_builder_name(enum_ident.to_string(), var_type_name.inner_name().to_string()); + let ty = var_type + .type_id() + .or_else(|| var_type.transparent_type_id()) + .and_then(|id| ctx.type_def(id)) + .expect("type not found!"); #[allow(clippy::single_match_else)] - let (params, inner_builder) = match &*ty.borrow() { - // RType::Enum(it) => get_enum_params(it, ctx), - RType::Struct(it) => (get_struct_params(it, ctx), struct_builder_name(it)), - _ => panic!(), + let (params, inner_builder) = match ty { + TypeDef::Struct(it) => (get_struct_params(it, ctx), struct_builder_name(it)), + TypeDef::Enum(_) => panic!("Unsupported!"), }; let params = params.into_iter().filter(Param::not_default).collect_vec(); let fields = params.iter().map(|it| it.ident.clone()); let (generic_params, where_clause) = get_generic_params(¶ms); - let inner_ident = var_type.get_ident(); - let mut inner = quote!(self.#inner_builder(#(#fields),*)); let mut does_alloc = false; - if matches!(inner_ident, TypeIdentResult::Box(_)) { + if matches!(var_type_name, TypeName::Box(_)) { inner = quote!(self.alloc(#inner)); does_alloc = true; } @@ -191,8 +181,8 @@ fn generate_enum_variant_builder_fn( let mut docs = DocComment::new(format!(" Build {article} [`{enum_ident}::{var_ident}`]")) .with_params(¶ms); if does_alloc { - let inner_name = inner_ident.inner_ident().to_string(); - let inner_article = article_for(&inner_name); + let inner_name = var_type_name.inner_name(); + let inner_article = article_for(inner_name); docs = docs.with_description(format!( "This node contains {inner_article} [`{inner_name}`] that will be stored in the memory arena." )); @@ -213,24 +203,24 @@ fn generate_enum_variant_builder_fn( /// Generate a conversion function that takes some struct and creates an enum /// variant containing that struct using the `IntoIn` trait. fn generate_enum_from_variant_builder_fn( - enum_: &REnum, - variant: &Variant, - _: &CodegenCtx, + enum_: &EnumDef, + variant: &VariantDef, + _: &LateCtx, ) -> TokenStream { assert_eq!(variant.fields.len(), 1); let enum_ident = enum_.ident(); - let enum_type = &enum_.as_type(); - let var_ident = &variant.ident; - let var_type = &variant.fields.iter().next().expect("we have already asserted this one!").ty; - let struct_ident = var_type.get_ident().inner_ident().to_string(); - let fn_name = enum_builder_name(enum_ident.to_string(), format!("From{struct_ident}")); - - let from_article = article_for(struct_ident); + let enum_type = &enum_.to_type(); + let var_ident = &variant.ident(); + let var_type_ref = &variant.fields.first().expect("we have already asserted this one!").typ; + let var_type_name = var_type_ref.name().inner_name(); + let var_type = var_type_ref.to_type(); + let fn_name = enum_builder_name(enum_ident.to_string(), format!("From{var_type_name}")); + + let from_article = article_for(var_type_name); let to_article = article_for(enum_ident.to_string()); let docs = DocComment::new(format!( - " Convert {from_article} [`{}`] into {to_article} [`{enum_ident}::{var_ident}`]", - var_type.get_ident().inner_ident() + " Convert {from_article} [`{var_type_name}`] into {to_article} [`{enum_ident}::{var_ident}`]", )); quote! { endl!(); @@ -242,7 +232,7 @@ fn generate_enum_from_variant_builder_fn( } } -fn default_init_field((ident, typ): &(&Ident, &Type)) -> bool { +fn default_init_field(field: &FieldDef) -> bool { macro_rules! field { ($ident:ident: $ty:ty) => { (stringify!($ident), stringify!($ty)) @@ -256,21 +246,23 @@ fn default_init_field((ident, typ): &(&Ident, &Type)) -> bool { field!(reference_flag: ReferenceFlag), ]); } + + let ident = field.ident().expect("expected named field"); if let Some(default_type) = DEFAULT_FIELDS.get(ident.to_string().as_str()) { - *default_type == typ.to_token_stream().to_string().replace(' ', "") + *default_type == field.typ.raw() } else { false } } -fn generate_struct_builder_fn(ty: &RStruct, ctx: &CodegenCtx) -> TokenStream { +fn generate_struct_builder_fn(ty: &StructDef, ctx: &LateCtx) -> TokenStream { fn default_field(param: &Param) -> TokenStream { debug_assert!(param.is_default); let ident = ¶m.ident; quote!(#ident: Default::default()) } let ident = ty.ident(); - let as_type = ty.as_type(); + let as_type = ty.to_type(); let fn_name = struct_builder_name(ty); let params = get_struct_params(ty, ctx); @@ -328,12 +320,12 @@ fn generate_struct_builder_fn(ty: &RStruct, ctx: &CodegenCtx) -> TokenStream { #[derive(Debug)] struct Param { is_default: bool, - info: TypeAnalyzeResult, + analysis: TypeAnalysis, ident: Ident, ty: Type, generic: Option<(/* predicate */ TokenStream, /* param name */ TokenStream)>, into_in: bool, - docs: Option, + docs: Vec, } impl Param { @@ -496,86 +488,21 @@ impl ToTokens for DocComment<'_> { newline!(); tokens.extend(quote!( #[doc = " ## Parameters"])); for param in self.params { - match ¶m.docs { + let docs = param.docs.first(); + let docs = match docs { Some(docs) => { - let docs = format!(" - {}: {}", param.ident, docs.trim()); - tokens.extend(quote!( - #[doc = #docs] - )); + format!(" - {}: {}", param.ident, docs.trim()) } None if param.ident == "span" => { - tokens.extend(quote!( - #[doc = " - span: The [`Span`] covering this node"] - )); + " - span: The [`Span`] covering this node".to_string() } None => { - let docs = format!(" - {}", param.ident); - tokens.extend(quote!(#[doc = #docs])); - } - } - } - } - } -} -fn get_doc_comment(attrs: &[Attribute]) -> Option { - attrs.iter().find_map(|attr| match &attr.meta { - Meta::NameValue(MetaNameValue { path, value: Expr::Lit(lit), .. }) => { - if !path.is_ident("doc") { - return None; - } - - match &lit.lit { - Lit::Str(lit) => Some(lit.value()), - _ => None, - } - } - _ => None, - }) -} - -// TODO: remove me -#[allow(dead_code)] -fn get_enum_params(enum_: &REnum, ctx: &CodegenCtx) -> Vec { - let as_type = enum_.as_type(); - let inner_type = match &as_type { - ty @ Type::Path(TypePath { path, .. }) if path.get_ident().is_none() => { - assert_eq!(path.segments.len(), 1); - let seg1 = &path.segments[0]; - if seg1.ident == "Box" { - match &seg1.arguments { - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args, .. - }) => { - assert!(matches!(args[0], GenericArgument::Lifetime(_))); - let GenericArgument::Type(ref inner_type) = args[1] else { - panic!("Unsupported box type!") - }; - inner_type.clone() + format!(" - {}", param.ident) } - _ => panic!("Unsupported box type!"), - } - } else { - ty.clone() + }; + tokens.extend(quote!(#[doc = #docs])); } } - ty => ty.clone(), - }; - let inner = - ctx.find(&inner_type.get_ident().inner_ident().to_string()).expect("type not found!"); - match &*TypeRef::clone(&inner).borrow() { - RType::Enum(_) => { - vec![Param { - is_default: false, - info: as_type.analyze(ctx), - ident: format_ident!("inner"), - ty: inner_type.clone(), - generic: None, - into_in: false, - docs: None, - }] - } - RType::Struct(it) => get_struct_params(it, ctx), - _ => panic!(), } } @@ -602,59 +529,47 @@ fn get_generic_params( } // TODO: currently doesn't support multiple `Atom` or `&'a str` params. -fn get_struct_params(struct_: &RStruct, ctx: &CodegenCtx) -> Vec { +fn get_struct_params(struct_: &StructDef, ctx: &LateCtx) -> Vec { // generic param postfix let mut t_count = 0; let mut t_param = move || { t_count += 1; format_ident!("T{t_count}").to_token_stream() }; - struct_ - .item - .fields - .iter() - .map(|f| { - let id = f.ident.as_ref().expect("expected named ident! on struct"); - let docs = get_doc_comment(&f.attrs); - ((id, &f.ty), docs) - }) - .fold(Vec::new(), |mut acc, (ref it, docs)| { - let (id, ty) = *it; - let info = ty.analyze(ctx); - let (interface_typ, generic_typ) = match (&info.wrapper, &info.type_ref) { - (TypeWrapper::Box, Some(ref type_ref)) => { - let t = t_param(); - let typ = type_ref.borrow().as_type().unwrap(); - (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, Box<'a, #typ>>), t))) - } - (TypeWrapper::OptBox, Some(ref type_ref)) => { - let t = t_param(); - let typ = type_ref.borrow().as_type().unwrap(); - ( - Some(parse_quote!(#t)), - Some((quote!(#t: IntoIn<'a, Option>>), t)), - ) - } - (TypeWrapper::Ref, None) if ty.get_ident().inner_ident() == "str" => { - let t = format_ident!("S").to_token_stream(); - (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, &'a str>), t))) - } - (TypeWrapper::None, None) if ty.get_ident().inner_ident() == "Atom" => { - let t = format_ident!("A").to_token_stream(); - (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, Atom<'a>>), t))) - } - _ => (None, None), - }; - let ty = interface_typ.unwrap_or_else(|| ty.clone()); - acc.push(Param { - is_default: default_init_field(it), - info, - ident: id.clone(), - ty, - into_in: generic_typ.is_some(), - generic: generic_typ, - docs, - }); - acc - }) + struct_.fields.iter().fold(Vec::new(), |mut acc, field| { + let analysis = field.typ.analysis(); + let type_def = field.typ.transparent_type_id().and_then(|id| ctx.type_def(id)); + let (interface_typ, generic_typ) = match (&analysis.wrapper, type_def) { + (TypeWrapper::Box, Some(def)) => { + let t = t_param(); + let typ = def.to_type(); + (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, Box<'a, #typ>>), t))) + } + (TypeWrapper::OptBox, Some(def)) => { + let t = t_param(); + let typ = def.to_type(); + (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, Option>>), t))) + } + (TypeWrapper::Ref, None) if field.typ.is_str_slice() => { + let t = format_ident!("S").to_token_stream(); + (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, &'a str>), t))) + } + (TypeWrapper::None, None) if field.typ.name().inner_name() == "Atom" => { + let t = format_ident!("A").to_token_stream(); + (Some(parse_quote!(#t)), Some((quote!(#t: IntoIn<'a, Atom<'a>>), t))) + } + _ => (None, None), + }; + let ty = interface_typ.unwrap_or_else(|| field.typ.to_type()); + acc.push(Param { + is_default: default_init_field(field), + analysis: analysis.clone(), + ident: field.ident().expect("expected named ident! on struct"), + ty, + into_in: generic_typ.is_some(), + generic: generic_typ, + docs: field.docs.clone(), + }); + acc + }) } diff --git a/tasks/ast_codegen/src/generators/ast_kind.rs b/tasks/ast_codegen/src/generators/ast_kind.rs index bc4c0b807b006..f75ce47592d81 100644 --- a/tasks/ast_codegen/src/generators/ast_kind.rs +++ b/tasks/ast_codegen/src/generators/ast_kind.rs @@ -3,8 +3,10 @@ use quote::quote; use syn::{parse_quote, Arm, Ident, Type, Variant}; use crate::{ - markers::get_visit_markers, output, schema::RType, util::TypeExt, CodegenCtx, Generator, - GeneratorOutput, TypeRef, + output, + schema::{GetIdent, ToType, TypeDef}, + util::ToIdent, + Generator, GeneratorOutput, LateCtx, }; use super::{define_generator, generated_header}; @@ -86,48 +88,47 @@ pub fn aliased_nodes() -> [(Ident, Type); 1] { [(pq!(ExpressionArrayElement), pq!(Expression<'a>))] } -pub fn process_types(ty: &TypeRef) -> Vec<(Ident, Type)> { - let aliases = match &*ty.borrow() { - RType::Enum(enum_) => enum_ - .item +pub fn process_types(def: &TypeDef, _: &LateCtx) -> Vec<(Ident, Type)> { + let aliases = match def { + TypeDef::Enum(enum_) => enum_ .variants .iter() - .map(|it| (it, get_visit_markers(&it.attrs).transpose().unwrap())) - .filter(|(_, markers)| markers.as_ref().is_some_and(|mk| mk.visit_as.is_some())) - .filter_map(|(it, markers)| { - markers.map(|markers| { - let field = it.fields.iter().next().unwrap(); - let type_name = field.ty.get_ident().inner_ident(); - (markers.visit_as.expect("Already checked"), parse_quote!(#type_name<'a>)) + // .map(|it| (it, get_visit_markers(&it.attrs).transpose().unwrap())) + .filter(|it| it.markers.visit.as_ref().is_some_and(|mk| mk.visit_as.is_some())) + .filter_map(|var| { + var.markers.visit.as_ref().map(|markers| { + let field = var.fields.first().unwrap(); + let type_name = field.typ.name().inner_name(); + ( + markers.visit_as.clone().expect("Already checked"), + parse_quote!(#type_name<'a>), + ) }) }) .collect_vec(), - RType::Struct(struct_) => struct_ - .item + TypeDef::Struct(struct_) => struct_ .fields .iter() - .map(|it| (it, get_visit_markers(&it.attrs).transpose().unwrap())) - .filter(|(_, markers)| markers.as_ref().is_some_and(|mk| mk.visit_as.is_some())) - .filter_map(|(field, markers)| { - markers.map(|markers| { - let type_name = field.ty.get_ident().inner_ident(); - (markers.visit_as.expect("Already checked"), parse_quote!(#type_name<'a>)) + // .map(|it| (it, get_visit_markers(&it.attrs).transpose().unwrap())) + .filter(|it| it.markers.visit.as_ref().is_some_and(|mk| mk.visit_as.is_some())) + .filter_map(|field| { + field.markers.visit.as_ref().map(|markers| { + let type_name = field.typ.name().inner_name().to_ident(); + ( + markers.visit_as.clone().expect("Already checked"), + parse_quote!(#type_name<'a>), + ) }) }) .collect_vec(), - _ => panic!(), }; - Some(ty) + Some(def) .into_iter() - .map(|kind| { - if let kind @ (RType::Enum(_) | RType::Struct(_)) = &*kind.borrow() { - let ident = kind.ident().unwrap().clone(); - let typ = kind.as_type().unwrap(); - (ident, typ) - } else { - panic!() - } + .map(|def| { + let ident = def.ident(); + let typ = def.to_type(); + (ident, typ) }) .chain(aliases) .collect() @@ -138,15 +139,15 @@ impl Generator for AstKindGenerator { stringify!(AstKindGenerator) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { let have_kinds: Vec<(Ident, Type)> = ctx - .ty_table + .schema.definitions .iter() - .filter(|it| it.borrow().visitable()) + .filter(|it| it.visitable()) .filter( - |maybe_kind| matches!(&*maybe_kind.borrow(), kind @ (RType::Enum(_) | RType::Struct(_)) if kind.visitable()) + |maybe_kind| matches!(maybe_kind, kind @ (TypeDef::Enum(_) | TypeDef::Struct(_)) if kind.visitable()) ) - .flat_map(process_types) + .flat_map(|it| process_types(it, ctx)) .filter(blacklist) .chain(aliased_nodes()) .collect(); diff --git a/tasks/ast_codegen/src/generators/impl_get_span.rs b/tasks/ast_codegen/src/generators/impl_get_span.rs index 70a1f583f5b07..5142c90fc8514 100644 --- a/tasks/ast_codegen/src/generators/impl_get_span.rs +++ b/tasks/ast_codegen/src/generators/impl_get_span.rs @@ -1,11 +1,11 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::Variant; use crate::{ output, - schema::{REnum, RStruct, RType}, - CodegenCtx, Generator, GeneratorOutput, + schema::{EnumDef, GetGenerics, StructDef, ToType, TypeDef}, + util::ToIdent, + Generator, GeneratorOutput, LateCtx, }; use super::{define_generator, generated_header}; @@ -19,17 +19,15 @@ impl Generator for ImplGetSpanGenerator { stringify!(ImplGetSpanGenerator) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { let impls: Vec = ctx - .ty_table + .schema + .definitions .iter() - .map(|it| it.borrow()) - .filter(|it| it.visitable()) - .filter(|it| matches!(&**it, RType::Enum(_) | RType::Struct(_))) - .map(|kind| match &*kind { - RType::Enum(it) => impl_enum(it), - RType::Struct(it) => impl_struct(it), - _ => unreachable!("already filtered out!"), + .filter(|def| def.visitable()) + .map(|def| match &def { + TypeDef::Enum(it) => impl_enum(it), + TypeDef::Struct(it) => impl_struct(it), }) .collect(); @@ -51,13 +49,13 @@ impl Generator for ImplGetSpanGenerator { } } -fn impl_enum(it @ REnum { item, .. }: &REnum) -> TokenStream { - let typ = it.as_type(); - let generics = &item.generics; - let (matches, matches_mut): (Vec, Vec) = item - .variants - .iter() - .map(|Variant { ident, .. }| { +fn impl_enum(def: &EnumDef) -> TokenStream { + let typ = def.to_type(); + let generics = &def.generics(); + let (matches, matches_mut): (Vec, Vec) = def + .all_variants() + .map(|var| { + let ident = var.ident(); (quote!(Self :: #ident(it) => it.span()), quote!(Self :: #ident(it) => it.span_mut())) }) .unzip(); @@ -83,13 +81,12 @@ fn impl_enum(it @ REnum { item, .. }: &REnum) -> TokenStream { } } -fn impl_struct(it @ RStruct { item, .. }: &RStruct) -> TokenStream { - let typ = it.as_type(); - let generics = &item.generics; - let inner_span_hint = - item.fields.iter().find(|it| it.attrs.iter().any(|a| a.path().is_ident("span"))); +fn impl_struct(def: &StructDef) -> TokenStream { + let typ = def.to_type(); + let generics = &def.generics(); + let inner_span_hint = def.fields.iter().find(|it| it.markers.span); let (span, span_mut) = if let Some(span_field) = inner_span_hint { - let ident = span_field.ident.as_ref().unwrap(); + let ident = span_field.name.as_ref().map(ToIdent::to_ident).unwrap(); (quote!(self.#ident.span()), quote!(self.#ident.span_mut())) } else { (quote!(self.span), quote!(&mut self.span)) diff --git a/tasks/ast_codegen/src/generators/mod.rs b/tasks/ast_codegen/src/generators/mod.rs index c8476221c53e9..2bce32dda52a5 100644 --- a/tasks/ast_codegen/src/generators/mod.rs +++ b/tasks/ast_codegen/src/generators/mod.rs @@ -46,22 +46,25 @@ pub use ast_kind::AstKindGenerator; pub use impl_get_span::ImplGetSpanGenerator; pub use visit::{VisitGenerator, VisitMutGenerator}; -use crate::{CodegenCtx, GeneratorOutput}; +use crate::{GeneratorOutput, LateCtx}; pub trait Generator { fn name(&self) -> &'static str; - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput; + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput; } macro_rules! define_generator { ($vis:vis struct $ident:ident $($lifetime:lifetime)? $($rest:tt)*) => { $vis struct $ident $($lifetime)? $($rest)* impl $($lifetime)? $crate::Runner for $ident $($lifetime)? { + type Context = $crate::LateCtx; + type Output = $crate::GeneratorOutput; + fn name(&self) -> &'static str { $crate::Generator::name(self) } - fn run(&mut self, ctx: &$crate::CodegenCtx) -> $crate::Result<$crate::GeneratorOutput> { + fn run(&mut self, ctx: &$crate::LateCtx) -> $crate::Result { Ok(self.generate(ctx)) } } diff --git a/tasks/ast_codegen/src/generators/visit.rs b/tasks/ast_codegen/src/generators/visit.rs index d9ae1ec99f35a..de400836e0d0e 100644 --- a/tasks/ast_codegen/src/generators/visit.rs +++ b/tasks/ast_codegen/src/generators/visit.rs @@ -8,13 +8,11 @@ use syn::{parse_quote, Ident}; use crate::{ generators::{ast_kind::BLACK_LIST as KIND_BLACK_LIST, insert}, - markers::{ - get_scope_attr, get_scope_markers, get_visit_markers, ScopeMarkers, VisitArg, VisitMarkers, - }, + markers::{ScopeMarkers, VisitArg, VisitMarkers}, output, - schema::{Inherit, REnum, RStruct, RType}, - util::{StrExt, TokenStreamExt, TypeExt, TypeWrapper}, - CodegenCtx, Generator, GeneratorOutput, TypeRef, + schema::{EnumDef, GetIdent, StructDef, ToType, TypeDef}, + util::{StrExt, ToIdent, TokenStreamExt, TypeWrapper}, + Generator, GeneratorOutput, LateCtx, }; use super::{define_generator, generated_header}; @@ -32,7 +30,7 @@ impl Generator for VisitGenerator { stringify!(VisitGenerator) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { GeneratorOutput::Stream(( output(crate::AST_CRATE, "visit.rs"), generate_visit::(ctx), @@ -45,7 +43,7 @@ impl Generator for VisitMutGenerator { stringify!(VisitMutGenerator) } - fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput { + fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput { GeneratorOutput::Stream(( output(crate::AST_CRATE, "visit_mut.rs"), generate_visit::(ctx), @@ -61,7 +59,7 @@ const CLIPPY_ALLOW: &str = "\ clippy::semicolon_if_nothing_returned,\ clippy::match_wildcard_for_single_variants"; -fn generate_visit(ctx: &CodegenCtx) -> TokenStream { +fn generate_visit(ctx: &LateCtx) -> TokenStream { let header = generated_header!(); // we evaluate it outside of quote to take advantage of expression evaluation // otherwise the `\n\` wouldn't work! @@ -155,7 +153,7 @@ fn generate_visit(ctx: &CodegenCtx) -> TokenStream { } struct VisitBuilder<'a> { - ctx: &'a CodegenCtx, + ctx: &'a LateCtx, is_mut: bool, @@ -165,23 +163,21 @@ struct VisitBuilder<'a> { } impl<'a> VisitBuilder<'a> { - fn new(ctx: &'a CodegenCtx, is_mut: bool) -> Self { + fn new(ctx: &'a LateCtx, is_mut: bool) -> Self { Self { ctx, is_mut, visits: Vec::new(), walks: Vec::new(), cache: HashMap::new() } } fn build(mut self) -> (/* visits */ Vec, /* walks */ Vec) { - let program = { - let types: Vec<&TypeRef> = - self.ctx.ty_table.iter().filter(|it| it.borrow().visitable()).collect_vec(); - TypeRef::clone( - types - .iter() - .find(|it| it.borrow().ident().is_some_and(|ident| ident == "Program")) - .expect("Couldn't find the `Program` type!"), - ) - }; + let program = self + .ctx + .schema + .definitions + .iter() + .filter(|it| it.visitable()) + .find(|it| it.name() == "Program") + .expect("Couldn't find the `Program` type!"); - self.get_visitor(&program, false, None); + self.get_visitor(program, false, None); (self.visits, self.walks) } @@ -214,21 +210,20 @@ impl<'a> VisitBuilder<'a> { fn get_visitor( &mut self, - ty: &TypeRef, + def: &TypeDef, collection: bool, - visit_as: Option<&Ident>, + visit_as: Option, ) -> Cow<'a, Ident> { let cache_ix = usize::from(collection); let (ident, as_type) = { - let ty = ty.borrow(); - debug_assert!(ty.visitable(), "{ty:?}"); + debug_assert!(def.visitable(), "{def:?}"); - let ident = ty.ident().unwrap(); - let as_type = ty.as_type().unwrap(); + let ident = def.name().to_ident(); + let as_type = def.to_type(); - let ident = visit_as.unwrap_or(ident); + let ident = visit_as.clone().unwrap_or(ident); - (ident.clone(), if collection { parse_quote!(Vec<'a, #as_type>) } else { as_type }) + (ident, if collection { parse_quote!(Vec<'a, #as_type>) } else { as_type }) }; // is it already generated? @@ -291,7 +286,7 @@ impl<'a> VisitBuilder<'a> { self.walks.push(TokenStream::default()); let (walk_body, may_inline) = if collection { - let singular_visit = self.get_visitor(ty, false, None); + let singular_visit = self.get_visitor(def, false, None); let iter = self.get_iter(); ( quote! { @@ -302,17 +297,17 @@ impl<'a> VisitBuilder<'a> { true, ) } else { - match &*ty.borrow() { + match def { // TODO: this one is a hot-fix to prevent flattening aliased `Expression`s, // Such as `ExpressionArrayElement` and `ClassHeritage`. // Shouldn't be an edge case, - RType::Enum(enum_) - if enum_.item.ident == "Expression" - && visit_as.is_some_and(|it| { + TypeDef::Enum(enum_) + if enum_.name == "Expression" + && visit_as.as_ref().is_some_and(|it| { it == "ExpressionArrayElement" || it == "ClassHeritage" }) => { - let kind = self.kind_type(visit_as.unwrap()); + let kind = self.kind_type(visit_as.as_ref().unwrap()); ( quote! { let kind = #kind; @@ -323,9 +318,8 @@ impl<'a> VisitBuilder<'a> { false, ) } - RType::Enum(enum_) => self.generate_enum_walk(enum_, visit_as), - RType::Struct(struct_) => self.generate_struct_walk(struct_, visit_as), - _ => panic!(), + TypeDef::Enum(enum_) => self.generate_enum_walk(enum_, visit_as), + TypeDef::Struct(struct_) => self.generate_struct_walk(struct_, visit_as), } }; @@ -346,19 +340,16 @@ impl<'a> VisitBuilder<'a> { fn generate_enum_walk( &mut self, - enum_: &REnum, - visit_as: Option<&Ident>, + enum_: &EnumDef, + visit_as: Option, ) -> (TokenStream, /* inline */ bool) { let ident = enum_.ident(); let mut non_exhaustive = false; let variants_matches = enum_ - .item .variants .iter() - .filter(|it| !it.attrs.iter().any(|a| a.path().is_ident("inherit"))) - .map(|it| (it, get_visit_markers(&it.attrs).transpose().unwrap())) - .filter(|(_, markers)| { - if markers.as_ref().is_some_and(|mk| mk.ignore) { + .filter(|var| { + if var.markers.visit.as_ref().is_some_and(|mk| mk.ignore) { // We are ignoring some variants so the match is no longer exhaustive. non_exhaustive = true; false @@ -366,22 +357,25 @@ impl<'a> VisitBuilder<'a> { true } }) - .filter_map(|(it, markers)| { - let typ = it + .filter_map(|var| { + let typ = var .fields .iter() .exactly_one() - .map(|f| &f.ty) + .map(|f| &f.typ) .map_err(|_| "We only support visited enum nodes with exactly one field!") .unwrap(); - let variant_name = &it.ident; - let typ = self.ctx.find(&typ.get_ident().inner_ident().to_string())?; - let borrowed = typ.borrow(); - let visitable = borrowed.visitable(); + let variant_name = &var.ident(); + let type_id = typ.transparent_type_id()?; + let def = self.ctx.type_def(type_id)?; + let visitable = def.visitable(); if visitable { - let visit = self.get_visitor(&typ, false, None); - let (args_def, args) = markers - .map(|mk| mk.visit_args.unwrap_or_default()) + let visit = self.get_visitor(def, false, None); + let (args_def, args) = var + .markers + .visit + .as_ref() + .map(|mk| mk.visit_args.clone().unwrap_or_default()) .into_iter() .flatten() .fold((Vec::new(), Vec::new()), Self::visit_args_fold); @@ -402,17 +396,17 @@ impl<'a> VisitBuilder<'a> { }) .collect_vec(); - let inherit_matches = enum_.meta.inherits.iter().filter_map(|it| { - let Inherit::Linked { super_, .. } = it else { panic!("Unresolved inheritance!") }; - let type_name = super_.get_ident().as_ident().unwrap().to_string(); - let typ = self.ctx.find(&type_name)?; - if typ.borrow().visitable() { + let inherit_matches = enum_.inherits.iter().filter_map(|it| { + let super_ = &it.super_; + let type_name = super_.name().as_name().unwrap().to_string(); + let def = super_.type_id().and_then(|id| self.ctx.type_def(id))?; + if def.visitable() { let snake_name = type_name.to_case(Case::Snake); let match_macro = format_ident!("match_{snake_name}"); let match_macro = quote!(#match_macro!(#ident)); // HACK: edge case till we get attributes to work with inheritance. let visit_as = if ident == "ArrayExpressionElement" - && super_.get_ident().inner_ident() == "Expression" + && super_.name().inner_name() == "Expression" { Some(format_ident!("ExpressionArrayElement")) } else { @@ -423,7 +417,7 @@ impl<'a> VisitBuilder<'a> { } else { format_ident!("to_{snake_name}") }; - let visit = self.get_visitor(&typ, false, visit_as.as_ref()); + let visit = self.get_visitor(def, false, visit_as); Some(quote!(#match_macro => visitor.#visit(it.#to_child()))) } else { None @@ -437,7 +431,7 @@ impl<'a> VisitBuilder<'a> { if KIND_BLACK_LIST.contains(&ident.to_string().as_str()) { tk } else { - let kind = self.kind_type(ident); + let kind = self.kind_type(&ident); quote! { let kind = #kind; visitor.enter_node(kind); @@ -456,15 +450,13 @@ impl<'a> VisitBuilder<'a> { fn generate_struct_walk( &mut self, - struct_: &RStruct, - visit_as: Option<&Ident>, + struct_: &StructDef, + visit_as: Option, ) -> (TokenStream, /* inline */ bool) { let ident = visit_as.unwrap_or_else(|| struct_.ident()); - let scope_events = get_scope_attr(struct_.item.attrs.iter()) - .transpose() - .unwrap() - .map_or_else(Default::default, |scope_args| { - let cond = scope_args.r#if.map(|cond| { + let scope_events = + struct_.markers.scope.as_ref().map_or_else(Default::default, |markers| { + let cond = markers.r#if.as_ref().map(|cond| { let cond = cond.to_token_stream().replace_ident("self", &format_ident!("it")); quote!(let scope_events_cond = #cond;) }); @@ -479,10 +471,11 @@ impl<'a> VisitBuilder<'a> { tk } }; - let flags = scope_args + let flags = markers .flags - .map_or_else(|| quote!(ScopeFlags::empty()), |it| it.to_token_stream()); - let flags = if let Some(strict_if) = scope_args.strict_if { + .as_ref() + .map_or_else(|| quote!(ScopeFlags::empty()), ToTokens::to_token_stream); + let flags = if let Some(strict_if) = &markers.strict_if { let strict_if = strict_if.to_token_stream().replace_ident("self", &format_ident!("it")); quote! {{ @@ -510,7 +503,7 @@ impl<'a> VisitBuilder<'a> { TokenStream::default(), ) } else { - let kind = self.kind_type(ident); + let kind = self.kind_type(&ident); ( quote! { let kind = #kind; @@ -523,39 +516,41 @@ impl<'a> VisitBuilder<'a> { let mut enter_scope_at = 0; let mut enter_node_at = 0; let fields_visits: Vec = struct_ - .item .fields .iter() .enumerate() - .map(|(ix, it)| (ix, it, get_visit_markers(&it.attrs).transpose())) - .filter_map(|(ix, it, markers)| { - let ty_res = it.ty.analyze(self.ctx); - let typ = ty_res.type_ref?; - if !typ.borrow().visitable() { + .filter_map(|(ix, field)| { + let analysis = field.typ.analysis(); + let def = field.typ.transparent_type_id().and_then(|id| self.ctx.type_def(id))?; + if !def.visitable() { return None; } - let typ_wrapper = ty_res.wrapper; - let markers = markers.unwrap(); - let visit_as = markers.as_ref().and_then(|mk| mk.visit_as.clone()); - let visit_args = markers.and_then(|mk| mk.visit_args); - - let have_enter_scope = get_scope_markers(&it.attrs) - .is_some_and(|it| matches!(it, Ok(ScopeMarkers { enter_before: true }))); - let have_enter_node = get_visit_markers(&it.attrs) - .is_some_and(|it| matches!(it, Ok(VisitMarkers { enter_before: true, .. }))); + let typ_wrapper = &analysis.wrapper; + let markers = &field.markers; + let visit_as = markers.visit.as_ref().and_then(|mk| mk.visit_as.clone()); + let visit_args = markers.visit.as_ref().and_then(|mk| mk.visit_args.clone()); + + let have_enter_scope = markers + .scope + .as_ref() + .is_some_and(|it| matches!(it, ScopeMarkers { enter_before: true })); + let have_enter_node = markers + .visit + .as_ref() + .is_some_and(|it| matches!(it, VisitMarkers { enter_before: true, .. })); let (args_def, args) = visit_args .map(|it| it.into_iter().fold((Vec::new(), Vec::new()), Self::visit_args_fold)) .unwrap_or_default(); let visit = self.get_visitor( - &typ, + def, matches!( typ_wrapper, TypeWrapper::Vec | TypeWrapper::VecBox | TypeWrapper::OptVec ), - visit_as.as_ref(), + visit_as, ); - let name = it.ident.as_ref().expect("expected named fields!"); + let name = field.ident().expect("expected named fields!"); let borrowed_field = self.with_ref_pat(quote!(it.#name)); let mut result = match typ_wrapper { TypeWrapper::Opt | TypeWrapper::OptBox | TypeWrapper::OptVec => quote! { diff --git a/tasks/ast_codegen/src/main.rs b/tasks/ast_codegen/src/main.rs index 997a0c3b3b155..f2049b1dd6ed0 100644 --- a/tasks/ast_codegen/src/main.rs +++ b/tasks/ast_codegen/src/main.rs @@ -2,12 +2,12 @@ const AST_CRATE: &str = "crates/oxc_ast"; #[allow(dead_code)] const AST_MACROS_CRATE: &str = "crates/oxc_ast_macros"; -mod defs; mod fmt; mod generators; mod layout; mod markers; mod passes; +mod rust_ast; mod schema; mod util; @@ -16,31 +16,31 @@ use std::{cell::RefCell, collections::HashMap, io::Read, path::PathBuf, rc::Rc}; use bpaf::{Bpaf, Parser}; use fmt::{cargo_fmt, pprint}; use itertools::Itertools; -use passes::{BuildSchema, CalcLayout, Linker, Pass}; +use passes::{CalcLayout, Linker, Pass}; use proc_macro2::TokenStream; use syn::parse_file; -use defs::TypeDef; use generators::{ AssertLayouts, AstBuilderGenerator, AstKindGenerator, Generator, VisitGenerator, VisitMutGenerator, }; -use schema::{Module, REnum, RStruct, RType, Schema}; +use rust_ast::AstRef; +use schema::{lower_ast_types, Schema, TypeDef}; use util::{write_all_to, NormalizeError}; use crate::generators::ImplGetSpanGenerator; type Result = std::result::Result; type TypeId = usize; -type TypeName = String; -type TypeTable = Vec; -type IdentTable = HashMap; -type TypeRef = Rc>; +type TypeTable = Vec; +type DefTable = Vec; +type IdentTable = HashMap; #[derive(Default)] struct AstCodegen { files: Vec, - runners: Vec>, + passes: Vec>>, + builders: Vec>>, } type GeneratedStream = (/* output path */ PathBuf, TokenStream); @@ -116,55 +116,71 @@ impl GeneratorOutput { } } -struct CodegenCtx { +struct EarlyCtx { ty_table: TypeTable, ident_table: IdentTable, - schema: RefCell, - mods: RefCell>, + mods: RefCell>, } -struct CodegenResult { - schema: Schema, - outputs: Vec<(/* generator name */ &'static str, /* output */ GeneratorOutput)>, -} - -impl CodegenCtx { - fn new(mods: Vec) -> Self { +impl EarlyCtx { + fn new(mods: Vec) -> Self { // worst case len let len = mods.iter().fold(0, |acc, it| acc + it.items.len()); - let defs = mods.iter().flat_map(|it| it.items.iter()); + let adts = mods.iter().flat_map(|it| it.items.iter()); let mut ty_table = TypeTable::with_capacity(len); let mut ident_table = IdentTable::with_capacity(len); - for def in defs { - if let Some(ident) = def.borrow().ident() { + for adt in adts { + if let Some(ident) = adt.borrow().ident() { let ident = ident.to_string(); let type_id = ty_table.len(); - ty_table.push(TypeRef::clone(def)); + ty_table.push(AstRef::clone(adt)); ident_table.insert(ident, type_id); } } - Self { - ty_table, - ident_table, - schema: RefCell::new(Schema::default()), - mods: RefCell::new(mods), - } + Self { ty_table, ident_table, mods: RefCell::new(mods) } + } + + fn into_late_ctx(self) -> LateCtx { + let schema = lower_ast_types(&self); + + LateCtx { schema } + } + + fn find(&self, key: &String) -> Option { + self.type_id(key).map(|id| AstRef::clone(&self.ty_table[id])) } - fn find(&self, key: &TypeName) -> Option { - self.type_id(key).map(|id| TypeRef::clone(&self.ty_table[*id])) + fn type_id(&self, key: &String) -> Option { + self.ident_table.get(key).copied() } - fn type_id<'b>(&'b self, key: &'b TypeName) -> Option<&'b TypeId> { - self.ident_table.get(key) + fn ast_ref(&self, id: TypeId) -> AstRef { + AstRef::clone(&self.ty_table[id]) + } +} + +struct LateCtx { + schema: Schema, +} + +struct CodegenResult { + schema: Schema, + outputs: Vec<(/* generator name */ &'static str, /* output */ GeneratorOutput)>, +} + +impl LateCtx { + fn type_def(&self, id: TypeId) -> Option<&TypeDef> { + self.schema.definitions.get(id) } } trait Runner { + type Context; + type Output; fn name(&self) -> &'static str; - fn run(&mut self, ctx: &CodegenCtx) -> Result; + fn run(&mut self, ctx: &Self::Context) -> Result; } impl AstCodegen { @@ -180,18 +196,18 @@ impl AstCodegen { #[must_use] fn pass

(mut self, pass: P) -> Self where - P: Pass + Runner + 'static, + P: Pass + Runner + 'static, { - self.runners.push(Box::new(pass)); + self.passes.push(Box::new(pass)); self } #[must_use] fn gen(mut self, generator: G) -> Self where - G: Generator + Runner + 'static, + G: Generator + Runner + 'static, { - self.runners.push(Box::new(generator)); + self.builders.push(Box::new(generator)); self } @@ -199,22 +215,31 @@ impl AstCodegen { let modules = self .files .into_iter() - .map(Module::from) - .map(Module::load) - .map_ok(Module::expand) + .map(rust_ast::Module::from) + .map(rust_ast::Module::load) + .map_ok(rust_ast::Module::expand) .flatten() - .map_ok(Module::analyze) + .map_ok(rust_ast::Module::analyze) .collect::>>>()??; - let ctx = CodegenCtx::new(modules); + // early passes + let ctx = { + let ctx = EarlyCtx::new(modules); + _ = self + .passes + .into_iter() + .map(|mut runner| runner.run(&ctx).map(|res| (runner.name(), res))) + .collect::>>()?; + ctx.into_late_ctx() + }; let outputs = self - .runners + .builders .into_iter() .map(|mut runner| runner.run(&ctx).map(|res| (runner.name(), res))) .collect::>>()?; - Ok(CodegenResult { outputs, schema: ctx.schema.into_inner() }) + Ok(CodegenResult { outputs, schema: ctx.schema }) } } @@ -263,7 +288,6 @@ fn main() -> std::result::Result<(), Box> { .fold(AstCodegen::default(), AstCodegen::add_file) .pass(Linker) .pass(CalcLayout) - .pass(BuildSchema) .gen(AssertLayouts) .gen(AstKindGenerator) .gen(AstBuilderGenerator) diff --git a/tasks/ast_codegen/src/markers.rs b/tasks/ast_codegen/src/markers.rs index 31995ae7af928..719b6d3fe402e 100644 --- a/tasks/ast_codegen/src/markers.rs +++ b/tasks/ast_codegen/src/markers.rs @@ -12,7 +12,7 @@ use syn::{ use crate::util::NormalizeError; /// A single visit argument passed via `#[visit(args(...))]` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VisitArg { pub ident: Ident, pub value: Expr, @@ -33,7 +33,7 @@ impl Parse for VisitArg { /// A struct containing `#[visit(args(...))]` items /// ^^^^^^^^^ -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct VisitArgs(Punctuated); impl IntoIterator for VisitArgs { @@ -51,7 +51,7 @@ impl Parse for VisitArgs { } /// A struct representing `#[visit(...)]` markers -#[derive(Debug)] +#[derive(Default, Debug)] pub struct VisitMarkers { pub visit_as: Option, pub visit_args: Option, @@ -60,6 +60,7 @@ pub struct VisitMarkers { } /// A struct representing `#[scope(...)]` markers +#[derive(Debug)] pub struct ScopeMarkers { pub enter_before: bool, } diff --git a/tasks/ast_codegen/src/passes/build_schema.rs b/tasks/ast_codegen/src/passes/build_schema.rs deleted file mode 100644 index 0b62e61d0e383..0000000000000 --- a/tasks/ast_codegen/src/passes/build_schema.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::{define_pass, Pass}; - -define_pass! { - pub struct BuildSchema; -} - -impl Pass for BuildSchema { - fn name(&self) -> &'static str { - stringify!(BuildSchema) - } - - fn once(&mut self, ctx: &crate::CodegenCtx) -> crate::Result { - for m in ctx.mods.borrow_mut().iter_mut() { - m.build_in(&mut ctx.schema.borrow_mut())?; - } - Ok(true) - } -} diff --git a/tasks/ast_codegen/src/passes/calc_layout.rs b/tasks/ast_codegen/src/passes/calc_layout.rs index f9ccfaa298f37..d772eb76f3cd1 100644 --- a/tasks/ast_codegen/src/passes/calc_layout.rs +++ b/tasks/ast_codegen/src/passes/calc_layout.rs @@ -10,9 +10,9 @@ use syn::Type; use crate::{ layout::{KnownLayout, Layout}, - schema::{REnum, RStruct, RType}, - util::{NormalizeError, TypeAnalyzeResult, TypeExt, TypeWrapper}, - CodegenCtx, Result, TypeRef, + rust_ast::{AstRef, AstType, Enum, Struct}, + util::{NormalizeError, TypeAnalysis, TypeExt, TypeWrapper}, + EarlyCtx, Result, }; use super::{define_pass, Pass}; @@ -28,7 +28,7 @@ impl Pass for CalcLayout { stringify!(CalcLayout) } - fn each(&mut self, ty: &mut RType, ctx: &CodegenCtx) -> crate::Result { + fn each(&mut self, ty: &mut AstType, ctx: &EarlyCtx) -> crate::Result { calc_layout(ty, ctx) } } @@ -65,14 +65,14 @@ impl From<(Layout, Layout)> for PlatformLayout { /// Calculates the layout of `ty` by mutating it. /// Returns `false` if the layout is unknown at this point. -pub fn calc_layout(ty: &mut RType, ctx: &CodegenCtx) -> Result { +pub fn calc_layout(ty: &mut AstType, ctx: &EarlyCtx) -> Result { let unknown_layout = ty .layout_32() .and_then(|x32| ty.layout_64().map(|x64| PlatformLayout(x64, x32))) .is_ok_and(|pl| pl.is_unknown()); let layout = match ty { - RType::Enum(enum_) if unknown_layout => calc_enum_layout(enum_, ctx), - RType::Struct(struct_) if unknown_layout => calc_struct_layout(struct_, ctx), + AstType::Enum(enum_) if unknown_layout => calc_enum_layout(enum_, ctx), + AstType::Struct(struct_) if unknown_layout => calc_struct_layout(struct_, ctx), _ => return Ok(true), }?; if layout.is_unknown() { @@ -84,8 +84,8 @@ pub fn calc_layout(ty: &mut RType, ctx: &CodegenCtx) -> Result { } } -fn calc_enum_layout(ty: &mut REnum, ctx: &CodegenCtx) -> Result { - fn collect_variant_layouts(ty: &REnum, ctx: &CodegenCtx) -> Result> { +fn calc_enum_layout(ty: &mut Enum, ctx: &EarlyCtx) -> Result { + fn collect_variant_layouts(ty: &Enum, ctx: &EarlyCtx) -> Result> { // all unit variants? if ty.item.variants.iter().all(|var| var.fields.is_empty()) { // all AST enums are `repr(u8)` so it would have a 1 byte layout/alignment, @@ -145,8 +145,8 @@ fn calc_enum_layout(ty: &mut REnum, ctx: &CodegenCtx) -> Result Ok(PlatformLayout(Layout::from(x64), Layout::from(x32))) } -fn calc_struct_layout(ty: &mut RStruct, ctx: &CodegenCtx) -> Result { - fn collect_field_layouts(ty: &RStruct, ctx: &CodegenCtx) -> Result> { +fn calc_struct_layout(ty: &mut Struct, ctx: &EarlyCtx) -> Result { + fn collect_field_layouts(ty: &Struct, ctx: &EarlyCtx) -> Result> { if ty.item.fields.is_empty() { Ok(vec![PlatformLayout::zero()]) } else { @@ -199,8 +199,8 @@ fn calc_struct_layout(ty: &mut RStruct, ctx: &CodegenCtx) -> Result Result { - fn is_slice(ty: &TypeAnalyzeResult) -> bool { +fn calc_type_layout(ty: &TypeAnalysis, ctx: &EarlyCtx) -> Result { + fn is_slice(ty: &TypeAnalysis) -> bool { if let Type::Reference(typ) = &ty.typ { // TODO: support for &[T] slices. typ.elem.get_ident().as_ident().is_some_and(|id| id == "str") @@ -216,10 +216,10 @@ fn calc_type_layout(ty: &TypeAnalyzeResult, ctx: &CodegenCtx) -> Result| -> Result { - let result = if let Some(type_ref) = &type_ref { - if calc_layout(&mut type_ref.borrow_mut(), ctx)? { - type_ref.borrow().layouts().map(PlatformLayout::from)? + let get_layout = |ast_ref: Option<&AstRef>| -> Result { + let result = if let Some(ast_ref) = &ast_ref { + if calc_layout(&mut ast_ref.borrow_mut(), ctx)? { + ast_ref.borrow().layouts().map(PlatformLayout::from)? } else { PlatformLayout::UNKNOWN } @@ -265,13 +265,15 @@ fn calc_type_layout(ty: &TypeAnalyzeResult, ctx: &CodegenCtx) -> Result PlatformLayout::wide_ptr(), TypeWrapper::Ref | TypeWrapper::Box | TypeWrapper::OptBox => PlatformLayout::ptr(), - TypeWrapper::None => get_layout(ty.type_ref.as_ref())?, + TypeWrapper::None => get_layout(ty.type_id.map(|id| ctx.ast_ref(id)).as_ref())?, TypeWrapper::Opt => { - let PlatformLayout(x64, x32) = get_layout(ty.type_ref.as_ref())?; + let PlatformLayout(x64, x32) = + get_layout(ty.type_id.map(|id| ctx.ast_ref(id)).as_ref())?; PlatformLayout(try_fold_option(x64), try_fold_option(x32)) } TypeWrapper::Complex => { - let PlatformLayout(x64, x32) = get_layout(ty.type_ref.as_ref())?; + let PlatformLayout(x64, x32) = + get_layout(ty.type_id.map(|id| ctx.ast_ref(id)).as_ref())?; PlatformLayout(x64, x32) } }; diff --git a/tasks/ast_codegen/src/passes/linker.rs b/tasks/ast_codegen/src/passes/linker.rs index 480bb8edc0943..860e1d8b60903 100644 --- a/tasks/ast_codegen/src/passes/linker.rs +++ b/tasks/ast_codegen/src/passes/linker.rs @@ -2,9 +2,9 @@ use std::borrow::Cow; use syn::parse_quote; -use crate::{schema::Inherit, util::NormalizeError}; +use crate::{rust_ast::Inherit, util::NormalizeError, EarlyCtx}; -use super::{define_pass, CodegenCtx, Pass, RType, Result}; +use super::{define_pass, AstType, Pass, Result}; pub trait Unresolved { fn unresolved(&self) -> bool; @@ -39,9 +39,9 @@ impl Pass for Linker { /// # Panics /// On invalid inheritance. - fn each(&mut self, ty: &mut RType, ctx: &CodegenCtx) -> crate::Result { + fn each(&mut self, ty: &mut AstType, ctx: &EarlyCtx) -> crate::Result { // Exit early if it isn't an enum, We only link to resolve enum inheritance! - let RType::Enum(ty) = ty else { + let AstType::Enum(ty) = ty else { return Ok(true); }; @@ -62,7 +62,7 @@ impl Pass for Linker { let linkee = linkee.borrow(); let inherit_value = format!(r#""{}""#, linkee.ident().unwrap()); let variants = match &*linkee { - RType::Enum(enum_) => { + AstType::Enum(enum_) => { if enum_.meta.inherits.unresolved() { return Ok(Err(it)); } diff --git a/tasks/ast_codegen/src/passes/mod.rs b/tasks/ast_codegen/src/passes/mod.rs index 2a149382fb8be..972a075eb8d0e 100644 --- a/tasks/ast_codegen/src/passes/mod.rs +++ b/tasks/ast_codegen/src/passes/mod.rs @@ -1,4 +1,3 @@ -mod build_schema; mod calc_layout; mod linker; @@ -6,9 +5,8 @@ use std::collections::VecDeque; use itertools::Itertools; -use crate::{schema::RType, CodegenCtx, Result}; +use crate::{rust_ast::AstType, EarlyCtx, Result}; -pub use build_schema::BuildSchema; pub use calc_layout::CalcLayout; pub use linker::Linker; @@ -16,16 +14,16 @@ pub trait Pass { fn name(&self) -> &'static str; /// Returns false if can't resolve - fn once(&mut self, _ctx: &CodegenCtx) -> Result { + fn once(&mut self, _ctx: &EarlyCtx) -> Result { Ok(true) } /// Returns false if can't resolve - fn each(&mut self, _ty: &mut RType, _ctx: &CodegenCtx) -> Result { + fn each(&mut self, _ty: &mut AstType, _ctx: &EarlyCtx) -> Result { Ok(true) } - fn call(&mut self, ctx: &CodegenCtx) -> Result { + fn call(&mut self, ctx: &EarlyCtx) -> Result { // call once if !self.once(ctx)? { return Ok(false); @@ -37,7 +35,7 @@ pub trait Pass { ctx.ident_table.iter().sorted_by_key(|it| it.1).map(|it| it.0).collect::>(); while let Some(next) = unresolved.pop_back() { - let next_id = *ctx.type_id(next).unwrap(); + let next_id = ctx.type_id(next).unwrap(); let val = &mut ctx.ty_table[next_id].borrow_mut(); @@ -53,13 +51,14 @@ macro_rules! define_pass { ($vis:vis struct $ident:ident $($lifetime:lifetime)? $($rest:tt)*) => { $vis struct $ident $($lifetime)? $($rest)* impl $($lifetime)? $crate::Runner for $ident $($lifetime)? { + type Context = $crate::EarlyCtx; + type Output = (); fn name(&self) -> &'static str { $crate::Pass::name(self) } - fn run(&mut self, ctx: &$crate::CodegenCtx) -> $crate::Result<$crate::GeneratorOutput> { - self.call(ctx)?; - Ok($crate::GeneratorOutput::None) + fn run(&mut self, ctx: &Self::Context) -> $crate::Result { + self.call(ctx).map(|_| ()) } } }; diff --git a/tasks/ast_codegen/src/schema.rs b/tasks/ast_codegen/src/rust_ast.rs similarity index 74% rename from tasks/ast_codegen/src/schema.rs rename to tasks/ast_codegen/src/rust_ast.rs index 85a2466cc9e50..6778cf0c88c29 100644 --- a/tasks/ast_codegen/src/schema.rs +++ b/tasks/ast_codegen/src/rust_ast.rs @@ -5,18 +5,18 @@ use syn::{ parse::{Parse, ParseBuffer}, parse_quote, punctuated::Punctuated, - Attribute, Generics, Ident, Item, ItemConst, ItemEnum, ItemMacro, ItemStruct, ItemUse, Meta, - Path, Token, Type, Variant, Visibility, + Attribute, Generics, Ident, Item, ItemEnum, ItemMacro, ItemStruct, Meta, Path, Token, Type, + Variant, Visibility, }; -use crate::{layout::Layout, util::NormalizeError, TypeName}; +use crate::{ + layout::Layout, + util::{unexpanded_macro_err, NormalizeError}, +}; -use super::{parse_file, Itertools, PathBuf, Rc, Read, RefCell, Result, TypeDef, TypeRef}; +use super::{parse_file, Itertools, PathBuf, Rc, Read, RefCell, Result}; -#[derive(Debug, Default, serde::Serialize)] -pub struct Schema { - pub definitions: Vec, -} +pub type AstRef = Rc>; #[derive(Debug, Clone)] pub enum Inherit { @@ -40,12 +40,12 @@ pub struct EnumMeta { } #[derive(Debug)] -pub struct REnum { +pub struct Enum { pub item: ItemEnum, pub meta: EnumMeta, } -impl REnum { +impl Enum { pub fn with_meta(item: ItemEnum, meta: EnumMeta) -> Self { Self { item, meta } } @@ -61,7 +61,7 @@ impl REnum { } } -impl From for REnum { +impl From for Enum { fn from(item: ItemEnum) -> Self { Self { item, meta: EnumMeta::default() } } @@ -77,12 +77,12 @@ pub struct StructMeta { } #[derive(Debug)] -pub struct RStruct { +pub struct Struct { pub item: ItemStruct, pub meta: StructMeta, } -impl RStruct { +impl Struct { pub fn ident(&self) -> &Ident { &self.item.ident } @@ -94,60 +94,54 @@ impl RStruct { } } -impl From for RStruct { +impl From for Struct { fn from(item: ItemStruct) -> Self { Self { item, meta: StructMeta::default() } } } #[derive(Debug)] -pub enum RType { - Enum(REnum), - Struct(RStruct), +pub enum AstType { + Enum(Enum), + Struct(Struct), - Use(ItemUse), - Const(ItemConst), + // we need this to expand `inherit` macro calls. Macro(ItemMacro), } -impl ToTokens for RType { +impl ToTokens for AstType { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Enum(it) => it.item.to_tokens(tokens), Self::Struct(it) => it.item.to_tokens(tokens), - Self::Use(it) => it.to_tokens(tokens), - Self::Const(it) => it.to_tokens(tokens), Self::Macro(it) => it.to_tokens(tokens), } } } -impl RType { +impl AstType { pub fn ident(&self) -> Option<&Ident> { match self { - RType::Enum(ty) => Some(ty.ident()), - RType::Struct(ty) => Some(ty.ident()), - - RType::Use(_) => None, - RType::Macro(tt) => tt.ident.as_ref(), - RType::Const(tt) => Some(&tt.ident), + AstType::Enum(ty) => Some(ty.ident()), + AstType::Struct(ty) => Some(ty.ident()), + AstType::Macro(tt) => tt.ident.as_ref(), } } pub fn as_type(&self) -> Option { match self { - RType::Enum(it) => Some(it.as_type()), - RType::Struct(it) => Some(it.as_type()), - _ => None, + AstType::Enum(it) => Some(it.as_type()), + AstType::Struct(it) => Some(it.as_type()), + AstType::Macro(_) => None, } } pub fn visitable(&self) -> bool { match self { - RType::Enum(it) => it.meta.visitable, - RType::Struct(it) => it.meta.visitable, - _ => false, + AstType::Enum(it) => it.meta.visitable, + AstType::Struct(it) => it.meta.visitable, + AstType::Macro(_) => false, } } @@ -159,35 +153,35 @@ impl RType { }}; } match self { - RType::Enum(it) => assign!(it), - RType::Struct(it) => assign!(it), - _ => return Err("Unsupported type!".to_string()), + AstType::Enum(it) => assign!(it), + AstType::Struct(it) => assign!(it), + AstType::Macro(it) => return Err(unexpanded_macro_err(it)), } Ok(()) } pub fn set_ast(&mut self, value: bool) -> Result<()> { match self { - RType::Enum(it) => it.meta.ast = value, - RType::Struct(it) => it.meta.ast = value, - _ => return Err("Unsupported type!".to_string()), + AstType::Enum(it) => it.meta.ast = value, + AstType::Struct(it) => it.meta.ast = value, + AstType::Macro(it) => return Err(unexpanded_macro_err(it)), } Ok(()) } pub fn layout_32(&self) -> Result { match self { - RType::Enum(it) => Ok(it.meta.layout_32.clone()), - RType::Struct(it) => Ok(it.meta.layout_32.clone()), - _ => Err("Unsupported type!".to_string()), + AstType::Enum(it) => Ok(it.meta.layout_32.clone()), + AstType::Struct(it) => Ok(it.meta.layout_32.clone()), + AstType::Macro(it) => Err(unexpanded_macro_err(it)), } } pub fn layout_64(&self) -> Result { match self { - RType::Enum(it) => Ok(it.meta.layout_64.clone()), - RType::Struct(it) => Ok(it.meta.layout_64.clone()), - _ => Err("Unsupported type!".to_string()), + AstType::Enum(it) => Ok(it.meta.layout_64.clone()), + AstType::Struct(it) => Ok(it.meta.layout_64.clone()), + AstType::Macro(it) => Err(unexpanded_macro_err(it)), } } @@ -203,23 +197,21 @@ impl RType { }}; } match self { - RType::Enum(it) => assign!(it), - RType::Struct(it) => assign!(it), - _ => return Err("Unsupported type!".to_string()), + AstType::Enum(it) => assign!(it), + AstType::Struct(it) => assign!(it), + AstType::Macro(it) => return Err(unexpanded_macro_err(it)), } Ok(()) } } -impl TryFrom for RType { +impl TryFrom for AstType { type Error = String; fn try_from(item: Item) -> Result { match item { - Item::Enum(it) => Ok(RType::Enum(it.into())), - Item::Struct(it) => Ok(RType::Struct(it.into())), - Item::Macro(it) => Ok(RType::Macro(it)), - Item::Use(it) => Ok(RType::Use(it)), - Item::Const(it) => Ok(RType::Const(it)), + Item::Enum(it) => Ok(AstType::Enum(it.into())), + Item::Struct(it) => Ok(AstType::Struct(it.into())), + Item::Macro(it) => Ok(AstType::Macro(it)), _ => Err(String::from("Unsupported Item!")), } } @@ -232,10 +224,10 @@ pub struct Module { // TODO: remove me #[allow(dead_code)] #[allow(clippy::struct_field_names)] - pub module: TypeName, + pub module: String, pub shebang: Option, pub attrs: Vec, - pub items: Vec, + pub items: Vec, pub loaded: bool, } @@ -265,7 +257,7 @@ impl Module { .items .into_iter() .filter(|it| match it { - Item::Enum(_) | Item::Struct(_) | Item::Use(_) | Item::Const(_) => true, + Item::Enum(_) | Item::Struct(_) => true, // These contain enums with inheritance Item::Macro(m) if m.mac.path.is_ident("inherit_variants") => true, _ => false, @@ -297,20 +289,11 @@ impl Module { self.items.iter().try_for_each(analyze)?; Ok(self) } - - pub fn build_in(&self, schema: &mut Schema) -> Result<()> { - if !self.loaded { - return Err(String::from(LOAD_ERROR)); - } - - schema.definitions.extend(self.items.iter().filter_map(|it| (&*it.borrow()).into())); - Ok(()) - } } -pub fn expand(type_def: &TypeRef) -> Result<()> { - let to_replace = match &*type_def.borrow() { - RType::Macro(mac) => { +pub fn expand(ast_ref: &AstRef) -> Result<()> { + let to_replace = match &*ast_ref.borrow() { + AstType::Macro(mac) => { let (enum_, inherits) = mac .mac .parse_body_with(|input: &ParseBuffer| { @@ -361,7 +344,7 @@ pub fn expand(type_def: &TypeRef) -> Result<()> { )) }) .normalize()?; - Some(RType::Enum(REnum::with_meta( + Some(AstType::Enum(Enum::with_meta( enum_, EnumMeta { inherits: inherits.into_iter().map(Into::into).collect(), @@ -373,21 +356,21 @@ pub fn expand(type_def: &TypeRef) -> Result<()> { }; if let Some(to_replace) = to_replace { - *type_def.borrow_mut() = to_replace; + *ast_ref.borrow_mut() = to_replace; } Ok(()) } -pub fn analyze(type_def: &TypeRef) -> Result<()> { +pub fn analyze(ast_ref: &AstRef) -> Result<()> { enum AstAttr { None, Mark, Visit, } - let ast_attr = match &*type_def.borrow() { - RType::Enum(REnum { item: ItemEnum { attrs, .. }, .. }) - | RType::Struct(RStruct { item: ItemStruct { attrs, .. }, .. }) => { + let ast_attr = match &*ast_ref.borrow() { + AstType::Enum(Enum { item: ItemEnum { attrs, .. }, .. }) + | AstType::Struct(Struct { item: ItemStruct { attrs, .. }, .. }) => { let attr = attrs.iter().find(|attr| attr.path().is_ident("ast")); let attr = match attr { Some(Attribute { meta: Meta::Path(_), .. }) => AstAttr::Mark, @@ -405,18 +388,18 @@ pub fn analyze(type_def: &TypeRef) -> Result<()> { }; Some(attr) } - _ => None, + AstType::Macro(_) => None, }; #[allow(clippy::match_same_arms)] match ast_attr { Some(AstAttr::Visit) => { - type_def.borrow_mut().set_ast(true)?; - type_def.borrow_mut().set_visitable(true)?; + ast_ref.borrow_mut().set_ast(true)?; + ast_ref.borrow_mut().set_visitable(true)?; } Some(AstAttr::Mark) => { // AST without visit! - type_def.borrow_mut().set_ast(true)?; + ast_ref.borrow_mut().set_ast(true)?; } Some(AstAttr::None) => return Err(String::from("All `enums` and `structs` defined in the source of truth should be marked with an `#[ast]` attribute!")), None => { /* unrelated items like `use`, `type` and `macro` definitions */ } diff --git a/tasks/ast_codegen/src/schema/defs.rs b/tasks/ast_codegen/src/schema/defs.rs new file mode 100644 index 0000000000000..35ca7e20a3f99 --- /dev/null +++ b/tasks/ast_codegen/src/schema/defs.rs @@ -0,0 +1,168 @@ +use serde::Serialize; + +use super::{with_either, TypeName}; +use crate::{ + markers::{ScopeAttr, ScopeMarkers, VisitMarkers}, + util::{ToIdent, TypeAnalysis, TypeWrapper}, + TypeId, +}; + +#[derive(Debug, Serialize)] +pub enum TypeDef { + Struct(StructDef), + Enum(EnumDef), +} + +impl TypeDef { + pub fn id(&self) -> TypeId { + with_either!(self, it => it.id) + } + + pub fn name(&self) -> &String { + with_either!(self, it => &it.name) + } + + pub fn visitable(&self) -> bool { + with_either!(self, it => it.visitable) + } +} + +#[derive(Debug, Serialize)] +pub struct StructDef { + pub id: TypeId, + pub name: String, + pub visitable: bool, + pub fields: Vec, + pub has_lifetime: bool, + pub size_64: usize, + pub align_64: usize, + pub offsets_64: Option>, + pub size_32: usize, + pub align_32: usize, + pub offsets_32: Option>, + #[serde(skip)] + pub markers: OuterMarkers, +} + +#[derive(Debug, Serialize)] +pub struct EnumDef { + pub id: TypeId, + pub name: String, + pub visitable: bool, + pub variants: Vec, + /// For `@inherits` inherited enum variants + pub inherits: Vec, + pub has_lifetime: bool, + pub size_64: usize, + pub align_64: usize, + pub offsets_64: Option>, + pub size_32: usize, + pub align_32: usize, + pub offsets_32: Option>, +} + +impl EnumDef { + /// Returns an iterator that would first walk all "real" variants and moves onto inherited ones + /// based on the inheritance order. + pub fn all_variants(&self) -> impl Iterator { + self.variants.iter().chain(self.inherits.iter().flat_map(|it| it.variants.iter())) + } +} + +#[derive(Debug, Serialize)] +pub struct VariantDef { + pub name: String, + pub fields: Vec, + pub discriminant: Option, + pub markers: InnerMarkers, +} + +impl VariantDef { + pub fn ident(&self) -> syn::Ident { + self.name.to_ident() + } +} + +#[derive(Debug, Serialize)] +pub struct InheritDef { + pub super_: TypeRef, + pub variants: Vec, +} + +#[derive(Debug, Serialize)] +pub struct FieldDef { + /// `None` if unnamed + pub name: Option, + pub typ: TypeRef, + pub markers: InnerMarkers, + pub docs: Vec, +} + +impl FieldDef { + pub fn ident(&self) -> Option { + self.name.as_ref().map(ToIdent::to_ident) + } +} + +#[derive(Debug, Serialize)] +pub struct TypeRef { + pub(super) id: Option, + pub(super) name: TypeName, + + #[serde(skip)] + pub(super) transparent_id: Option, + + #[serde(skip)] + pub(super) raw: String, + #[serde(skip)] + pub(super) analysis: TypeAnalysis, +} + +impl TypeRef { + /// It is `None` for foreign types. + #[inline] + pub fn type_id(&self) -> Option { + self.id + } + + /// Reflects the inner most type id of `Adt1>>` + #[inline] + pub fn transparent_type_id(&self) -> Option { + self.transparent_id + } + + /// Reflects the inner type id of `Box` + #[inline] + pub fn name(&self) -> &TypeName { + &self.name + } + + #[inline] + pub fn analysis(&self) -> &TypeAnalysis { + &self.analysis + } + + #[inline] + pub fn raw(&self) -> &str { + &self.raw + } + + pub fn is_str_slice(&self) -> bool { + matches!(self.analysis().wrapper, TypeWrapper::Ref if self.name.inner_name() == "str") + } +} + +#[derive(Debug)] +pub struct OuterMarkers { + pub scope: Option, +} + +#[derive(Debug, Serialize)] +pub struct InnerMarkers { + /// marker that hints to fold span in here + pub span: bool, + #[serde(skip)] + pub visit: Option, + #[serde(skip)] + pub scope: Option, +} diff --git a/tasks/ast_codegen/src/schema/get_generics.rs b/tasks/ast_codegen/src/schema/get_generics.rs new file mode 100644 index 0000000000000..11de9c075d22e --- /dev/null +++ b/tasks/ast_codegen/src/schema/get_generics.rs @@ -0,0 +1,38 @@ +use syn::parse_quote; + +use super::{ + defs::{EnumDef, StructDef, TypeDef}, + with_either, +}; + +pub trait GetGenerics { + fn has_lifetime(&self) -> bool { + false + } + + fn generics(&self) -> Option { + if self.has_lifetime() { + Some(parse_quote!(<'a>)) + } else { + None + } + } +} + +impl GetGenerics for TypeDef { + fn has_lifetime(&self) -> bool { + with_either!(self, it => it.has_lifetime()) + } +} + +impl GetGenerics for StructDef { + fn has_lifetime(&self) -> bool { + self.has_lifetime + } +} + +impl GetGenerics for EnumDef { + fn has_lifetime(&self) -> bool { + self.has_lifetime + } +} diff --git a/tasks/ast_codegen/src/schema/get_ident.rs b/tasks/ast_codegen/src/schema/get_ident.rs new file mode 100644 index 0000000000000..4b5ae5965d7c2 --- /dev/null +++ b/tasks/ast_codegen/src/schema/get_ident.rs @@ -0,0 +1,28 @@ +use crate::util::ToIdent; + +use super::{ + defs::{EnumDef, StructDef, TypeDef}, + with_either, +}; + +pub trait GetIdent { + fn ident(&self) -> syn::Ident; +} + +impl GetIdent for TypeDef { + fn ident(&self) -> syn::Ident { + with_either!(self, it => it.ident()) + } +} + +impl GetIdent for StructDef { + fn ident(&self) -> syn::Ident { + self.name.to_ident() + } +} + +impl GetIdent for EnumDef { + fn ident(&self) -> syn::Ident { + self.name.to_ident() + } +} diff --git a/tasks/ast_codegen/src/schema/mod.rs b/tasks/ast_codegen/src/schema/mod.rs new file mode 100644 index 0000000000000..fcbf4a0e5ba2f --- /dev/null +++ b/tasks/ast_codegen/src/schema/mod.rs @@ -0,0 +1,266 @@ +mod defs; +mod get_generics; +mod get_ident; +mod to_type; + +use crate::rust_ast as rust; +use crate::{ + layout::KnownLayout, + markers::{get_scope_attr, get_scope_markers, get_visit_markers}, + util::{unexpanded_macro_err, TypeExt}, + DefTable, Result, +}; +use quote::ToTokens; +use serde::Serialize; + +pub use defs::*; +pub use get_generics::GetGenerics; +pub use get_ident::GetIdent; +pub use to_type::ToType; + +#[derive(Debug, Serialize)] +pub enum TypeName { + Ident(String), + Vec(Box), + Box(Box), + Opt(Box), + Ref(Box), + /// We bailed on detecting wrapper + Complex(Box), +} + +impl TypeName { + pub fn inner_name(&self) -> &str { + match self { + Self::Ident(it) => it, + Self::Complex(it) | Self::Vec(it) | Self::Box(it) | Self::Opt(it) | Self::Ref(it) => { + it.inner_name() + } + } + } + + pub fn as_name(&self) -> Option<&str> { + if let Self::Ident(it) = self { + Some(it) + } else { + None + } + } + + /// First identifier of multi-part type + /// + /// `Adt` + /// ^^^ + /// + /// Panics + /// + /// When `self` is `TypeName::Ref` or `TypeName::Complex`. + /// + pub fn first_ident(&self) -> &str { + match self { + Self::Ident(it) => it.as_str(), + Self::Vec(_) => "Vec", + Self::Box(_) => "Box", + Self::Opt(_) => "Option", + Self::Ref(_) | Self::Complex(_) => panic!(), + } + } +} + +impl<'a> From> for TypeName { + fn from(it: crate::util::TypeIdentResult<'a>) -> Self { + use crate::util::TypeIdentResult; + match it { + TypeIdentResult::Ident(it) => Self::Ident(it.to_string()), + TypeIdentResult::Vec(it) => Self::Vec(Box::new(Self::from(*it))), + TypeIdentResult::Box(it) => Self::Box(Box::new(Self::from(*it))), + TypeIdentResult::Option(it) => Self::Opt(Box::new(Self::from(*it))), + TypeIdentResult::Reference(it) => Self::Ref(Box::new(Self::from(*it))), + TypeIdentResult::Complex(it) => Self::Complex(Box::new(Self::from(*it))), + } + } +} + +#[derive(Debug, Default, serde::Serialize)] +pub struct Schema { + pub definitions: DefTable, +} + +fn parse_outer_markers(attrs: &Vec) -> Result { + Ok(OuterMarkers { scope: get_scope_attr(attrs).transpose()? }) +} + +fn parse_inner_markers(attrs: &Vec) -> Result { + Ok(InnerMarkers { + span: attrs.iter().any(|a| a.path().is_ident("span")), + visit: get_visit_markers(attrs).transpose()?, + scope: get_scope_markers(attrs).transpose()?, + }) +} + +// lower `AstType` to `TypeDef`. +pub fn lower_ast_types(ctx: &crate::EarlyCtx) -> Schema { + let definitions = ctx + .mods + .borrow() + .iter() + .flat_map(|it| &it.items) + .map(|it| lower_ast_type(&it.borrow(), ctx)) + .collect(); + Schema { definitions } +} + +fn lower_ast_type(ty: &rust::AstType, ctx: &crate::EarlyCtx) -> TypeDef { + match ty { + rust::AstType::Enum(it) => TypeDef::Enum(lower_ast_enum(it, ctx)), + rust::AstType::Struct(it) => TypeDef::Struct(lower_ast_struct(it, ctx)), + rust::AstType::Macro(it) => panic!("{}", unexpanded_macro_err(it)), + } +} + +fn lower_ast_enum(it @ rust::Enum { item, meta }: &rust::Enum, ctx: &crate::EarlyCtx) -> EnumDef { + let (size_64, align_64, offsets_64) = meta + .layout_64 + .clone() + .layout() + .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); + let (size_32, align_32, offsets_32) = meta + .layout_32 + .clone() + .layout() + .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); + EnumDef { + id: ctx.type_id(&it.ident().to_string()).unwrap(), + name: it.ident().to_string(), + visitable: meta.visitable, + variants: item + .variants + .iter() + .filter(|it| !it.attrs.iter().any(|it| it.path().is_ident("inherit"))) + .map(|var| lower_variant(var, ctx)) + .collect(), + inherits: meta.inherits.iter().map(|it| lower_inherit(it, ctx)).collect(), + has_lifetime: item.generics.lifetimes().count() > 0, + + size_64, + align_64, + offsets_64, + size_32, + align_32, + offsets_32, + } +} + +fn lower_ast_struct( + it @ rust::Struct { item, meta }: &rust::Struct, + ctx: &crate::EarlyCtx, +) -> StructDef { + let (size_64, align_64, offsets_64) = meta + .layout_64 + .clone() + .layout() + .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); + let (size_32, align_32, offsets_32) = meta + .layout_32 + .clone() + .layout() + .map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack); + StructDef { + id: ctx.type_id(&it.ident().to_string()).unwrap(), + name: it.ident().to_string(), + visitable: meta.visitable, + fields: item.fields.iter().map(|fi| lower_field(fi, ctx)).collect(), + has_lifetime: item.generics.lifetimes().count() > 0, + size_64, + align_64, + offsets_64, + size_32, + align_32, + offsets_32, + markers: parse_outer_markers(&item.attrs).unwrap(), + } +} + +fn lower_variant(variant: &syn::Variant, ctx: &crate::EarlyCtx) -> VariantDef { + VariantDef { + name: variant.ident.to_string(), + discriminant: variant.discriminant.as_ref().map(|(_, disc)| match disc { + syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit), .. }) => { + lit.base10_parse().expect("invalid base10 enum discriminant") + } + _ => panic!("invalid enum discriminant"), + }), + fields: variant.fields.iter().map(|fi| lower_field(fi, ctx)).collect(), + markers: parse_inner_markers(&variant.attrs).unwrap(), + } +} + +fn lower_inherit(inherit: &rust::Inherit, ctx: &crate::EarlyCtx) -> InheritDef { + match inherit { + rust::Inherit::Linked { super_, variants } => InheritDef { + super_: create_type_ref(super_, ctx), + variants: variants.iter().map(|var| lower_variant(var, ctx)).collect(), + }, + rust::Inherit::Unlinked(_) => { + panic!("`Unlinked` inherits can't be converted to a valid `InheritDef`!") + } + } +} + +fn lower_field(field: &syn::Field, ctx: &crate::EarlyCtx) -> FieldDef { + FieldDef { + name: field.ident.as_ref().map(ToString::to_string), + typ: create_type_ref(&field.ty, ctx), + markers: parse_inner_markers(&field.attrs).unwrap(), + docs: get_docs(&field.attrs), + } +} + +fn create_type_ref(ty: &syn::Type, ctx: &crate::EarlyCtx) -> TypeRef { + let ident = ty.get_ident(); + let id = ident.as_ident().and_then(|id| ctx.type_id(&id.to_string())); + let transparent_id = ctx.type_id(&ident.inner_ident().to_string()); + let raw = ty.to_token_stream().to_string().replace(' ', ""); + TypeRef { + id, + transparent_id, + raw, + name: TypeName::from(ty.get_ident()), + analysis: ty.analyze(ctx), + } +} + +fn get_docs(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|attr| { + if let syn::Meta::NameValue(syn::MetaNameValue { + path, + value: syn::Expr::Lit(lit), + .. + }) = &attr.meta + { + if !path.is_ident("doc") { + return None; + } + match &lit.lit { + syn::Lit::Str(lit) => Some(lit.value()), + _ => None, + } + } else { + None + } + }) + .collect() +} + +macro_rules! with_either { + ($def:expr, $it:ident => $body:expr) => { + match $def { + TypeDef::Struct($it) => $body, + TypeDef::Enum($it) => $body, + } + }; +} + +use with_either; diff --git a/tasks/ast_codegen/src/schema/to_type.rs b/tasks/ast_codegen/src/schema/to_type.rs new file mode 100644 index 0000000000000..5cf18ba130a22 --- /dev/null +++ b/tasks/ast_codegen/src/schema/to_type.rs @@ -0,0 +1,58 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse_quote; + +use super::{ + defs::{EnumDef, StructDef, TypeDef, TypeRef}, + GetGenerics, GetIdent, +}; + +pub trait ToType { + fn to_type(&self) -> syn::Type; + fn to_type_elide(&self) -> syn::Type; + fn to_type_with_explicit_generics(&self, generics: TokenStream) -> syn::Type; +} + +impl ToType for TypeRef { + fn to_type(&self) -> syn::Type { + syn::parse_str(self.raw()).unwrap() + } + + fn to_type_elide(&self) -> syn::Type { + self.to_type_with_explicit_generics(proc_macro2::TokenStream::default()) + } + + fn to_type_with_explicit_generics(&self, generics: proc_macro2::TokenStream) -> syn::Type { + let ident = self.name().first_ident(); + parse_quote!(#ident #generics) + } +} + +auto_impl_to_type! { + TypeDef, + EnumDef, + StructDef, +} + +macro_rules! auto_impl_to_type { + ($($ty:ty,)+) => ( + $( + impl ToType for $ty { + fn to_type(&self) -> syn::Type { + self.to_type_with_explicit_generics(self.generics().to_token_stream()) + } + + fn to_type_elide(&self) -> syn::Type { + self.to_type_with_explicit_generics(TokenStream::default()) + } + + fn to_type_with_explicit_generics(&self, generics: TokenStream) -> syn::Type { + let name = self.ident(); + parse_quote!(#name #generics) + } + } + )+ + ) +} + +use auto_impl_to_type; diff --git a/tasks/ast_codegen/src/util.rs b/tasks/ast_codegen/src/util.rs index ed924a0762de5..8ebbde3efc229 100644 --- a/tasks/ast_codegen/src/util.rs +++ b/tasks/ast_codegen/src/util.rs @@ -1,9 +1,10 @@ use itertools::Itertools; use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::ToTokens; -use syn::{GenericArgument, Ident, PathArguments, Type, TypePath}; +use quote::{format_ident, ToTokens}; +use serde::Serialize; +use syn::{spanned::Spanned, GenericArgument, Ident, ItemMacro, PathArguments, Type, TypePath}; -use crate::{CodegenCtx, TypeRef}; +use crate::{EarlyCtx, TypeId}; pub trait NormalizeError { fn normalize(self) -> crate::Result; @@ -47,7 +48,7 @@ pub trait TokenStreamExt { pub trait TypeExt { fn get_ident(&self) -> TypeIdentResult; - fn analyze(&self, ctx: &CodegenCtx) -> TypeAnalyzeResult; + fn analyze(&self, ctx: &EarlyCtx) -> TypeAnalysis; } pub trait StrExt: AsRef { @@ -59,15 +60,19 @@ pub trait StrExt: AsRef { fn to_plural(self) -> String; } +pub trait ToIdent { + fn to_ident(&self) -> Ident; +} + #[derive(Debug)] pub enum TypeIdentResult<'a> { - /// We bailed on detecting wrapper - Complex(Box>), Ident(&'a Ident), Vec(Box>), Box(Box>), Option(Box>), Reference(Box>), + /// We bailed on detecting wrapper + Complex(Box>), } impl<'a> TypeIdentResult<'a> { @@ -113,7 +118,7 @@ impl<'a> TypeIdentResult<'a> { // TODO: remove me #[allow(dead_code)] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Serialize)] pub enum TypeWrapper { None, Box, @@ -128,10 +133,12 @@ pub enum TypeWrapper { Complex, } -#[derive(Debug)] -pub struct TypeAnalyzeResult { - pub type_ref: Option, +#[derive(Debug, Clone, Serialize)] +pub struct TypeAnalysis { + pub type_id: Option, pub wrapper: TypeWrapper, + // pub name: String, + #[serde(skip)] pub typ: Type, } @@ -182,7 +189,7 @@ impl TypeExt for Type { } } - fn analyze(&self, ctx: &CodegenCtx) -> TypeAnalyzeResult { + fn analyze(&self, ctx: &EarlyCtx) -> TypeAnalysis { fn analyze<'a>(res: &'a TypeIdentResult) -> Option<(&'a Ident, TypeWrapper)> { let mut wrapper = TypeWrapper::None; let ident = match res { @@ -226,15 +233,11 @@ impl TypeExt for Type { } let type_ident = self.get_ident(); let Some((type_ident, wrapper)) = analyze(&type_ident) else { - return TypeAnalyzeResult { - type_ref: None, - wrapper: TypeWrapper::Ref, - typ: self.clone(), - }; + return TypeAnalysis { type_id: None, wrapper: TypeWrapper::Ref, typ: self.clone() }; }; - let type_ref = ctx.find(&type_ident.to_string()); - TypeAnalyzeResult { type_ref, wrapper, typ: self.clone() } + let type_id = ctx.type_id(&type_ident.to_string()); + TypeAnalysis { type_id, wrapper, typ: self.clone() } } } @@ -279,6 +282,15 @@ impl TokenStreamExt for TokenStream { } } +impl ToIdent for S +where + S: AsRef, +{ + fn to_ident(&self) -> Ident { + format_ident!("{}", self.as_ref()) + } +} + pub fn write_all_to>(data: &[u8], path: S) -> std::io::Result<()> { use std::{fs, io::Write}; let path = path.as_ref(); @@ -289,3 +301,7 @@ pub fn write_all_to>(data: &[u8], path: S) -> std::io: file.write_all(data)?; Ok(()) } + +pub fn unexpanded_macro_err(mac: &ItemMacro) -> String { + format!("Unexpanded macro: {:?}:{:?}", mac.ident, mac.span()) +}