Skip to content

Commit 2bc1978

Browse files
committed
fix(transformer/legacy-decorator): correct generating metadata for getter/setter methods (#14495)
* Fixes: #14493 Getter and setter methods should not have `design:returntype` metadata according to TypeScript's `emitDecoratorMetadata` behavior. They should only have `design:type` and `design:paramtypes`. **And `design:type` is different for the getter and setter:** Getter: `design:type` is the same as the return type of method. Setter: `design:type` is the same as the type of the first parameter of the method
1 parent 868ff99 commit 2bc1978

File tree

6 files changed

+186
-64
lines changed

6 files changed

+186
-64
lines changed

crates/oxc_transformer/src/decorator/legacy/metadata.rs

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,25 @@ enum EnumType {
113113
Object,
114114
}
115115

116+
/// Metadata for decorated methods
117+
pub(super) struct MethodMetadata<'a> {
118+
/// The `design:type` metadata expression
119+
pub r#type: Expression<'a>,
120+
/// The `design:paramtypes` metadata expression
121+
pub param_types: Expression<'a>,
122+
/// The `design:returntype` metadata expression (optional, omitted for getters/setters)
123+
pub return_type: Option<Expression<'a>>,
124+
}
125+
116126
pub struct LegacyDecoratorMetadata<'a, 'ctx> {
117127
ctx: &'ctx TransformCtx<'a>,
118-
/// Stack of method metadata arrays, each array contains 3 expressions:
119-
/// `[design:type, design:paramtypes, design:returntype]`
128+
/// Stack of method metadata.
120129
///
121130
/// Only the method that needs to be pushed onto a stack is the method metadata,
122131
/// which should be inserted after all real decorators. However, method parameters
123132
/// will be processed before the metadata generation, so we need to temporarily store
124133
/// them in a stack and pop them when in exit_method_definition.
125-
method_metadata_stack: SparseStack<[Expression<'a>; 3]>,
134+
method_metadata_stack: SparseStack<MethodMetadata<'a>>,
126135
/// Stack of constructor metadata expressions, each expression
127136
/// is the `design:paramtypes`.
128137
///
@@ -182,7 +191,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecoratorMetadata<'a, '_> {
182191
&& constructor.value.params.items.iter().all(|param| param.decorators.is_empty()))
183192
{
184193
let serialized_type =
185-
self.serialize_parameter_types_of_node(&constructor.value.params, ctx);
194+
self.serialize_parameters_types_of_node(&constructor.value.params, ctx);
186195

187196
Some(self.create_metadata("design:paramtypes", serialized_type, ctx))
188197
} else {
@@ -208,18 +217,32 @@ impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecoratorMetadata<'a, '_> {
208217
|| method.value.params.items.iter().any(|param| !param.decorators.is_empty()));
209218

210219
let metadata = is_decorated.then(|| {
211-
[
212-
self.create_metadata("design:type", Self::global_function(ctx), ctx),
213-
{
214-
let serialized_type =
215-
self.serialize_parameter_types_of_node(&method.value.params, ctx);
216-
self.create_metadata("design:paramtypes", serialized_type, ctx)
217-
},
218-
{
219-
let serialized_type = self.serialize_return_type_of_node(&method.value, ctx);
220-
self.create_metadata("design:returntype", serialized_type, ctx)
221-
},
222-
]
220+
// TypeScript only emits `design:returntype` for regular methods,
221+
// not for getters or setters.
222+
223+
let (design_type, return_type) = if method.kind.is_get() {
224+
// For getters, the design type is the type of the property
225+
(self.serialize_return_type_of_node(&method.value, ctx), None)
226+
} else if method.kind.is_set()
227+
&& let Some(param) = method.value.params.items.first()
228+
{
229+
// For setters, the design type is the type of the first parameter
230+
(self.serialize_parameter_types_of_node(param, ctx), None)
231+
} else {
232+
// For methods, the design type is always `Function`
233+
(
234+
Self::global_function(ctx),
235+
Some(self.serialize_return_type_of_node(&method.value, ctx)),
236+
)
237+
};
238+
239+
let param_types = self.serialize_parameters_types_of_node(&method.value.params, ctx);
240+
241+
MethodMetadata {
242+
r#type: self.create_metadata("design:type", design_type, ctx),
243+
param_types: self.create_metadata("design:paramtypes", param_types, ctx),
244+
return_type: return_type.map(|t| self.create_metadata("design:returntype", t, ctx)),
245+
}
223246
});
224247

225248
self.method_metadata_stack.push(metadata);
@@ -302,7 +325,7 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
302325
enum_type
303326
}
304327

305-
pub fn pop_method_metadata(&mut self) -> Option<[Expression<'a>; 3]> {
328+
pub fn pop_method_metadata(&mut self) -> Option<MethodMetadata<'a>> {
306329
self.method_metadata_stack.pop()
307330
}
308331

@@ -407,21 +430,15 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
407430
}
408431

409432
/// Serializes the type of a node for use with decorator type metadata.
410-
fn serialize_parameter_types_of_node(
433+
fn serialize_parameters_types_of_node(
411434
&mut self,
412435
params: &FormalParameters<'a>,
413436
ctx: &mut TraverseCtx<'a>,
414437
) -> Expression<'a> {
415438
let mut elements =
416439
ctx.ast.vec_with_capacity(params.items.len() + usize::from(params.rest.is_some()));
417440
elements.extend(params.items.iter().map(|param| {
418-
let type_annotation = match &param.pattern.kind {
419-
BindingPatternKind::AssignmentPattern(pattern) => {
420-
pattern.left.type_annotation.as_ref()
421-
}
422-
_ => param.pattern.type_annotation.as_ref(),
423-
};
424-
ArrayExpressionElement::from(self.serialize_type_annotation(type_annotation, ctx))
441+
ArrayExpressionElement::from(self.serialize_parameter_types_of_node(param, ctx))
425442
}));
426443

427444
if let Some(rest) = &params.rest {
@@ -432,6 +449,18 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
432449
ctx.ast.expression_array(SPAN, elements)
433450
}
434451

452+
fn serialize_parameter_types_of_node(
453+
&mut self,
454+
param: &FormalParameter<'a>,
455+
ctx: &mut TraverseCtx<'a>,
456+
) -> Expression<'a> {
457+
let type_annotation = match &param.pattern.kind {
458+
BindingPatternKind::AssignmentPattern(pattern) => pattern.left.type_annotation.as_ref(),
459+
_ => param.pattern.type_annotation.as_ref(),
460+
};
461+
self.serialize_type_annotation(type_annotation, ctx)
462+
}
463+
435464
/// Serializes the return type of a node for use with decorator type metadata.
436465
fn serialize_return_type_of_node(
437466
&mut self,

crates/oxc_transformer/src/decorator/legacy/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,11 @@ impl<'a> LegacyDecorator<'a, '_> {
999999
decorations.push(ArrayExpressionElement::from(metadata));
10001000
}
10011001
} else if let Some(metadata) = self.metadata.pop_method_metadata() {
1002-
decorations.extend(metadata.map(ArrayExpressionElement::from));
1002+
decorations.push(ArrayExpressionElement::from(metadata.r#type));
1003+
decorations.push(ArrayExpressionElement::from(metadata.param_types));
1004+
if let Some(return_type) = metadata.return_type {
1005+
decorations.push(ArrayExpressionElement::from(return_type));
1006+
}
10031007
}
10041008
}
10051009

tasks/coverage/snapshots/semantic_typescript.snap

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8672,7 +8672,7 @@ rebuilt : [ReferenceId(13)]
86728672

86738673
semantic Error: tasks/coverage/typescript/tests/cases/compiler/decoratorMetadataElidedImport.ts
86748674
Symbol reference IDs mismatch for "Observable":
8675-
after transform: SymbolId(0): [ReferenceId(2), ReferenceId(8), ReferenceId(9)]
8675+
after transform: SymbolId(0): [ReferenceId(2), ReferenceId(4), ReferenceId(5)]
86768676
rebuilt : SymbolId(0): [ReferenceId(12), ReferenceId(13)]
86778677

86788678
semantic Error: tasks/coverage/typescript/tests/cases/compiler/decoratorMetadataElidedImportOnDeclare.ts
@@ -8727,22 +8727,22 @@ Reference symbol mismatch for "decorator":
87278727
after transform: SymbolId(0) "decorator"
87288728
rebuilt : <None>
87298729
Reference flags mismatch for "Promise":
8730-
after transform: ReferenceId(26): ReferenceFlags(Read | Type)
8730+
after transform: ReferenceId(30): ReferenceFlags(Read | Type)
87318731
rebuilt : ReferenceId(28): ReferenceFlags(Read)
87328732
Reference flags mismatch for "Promise":
8733-
after transform: ReferenceId(27): ReferenceFlags(Read | Type)
8733+
after transform: ReferenceId(31): ReferenceFlags(Read | Type)
87348734
rebuilt : ReferenceId(29): ReferenceFlags(Read)
87358735
Reference flags mismatch for "Promise":
8736-
after transform: ReferenceId(32): ReferenceFlags(Read | Type)
8736+
after transform: ReferenceId(25): ReferenceFlags(Read | Type)
87378737
rebuilt : ReferenceId(34): ReferenceFlags(Read)
87388738
Reference flags mismatch for "Promise":
8739-
after transform: ReferenceId(33): ReferenceFlags(Read | Type)
8739+
after transform: ReferenceId(26): ReferenceFlags(Read | Type)
87408740
rebuilt : ReferenceId(35): ReferenceFlags(Read)
87418741
Unresolved references mismatch:
87428742
after transform: ["Function", "MethodDecorator", "Object", "Promise", "require"]
87438743
rebuilt : ["Function", "Object", "Promise", "decorator", "require"]
87448744
Unresolved reference IDs mismatch for "Promise":
8745-
after transform: [ReferenceId(3), ReferenceId(5), ReferenceId(6), ReferenceId(11), ReferenceId(19), ReferenceId(26), ReferenceId(27), ReferenceId(32), ReferenceId(33)]
8745+
after transform: [ReferenceId(3), ReferenceId(5), ReferenceId(6), ReferenceId(9), ReferenceId(17), ReferenceId(25), ReferenceId(26), ReferenceId(30), ReferenceId(31)]
87468746
rebuilt : [ReferenceId(12), ReferenceId(20), ReferenceId(28), ReferenceId(29), ReferenceId(34), ReferenceId(35)]
87478747

87488748
semantic Error: tasks/coverage/typescript/tests/cases/compiler/decoratorMetadataRestParameterWithImportedType.ts
@@ -8864,7 +8864,7 @@ rebuilt : ["Object", "database"]
88648864

88658865
semantic Error: tasks/coverage/typescript/tests/cases/compiler/decoratorReferenceOnOtherProperty.ts
88668866
Symbol reference IDs mismatch for "Yoha":
8867-
after transform: SymbolId(0): [ReferenceId(1), ReferenceId(5), ReferenceId(6)]
8867+
after transform: SymbolId(0): [ReferenceId(1), ReferenceId(4), ReferenceId(5)]
88688868
rebuilt : SymbolId(0): [ReferenceId(8), ReferenceId(9)]
88698869

88708870
semantic Error: tasks/coverage/typescript/tests/cases/compiler/decoratorReferences.ts
@@ -35962,8 +35962,8 @@ Reference symbol mismatch for "dec":
3596235962
after transform: SymbolId(0) "dec"
3596335963
rebuilt : <None>
3596435964
Unresolved references mismatch:
35965-
after transform: ["Function", "TypedPropertyDescriptor", "require"]
35966-
rebuilt : ["Function", "dec", "require"]
35965+
after transform: ["TypedPropertyDescriptor", "require"]
35966+
rebuilt : ["dec", "require"]
3596735967

3596835968
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor2.ts
3596935969
Bindings mismatch:
@@ -35976,8 +35976,8 @@ Reference symbol mismatch for "dec":
3597635976
after transform: SymbolId(0) "dec"
3597735977
rebuilt : <None>
3597835978
Unresolved references mismatch:
35979-
after transform: ["Function", "TypedPropertyDescriptor", "require"]
35980-
rebuilt : ["Function", "dec", "require"]
35979+
after transform: ["TypedPropertyDescriptor", "require"]
35980+
rebuilt : ["dec", "require"]
3598135981

3598235982
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor4.ts
3598335983
Bindings mismatch:
@@ -35990,8 +35990,8 @@ Reference symbol mismatch for "dec":
3599035990
after transform: SymbolId(0) "dec"
3599135991
rebuilt : <None>
3599235992
Unresolved references mismatch:
35993-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
35994-
rebuilt : ["Function", "Number", "dec", "require"]
35993+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
35994+
rebuilt : ["Number", "dec", "require"]
3599535995

3599635996
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor5.ts
3599735997
Bindings mismatch:
@@ -36004,8 +36004,8 @@ Reference symbol mismatch for "dec":
3600436004
after transform: SymbolId(0) "dec"
3600536005
rebuilt : <None>
3600636006
Unresolved references mismatch:
36007-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
36008-
rebuilt : ["Function", "Number", "dec", "require"]
36007+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
36008+
rebuilt : ["Number", "dec", "require"]
3600936009

3601036010
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor8.ts
3601136011
Bindings mismatch:
@@ -36033,8 +36033,8 @@ Reference symbol mismatch for "dec":
3603336033
after transform: SymbolId(0) "dec"
3603436034
rebuilt : <None>
3603536035
Unresolved references mismatch:
36036-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
36037-
rebuilt : ["Function", "Number", "dec", "require"]
36036+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
36037+
rebuilt : ["Number", "dec", "require"]
3603836038

3603936039
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/constructor/decoratorOnClassConstructor3.ts
3604036040
Unresolved references mismatch:
@@ -38551,8 +38551,8 @@ Reference symbol mismatch for "dec":
3855138551
after transform: SymbolId(0) "dec"
3855238552
rebuilt : <None>
3855338553
Unresolved references mismatch:
38554-
after transform: ["Function", "TypedPropertyDescriptor"]
38555-
rebuilt : ["Function", "dec"]
38554+
after transform: ["TypedPropertyDescriptor"]
38555+
rebuilt : ["dec"]
3855638556

3855738557
semantic Error: tasks/coverage/typescript/tests/cases/conformance/es6/decorators/class/decoratorOnClass1.es6.ts
3855838558
Bindings mismatch:
@@ -39372,10 +39372,10 @@ Bindings mismatch:
3937239372
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "dec", "method3"]
3937339373
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "method3"]
3937439374
Reference flags mismatch for "_method":
39375-
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
39375+
after transform: ReferenceId(31): ReferenceFlags(Read | Write)
3937639376
rebuilt : ReferenceId(2): ReferenceFlags(Write)
3937739377
Reference flags mismatch for "_method2":
39378-
after transform: ReferenceId(48): ReferenceFlags(Read | Write)
39378+
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
3937939379
rebuilt : ReferenceId(4): ReferenceFlags(Write)
3938039380
Reference symbol mismatch for "dec":
3938139381
after transform: SymbolId(0) "dec"
@@ -39396,8 +39396,8 @@ Reference symbol mismatch for "dec":
3939639396
after transform: SymbolId(0) "dec"
3939739397
rebuilt : <None>
3939839398
Unresolved references mismatch:
39399-
after transform: ["Function", "Object", "require"]
39400-
rebuilt : ["Function", "Object", "dec", "require"]
39399+
after transform: ["Object", "require"]
39400+
rebuilt : ["Object", "dec", "require"]
3940139401

