From 6453663e1746746e36f98bd71c66cf234d6f87af Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 29 Jan 2025 17:03:45 +0900 Subject: [PATCH 1/2] fix(ast): serialize `Function.params` like estree --- crates/oxc_ast/src/ast/js.rs | 10 ++ crates/oxc_ast/src/ast/ts.rs | 5 + crates/oxc_ast/src/generated/derive_estree.rs | 3 +- crates/oxc_ast/src/serialize.rs | 2 +- .../test/__snapshots__/parse.test.ts.snap | 110 ++++++++++++++++++ napi/parser/test/parse.test.ts | 8 ++ npm/oxc-types/types.d.ts | 33 +++--- 7 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 napi/parser/test/__snapshots__/parse.test.ts.snap diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index f55446ef4af8d..83b53aeca8253 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1552,6 +1552,9 @@ pub struct BindingRestElement<'a> { )] #[derive(Debug)] #[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)] +// https://github.com/estree/estree/blob/master/es5.md#patterns +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61c555bfc93e985b313263a42ed78074570d08/types/estree/index.d.ts#L411 +#[estree(add_ts_def = "type ParamPattern = FormalParameter | FormalParameterRest")] pub struct Function<'a> { pub span: Span, pub r#type: FunctionType, @@ -1590,6 +1593,7 @@ pub struct Function<'a> { /// Function parameters. /// /// Does not include `this` parameters used by some TypeScript functions. + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, /// The TypeScript return type annotation. #[ts] @@ -1651,10 +1655,15 @@ pub struct FormalParameters<'a> { #[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)] // Pluralize as `FormalParameterList` to avoid naming clash with `FormalParameters`. #[plural(FormalParameterList)] +#[estree(no_type)] pub struct FormalParameter<'a> { pub span: Span, #[ts] pub decorators: Vec<'a, Decorator<'a>>, + #[estree( + flatten, + ts_type = "(BindingIdentifier | ObjectPattern | ArrayPattern | AssignmentPattern)" + )] pub pattern: BindingPattern<'a>, #[ts] pub accessibility: Option, @@ -1704,6 +1713,7 @@ pub struct ArrowFunctionExpression<'a> { pub r#async: bool, #[ts] pub type_parameters: Option>>, + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, #[ts] pub return_type: Option>>, diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 50c3297e0539a..905f74f4b6041 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -973,6 +973,7 @@ pub struct TSCallSignatureDeclaration<'a> { pub span: Span, pub type_parameters: Option>>, pub this_param: Option>, + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, pub return_type: Option>>, } @@ -1009,6 +1010,7 @@ pub struct TSMethodSignature<'a> { pub kind: TSMethodSignatureKind, pub type_parameters: Option>>, pub this_param: Option>>, + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, pub return_type: Option>>, pub scope_id: Cell>, @@ -1022,6 +1024,7 @@ pub struct TSMethodSignature<'a> { pub struct TSConstructSignatureDeclaration<'a> { pub span: Span, pub type_parameters: Option>>, + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, pub return_type: Option>>, pub scope_id: Cell>, @@ -1343,6 +1346,7 @@ pub struct TSFunctionType<'a> { /// ``` pub this_param: Option>>, /// Function parameters. Akin to [`Function::params`]. + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, /// Return type of the function. /// ```ts @@ -1359,6 +1363,7 @@ pub struct TSConstructorType<'a> { pub span: Span, pub r#abstract: bool, pub type_parameters: Option>>, + #[estree(ts_type = "ParamPattern[]")] pub params: Box<'a, FormalParameters<'a>>, pub return_type: Box<'a, TSTypeAnnotation<'a>>, } diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index e346a8fc868bc..0a24872ab4a4e 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -1355,10 +1355,9 @@ impl Serialize for FunctionType { impl Serialize for FormalParameter<'_> { fn serialize(&self, serializer: S) -> Result { let mut map = serializer.serialize_map(None)?; - map.serialize_entry("type", "FormalParameter")?; self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?; map.serialize_entry("decorators", &self.decorators)?; - map.serialize_entry("pattern", &self.pattern)?; + self.pattern.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?; map.serialize_entry("accessibility", &self.accessibility)?; map.serialize_entry("readonly", &self.readonly)?; map.serialize_entry("override", &self.r#override)?; diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index f45fd9b203864..fa7d098189c9a 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -192,7 +192,7 @@ impl Serialize for FormalParameters<'_> { kind: self.kind, items: ElementsAndRest::new(&self.items, converted_rest.as_ref()), }; - converted.serialize(serializer) + converted.items.serialize(serializer) } } diff --git a/napi/parser/test/__snapshots__/parse.test.ts.snap b/napi/parser/test/__snapshots__/parse.test.ts.snap new file mode 100644 index 0000000000000..35c4a1daff367 --- /dev/null +++ b/napi/parser/test/__snapshots__/parse.test.ts.snap @@ -0,0 +1,110 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`parse > estree function params 1`] = ` +{ + "async": true, + "body": { + "directives": [], + "end": 48, + "start": 46, + "statements": [], + "type": "FunctionBody", + }, + "declare": false, + "end": 48, + "generator": false, + "id": { + "end": 19, + "name": "test", + "start": 15, + "type": "Identifier", + }, + "params": [ + { + "accessibility": null, + "decorators": [], + "end": 21, + "name": "x", + "optional": false, + "override": false, + "readonly": false, + "start": 20, + "type": "Identifier", + "typeAnnotation": null, + }, + { + "accessibility": null, + "decorators": [], + "end": 28, + "optional": false, + "override": false, + "properties": [ + { + "computed": false, + "end": 26, + "key": { + "end": 26, + "name": "y", + "start": 25, + "type": "Identifier", + }, + "shorthand": true, + "start": 25, + "type": "BindingProperty", + "value": { + "end": 26, + "name": "y", + "optional": false, + "start": 25, + "type": "Identifier", + "typeAnnotation": null, + }, + }, + ], + "readonly": false, + "start": 23, + "type": "ObjectPattern", + "typeAnnotation": null, + }, + { + "accessibility": null, + "decorators": [], + "elements": [ + { + "end": 33, + "name": "z", + "optional": false, + "start": 32, + "type": "Identifier", + "typeAnnotation": null, + }, + ], + "end": 35, + "optional": false, + "override": false, + "readonly": false, + "start": 30, + "type": "ArrayPattern", + "typeAnnotation": null, + }, + { + "argument": { + "end": 44, + "name": "rest", + "start": 40, + "type": "Identifier", + }, + "end": 44, + "optional": false, + "start": 37, + "type": "RestElement", + "typeAnnotation": null, + }, + ], + "returnType": null, + "start": 0, + "thisParam": null, + "type": "FunctionDeclaration", + "typeParameters": null, +} +`; diff --git a/napi/parser/test/parse.test.ts b/napi/parser/test/parse.test.ts index 85ba729308e30..e921eafe507a5 100644 --- a/napi/parser/test/parse.test.ts +++ b/napi/parser/test/parse.test.ts @@ -26,6 +26,14 @@ describe('parse', () => { }); expect(code.substring(comment.start, comment.end)).toBe('/*' + comment.value + '*/'); }); + + it('estree function params', async () => { + const ret = await parseAsync( + 'test.js', + `async function test(x, { y }, [ z ], ...rest) {}`, + ); + expect(ret.program.body[0]).matchSnapshot(); + }); }); describe('error', () => { diff --git a/npm/oxc-types/types.d.ts b/npm/oxc-types/types.d.ts index 809659fd5cba5..ed79ac1a8fd01 100644 --- a/npm/oxc-types/types.d.ts +++ b/npm/oxc-types/types.d.ts @@ -718,11 +718,13 @@ export interface Function extends Span { declare: boolean; typeParameters: TSTypeParameterDeclaration | null; thisParam: TSThisParameter | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation | null; body: FunctionBody | null; } +export type ParamPattern = FormalParameter | FormalParameterRest; + export type FunctionType = | 'FunctionDeclaration' | 'FunctionExpression' @@ -742,14 +744,15 @@ export interface FormalParameterRest extends Span { optional: boolean; } -export interface FormalParameter extends Span { - type: 'FormalParameter'; - decorators: Array; - pattern: BindingPattern; - accessibility: TSAccessibility | null; - readonly: boolean; - override: boolean; -} +export type FormalParameter = + & ({ + decorators: Array; + accessibility: TSAccessibility | null; + readonly: boolean; + override: boolean; + }) + & Span + & (BindingIdentifier | ObjectPattern | ArrayPattern | AssignmentPattern); export type FormalParameterKind = 'FormalParameter' | 'UniqueFormalParameters' | 'ArrowFormalParameters' | 'Signature'; @@ -764,7 +767,7 @@ export interface ArrowFunctionExpression extends Span { expression: boolean; async: boolean; typeParameters: TSTypeParameterDeclaration | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation | null; body: FunctionBody; } @@ -1532,7 +1535,7 @@ export interface TSCallSignatureDeclaration extends Span { type: 'TSCallSignatureDeclaration'; typeParameters: TSTypeParameterDeclaration | null; thisParam: TSThisParameter | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation | null; } @@ -1546,14 +1549,14 @@ export interface TSMethodSignature extends Span { kind: TSMethodSignatureKind; typeParameters: TSTypeParameterDeclaration | null; thisParam: TSThisParameter | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation | null; } export interface TSConstructSignatureDeclaration extends Span { type: 'TSConstructSignatureDeclaration'; typeParameters: TSTypeParameterDeclaration | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation | null; } @@ -1642,7 +1645,7 @@ export interface TSFunctionType extends Span { type: 'TSFunctionType'; typeParameters: TSTypeParameterDeclaration | null; thisParam: TSThisParameter | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation; } @@ -1650,7 +1653,7 @@ export interface TSConstructorType extends Span { type: 'TSConstructorType'; abstract: boolean; typeParameters: TSTypeParameterDeclaration | null; - params: FormalParameters; + params: ParamPattern[]; returnType: TSTypeAnnotation; } From 9f2240e0d1633fdcb1355a736175f68989840f6e Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Wed, 5 Feb 2025 02:15:15 +0000 Subject: [PATCH 2/2] Remove dead code --- crates/oxc_ast/src/serialize.rs | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index fa7d098189c9a..5c0ff6ca9a057 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -11,10 +11,10 @@ use oxc_span::{Atom, Span}; use oxc_syntax::number::BigintBase; use crate::ast::{ - BigIntLiteral, BindingPatternKind, BooleanLiteral, Directive, Elision, FormalParameter, - FormalParameterKind, FormalParameters, JSXElementName, JSXIdentifier, - JSXMemberExpressionObject, NullLiteral, NumericLiteral, Program, RegExpFlags, RegExpLiteral, - RegExpPattern, Statement, StringLiteral, TSModuleBlock, TSTypeAnnotation, + BigIntLiteral, BindingPatternKind, BooleanLiteral, Directive, Elision, FormalParameters, + JSXElementName, JSXIdentifier, JSXMemberExpressionObject, NullLiteral, NumericLiteral, Program, + RegExpFlags, RegExpLiteral, RegExpPattern, Statement, StringLiteral, TSModuleBlock, + TSTypeAnnotation, }; #[derive(Serialize)] @@ -187,24 +187,10 @@ impl Serialize for FormalParameters<'_> { type_annotation: &rest.argument.type_annotation, optional: rest.argument.optional, }); - let converted = SerFormalParameters { - span: self.span, - kind: self.kind, - items: ElementsAndRest::new(&self.items, converted_rest.as_ref()), - }; - converted.items.serialize(serializer) + ElementsAndRest::new(&self.items, converted_rest.as_ref()).serialize(serializer) } } -#[derive(Serialize)] -#[serde(tag = "type", rename = "FormalParameters")] -struct SerFormalParameters<'a, 'b> { - #[serde(flatten)] - span: Span, - kind: FormalParameterKind, - items: ElementsAndRest<'b, FormalParameter<'a>, SerFormalParameterRest<'a, 'b>>, -} - #[derive(Serialize)] #[serde(tag = "type", rename = "RestElement", rename_all = "camelCase")] struct SerFormalParameterRest<'a, 'b> {