diff --git a/.github/generated/ast_changes_watch_list.yml b/.github/generated/ast_changes_watch_list.yml index 42604240c1c21..d629ebcbff853 100644 --- a/.github/generated/ast_changes_watch_list.yml +++ b/.github/generated/ast_changes_watch_list.yml @@ -72,6 +72,7 @@ src: - 'napi/parser/generated/lazy/constructors.mjs' - 'napi/parser/generated/lazy/types.mjs' - 'napi/parser/generated/lazy/walk.mjs' + - 'napi/parser/generated/visit/keys.mjs' - 'napi/parser/src/generated/assert_layouts.rs' - 'napi/parser/src/generated/derive_estree.rs' - 'napi/parser/src/generated/raw_transfer_constants.rs' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21ad1fffeae02..65df2dec2e368 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -357,6 +357,9 @@ jobs: cache-key: ast_changes save-cache: ${{ github.ref_name == 'main' }} + - uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4 + if: steps.filter.outputs.src == 'true' + - name: Restore dprint plugin cache if: steps.filter.outputs.src == 'true' uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 diff --git a/Cargo.lock b/Cargo.lock index 8fea6cb66b9cc..07415cc9ca23b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1791,6 +1791,7 @@ dependencies = [ "rayon", "rustc-hash", "serde", + "serde_json", "syn", ] diff --git a/napi/parser/README.md b/napi/parser/README.md index 9d79df978583c..a704dccb6d4a3 100644 --- a/napi/parser/README.md +++ b/napi/parser/README.md @@ -41,6 +41,12 @@ import { Statement } from '@oxc-project/types'; [oxc-walker](https://www.npmjs.com/package/oxc-walker) or [estree-walker](https://www.npmjs.com/package/estree-walker) can be used. +This package exports visitor keys which can be used with any ESTree walker. + +```js +import { visitorKeys } from 'oxc-parser'; +``` + ### Fast Mode By default, Oxc parser does not produce semantic errors where symbols and scopes are needed. diff --git a/napi/parser/generated/visit/keys.mjs b/napi/parser/generated/visit/keys.mjs new file mode 100644 index 0000000000000..3c073895b20b7 --- /dev/null +++ b/napi/parser/generated/visit/keys.mjs @@ -0,0 +1,172 @@ +// Auto-generated code, DO NOT EDIT DIRECTLY! +// To edit this generated file you have to edit `tasks/ast_tools/src/generators/estree_visit.rs`. + +export default { + // Leaf nodes + DebuggerStatement: [], + EmptyStatement: [], + Literal: [], + PrivateIdentifier: [], + Super: [], + TemplateElement: [], + ThisExpression: [], + JSXClosingFragment: [], + JSXEmptyExpression: [], + JSXIdentifier: [], + JSXOpeningFragment: [], + JSXText: [], + TSAnyKeyword: [], + TSBigIntKeyword: [], + TSBooleanKeyword: [], + TSIntrinsicKeyword: [], + TSJSDocUnknownType: [], + TSNeverKeyword: [], + TSNullKeyword: [], + TSNumberKeyword: [], + TSObjectKeyword: [], + TSStringKeyword: [], + TSSymbolKeyword: [], + TSThisType: [], + TSUndefinedKeyword: [], + TSUnknownKeyword: [], + TSVoidKeyword: [], + // Non-leaf nodes + AccessorProperty: ['decorators', 'key', 'typeAnnotation', 'value'], + ArrayExpression: ['elements'], + ArrayPattern: ['decorators', 'elements', 'typeAnnotation'], + ArrowFunctionExpression: ['typeParameters', 'params', 'returnType', 'body'], + AssignmentExpression: ['left', 'right'], + AssignmentPattern: ['decorators', 'left', 'right', 'typeAnnotation'], + AwaitExpression: ['argument'], + BinaryExpression: ['left', 'right'], + BlockStatement: ['body'], + BreakStatement: ['label'], + CallExpression: ['callee', 'typeArguments', 'arguments'], + CatchClause: ['param', 'body'], + ChainExpression: ['expression'], + ClassBody: ['body'], + ClassDeclaration: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], + ClassExpression: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], + ConditionalExpression: ['test', 'consequent', 'alternate'], + ContinueStatement: ['label'], + Decorator: ['expression'], + DoWhileStatement: ['body', 'test'], + ExportAllDeclaration: ['exported', 'source', 'attributes'], + ExportDefaultDeclaration: ['declaration'], + ExportNamedDeclaration: ['declaration', 'specifiers', 'source', 'attributes'], + ExportSpecifier: ['local', 'exported'], + ExpressionStatement: ['expression'], + ForInStatement: ['left', 'right', 'body'], + ForOfStatement: ['left', 'right', 'body'], + ForStatement: ['init', 'test', 'update', 'body'], + FunctionDeclaration: ['id', 'typeParameters', 'params', 'returnType', 'body'], + FunctionExpression: ['id', 'typeParameters', 'params', 'returnType', 'body'], + Identifier: ['decorators', 'typeAnnotation'], + IfStatement: ['test', 'consequent', 'alternate'], + ImportAttribute: ['key', 'value'], + ImportDeclaration: ['specifiers', 'source', 'attributes'], + ImportDefaultSpecifier: ['local'], + ImportExpression: ['source', 'options'], + ImportNamespaceSpecifier: ['local'], + ImportSpecifier: ['imported', 'local'], + LabeledStatement: ['label', 'body'], + LogicalExpression: ['left', 'right'], + MemberExpression: ['object', 'property'], + MetaProperty: ['meta', 'property'], + MethodDefinition: ['decorators', 'key', 'value'], + NewExpression: ['callee', 'typeArguments', 'arguments'], + ObjectExpression: ['properties'], + ObjectPattern: ['decorators', 'properties', 'typeAnnotation'], + ParenthesizedExpression: ['expression'], + Program: ['body'], + Property: ['key', 'value'], + PropertyDefinition: ['decorators', 'key', 'typeAnnotation', 'value'], + RestElement: ['decorators', 'argument', 'typeAnnotation'], + ReturnStatement: ['argument'], + SequenceExpression: ['expressions'], + SpreadElement: ['argument'], + StaticBlock: ['body'], + SwitchCase: ['test', 'consequent'], + SwitchStatement: ['discriminant', 'cases'], + TaggedTemplateExpression: ['tag', 'typeArguments', 'quasi'], + TemplateLiteral: ['quasis', 'expressions'], + ThrowStatement: ['argument'], + TryStatement: ['block', 'handler', 'finalizer'], + UnaryExpression: ['argument'], + UpdateExpression: ['argument'], + V8IntrinsicExpression: ['name', 'arguments'], + VariableDeclaration: ['declarations'], + VariableDeclarator: ['id', 'init'], + WhileStatement: ['test', 'body'], + WithStatement: ['object', 'body'], + YieldExpression: ['argument'], + JSXAttribute: ['name', 'value'], + JSXClosingElement: ['name'], + JSXElement: ['openingElement', 'children', 'closingElement'], + JSXExpressionContainer: ['expression'], + JSXFragment: ['openingFragment', 'children', 'closingFragment'], + JSXMemberExpression: ['object', 'property'], + JSXNamespacedName: ['namespace', 'name'], + JSXOpeningElement: ['name', 'typeArguments', 'attributes'], + JSXSpreadAttribute: ['argument'], + JSXSpreadChild: ['expression'], + TSAbstractAccessorProperty: ['decorators', 'key', 'typeAnnotation'], + TSAbstractMethodDefinition: ['key', 'value'], + TSAbstractPropertyDefinition: ['decorators', 'key', 'typeAnnotation'], + TSArrayType: ['elementType'], + TSAsExpression: ['expression', 'typeAnnotation'], + TSCallSignatureDeclaration: ['typeParameters', 'params', 'returnType'], + TSClassImplements: ['expression', 'typeArguments'], + TSConditionalType: ['checkType', 'extendsType', 'trueType', 'falseType'], + TSConstructSignatureDeclaration: ['typeParameters', 'params', 'returnType'], + TSConstructorType: ['typeParameters', 'params', 'returnType'], + TSDeclareFunction: ['id', 'typeParameters', 'params', 'returnType', 'body'], + TSEmptyBodyFunctionExpression: ['id', 'typeParameters', 'params', 'returnType'], + TSEnumBody: ['members'], + TSEnumDeclaration: ['id', 'body'], + TSEnumMember: ['id', 'initializer'], + TSExportAssignment: ['expression'], + TSExternalModuleReference: ['expression'], + TSFunctionType: ['typeParameters', 'params', 'returnType'], + TSImportEqualsDeclaration: ['id', 'moduleReference'], + TSImportType: ['argument', 'options', 'qualifier', 'typeArguments'], + TSIndexSignature: ['parameters', 'typeAnnotation'], + TSIndexedAccessType: ['objectType', 'indexType'], + TSInferType: ['typeParameter'], + TSInstantiationExpression: ['expression', 'typeArguments'], + TSInterfaceBody: ['body'], + TSInterfaceDeclaration: ['id', 'typeParameters', 'extends', 'body'], + TSInterfaceHeritage: ['expression', 'typeArguments'], + TSIntersectionType: ['types'], + TSJSDocNonNullableType: ['typeAnnotation'], + TSJSDocNullableType: ['typeAnnotation'], + TSLiteralType: ['literal'], + TSMappedType: ['key', 'constraint', 'nameType', 'typeAnnotation'], + TSMethodSignature: ['key', 'typeParameters', 'params', 'returnType'], + TSModuleBlock: ['body'], + TSModuleDeclaration: ['id', 'body'], + TSNamedTupleMember: ['label', 'elementType'], + TSNamespaceExportDeclaration: ['id'], + TSNonNullExpression: ['expression'], + TSOptionalType: ['typeAnnotation'], + TSParameterProperty: ['decorators', 'parameter'], + TSParenthesizedType: ['typeAnnotation'], + TSPropertySignature: ['key', 'typeAnnotation'], + TSQualifiedName: ['left', 'right'], + TSRestType: ['typeAnnotation'], + TSSatisfiesExpression: ['expression', 'typeAnnotation'], + TSTemplateLiteralType: ['quasis', 'types'], + TSTupleType: ['elementTypes'], + TSTypeAliasDeclaration: ['id', 'typeParameters', 'typeAnnotation'], + TSTypeAnnotation: ['typeAnnotation'], + TSTypeAssertion: ['typeAnnotation', 'expression'], + TSTypeLiteral: ['members'], + TSTypeOperator: ['typeAnnotation'], + TSTypeParameter: ['name', 'constraint', 'default'], + TSTypeParameterDeclaration: ['params'], + TSTypeParameterInstantiation: ['params'], + TSTypePredicate: ['parameterName', 'typeAnnotation'], + TSTypeQuery: ['exprName', 'typeArguments'], + TSTypeReference: ['typeName', 'typeArguments'], + TSUnionType: ['types'], +}; diff --git a/napi/parser/package.json b/napi/parser/package.json index 63bb1f6c34575..7129a2d50149b 100644 --- a/napi/parser/package.json +++ b/napi/parser/package.json @@ -44,6 +44,7 @@ "generated/lazy/constructors.mjs", "generated/lazy/types.mjs", "generated/lazy/walk.mjs", + "generated/visit/keys.mjs", "src-js/bindings.mjs", "src-js/index.d.ts", "src-js/index.mjs", @@ -68,6 +69,7 @@ "devDependencies": { "@codspeed/vitest-plugin": "^4.0.0", "@napi-rs/wasm-runtime": "catalog:", + "@typescript-eslint/visitor-keys": "^8.44.0", "@vitest/browser": "3.2.4", "esbuild": "^0.25.0", "playwright": "^1.51.0", diff --git a/napi/parser/scripts/visitor-keys.mjs b/napi/parser/scripts/visitor-keys.mjs new file mode 100644 index 0000000000000..fde0ed9fcf9ff --- /dev/null +++ b/napi/parser/scripts/visitor-keys.mjs @@ -0,0 +1,4 @@ +import { visitorKeys } from '@typescript-eslint/visitor-keys'; + +const keys = Object.entries(visitorKeys).map(([name, keys]) => ({ name, keys })); +console.log(JSON.stringify(keys)); diff --git a/napi/parser/src-js/index.mjs b/napi/parser/src-js/index.mjs index 0a9106383854a..5b956cca538ea 100644 --- a/napi/parser/src-js/index.mjs +++ b/napi/parser/src-js/index.mjs @@ -2,6 +2,8 @@ import { createRequire } from 'node:module'; import { parseAsync as parseAsyncBinding, parseSync as parseSyncBinding } from './bindings.mjs'; import { wrap } from './wrap.mjs'; +export { default as visitorKeys } from '../generated/visit/keys.mjs'; + export { ExportExportNameKind, ExportImportNameKind, diff --git a/napi/parser/test/parse.test.ts b/napi/parser/test/parse.test.ts index 3882a8ebdb41c..94765001b6ac4 100644 --- a/napi/parser/test/parse.test.ts +++ b/napi/parser/test/parse.test.ts @@ -1,7 +1,7 @@ import { Worker } from 'node:worker_threads'; import { describe, expect, it, test } from 'vitest'; -import { parseAsync, parseSync } from '../src-js/index.mjs'; +import { parseAsync, parseSync, visitorKeys } from '../src-js/index.mjs'; import type { ExpressionStatement, ParserOptions, @@ -874,6 +874,14 @@ describe('error', () => { }); }); +it('visitor keys', () => { + expect(visitorKeys.Literal).toEqual([]); + expect(visitorKeys.VariableDeclaration).toEqual(['declarations']); + expect(visitorKeys.ObjectPattern).toEqual(['decorators', 'properties', 'typeAnnotation']); + expect(visitorKeys.ParenthesizedExpression).toEqual(['expression']); + expect(visitorKeys.V8IntrinsicExpression).toEqual(['name', 'arguments']); +}); + describe('worker', () => { it('should run', async () => { const code = await new Promise((resolve, reject) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0ebac4b657d3..e5bc5649a1c43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ importers: '@napi-rs/wasm-runtime': specifier: 'catalog:' version: 1.0.5 + '@typescript-eslint/visitor-keys': + specifier: ^8.44.0 + version: 8.44.0 '@vitest/browser': specifier: 3.2.4 version: 3.2.4(playwright@1.55.0)(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1))(vitest@3.2.4) @@ -1771,6 +1774,14 @@ packages: '@types/vscode@1.93.0': resolution: {integrity: sha512-kUK6jAHSR5zY8ps42xuW89NLcBpw1kOabah7yv38J8MyiYuOHxLQBi0e7zeXbQgVefDy/mZZetqEFC+Fl5eIEQ==} + '@typescript-eslint/types@8.44.0': + resolution: {integrity: sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.44.0': + resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typespec/ts-http-runtime@0.3.1': resolution: {integrity: sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww==} engines: {node: '>=20.0.0'} @@ -2392,6 +2403,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -5380,6 +5395,13 @@ snapshots: '@types/vscode@1.93.0': {} + '@typescript-eslint/types@8.44.0': {} + + '@typescript-eslint/visitor-keys@8.44.0': + dependencies: + '@typescript-eslint/types': 8.44.0 + eslint-visitor-keys: 4.2.1 + '@typespec/ts-http-runtime@0.3.1': dependencies: http-proxy-agent: 7.0.2 @@ -6061,6 +6083,8 @@ snapshots: escape-string-regexp@4.0.0: {} + eslint-visitor-keys@4.2.1: {} + esprima@4.0.1: {} estree-walker@3.0.3: diff --git a/tasks/ast_tools/Cargo.toml b/tasks/ast_tools/Cargo.toml index c8bbd1766773e..95db63fe66a10 100644 --- a/tasks/ast_tools/Cargo.toml +++ b/tasks/ast_tools/Cargo.toml @@ -23,7 +23,7 @@ cow-utils = { workspace = true } indexmap = { workspace = true } itertools = { workspace = true } lazy-regex = { workspace = true } -oxc_index = { workspace = true } +oxc_index = { workspace = true, features = ["serde"] } phf = { workspace = true, features = ["macros"] } phf_codegen = { workspace = true } prettyplease = { workspace = true } @@ -31,5 +31,6 @@ proc-macro2 = { workspace = true } quote = { workspace = true } rayon = { workspace = true } rustc-hash = { workspace = true } -serde = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } syn = { workspace = true, features = ["clone-impls", "derive", "extra-traits", "full", "parsing", "printing", "proc-macro"] } diff --git a/tasks/ast_tools/src/generators/estree_visit.rs b/tasks/ast_tools/src/generators/estree_visit.rs new file mode 100644 index 0000000000000..fe17147495b8f --- /dev/null +++ b/tasks/ast_tools/src/generators/estree_visit.rs @@ -0,0 +1,146 @@ +//! Generator of ESTree visitor keys. + +use std::{ + cmp::Ordering, + fmt::{self, Display}, + process::{Command, Stdio}, +}; + +use serde::Deserialize; + +use oxc_index::{IndexVec, define_index_type}; + +use crate::{ + Codegen, Generator, NAPI_PARSER_PACKAGE_PATH, + output::Output, + schema::Schema, + utils::{string, write_it}, +}; + +use super::define_generator; + +define_index_type! { + pub struct NodeId = u32; +} + +impl Display for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.raw().fmt(f) + } +} + +pub struct ESTreeVisitGenerator; + +define_generator!(ESTreeVisitGenerator); + +impl Generator for ESTreeVisitGenerator { + fn generate_many(&self, _schema: &Schema, codegen: &Codegen) -> Vec { + let visitor_keys = generate(codegen); + + vec![Output::Javascript { + path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/visit/keys.mjs"), + code: visitor_keys, + }] + } +} + +/// Details of a node's name and visitor keys. +#[derive(Deserialize, Debug)] +struct NodeKeys { + name: String, + keys: Vec, +} + +/// Generate visitor keys. +fn generate(codegen: &Codegen) -> String { + // Run `napi/parser/scripts/visitor-keys.mjs` to get visitor keys from TS-ESLint + let script_path = codegen.root_path().join("napi/parser/scripts/visitor-keys.mjs"); + + let output = Command::new("node") + .arg(script_path) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .unwrap(); + assert!(output.status.success() && output.stderr.is_empty()); + let json = String::from_utf8(output.stdout).unwrap(); + let mut nodes: IndexVec = serde_json::from_str(&json).unwrap(); + + // Remove types which do not exist in Oxc AST + // TODO: Why don't they exist? + let remove = [ + "TSAbstractKeyword", + "TSAsyncKeyword", + "TSDeclareKeyword", + "TSExportKeyword", + "TSPrivateKeyword", + "TSProtectedKeyword", + "TSPublicKeyword", + "TSReadonlyKeyword", + "TSStaticKeyword", + "ExperimentalRestProperty", + "ExperimentalSpreadProperty", + ]; + nodes.retain(|node| !remove.contains(&node.name.as_str())); + + // Add types which don't exist in TS-ESTree AST + let extra: &[(&str, &[&str])] = &[ + ("ParenthesizedExpression", &["expression"]), + ("V8IntrinsicExpression", &["name", "arguments"]), + ("TSParenthesizedType", &["typeAnnotation"]), + ("TSJSDocNonNullableType", &["typeAnnotation"]), + ("TSJSDocNullableType", &["typeAnnotation"]), + ("TSJSDocUnknownType", &[]), + ]; + nodes.extend(extra.iter().map(|&(name, keys)| NodeKeys { + name: name.to_string(), + keys: keys.iter().map(|&key| key.to_string()).collect(), + })); + + // Sort by: + // * Leaf nodes before non-leaf nodes. + // * JS first, then JSX, then TS. + // * Alphabetical order. + nodes.sort_by(|v1, v2| match (v1.keys.is_empty(), v2.keys.is_empty()) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => { + let name1 = v1.name.as_str(); + let name2 = v2.name.as_str(); + let is_jsx1 = name1.starts_with("JSX"); + let is_ts1 = name1.starts_with("TS"); + let is_jsx2 = name2.starts_with("JSX"); + let is_ts2 = name2.starts_with("TS"); + + match (is_jsx1 || is_ts1, is_jsx2 || is_ts2) { + (false, true) => Ordering::Less, + (true, false) => Ordering::Greater, + (true, true) if is_ts1 != is_ts2 => is_ts1.cmp(&is_ts2), + _ => name1.cmp(name2), + } + } + }); + + #[rustfmt::skip] + let mut visitor_keys = string!(" + export default { + // Leaf nodes + "); + + let mut leaf_nodes_count = None; + for (node_id, node) in nodes.iter_enumerated() { + let is_leaf = node.keys.is_empty(); + if leaf_nodes_count.is_none() && !is_leaf { + leaf_nodes_count = Some(node_id.raw()); + visitor_keys.push_str("// Non-leaf nodes\n"); + } + + let node_name = node.name.as_str(); + let keys = &node.keys; + write_it!(visitor_keys, "{node_name}: {keys:?},\n"); + } + + visitor_keys.push_str("};"); + + visitor_keys +} diff --git a/tasks/ast_tools/src/generators/mod.rs b/tasks/ast_tools/src/generators/mod.rs index 3ab80fa270879..9165d6e09a2c1 100644 --- a/tasks/ast_tools/src/generators/mod.rs +++ b/tasks/ast_tools/src/generators/mod.rs @@ -7,6 +7,7 @@ use crate::{ mod assert_layouts; mod ast_builder; mod ast_kind; +mod estree_visit; mod formatter; mod get_id; mod raw_transfer; @@ -19,6 +20,7 @@ mod visit; pub use assert_layouts::AssertLayouts; pub use ast_builder::AstBuilderGenerator; pub use ast_kind::AstKindGenerator; +pub use estree_visit::ESTreeVisitGenerator; pub use formatter::{FormatterAstNodesGenerator, FormatterFormatGenerator}; pub use get_id::GetIdGenerator; pub use raw_transfer::RawTransferGenerator; diff --git a/tasks/ast_tools/src/main.rs b/tasks/ast_tools/src/main.rs index f77c98d7be231..54dd4d1efb976 100644 --- a/tasks/ast_tools/src/main.rs +++ b/tasks/ast_tools/src/main.rs @@ -283,6 +283,7 @@ const GENERATORS: &[&(dyn Generator + Sync)] = &[ &generators::VisitGenerator, &generators::ScopesCollectorGenerator, &generators::Utf8ToUtf16ConverterGenerator, + &generators::ESTreeVisitGenerator, &generators::RawTransferGenerator, &generators::RawTransferLazyGenerator, &generators::TypescriptGenerator, diff --git a/tasks/ast_tools/src/utils.rs b/tasks/ast_tools/src/utils.rs index 70db2f3c827f9..10e654c54a06e 100644 --- a/tasks/ast_tools/src/utils.rs +++ b/tasks/ast_tools/src/utils.rs @@ -120,3 +120,13 @@ macro_rules! write_it { }} } pub(crate) use write_it; + +/// Macro to create a `String`. +/// +/// e.g. `string!("hello")` -> "hello".to_string(). +macro_rules! string { + ($s:literal) => { + $s.to_string() + }; +} +pub(crate) use string;