Skip to content

Commit

Permalink
fix(ast): add RestElements in serialized AST to elements array (#2567)
Browse files Browse the repository at this point in the history
A step towards #2463.

This PR adds `rest` onto end of `elements` / `properties` array in JSON
AST for `ObjectPattern`, `ArrayPattern`, `ObjectAssignmentTarget`,
`ArrayAssignmentTarget` and `FormalParameters`.
  • Loading branch information
overlookmotel committed Mar 8, 2024
1 parent 2609e90 commit 88f94bb
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 16 deletions.
55 changes: 41 additions & 14 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ export interface BindingIdentifier extends Span { type: "Identifier", name: Atom
export interface IdentifierReference extends Span { type: "Identifier", name: Atom }
export interface IdentifierName extends Span { type: "Identifier", name: Atom }
export interface LabelIdentifier extends Span { type: "Identifier", name: Atom }
export interface AssignmentTargetRest extends Span { type: "RestElement", target: AssignmentTarget }
export interface BindingRestElement extends Span { type: "RestElement", argument: BindingPattern }
export interface FormalParameterRest extends Span {
type: "RestElement",
argument: BindingPatternKind,
typeAnnotation?: TSTypeAnnotation,
optional: boolean,
}
"#;

#[derive(Debug, Hash)]
Expand Down Expand Up @@ -970,13 +978,18 @@ pub enum AssignmentTargetPattern<'a> {
ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>),
}

// See serializer in serialize.rs
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub struct ArrayAssignmentTarget<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg_attr(feature = "wasm", serde(flatten))]
pub span: Span,
#[cfg_attr(
feature = "wasm",
tsify(type = "Array<AssignmentTargetMaybeDefault | AssignmentTargetRest | null>")
)]
pub elements: Vec<'a, Option<AssignmentTargetMaybeDefault<'a>>>,
#[cfg_attr(feature = "wasm", serde(skip))]
pub rest: Option<AssignmentTargetRest<'a>>,
pub trailing_comma: Option<Span>,
}
Expand All @@ -990,13 +1003,18 @@ impl<'a> ArrayAssignmentTarget<'a> {
}
}

// See serializer in serialize.rs
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub struct ObjectAssignmentTarget<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg_attr(feature = "wasm", serde(flatten))]
pub span: Span,
#[cfg_attr(
feature = "wasm",
tsify(type = "Array<AssignmentTargetProperty | AssignmentTargetRest>")
)]
pub properties: Vec<'a, AssignmentTargetProperty<'a>>,
#[cfg_attr(feature = "wasm", serde(skip))]
pub rest: Option<AssignmentTargetRest<'a>>,
}

Expand All @@ -1018,11 +1036,11 @@ impl<'a> ObjectAssignmentTarget<'a> {
}

#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename = "RestElement"))]
pub struct AssignmentTargetRest<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
#[cfg_attr(feature = "serde", serde(rename = "argument"))]
pub target: AssignmentTarget<'a>,
}

Expand Down Expand Up @@ -1623,6 +1641,7 @@ impl<'a> BindingPattern<'a> {

#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(untagged))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub enum BindingPatternKind<'a> {
/// `const a = 1`
BindingIdentifier(Box<'a, BindingIdentifier<'a>>),
Expand Down Expand Up @@ -1661,13 +1680,15 @@ pub struct AssignmentPattern<'a> {
pub right: Expression<'a>,
}

// See serializer in serialize.rs
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub struct ObjectPattern<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg_attr(feature = "wasm", serde(flatten))]
pub span: Span,
#[cfg_attr(feature = "wasm", tsify(type = "Array<BindingProperty | BindingRestElement>"))]
pub properties: Vec<'a, BindingProperty<'a>>,
#[cfg_attr(feature = "wasm", serde(skip))]
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
}

Expand All @@ -1693,13 +1714,18 @@ pub struct BindingProperty<'a> {
pub computed: bool,
}

// See serializer in serialize.rs
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub struct ArrayPattern<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg_attr(feature = "wasm", serde(flatten))]
pub span: Span,
#[cfg_attr(
feature = "wasm",
tsify(type = "Array<BindingPattern | BindingRestElement | null>")
)]
pub elements: Vec<'a, Option<BindingPattern<'a>>>,
#[cfg_attr(feature = "wasm", serde(skip))]
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
}

Expand All @@ -1714,8 +1740,7 @@ impl<'a> ArrayPattern<'a> {
}

#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename = "RestElement"))]
pub struct BindingRestElement<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
Expand Down Expand Up @@ -1799,14 +1824,16 @@ pub enum FunctionType {
}

/// <https://tc39.es/ecma262/#prod-FormalParameters>
// See serializer in serialize.rs
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
pub struct FormalParameters<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg_attr(feature = "wasm", serde(flatten))]
pub span: Span,
pub kind: FormalParameterKind,
#[cfg_attr(feature = "wasm", tsify(type = "Array<FormalParameter | FormalParameterRest>"))]
pub items: Vec<'a, FormalParameter<'a>>,
#[cfg_attr(feature = "wasm", serde(skip))]
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
}

