Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/oxc_ast/src/ast/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ pub enum TSAccessibility {
#[plural(TSClassImplementsList)]
pub struct TSClassImplements<'a> {
pub span: Span,
#[estree(via = TSClassImplementsExpression)]
pub expression: TSTypeName<'a>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_ast/src/generated/derive_estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2851,7 +2851,7 @@ impl ESTree for TSClassImplements<'_> {
state.serialize_field("type", &JsonSafeString("TSClassImplements"));
state.serialize_field("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("expression", &self.expression);
state.serialize_field("expression", &crate::serialize::TSClassImplementsExpression(self));
state.serialize_field("typeArguments", &self.type_arguments);
state.end();
}
Expand Down
73 changes: 73 additions & 0 deletions crates/oxc_ast/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,79 @@ impl ESTree for TSTypeNameIdentifierReference<'_, '_> {
}
}

/// Serializer for `expression` field of `TSClassImplements`.
///
/// Our AST represents `X.Y` in `class C implements X.Y {}` as a `TSQualifiedName`.
/// TS-ESTree represents `X.Y` as a `MemberExpression`.
///
/// Where there are more parts e.g. `class C implements X.Y.Z {}`, the `TSQualifiedName`s (Oxc)
/// or `MemberExpression`s (TS-ESTree) are nested.
#[ast_meta]
#[estree(
ts_type = "IdentifierReference | ThisExpression | MemberExpression",
raw_deser = "
let expression = DESER[TSTypeName](POS_OFFSET.expression);
if (expression.type === 'TSQualifiedName') {
let parent = expression = {
type: 'MemberExpression',
start: expression.start,
end: expression.end,
object: expression.left,
property: expression.right,
computed: false,
optional: false,
};

while (parent.object.type === 'TSQualifiedName') {
const object = parent.object;
parent = parent.object = {
type: 'MemberExpression',
start: object.start,
end: object.end,
object: object.left,
property: object.right,
computed: false,
optional: false,
};
}
}
expression
"
)]
pub struct TSClassImplementsExpression<'a, 'b>(pub &'b TSClassImplements<'a>);

impl ESTree for TSClassImplementsExpression<'_, '_> {
#[inline] // Because it just delegates
fn serialize<S: Serializer>(&self, serializer: S) {
TSTypeNameAsMemberExpression(&self.0.expression).serialize(serializer);
}
}

struct TSTypeNameAsMemberExpression<'a, 'b>(&'b TSTypeName<'a>);

impl ESTree for TSTypeNameAsMemberExpression<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self.0 {
TSTypeName::IdentifierReference(ident) => {
TSTypeNameIdentifierReference(ident).serialize(serializer);
}
TSTypeName::QualifiedName(name) => {
// Convert to `TSQualifiedName` to `MemberExpression`.
// Recursively convert `left` to `MemberExpression` too if it's a `TSQualifiedName`.
let mut state = serializer.serialize_struct();
state.serialize_field("type", &JsonSafeString("MemberExpression"));
state.serialize_field("start", &name.span.start);
state.serialize_field("end", &name.span.end);
state.serialize_field("object", &TSTypeNameAsMemberExpression(&name.left));
state.serialize_field("property", &name.right);
state.serialize_field("computed", &false);
state.serialize_field("optional", &false);
state.end();
}
}
}
}

/// Serializer for `params` field of `TSCallSignatureDeclaration`.
///
/// These add `this_param` to start of the `params` array.
Expand Down
27 changes: 26 additions & 1 deletion napi/parser/deserialize-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -1622,11 +1622,36 @@ function deserializeTSTypeAliasDeclaration(pos) {
}

function deserializeTSClassImplements(pos) {
let expression = deserializeTSTypeName(pos + 8);
if (expression.type === 'TSQualifiedName') {
let parent = expression = {
type: 'MemberExpression',
start: expression.start,
end: expression.end,
object: expression.left,
property: expression.right,
computed: false,
optional: false,
};

while (parent.object.type === 'TSQualifiedName') {
const object = parent.object;
parent = parent.object = {
type: 'MemberExpression',
start: object.start,
end: object.end,
object: object.left,
property: object.right,
computed: false,
optional: false,
};
}
}
return {
type: 'TSClassImplements',
start: deserializeU32(pos),
end: deserializeU32(pos + 4),
expression: deserializeTSTypeName(pos + 8),
expression,
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24),
};
}
Expand Down
27 changes: 26 additions & 1 deletion napi/parser/deserialize-ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -1774,11 +1774,36 @@ function deserializeTSTypeAliasDeclaration(pos) {
}

