Skip to content

Commit b478cc2

Browse files
committed
feat(napi/parser): preserveParens option for raw transfer
1 parent 42b1000 commit b478cc2

File tree

8 files changed

+76
-26
lines changed

8 files changed

+76
-26
lines changed

crates/oxc_ast/src/ast/js.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,7 @@ pub enum ChainElement<'a> {
10691069
#[ast(visit)]
10701070
#[derive(Debug)]
10711071
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
1072+
#[estree(via = ParenthesizedExpressionConverter)]
10721073
pub struct ParenthesizedExpression<'a> {
10731074
pub span: Span,
10741075
pub expression: Expression<'a>,

crates/oxc_ast/src/generated/derive_estree.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -773,11 +773,7 @@ impl ESTree for ChainElement<'_> {
773773

774774
impl ESTree for ParenthesizedExpression<'_> {
775775
fn serialize<S: Serializer>(&self, serializer: S) {
776-
let mut state = serializer.serialize_struct();
777-
state.serialize_field("type", &JsonSafeString("ParenthesizedExpression"));
778-
state.serialize_field("expression", &self.expression);
779-
state.serialize_span(self.span);
780-
state.end();
776+
crate::serialize::js::ParenthesizedExpressionConverter(self).serialize(serializer)
781777
}
782778
}
783779

crates/oxc_ast/src/serialize/js.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,38 @@ impl ESTree for AssignmentTargetPropertyIdentifierInit<'_> {
447447
}
448448
}
449449
}
450+
451+
/// Converter for [`ParenthesizedExpression`].
452+
///
453+
/// In raw transfer, do not produce a `ParenthesizedExpression` node in AST if `preserveParens` is false.
454+
///
455+
/// Not useful in `oxc-parser`, as can use parser option `preserve_parens`.
456+
/// Required for `oxlint` plugins where we run parser with `preserve_parens` set to `true`,
457+
/// to preserve them on Rust side, but need to remove them on JS side.
458+
///
459+
/// ESTree implementation is unchanged from the auto-generated version.
460+
#[ast_meta]
461+
#[estree(raw_deser = "
462+
let node = DESER[Expression](POS_OFFSET.expression);
463+
if (preserveParens) {
464+
node = {
465+
type: 'ParenthesizedExpression',
466+
expression: node,
467+
start: DESER[u32]( POS_OFFSET.span.start ),
468+
end: DESER[u32]( POS_OFFSET.span.end ),
469+
};
470+
}
471+
node
472+
")]
473+
pub struct ParenthesizedExpressionConverter<'a, 'b>(pub &'b ParenthesizedExpression<'a>);
474+
475+
impl ESTree for ParenthesizedExpressionConverter<'_, '_> {
476+
fn serialize<S: Serializer>(&self, serializer: S) {
477+
let paren_expr = self.0;
478+
let mut state = serializer.serialize_struct();
479+
state.serialize_field("type", &JsonSafeString("ParenthesizedExpression"));
480+
state.serialize_field("expression", &paren_expr.expression);
481+
state.serialize_span(paren_expr.span);
482+
state.end();
483+
}
484+
}

napi/parser/bench.bench.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ for (const { filename, code } of fixtures) {
101101
const deserialize = isJsAst(buffer) ? deserializeJS : deserializeTS;
102102

103103
benchRaw('parser_napi_raw_deser_only', () => {
104-
deserialize(buffer, code, sourceByteLen);
104+
deserialize(buffer, code, sourceByteLen, true);
105105
});
106106

107107
// oxlint-disable-next-line no-unused-vars

napi/parser/generated/deserialize/js.mjs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
4+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen, preserveParens;
55

66
const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true }),
77
decodeStr = textDecoder.decode.bind(textDecoder),
88
{ fromCodePoint } = String;
99

