Skip to content

Commit 5ca03ef

Browse files
committed
fix(transformer): skip design:returntype metadata for getter/setter methods
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. This change: - Updates enter_method_definition to conditionally emit design:returntype only for regular methods - Adds comprehensive test case for getter/setter metadata emission - Ensures compatibility with TypeScript's decorator metadata implementation Fixes #14493
1 parent 92e1748 commit 5ca03ef

File tree

6 files changed

+176
-56
lines changed

6 files changed

+176
-56
lines changed

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

Lines changed: 55 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,33 @@ 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+
let should_add_return_type = !method.kind.is_get() && !method.kind.is_set();
223+
224+
let design_type = if method.kind.is_get() {
225+
// For getters, the design type is the type of the property
226+
self.serialize_return_type_of_node(&method.value, ctx)
227+
} else if method.kind.is_set()
228+
&& let Some(param) = method.value.params.items.first()
229+
{
230+
// For setters, the design type is the type of the first parameter
231+
self.serialize_parameter_types_of_node(param, ctx)
232+
} else {
233+
// For methods, the design type is always `Function`
234+
Self::global_function(ctx)
235+
};
236+
237+
let param_types = self.serialize_parameters_types_of_node(&method.value.params, ctx);
238+
239+
let return_type = should_add_return_type
240+
.then(|| self.serialize_return_type_of_node(&method.value, ctx));
241+
242+
MethodMetadata {
243+
r#type: self.create_metadata("design:type", design_type, ctx),
244+
param_types: self.create_metadata("design:paramtypes", param_types, ctx),
245+
return_type: return_type.map(|t| self.create_metadata("design:returntype", t, ctx)),
246+
}
223247
});
224248

225249
self.method_metadata_stack.push(metadata);
@@ -302,7 +326,7 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
302326
enum_type
303327
}
304328

305-
pub fn pop_method_metadata(&mut self) -> Option<[Expression<'a>; 3]> {
329+
pub fn pop_method_metadata(&mut self) -> Option<MethodMetadata<'a>> {
306330
self.method_metadata_stack.pop()
307331
}
308332

@@ -407,21 +431,15 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
407431
}
408432

409433
/// Serializes the type of a node for use with decorator type metadata.
410-
fn serialize_parameter_types_of_node(
434+
fn serialize_parameters_types_of_node(
411435
&mut self,
412436
params: &FormalParameters<'a>,
413437
ctx: &mut TraverseCtx<'a>,
414438
) -> Expression<'a> {
415439
let mut elements =
416440
ctx.ast.vec_with_capacity(params.items.len() + usize::from(params.rest.is_some()));
417441
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))
442+
ArrayExpressionElement::from(self.serialize_parameter_types_of_node(param, ctx))
425443
}));
426444

