feat(ast_tools): generate list of helper attributes in oxc_ast_macros crate (#8852)

Custom attributes used by generators (e.g. `#[visit]`) need to be added to the dummy `Ast` derive macro in `oxc_ast_macros`. Do this automatically, rather than requiring the person writing the generator to remember to do it.
This commit is contained in:
overlookmotel committed 2025-02-03 13:07:36 +00:00
1 parent 7b31849404
commit e693ff3a43
6 files changed
+44 -5

No files matched your search

+1
View File
@@ -20,6 +20,7 @@ src:
- 'crates/oxc_ast/src/generated/get_id.rs'
- 'crates/oxc_ast/src/generated/visit.rs'
- 'crates/oxc_ast/src/generated/visit_mut.rs'
- 'crates/oxc_ast_macros/src/lib.rs'
- 'crates/oxc_regular_expression/src/ast.rs'
- 'crates/oxc_regular_expression/src/generated/derive_clone_in.rs'
- 'crates/oxc_regular_expression/src/generated/derive_content_eq.rs'
+1 -1
View File
@@ -84,7 +84,7 @@ pub fn ast(_args: TokenStream, input: TokenStream) -> TokenStream {
/// Its only purpose is to allow the occurrence of helper attributes used in `tasks/ast_tools`.
///
/// Read [`macro@ast`] for further details.
#[proc_macro_derive(Ast, attributes(scope, visit, span, generate_derive, clone_in, estree, ts))]
#[proc_macro_derive(Ast, attributes(clone_in, estree, generate_derive, scope, span, ts, visit))]
pub fn ast_derive(_input: TokenStream) -> TokenStream {
TokenStream::new()
}
+5
View File
@@ -73,6 +73,11 @@ impl Codegen {
pub fn attr_processor(&self, attr_name: &str) -> Option<(AttrProcessor, AttrPositions)> {
self.attr_processors.get(attr_name).copied()
}
/// Get all attributes which derives and generators handle.
pub fn attrs(&self) -> Vec<&'static str> {
self.attr_processors.keys().copied().collect()
}
}
/// Runner trait.
+30 -2
View File
@@ -147,7 +147,6 @@
//! * Implement [`Generator::attrs`] / [`Derive::attrs`] to declare the generator's custom attributes.
//! * Implement [`Generator::parse_attr`] / [`Derive::parse_attr`] to parse those attributes
//! and mutate the "extension" types in [`Schema`] as required.
//! * Add the attributes' names to the list on `ast_derive` in `crates/oxc_ast_macros/src/lib.rs`.
//!
//! #### Attributes
//!
@@ -170,7 +169,7 @@
//! [`AttrLocation`]: parse::attr::AttrLocation
//! [`AttrPart`]: parse::attr::AttrPart
use std::fmt::Write;
use std::{fmt::Write, fs};
use bpaf::{Bpaf, Parser};
use rayon::prelude::*;
@@ -209,6 +208,9 @@ static SOURCE_PATHS: &[&str] = &[
/// Path to `oxc_ast` crate
const AST_CRATE: &str = "crates/oxc_ast";
/// Path to `oxc_ast_macros` crate's `lib.rs` file
const AST_MACROS_LIB_PATH: &str = "crates/oxc_ast_macros/src/lib.rs";
/// Path to write TS type definitions to
const TYPESCRIPT_DEFINITIONS_PATH: &str = "npm/oxc-types/types.d.ts";
@@ -288,6 +290,9 @@ fn main() {
logln!("All Derives and Generators... Done!");
// Edit `lib.rs` in `oxc_ast_macros` crate
outputs.push(generate_updated_proc_macro(&codegen));
// Add CI filter file to outputs
outputs.sort_unstable_by(|o1, o2| o1.path.cmp(&o2.path));
outputs.push(generate_ci_filter(&outputs));
@@ -326,3 +331,26 @@ fn generate_ci_filter(outputs: &[RawOutput]) -> RawOutput {
Output::Yaml { path: GITHUB_WATCH_LIST_PATH.to_string(), code }.into_raw(file!())
}
/// Update the list of helper attributes for `Ast` derive proc macro in `oxc_ast_macros` crate
/// to include all attrs which generators/derives utilize.
///
/// Unfortunately we can't add a separate generated file for this, as proc macros can only be declared
/// in the main `lib.rs` of a proc macro crate. So we have to edit the existing file.
fn generate_updated_proc_macro(codegen: &Codegen) -> RawOutput {
// Get all attrs which derives/generators use
let mut attrs = codegen.attrs();
attrs.push("generate_derive");
attrs.sort_unstable();
let attrs = attrs.join(", ");
// Load `oxc_ast_macros` crate's `lib.rs` file.
// Substitute list of used attrs into `#[proc_macro_derive(Ast, attributes(...))]`.
let code = fs::read_to_string(AST_MACROS_LIB_PATH).unwrap();
let (start, end) = code.split_once("#[proc_macro_derive(").unwrap();
let (_, end) = end.split_once(")]").unwrap();
assert!(end.starts_with("\npub fn ast_derive("));
let code = format!("{start}#[proc_macro_derive(Ast, attributes({attrs}))]{end}");
Output::RustString { path: AST_MACROS_LIB_PATH.to_string(), code }.into_raw("")
}
+6 -1
View File
@@ -9,7 +9,7 @@ mod javascript;
mod rust;
mod yaml;
use javascript::print_javascript;
use rust::print_rust;
use rust::{print_rust, rust_fmt};
use yaml::print_yaml;
/// Get path for an output.
@@ -32,6 +32,7 @@ fn add_header(code: &str, generator_path: &str, comment_start: &str) -> String {
#[expect(dead_code)]
pub enum Output {
Rust { path: String, tokens: TokenStream },
RustString { path: String, code: String },
Javascript { path: String, code: String },
Yaml { path: String, code: String },
Raw { path: String, code: String },
@@ -49,6 +50,10 @@ impl Output {
let code = print_rust(tokens, &generator_path);
(path, code)
}
Self::RustString { path, code } => {
let code = rust_fmt(&code);
(path, code)
}
Self::Javascript { path, code } => {
let code = print_javascript(&code, &generator_path);
(path, code)
+1 -1
View File
@@ -21,7 +21,7 @@ pub fn print_rust(tokens: TokenStream, generator_path: &str) -> String {
/// Format Rust code with `rustfmt`.
///
/// Does not format on disk - interfaces with `rustfmt` via stdin/stdout.
fn rust_fmt(source_text: &str) -> String {
pub fn rust_fmt(source_text: &str) -> String {
let mut rustfmt = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())