fix(ast): add RestElements in serialized AST to elements array (#2567)

A step towards #2463.

This PR adds `rest` onto end of `elements` / `properties` array in JSON
AST for `ObjectPattern`, `ArrayPattern`, `ObjectAssignmentTarget`,
`ArrayAssignmentTarget` and `FormalParameters`.
This commit is contained in:
overlookmotel 2024-03-08 02:56:12 +00:00 committed by GitHub
parent 2609e9021b
commit 88f94bb6f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 188 additions and 16 deletions

View file

@ -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)]
@ -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>,
}
@ -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>>,
}
@ -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>,
}
@ -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>>),
@ -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>>>,
}
@ -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>>>,
}
@ -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,
@ -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>>>,
}

View file

@ -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;
@ -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()
}
}