Expand Down
149 changes: 147 additions & 2 deletions crates/oxc_ast/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use serde::{
ser::{SerializeStruct, Serializer},
ser::{SerializeSeq, SerializeStruct, Serializer},
Serialize,
};

use crate::ast::{
BindingIdentifier, IdentifierName, IdentifierReference, LabelIdentifier, Program, RegExpFlags,
ArrayAssignmentTarget, ArrayPattern, AssignmentTargetMaybeDefault, AssignmentTargetProperty,
AssignmentTargetRest, BindingIdentifier, BindingPattern, BindingPatternKind, BindingProperty,
BindingRestElement, FormalParameter, FormalParameterKind, FormalParameters, IdentifierName,
IdentifierReference, LabelIdentifier, ObjectAssignmentTarget, ObjectPattern, Program,
RegExpFlags, TSTypeAnnotation,
};
use oxc_allocator::{Box, Vec};
use oxc_span::{Atom, Span};

pub struct EcmaFormatter;
Expand Down Expand Up @@ -92,3 +97,143 @@ impl<'a> Serialize for LabelIdentifier<'a> {
serialize_identifier(serializer, "LabelIdentifier", self.span, &self.name)
}
}

/// Serialize `ArrayAssignmentTarget`, `ObjectAssignmentTarget`, `ObjectPattern`, `ArrayPattern`
/// to be estree compatible, with `elements`/`properties` and `rest` fields combined.

impl<'a> Serialize for ArrayAssignmentTarget<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let converted = SerArrayAssignmentTarget {
span: self.span,
elements: ElementsAndRest::new(&self.elements, &self.rest),
trailing_comma: self.trailing_comma,
};
converted.serialize(serializer)
}
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "ArrayAssignmentTarget", rename_all = "camelCase")]
struct SerArrayAssignmentTarget<'a, 'b> {
#[serde(flatten)]
span: Span,
elements:
ElementsAndRest<'a, 'b, Option<AssignmentTargetMaybeDefault<'a>>, AssignmentTargetRest<'a>>,
trailing_comma: Option<Span>,
}

impl<'a> Serialize for ObjectAssignmentTarget<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let converted = SerObjectAssignmentTarget {
span: self.span,
properties: ElementsAndRest::new(&self.properties, &self.rest),
};
converted.serialize(serializer)
}
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "ObjectAssignmentTarget")]
struct SerObjectAssignmentTarget<'a, 'b> {
#[serde(flatten)]
span: Span,
properties: ElementsAndRest<'a, 'b, AssignmentTargetProperty<'a>, AssignmentTargetRest<'a>>,
}

impl<'a> Serialize for ObjectPattern<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let converted = SerObjectPattern {
span: self.span,
properties: ElementsAndRest::new(&self.properties, &self.rest),
};
converted.serialize(serializer)
}
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "ObjectPattern")]
struct SerObjectPattern<'a, 'b> {
#[serde(flatten)]
span: Span,
properties: ElementsAndRest<'a, 'b, BindingProperty<'a>, Box<'a, BindingRestElement<'a>>>,
}

impl<'a> Serialize for ArrayPattern<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let converted = SerArrayPattern {
span: self.span,
elements: ElementsAndRest::new(&self.elements, &self.rest),
};
converted.serialize(serializer)
}
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "ArrayPattern")]
struct SerArrayPattern<'a, 'b> {
#[serde(flatten)]
span: Span,
elements: ElementsAndRest<'a, 'b, Option<BindingPattern<'a>>, Box<'a, BindingRestElement<'a>>>,
}

/// Serialize `FormalParameters`, to be estree compatible, with `items` and `rest` fields combined
/// and `argument` field flattened.
impl<'a> Serialize for FormalParameters<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let converted_rest = self.rest.as_ref().map(|rest| SerFormalParameterRest {
span: rest.span,
argument: &rest.argument.kind,
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),
};
converted.serialize(serializer)
}
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "FormalParameters")]
struct SerFormalParameters<'a, 'b> {
#[serde(flatten)]
span: Span,
kind: FormalParameterKind,
items: ElementsAndRest<'a, 'b, FormalParameter<'a>, SerFormalParameterRest<'a, 'b>>,
}

#[derive(Serialize)]
#[serde(tag = "type", rename = "RestElement", rename_all = "camelCase")]
struct SerFormalParameterRest<'a, 'b> {
#[serde(flatten)]
span: Span,
argument: &'b BindingPatternKind<'a>,
type_annotation: &'b Option<Box<'a, TSTypeAnnotation<'a>>>,
optional: bool,
}

pub struct ElementsAndRest<'a, 'b, E, R> {
elements: &'b Vec<'a, E>,
rest: &'b Option<R>,
}

impl<'a, 'b, E, R> ElementsAndRest<'a, 'b, E, R> {
pub fn new(elements: &'b Vec<'a, E>, rest: &'b Option<R>) -> Self {
Self { elements, rest }
}
}

impl<'a, 'b, E: Serialize, R: Serialize> Serialize for ElementsAndRest<'a, 'b, E, R> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.elements.len() + 1))?;
for element in self.elements {
seq.serialize_element(element)?;
}
if let Some(rest) = self.rest {
seq.serialize_element(rest)?;
}
seq.end()
}
}

0 comments on commit 88f94bb

Please sign in to comment.