oxc/tasks/ast_codegen/src/generators/mod.rs
overlookmotel f418f62e7b refactor(ast_codegen): use doc comments instead of insert! (#4777)
Avoid the `insert!` macro in AST codegen. Use doc comments starting with special symbol `@` instead.

* Before: `insert!("// plain comment");`
* After: `///@ plain comment`
* Or: `//!@ plain comment`

Either `///@` or `//!@` is converted to plain `//` in output.

`//!@` is legal in top-of-file position, which allows us to inline `#![allow(...)]` attributes, which in my opinion makes the generators a bit easier to read.
2024-08-09 08:19:15 +00:00

152 lines
3.8 KiB
Rust

use std::path::PathBuf;
use proc_macro2::TokenStream;
use crate::codegen::LateCtx;
mod assert_layouts;
mod ast_builder;
mod ast_kind;
mod derive_clone_in;
mod derive_get_span;
mod visit;
pub use assert_layouts::AssertLayouts;
pub use ast_builder::AstBuilderGenerator;
pub use ast_kind::AstKindGenerator;
pub use derive_clone_in::DeriveCloneIn;
pub use derive_get_span::{DeriveGetSpan, DeriveGetSpanMut};
pub use visit::{VisitGenerator, VisitMutGenerator};
/// Inserts a newline in the `TokenStream`.
#[allow(unused)]
macro_rules! endl {
() => {
/* only works in the context of `quote` macro family! */
};
}
pub trait Generator {
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput;
}
pub type GeneratedTokenStream = (/* output path */ PathBuf, TokenStream);
pub type GeneratedDataStream = (/* output path */ PathBuf, Vec<u8>);
// TODO: remove me
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum GeneratorOutput {
None,
Info(Vec<u8>),
Data(GeneratedDataStream),
Stream(GeneratedTokenStream),
}
// TODO: remove me
#[allow(dead_code)]
impl GeneratorOutput {
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
pub fn expect_none(&self) {
assert!(self.is_none());
}
pub fn to_info(&self) -> &[u8] {
if let Self::Info(it) = self {
it
} else {
panic!();
}
}
pub fn to_data(&self) -> &GeneratedDataStream {
if let Self::Data(it) = self {
it
} else {
panic!();
}
}
pub fn to_stream(&self) -> &GeneratedTokenStream {
if let Self::Stream(it) = self {
it
} else {
panic!();
}
}
pub fn into_info(self) -> Vec<u8> {
if let Self::Info(it) = self {
it
} else {
panic!();
}
}
pub fn into_data(self) -> GeneratedDataStream {
if let Self::Data(it) = self {
it
} else {
panic!();
}
}
pub fn into_stream(self) -> GeneratedTokenStream {
if let Self::Stream(it) = self {
it
} else {
panic!();
}
}
}
macro_rules! define_generator {
($vis:vis struct $ident:ident $($lifetime:lifetime)? $($rest:tt)*) => {
$vis struct $ident $($lifetime)? $($rest)*
impl $($lifetime)? $crate::codegen::Runner for $ident $($lifetime)? {
type Context = $crate::codegen::LateCtx;
type Output = $crate::GeneratorOutput;
fn name(&self) -> &'static str {
stringify!($ident)
}
fn run(&mut self, ctx: &$crate::codegen::LateCtx) -> $crate::Result<Self::Output> {
Ok(self.generate(ctx))
}
}
};
}
pub(crate) use define_generator;
/// Similar to how `insert` macro works in the context of `quote` macro family, But this one can be
/// used outside and accepts expressions.
/// Wraps the result of the given expression in `insert!({value here});` and outputs it as `TokenStream`.
#[allow(unused)]
macro_rules! insert {
($fmt:literal $(, $args:expr)*) => {{
let txt = format!($fmt, $($args)*);
format!(r#"insert!("{}");"#, txt).parse::<proc_macro2::TokenStream>().unwrap()
}};
}
#[allow(unused_imports)]
pub(crate) use insert;
/// Creates a generated file warning + required information for a generated file.
macro_rules! generated_header {
() => {{
let file = file!().replace("\\", "/");
// TODO add generation date, AST source hash, etc here.
let edit_comment = format!("@ To edit this generated file you have to edit `{file}`");
quote::quote! {
//!@ Auto-generated code, DO NOT EDIT DIRECTLY!
#![doc = #edit_comment]
//!@@
}
}};
}
pub(crate) use generated_header;