3940239402
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-nonStaticPrivate.ts
3940339403
Bindings mismatch:
@@ -39410,18 +39410,18 @@ Reference symbol mismatch for "dec":
3941039410
after transform: SymbolId(0) "dec"
3941139411
rebuilt : <None>
3941239412
Unresolved references mismatch:
39413-
after transform: ["Function", "Object", "WeakSet", "require"]
39414-
rebuilt : ["Function", "Object", "WeakSet", "dec", "require"]
39413+
after transform: ["Object", "WeakSet", "require"]
39414+
rebuilt : ["Object", "WeakSet", "dec", "require"]
3941539415

3941639416
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-static.ts
3941739417
Bindings mismatch:
3941839418
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "dec", "method3"]
3941939419
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "method3"]
3942039420
Reference flags mismatch for "_method":
39421-
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
39421+
after transform: ReferenceId(31): ReferenceFlags(Read | Write)
3942239422
rebuilt : ReferenceId(2): ReferenceFlags(Write)
3942339423
Reference flags mismatch for "_method2":
39424-
after transform: ReferenceId(48): ReferenceFlags(Read | Write)
39424+
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
3942539425
rebuilt : ReferenceId(4): ReferenceFlags(Write)
3942639426
Reference symbol mismatch for "dec":
3942739427
after transform: SymbolId(0) "dec"
@@ -39442,8 +39442,8 @@ Reference symbol mismatch for "dec":
3944239442
after transform: SymbolId(0) "dec"
3944339443
rebuilt : <None>
3944439444
Unresolved references mismatch:
39445-
after transform: ["Function", "Object", "require"]
39446-
rebuilt : ["Function", "Object", "dec", "require"]
39445+
after transform: ["Object", "require"]
39446+
rebuilt : ["Object", "dec", "require"]
3944739447