427445
if let Some(rest) = &params.rest {
@@ -432,6 +450,18 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> {
432450
ctx.ast.expression_array(SPAN, elements)
433451
}
434452

453+
fn serialize_parameter_types_of_node(
454+
&mut self,
455+
param: &FormalParameter<'a>,
456+
ctx: &mut TraverseCtx<'a>,
457+
) -> Expression<'a> {
458+
let type_annotation = match &param.pattern.kind {
459+
BindingPatternKind::AssignmentPattern(pattern) => pattern.left.type_annotation.as_ref(),
460+
_ => param.pattern.type_annotation.as_ref(),
461+
};
462+
self.serialize_type_annotation(type_annotation, ctx)
463+
}
464+
435465
/// Serializes the return type of a node for use with decorator type metadata.
436466
fn serialize_return_type_of_node(
437467
&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: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35820,8 +35820,8 @@ Reference symbol mismatch for "dec":
3582035820
after transform: SymbolId(0) "dec"
3582135821
rebuilt : <None>
3582235822
Unresolved references mismatch:
35823-
after transform: ["Function", "TypedPropertyDescriptor", "require"]
35824-
rebuilt : ["Function", "dec", "require"]
35823+
after transform: ["TypedPropertyDescriptor", "require"]
35824+
rebuilt : ["dec", "require"]
3582535825

3582635826
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor2.ts
3582735827
Bindings mismatch:
@@ -35834,8 +35834,8 @@ Reference symbol mismatch for "dec":
3583435834
after transform: SymbolId(0) "dec"
3583535835
rebuilt : <None>
3583635836
Unresolved references mismatch:
35837-
after transform: ["Function", "TypedPropertyDescriptor", "require"]
35838-
rebuilt : ["Function", "dec", "require"]
35837+
after transform: ["TypedPropertyDescriptor", "require"]
35838+
rebuilt : ["dec", "require"]
3583935839

3584035840
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor4.ts
3584135841
Bindings mismatch:
@@ -35848,8 +35848,8 @@ Reference symbol mismatch for "dec":
3584835848
after transform: SymbolId(0) "dec"
3584935849
rebuilt : <None>
3585035850
Unresolved references mismatch:
35851-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
35852-
rebuilt : ["Function", "Number", "dec", "require"]
35851+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
35852+
rebuilt : ["Number", "dec", "require"]
3585335853

3585435854
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor5.ts
3585535855
Bindings mismatch:
@@ -35862,8 +35862,8 @@ Reference symbol mismatch for "dec":
3586235862
after transform: SymbolId(0) "dec"
3586335863
rebuilt : <None>
3586435864
Unresolved references mismatch:
35865-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
35866-
rebuilt : ["Function", "Number", "dec", "require"]
35865+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
35866+
rebuilt : ["Number", "dec", "require"]
3586735867

3586835868
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/accessor/decoratorOnClassAccessor8.ts
3586935869
Bindings mismatch:
@@ -35891,8 +35891,8 @@ Reference symbol mismatch for "dec":
3589135891
after transform: SymbolId(0) "dec"
3589235892
rebuilt : <None>
3589335893
Unresolved references mismatch:
35894-
after transform: ["Function", "Number", "TypedPropertyDescriptor", "require"]
35895-
rebuilt : ["Function", "Number", "dec", "require"]
35894+
after transform: ["Number", "TypedPropertyDescriptor", "require"]
35895+
rebuilt : ["Number", "dec", "require"]
3589635896

3589735897
semantic Error: tasks/coverage/typescript/tests/cases/conformance/decorators/class/constructor/decoratorOnClassConstructor3.ts
3589835898
Unresolved references mismatch:
@@ -38400,8 +38400,8 @@ Reference symbol mismatch for "dec":
3840038400
after transform: SymbolId(0) "dec"
3840138401
rebuilt : <None>
3840238402
Unresolved references mismatch:
38403-
after transform: ["Function", "TypedPropertyDescriptor"]
38404-
rebuilt : ["Function", "dec"]
38403+
after transform: ["TypedPropertyDescriptor"]
38404+
rebuilt : ["dec"]
3840538405

3840638406
semantic Error: tasks/coverage/typescript/tests/cases/conformance/es6/decorators/class/decoratorOnClass1.es6.ts
3840738407
Bindings mismatch:
@@ -39221,10 +39221,10 @@ Bindings mismatch:
3922139221
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "dec", "method3"]
3922239222
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "method3"]
3922339223
Reference flags mismatch for "_method":
39224-
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
39224+
after transform: ReferenceId(31): ReferenceFlags(Read | Write)
3922539225
rebuilt : ReferenceId(2): ReferenceFlags(Write)
3922639226
Reference flags mismatch for "_method2":
39227-
after transform: ReferenceId(48): ReferenceFlags(Read | Write)
39227+
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
3922839228
rebuilt : ReferenceId(4): ReferenceFlags(Write)
3922939229
Reference symbol mismatch for "dec":
3923039230
after transform: SymbolId(0) "dec"
@@ -39245,8 +39245,8 @@ Reference symbol mismatch for "dec":
3924539245
after transform: SymbolId(0) "dec"
3924639246
rebuilt : <None>
3924739247
Unresolved references mismatch:
39248-
after transform: ["Function", "Object", "require"]
39249-
rebuilt : ["Function", "Object", "dec", "require"]
39248+
after transform: ["Object", "require"]
39249+
rebuilt : ["Object", "dec", "require"]
3925039250