10-
export function deserialize(buffer, sourceTextInput, sourceByteLenInput) {
10+
export function deserialize(buffer, sourceTextInput, sourceByteLenInput, preserveParensInput) {
1111
uint8 = buffer;
1212
uint32 = buffer.uint32;
1313
float64 = buffer.float64;
1414

1515
sourceText = sourceTextInput;
1616
sourceByteLen = sourceByteLenInput;
1717
sourceIsAscii = sourceText.length === sourceByteLen;
18+
preserveParens = preserveParensInput;
1819

1920
const data = deserializeRawTransferData(uint32[536870902]);
2021

@@ -438,12 +439,16 @@ function deserializeChainExpression(pos) {
438439
}
439440

440441
function deserializeParenthesizedExpression(pos) {
441-
return {
442-
type: 'ParenthesizedExpression',
443-
expression: deserializeExpression(pos + 8),
444-
start: deserializeU32(pos),
445-
end: deserializeU32(pos + 4),
446-
};
442+
let node = deserializeExpression(pos + 8);
443+
if (preserveParens) {
444+
node = {
445+
type: 'ParenthesizedExpression',
446+
expression: node,
447+
start: deserializeU32(pos),
448+
end: deserializeU32(pos + 4),
449+
};
450+
}
451+
return node;
447452
}
448453

449454
function deserializeDirective(pos) {

napi/parser/generated/deserialize/ts.mjs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
4+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen, preserveParens;
55

66
const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true }),
77
decodeStr = textDecoder.decode.bind(textDecoder),
88
{ fromCodePoint } = String;
99

10-
export function deserialize(buffer, sourceTextInput, sourceByteLenInput) {
10+
export function deserialize(buffer, sourceTextInput, sourceByteLenInput, preserveParensInput) {
1111
uint8 = buffer;
1212
uint32 = buffer.uint32;
1313
float64 = buffer.float64;
1414

1515
sourceText = sourceTextInput;
1616
sourceByteLen = sourceByteLenInput;
1717
sourceIsAscii = sourceText.length === sourceByteLen;
18+
preserveParens = preserveParensInput;
1819

1920
const data = deserializeRawTransferData(uint32[536870902]);
2021

@@ -488,12 +489,16 @@ function deserializeChainExpression(pos) {
488489
}
489490

490491
function deserializeParenthesizedExpression(pos) {
491-
return {
492-
type: 'ParenthesizedExpression',
493-
expression: deserializeExpression(pos + 8),
494-
start: deserializeU32(pos),
495-
end: deserializeU32(pos + 4),
496-
};
492+
let node = deserializeExpression(pos + 8);
493+
if (preserveParens) {
494+
node = {
495+
type: 'ParenthesizedExpression',
496+
expression: node,
497+
start: deserializeU32(pos),
498+
end: deserializeU32(pos + 4),
499+
};
500+
}
501+
return node;
497502
}
498503

499504
function deserializeDirective(pos) {

napi/parser/raw-transfer/eager.mjs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ function deserialize(buffer, sourceText, sourceByteLen) {
5656
let data;
5757
if (isJsAst(buffer)) {
5858
if (deserializeJS === null) deserializeJS = require('../generated/deserialize/js.mjs').deserialize;
59-
data = deserializeJS(buffer, sourceText, sourceByteLen);
59+
60+
// `preserveParens` argument is unconditionally `true` here. If `options` contains `preserveParens: false`,
61+
// `ParenthesizedExpression` nodes won't be in AST anyway, so the value is irrelevant.
62+
data = deserializeJS(buffer, sourceText, sourceByteLen, true);
6063

6164
// Add a line comment for hashbang
6265
const { hashbang } = data.program;
@@ -65,7 +68,11 @@ function deserialize(buffer, sourceText, sourceByteLen) {
6568
}
6669
} else {
6770
if (deserializeTS === null) deserializeTS = require('../generated/deserialize/ts.mjs').deserialize;
68-
data = deserializeTS(buffer, sourceText, sourceByteLen);
71+
72+
// `preserveParens` argument is unconditionally `true` here. If `options` contains `preserveParens: false`,
73+
// `ParenthesizedExpression` nodes won't be in AST anyway, so the value is irrelevant.
74+
data = deserializeTS(buffer, sourceText, sourceByteLen, true);
75+
6976
// Note: Do not add line comment for hashbang, to match `@typescript-eslint/parser`.
7077
// See https://github.com/oxc-project/oxc/blob/ea784f5f082e4c53c98afde9bf983afd0b95e44e/napi/parser/src/lib.rs#L106-L130
7178
}

tasks/ast_tools/src/generators/raw_transfer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,21 @@ fn generate_deserializers(consts: Constants, schema: &Schema, codegen: &Codegen)
116116

117117
#[rustfmt::skip]
118118
let prelude = format!("
119-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
119+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen, preserveParens;
120120
121121
const textDecoder = new TextDecoder('utf-8', {{ ignoreBOM: true }}),
122122
decodeStr = textDecoder.decode.bind(textDecoder),
123123
{{ fromCodePoint }} = String;
124124
125-
export function deserialize(buffer, sourceTextInput, sourceByteLenInput) {{
125+
export function deserialize(buffer, sourceTextInput, sourceByteLenInput, preserveParensInput) {{
126126
uint8 = buffer;
127127
uint32 = buffer.uint32;
128128
float64 = buffer.float64;
129129
130130
sourceText = sourceTextInput;
131131
sourceByteLen = sourceByteLenInput;
132132
sourceIsAscii = sourceText.length === sourceByteLen;
133+
preserveParens = preserveParensInput;
133134
134135
const data = deserializeRawTransferData(uint32[{data_pointer_pos_32}]);
135136

0 commit comments

Comments
 (0)