3944839448
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-staticPrivate.ts
3944939449
Bindings mismatch:
@@ -39453,13 +39453,13 @@ Symbol span mismatch for "D":
3945339453
after transform: SymbolId(3): Span { start: 137, end: 138 }
3945439454
rebuilt : SymbolId(9): Span { start: 0, end: 0 }
3945539455
Symbol reference IDs mismatch for "D":
39456-
after transform: SymbolId(3): [ReferenceId(23), ReferenceId(25)]
39457-
rebuilt : SymbolId(9): [ReferenceId(20), ReferenceId(30), ReferenceId(33)]
39456+
after transform: SymbolId(3): [ReferenceId(20), ReferenceId(22)]
39457+
rebuilt : SymbolId(9): [ReferenceId(17), ReferenceId(27), ReferenceId(30)]
3945839458
Symbol span mismatch for "D":
3945939459
after transform: SymbolId(14): Span { start: 0, end: 0 }
3946039460
rebuilt : SymbolId(10): Span { start: 137, end: 138 }
3946139461
Symbol reference IDs mismatch for "D":
39462-
after transform: SymbolId(14): [ReferenceId(28)]
39462+
after transform: SymbolId(14): [ReferenceId(25)]
3946339463
rebuilt : SymbolId(10): []
3946439464
Reference symbol mismatch for "dec":
3946539465
after transform: SymbolId(0) "dec"
@@ -39474,8 +39474,8 @@ Reference symbol mismatch for "dec":
3947439474
after transform: SymbolId(0) "dec"
3947539475
rebuilt : <None>
3947639476
Unresolved references mismatch:
39477-
after transform: ["Function", "Object", "require"]
39478-
rebuilt : ["Function", "Object", "dec", "require"]
39477+
after transform: ["Object", "require"]
39478+
rebuilt : ["Object", "dec", "require"]
3947939479

