From bb522fa0e87d06e9cea6fffea17ae9e1fb71a4e6 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:36:12 +0000 Subject: [PATCH] fix(transformer/legacy-decorator): should fallback to `Object` when a type reference refers to a type symbol (#10633) Align TypeScript https://www.typescriptlang.org/play/?experimentalDecorators=true&emitDecoratorMetadata=true&target=99#code/JYWwDg9gTgLgBAbzgMQhANHAQgQypmATzAFM4AtNOAXzgDMoIQ4AiECAExYG4AoXjiQDGAGzxk6AVwB2QmMAjS4goQApecODDwBzEjABccHNMLoNcANYlCRgM4wowaTvObBdoU7AxoRgAqMpLCEACIknt6++LwAlEYAbhDAHHy8ojh2dnAAwiLZCBZCig5QknLQqgACKnBgeDggRqgYdQ0gAEzNVAA+2HiY9VCNAMxGlK1DjQAs42gAdBCLsYgWmsXSdhAiJPMiEDqqUyCD7R2nwyAjFzOxfJrUvI9AA --- .../src/decorator/legacy/metadata.rs | 32 +++++++++++---- .../snapshots/semantic_typescript.snap | 40 +++++-------------- .../snapshots/oxc.snap.md | 14 +++---- .../fixtures/oxc/metadata/imports/input.ts | 6 +-- .../fixtures/oxc/metadata/imports/output.ts | 6 ++- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/crates/oxc_transformer/src/decorator/legacy/metadata.rs b/crates/oxc_transformer/src/decorator/legacy/metadata.rs index 7d1c85b6eae2f..c0d86037c8d34 100644 --- a/crates/oxc_transformer/src/decorator/legacy/metadata.rs +++ b/crates/oxc_transformer/src/decorator/legacy/metadata.rs @@ -334,7 +334,11 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> { name: &TSTypeName<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let serialized_type = self.serialize_entity_name_as_expression_fallback(name, ctx); + let Some(serialized_type) = self.serialize_entity_name_as_expression_fallback(name, ctx) + else { + // Reach here means the referent is a type symbol, so use `Object` as fallback. + return Self::global_object(ctx); + }; let binding = self.ctx.var_declarations.create_uid_var_based_on_node(&serialized_type, ctx); let target = binding.create_write_target(ctx); let assignment = ctx.ast.expression_assignment( @@ -357,29 +361,35 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> { &mut self, name: &TSTypeName<'a>, ctx: &mut TraverseCtx<'a>, - ) -> Expression<'a> { + ) -> Option> { match name { // `A` -> `typeof A !== "undefined" && A` TSTypeName::IdentifierReference(ident) => { let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx); + if Self::is_type_symbol(binding.symbol_id, ctx) { + return None; + } let flags = Self::get_reference_flags(&binding, ctx); let ident1 = binding.create_expression(flags, ctx); let ident2 = binding.create_expression(flags, ctx); - Self::create_checked_value(ident1, ident2, ctx) + Some(Self::create_checked_value(ident1, ident2, ctx)) } TSTypeName::QualifiedName(qualified) => { if let TSTypeName::IdentifierReference(ident) = &qualified.left { // `A.B` -> `typeof A !== "undefined" && A.B` let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx); + if Self::is_type_symbol(binding.symbol_id, ctx) { + return None; + } let flags = Self::get_reference_flags(&binding, ctx); let ident1 = binding.create_expression(flags, ctx); let ident2 = binding.create_expression(flags, ctx); let member = create_property_access(SPAN, ident1, &qualified.right.name, ctx); - Self::create_checked_value(ident2, member, ctx) + Some(Self::create_checked_value(ident2, member, ctx)) } else { // `A.B.C` -> `typeof A !== "undefined" && (_a = A.B) !== void 0 && _a.C` let mut left = - self.serialize_entity_name_as_expression_fallback(&qualified.left, ctx); + self.serialize_entity_name_as_expression_fallback(&qualified.left, ctx)?; let binding = self.ctx.var_declarations.create_uid_var_based_on_node(&left, ctx); let Expression::LogicalExpression(logical) = &mut left else { unreachable!() }; @@ -401,7 +411,7 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> { let object = binding.create_read_expression(ctx); let member = create_property_access(SPAN, object, &qualified.right.name, ctx); - ctx.ast.expression_logical(SPAN, left, LogicalOperator::And, member) + Some(ctx.ast.expression_logical(SPAN, left, LogicalOperator::And, member)) } } } @@ -500,15 +510,21 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> { a.content_eq(b) } + #[inline] + fn is_type_symbol(symbol_id: Option, ctx: &TraverseCtx<'a>) -> bool { + symbol_id.is_some_and(|symbol_id| ctx.scoping().symbol_flags(symbol_id).is_type()) + } + fn get_reference_flags( binding: &MaybeBoundIdentifier<'a>, ctx: &TraverseCtx<'a>, ) -> ReferenceFlags { if let Some(symbol_id) = binding.symbol_id { - let flags = ctx.scoping().symbol_flags(symbol_id); + // Type symbols have filtered out in [`serialize_entity_name_as_expression_fallback`]. + debug_assert!(ctx.scoping().symbol_flags(symbol_id).is_value()); // `design::*type` would be called by `reflect-metadata` APIs, use `Read` flag // to avoid TypeScript remove it because only used as types. - if flags.is_value() { ReferenceFlags::Read } else { ReferenceFlags::Type } + ReferenceFlags::Read } else { // Unresolved reference ReferenceFlags::Type | ReferenceFlags::Read diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 4dac7fe1d4898..e8354b819e549 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -36082,19 +36082,13 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2)] rebuilt : ScopeId(0): [ScopeId(1)] Symbol span mismatch for "BulkEditPreviewProvider": after transform: SymbolId(1): Span { start: 106, end: 129 } -rebuilt : SymbolId(6): Span { start: 0, end: 0 } +rebuilt : SymbolId(5): Span { start: 0, end: 0 } Symbol reference IDs mismatch for "BulkEditPreviewProvider": -after transform: SymbolId(1): [ReferenceId(0), ReferenceId(12), ReferenceId(14), ReferenceId(16), ReferenceId(18)] -rebuilt : SymbolId(6): [ReferenceId(7), ReferenceId(9), ReferenceId(11), ReferenceId(22)] +after transform: SymbolId(1): [ReferenceId(0), ReferenceId(8), ReferenceId(10), ReferenceId(12), ReferenceId(14)] +rebuilt : SymbolId(5): [ReferenceId(7), ReferenceId(9), ReferenceId(11), ReferenceId(18)] Symbol span mismatch for "BulkEditPreviewProvider": -after transform: SymbolId(5): Span { start: 0, end: 0 } -rebuilt : SymbolId(7): Span { start: 106, end: 129 } -Reference symbol mismatch for "IFoo": -after transform: SymbolId(0) "IFoo" -rebuilt : -Reference symbol mismatch for "IFoo": -after transform: SymbolId(0) "IFoo" -rebuilt : +after transform: SymbolId(4): Span { start: 0, end: 0 } +rebuilt : SymbolId(6): Span { start: 106, end: 129 } Reference symbol mismatch for "IFoo": after transform: SymbolId(0) "IFoo" rebuilt : @@ -36695,35 +36689,23 @@ rebuilt : ["Function", "Object", "decorator"] semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts Bindings mismatch: -after transform: ScopeId(0): ["MyComponent", "Service", "_decorate", "_decorateMetadata", "_ref", "decorator"] -rebuilt : ScopeId(0): ["MyComponent", "_decorate", "_decorateMetadata", "_ref"] +after transform: ScopeId(0): ["MyComponent", "Service", "_decorate", "_decorateMetadata", "decorator"] +rebuilt : ScopeId(0): ["MyComponent", "_decorate", "_decorateMetadata"] Symbol span mismatch for "MyComponent": after transform: SymbolId(2): Span { start: 89, end: 100 } -rebuilt : SymbolId(3): Span { start: 0, end: 0 } +rebuilt : SymbolId(2): Span { start: 0, end: 0 } Symbol span mismatch for "MyComponent": -after transform: SymbolId(7): Span { start: 0, end: 0 } -rebuilt : SymbolId(4): Span { start: 89, end: 100 } +after transform: SymbolId(6): Span { start: 0, end: 0 } +rebuilt : SymbolId(3): Span { start: 89, end: 100 } Reference symbol mismatch for "decorator": after transform: SymbolId(1) "decorator" rebuilt : Reference symbol mismatch for "decorator": after transform: SymbolId(1) "decorator" rebuilt : -Reference symbol mismatch for "Service": -after transform: SymbolId(0) "Service" -rebuilt : -Reference flags mismatch for "Service": -after transform: ReferenceId(3): ReferenceFlags(Type) -rebuilt : ReferenceId(14): ReferenceFlags(Read) -Reference symbol mismatch for "Service": -after transform: SymbolId(0) "Service" -rebuilt : -Reference flags mismatch for "Service": -after transform: ReferenceId(4): ReferenceFlags(Type) -rebuilt : ReferenceId(15): ReferenceFlags(Read) Unresolved references mismatch: after transform: ["Function", "Object"] -rebuilt : ["Function", "Object", "Service", "decorator"] +rebuilt : ["Function", "Object", "decorator"] semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport2.ts Symbol flags mismatch for "Services": diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 22665953ddc7a..8a6eb671b1361 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -438,22 +438,22 @@ rebuilt : SymbolId(3): Span { start: 87, end: 94 } * oxc/metadata/imports/input.ts Bindings mismatch: -after transform: ScopeId(0): ["Bar", "Cls", "Foo", "_ref", "dec"] +after transform: ScopeId(0): ["Bar", "Cls", "Foo", "Zoo", "_ref", "dec"] rebuilt : ScopeId(0): ["Cls", "Foo", "_ref"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(2)] rebuilt : ScopeId(0): [ScopeId(1)] Symbol reference IDs mismatch for "Foo": -after transform: SymbolId(0): [ReferenceId(2), ReferenceId(3), ReferenceId(8), ReferenceId(9)] -rebuilt : SymbolId(0): [ReferenceId(7), ReferenceId(8)] +after transform: SymbolId(0): [ReferenceId(2), ReferenceId(3), ReferenceId(12), ReferenceId(13)] +rebuilt : SymbolId(0): [ReferenceId(9), ReferenceId(10)] Symbol span mismatch for "Cls": -after transform: SymbolId(6): Span { start: 135, end: 138 } +after transform: SymbolId(7): Span { start: 145, end: 148 } rebuilt : SymbolId(2): Span { start: 0, end: 0 } Symbol span mismatch for "Cls": -after transform: SymbolId(10): Span { start: 0, end: 0 } -rebuilt : SymbolId(3): Span { start: 135, end: 138 } +after transform: SymbolId(13): Span { start: 0, end: 0 } +rebuilt : SymbolId(3): Span { start: 145, end: 148 } Reference symbol mismatch for "dec": -after transform: SymbolId(2) "dec" +after transform: SymbolId(3) "dec" rebuilt : Unresolved references mismatch: after transform: ["Object", "PropertyDescriptor", "babelHelpers", "console"] diff --git a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/input.ts b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/input.ts index 7eecf4a57a331..5c39b8af77c5f 100644 --- a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/input.ts +++ b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/input.ts @@ -1,4 +1,4 @@ -import { Foo, Bar } from "mod"; +import { Foo, Bar, type Zoo } from "mod"; declare function dec( target: any, @@ -7,7 +7,7 @@ declare function dec( ): void; class Cls { - constructor(@dec param: Foo, param2: Foo | Bar) { - console.log(param, param2); + constructor(@dec param: Foo, param2: Foo | Bar, param3: Zoo, param4: Zoo.o.o) { + console.log(param, param2, param3, param4); } } diff --git a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/output.ts b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/output.ts index f92b5138ab108..0b697c48b0159 100644 --- a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/output.ts +++ b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/imports/output.ts @@ -2,8 +2,8 @@ import { Foo } from "mod"; var _ref; let Cls = class Cls { - constructor(param, param2) { - console.log(param, param2); + constructor(param, param2, param3, param4) { + console.log(param, param2, param3, param4); } }; @@ -14,6 +14,8 @@ Cls = babelHelpers.decorate( ? _ref : Object, Object, + Object, + Object, ]), babelHelpers.decorateParam(0, dec), ],