3925139251
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-nonStaticPrivate.ts
3925239252
Bindings mismatch:
@@ -39259,18 +39259,18 @@ Reference symbol mismatch for "dec":
3925939259
after transform: SymbolId(0) "dec"
3926039260
rebuilt : <None>
3926139261
Unresolved references mismatch:
39262-
after transform: ["Function", "Object", "WeakSet", "require"]
39263-
rebuilt : ["Function", "Object", "WeakSet", "dec", "require"]
39262+
after transform: ["Object", "WeakSet", "require"]
39263+
rebuilt : ["Object", "WeakSet", "dec", "require"]
3926439264

3926539265
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-static.ts
3926639266
Bindings mismatch:
3926739267
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "dec", "method3"]
3926839268
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_method", "_method2", "method3"]
3926939269
Reference flags mismatch for "_method":
39270-
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
39270+
after transform: ReferenceId(31): ReferenceFlags(Read | Write)
3927139271
rebuilt : ReferenceId(2): ReferenceFlags(Write)
3927239272
Reference flags mismatch for "_method2":
39273-
after transform: ReferenceId(48): ReferenceFlags(Read | Write)
39273+
after transform: ReferenceId(39): ReferenceFlags(Read | Write)
3927439274
rebuilt : ReferenceId(4): ReferenceFlags(Write)
3927539275
Reference symbol mismatch for "dec":
3927639276
after transform: SymbolId(0) "dec"
@@ -39291,8 +39291,8 @@ Reference symbol mismatch for "dec":
3929139291
after transform: SymbolId(0) "dec"
3929239292
rebuilt : <None>
3929339293
Unresolved references mismatch:
39294-
after transform: ["Function", "Object", "require"]
39295-
rebuilt : ["Function", "Object", "dec", "require"]
39294+
after transform: ["Object", "require"]
39295+
rebuilt : ["Object", "dec", "require"]
3929639296

3929739297
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/accessors/esDecorators-classDeclaration-accessors-staticPrivate.ts
3929839298
Bindings mismatch:
@@ -39302,13 +39302,13 @@ Symbol span mismatch for "D":
3930239302
after transform: SymbolId(3): Span { start: 137, end: 138 }
3930339303
rebuilt : SymbolId(9): Span { start: 0, end: 0 }
3930439304
Symbol reference IDs mismatch for "D":
39305-
after transform: SymbolId(3): [ReferenceId(23), ReferenceId(25)]
39306-
rebuilt : SymbolId(9): [ReferenceId(20), ReferenceId(30), ReferenceId(33)]
39305+
after transform: SymbolId(3): [ReferenceId(20), ReferenceId(22)]
39306+
rebuilt : SymbolId(9): [ReferenceId(17), ReferenceId(27), ReferenceId(30)]
3930739307
Symbol span mismatch for "D":
3930839308
after transform: SymbolId(14): Span { start: 0, end: 0 }
3930939309
rebuilt : SymbolId(10): Span { start: 137, end: 138 }
3931039310
Symbol reference IDs mismatch for "D":
39311-
after transform: SymbolId(14): [ReferenceId(28)]
39311+
after transform: SymbolId(14): [ReferenceId(25)]
3931239312
rebuilt : SymbolId(10): []
3931339313
Reference symbol mismatch for "dec":
3931439314
after transform: SymbolId(0) "dec"
@@ -39323,8 +39323,8 @@ Reference symbol mismatch for "dec":
3932339323
after transform: SymbolId(0) "dec"
3932439324
rebuilt : <None>
3932539325
Unresolved references mismatch:
39326-
after transform: ["Function", "Object", "require"]
39327-
rebuilt : ["Function", "Object", "dec", "require"]
39326+
after transform: ["Object", "require"]
39327+
rebuilt : ["Object", "dec", "require"]
3932839328