function deserializeTSClassImplements(pos) {
let expression = deserializeTSTypeName(pos + 8);
if (expression.type === 'TSQualifiedName') {
let parent = expression = {
type: 'MemberExpression',
start: expression.start,
end: expression.end,
object: expression.left,
property: expression.right,
computed: false,
optional: false,
};

while (parent.object.type === 'TSQualifiedName') {
const object = parent.object;
parent = parent.object = {
type: 'MemberExpression',
start: object.start,
end: object.end,
object: object.left,
property: object.right,
computed: false,
optional: false,
};
}
}
return {
type: 'TSClassImplements',
start: deserializeU32(pos),
end: deserializeU32(pos + 4),
expression: deserializeTSTypeName(pos + 8),
expression,
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24),
};
}
Expand Down
2 changes: 1 addition & 1 deletion npm/oxc-types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ export type TSAccessibility = 'private' | 'protected' | 'public';

export interface TSClassImplements extends Span {
type: 'TSClassImplements';
expression: TSTypeName;
expression: IdentifierReference | ThisExpression | MemberExpression;
typeArguments: TSTypeParameterInstantiation | null;
}

Expand Down
36 changes: 1 addition & 35 deletions tasks/coverage/snapshots/estree_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ commit: 15392346

estree_typescript Summary:
AST Parsed : 11245/11404 (98.61%)
Positive Passed: 11074/11404 (97.11%)
Positive Passed: 11091/11404 (97.26%)
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts
A class member cannot have the 'const' keyword.

Expand Down Expand Up @@ -46,18 +46,12 @@ Mismatch: tasks/coverage/typescript/tests/cases/compiler/bigintWithLib.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/bigintWithoutLib.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/bluebirdStaticThis.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/classExpressionPropertyModifiers.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/classHeritageWithTrailingSeparator.ts
Expected `{` but found `EOF`

Mismatch: tasks/coverage/typescript/tests/cases/compiler/classdecl.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/complicatedPrivacy.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/constDeclarations-errors.ts
Missing initializer in const declaration

Expand All @@ -78,18 +72,6 @@ Mismatch: tasks/coverage/typescript/tests/cases/compiler/controlFlowInstanceofWi
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/convertKeywordsYes.ts
Classes can't have a field named 'constructor'

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declFileGenericType2.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declFileModuleContinuation.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declFileWithInternalModuleNameConflictsInExtendsClause1.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declFileWithInternalModuleNameConflictsInExtendsClause2.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declFileWithInternalModuleNameConflictsInExtendsClause3.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declarationEmitHasTypesRefOnNamespaceUse.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/declareAlreadySeen.ts
declare' modifier already seen.

Expand Down Expand Up @@ -181,8 +163,6 @@ A rest parameter cannot be optional
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/fileWithNextLine3.ts
A 'return' statement can only be used within a function body.

Mismatch: tasks/coverage/typescript/tests/cases/compiler/genericClassImplementingGenericInterfaceFromAnotherModule.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/illegalModifiersOnClassElements.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Expand Down Expand Up @@ -222,8 +202,6 @@ Unexpected token
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/indexerSignatureWithRestParam.ts
Unexpected token

Mismatch: tasks/coverage/typescript/tests/cases/compiler/interfaceDeclaration3.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/jsFileCompilationAbstractModifier.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Expand Down Expand Up @@ -290,8 +268,6 @@ Expected a semicolon or an implicit semicolon after a statement, but found none
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/jsFileCompilationWithoutJsExtensions.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Mismatch: tasks/coverage/typescript/tests/cases/compiler/jsxElementClassTooManyParams.tsx

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/letDeclarations-invalidContexts.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Expand Down Expand Up @@ -355,13 +331,9 @@ Type parameters cannot appear on a constructor declaration
serde_json::from_str(oxc_json) Error: tasks/coverage/typescript/tests/cases/compiler/parsingDeepParenthensizedExpression.ts
recursion limit exceeded at line 3334 column 269

Mismatch: tasks/coverage/typescript/tests/cases/compiler/privacyClassImplementsClauseDeclFile.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/privacyImportParseErrors.ts
'export' modifier cannot be used here.

Mismatch: tasks/coverage/typescript/tests/cases/compiler/recursiveClassReferenceTest.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/regularExpressionScanning.ts
Unexpected flag a in regular expression literal

Expand Down Expand Up @@ -396,8 +368,6 @@ Unexpected token
Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/staticPrototypeProperty.ts
Classes may not have a static property named prototype

Mismatch: tasks/coverage/typescript/tests/cases/compiler/strictModeReservedWordInClassDeclaration.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/thisAssignmentInNamespaceDeclaration1.ts
Expected a semicolon or an implicit semicolon after a statement, but found none

Expand Down Expand Up @@ -657,8 +627,6 @@ Expected `,` but found `Identifier`
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts
Expected `from` but found `Identifier`

Mismatch: tasks/coverage/typescript/tests/cases/conformance/externalModules/typeOnly/implementsClause.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/externalModules/typeOnly/importSpecifiers_js.ts
Expected `,` but found `Identifier`

Expand All @@ -680,8 +648,6 @@ Expected `from` but found `=`
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/jsdoc/declarations/jsDeclarationsInterfaces.ts
Unexpected token

Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarationsLocalTypes.tsx

Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/jsx/jsxCheckJsxNoTypeArgumentsAllowed.tsx
Expected `>` but found `<`

Expand Down
Loading