3948039480
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/classSuper/esDecorators-classDeclaration-classSuper.1.ts
3948139481
Bindings mismatch:
@@ -40482,7 +40482,7 @@ Bindings mismatch:
4048240482
after transform: ScopeId(0): ["_C", "_decorateMetadata", "_defineProperty", "_get_x", "_method", "_set_x", "_y", "dec"]
4048340483
rebuilt : ScopeId(0): ["_C", "_decorateMetadata", "_defineProperty", "_get_x", "_method", "_set_x", "_y"]
4048440484
Symbol reference IDs mismatch for "_decorateMetadata":
40485-
after transform: SymbolId(10): [ReferenceId(24), ReferenceId(25), ReferenceId(26), ReferenceId(28), ReferenceId(29), ReferenceId(30), ReferenceId(32), ReferenceId(34), ReferenceId(35), ReferenceId(37), ReferenceId(39), ReferenceId(41), ReferenceId(42), ReferenceId(43), ReferenceId(45), ReferenceId(46), ReferenceId(47), ReferenceId(49), ReferenceId(51), ReferenceId(52), ReferenceId(54), ReferenceId(56)]
40485+
after transform: SymbolId(10): [ReferenceId(24), ReferenceId(25), ReferenceId(26), ReferenceId(27), ReferenceId(28), ReferenceId(31), ReferenceId(32), ReferenceId(34), ReferenceId(36), ReferenceId(38), ReferenceId(39), ReferenceId(40), ReferenceId(41), ReferenceId(42), ReferenceId(45), ReferenceId(46), ReferenceId(48), ReferenceId(50)]
4048640486
rebuilt : SymbolId(1): []
4048740487
Reference symbol mismatch for "dec":
4048840488
after transform: SymbolId(0) "dec"

0 commit comments

Comments
 (0)