3932939329
semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/classSuper/esDecorators-classDeclaration-classSuper.1.ts
3933039330
Bindings mismatch:
@@ -40331,7 +40331,7 @@ Bindings mismatch:
4033140331
after transform: ScopeId(0): ["_C", "_decorateMetadata", "_defineProperty", "_get_x", "_method", "_set_x", "_y", "dec"]
4033240332
rebuilt : ScopeId(0): ["_C", "_decorateMetadata", "_defineProperty", "_get_x", "_method", "_set_x", "_y"]
4033340333
Symbol reference IDs mismatch for "_decorateMetadata":
40334-
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)]
40334+
after transform: SymbolId(10): [ReferenceId(24), ReferenceId(25), ReferenceId(26), ReferenceId(27), ReferenceId(28), ReferenceId(30), ReferenceId(32), ReferenceId(34), ReferenceId(36), ReferenceId(38), ReferenceId(39), ReferenceId(40), ReferenceId(41), ReferenceId(42), ReferenceId(44), ReferenceId(46), ReferenceId(48), ReferenceId(50)]
4033540335
rebuilt : SymbolId(1): []
4033640336
Reference symbol mismatch for "dec":
4033740337
after transform: SymbolId(0) "dec"

tasks/transform_conformance/snapshots/oxc.snap.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
commit: 41d96516
22

3-
Passed: 188/316
3+
Passed: 188/317
44

55
# All Passed:
66
* babel-plugin-transform-class-static-block
@@ -544,7 +544,7 @@ x Output mismatch
544544
x Output mismatch
545545

546546

547-
# legacy-decorators (6/85)
547+
# legacy-decorators (6/86)
548548
* oxc/class-without-name-with-decorated_class/input.ts
549549
Bindings mismatch:
550550
after transform: ScopeId(0): ["dec"]
@@ -698,6 +698,26 @@ Symbol reference IDs mismatch for "ComputedEnum":
698698
after transform: SymbolId(25): [ReferenceId(19), ReferenceId(72)]
699699
rebuilt : SymbolId(15): [ReferenceId(52)]
700700

701+
* oxc/metadata/getter-setter-method/input.ts
702+
Bindings mismatch:
703+
after transform: ScopeId(0): ["C", "dec"]
704+
rebuilt : ScopeId(0): ["C"]
705+
Scope children mismatch:
706+
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
707+
rebuilt : ScopeId(0): [ScopeId(1)]
708+
Reference symbol mismatch for "dec":
709+
after transform: SymbolId(0) "dec"
710+
rebuilt : <None>
711+
Reference symbol mismatch for "dec":
712+
after transform: SymbolId(0) "dec"
713+
rebuilt : <None>
714+
Reference symbol mismatch for "dec":
715+
after transform: SymbolId(0) "dec"
716+
rebuilt : <None>
717+
Unresolved references mismatch:
718+
after transform: ["Function", "Number", "PropertyDescriptor", "String", "babelHelpers"]
719+
rebuilt : ["Function", "Number", "String", "babelHelpers", "dec"]
720+
701721
* oxc/metadata/imports/input.ts
702722
Bindings mismatch:
703723
after transform: ScopeId(0): ["Bar", "Cls", "Foo", "Zoo", "_ref", "dec"]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
declare function dec(
2+
target: any,
3+
propertyKey: string,
4+
descriptor: PropertyDescriptor,
5+
): void;
6+
7+
class Getter {
8+
@dec
9+
get address(): string {
10+
return "test";
11+
}
12+
13+
@dec
14+
regularMethod(): string {
15+
return "test";
16+
}
17+
}
18+
19+
class Setter {
20+
@dec
21+
set address(value: number) {}
22+
23+
@dec
24+
regularMethod(): string {
25+
return "test";
26+
}
27+
}

0 commit comments

Comments
 (0)