From 26fcf120c32548f1b93629ce7cec5cc701c0ecbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 26 Sep 2026 20:28:43 +0200 Subject: [PATCH 1/2] Add test for malformed object destructuring assignment in decorated class (crash) --- ...lformedObjectDestructuringAssignmentESDecorators.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tsc/testdata/tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts diff --git a/tsc/testdata/tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts b/tsc/testdata/tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts new file mode 100644 index 0000000000000..38f09256b7b7a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts @@ -0,0 +1,10 @@ +// @target: es2022 + +declare function dec(...args: any[]): any; + +@dec +class C { + static { + ({ m() {}, get g() { return 1; }, set s(v) {} } = this); + } +} From 1fa44f0f003e75d35b2f2e52223e8a8ed655941e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 26 Sep 2026 20:38:21 +0200 Subject: [PATCH 2/2] Fix crash on malformed object destructuring assignment in decorated class --- .../transformers/estransforms/esdecorator.go | 1 - ...ructuringAssignmentESDecorators.errors.txt | 21 ++++++ ...jectDestructuringAssignmentESDecorators.js | 71 +++++++++++++++++++ ...estructuringAssignmentESDecorators.symbols | 23 ++++++ ...tDestructuringAssignmentESDecorators.types | 27 +++++++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.js create mode 100644 tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.types diff --git a/tsc/internal/transformers/estransforms/esdecorator.go b/tsc/internal/transformers/estransforms/esdecorator.go index dc2e6edee8a4d..ba8c9484e72e0 100644 --- a/tsc/internal/transformers/estransforms/esdecorator.go +++ b/tsc/internal/transformers/estransforms/esdecorator.go @@ -2261,7 +2261,6 @@ func (tx *esDecoratorTransformer) visitAssignmentRestProperty(node *ast.Node) *a } func (tx *esDecoratorTransformer) visitObjectAssignmentElement(node *ast.Node) *ast.Node { - debug.Assert(ast.IsObjectBindingOrAssignmentElement(node)) if ast.IsSpreadAssignment(node) { return tx.visitAssignmentRestProperty(node) } diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt new file mode 100644 index 0000000000000..e398726486b0a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt @@ -0,0 +1,21 @@ +malformedObjectDestructuringAssignmentESDecorators.ts(6,12): error TS1136: Property assignment expected. +malformedObjectDestructuringAssignmentESDecorators.ts(6,24): error TS1136: Property assignment expected. +malformedObjectDestructuringAssignmentESDecorators.ts(6,47): error TS1136: Property assignment expected. + + +==== malformedObjectDestructuringAssignmentESDecorators.ts (3 errors) ==== + declare function dec(...args: any[]): any; + + @dec + class C { + static { + ({ m() {}, get g() { return 1; }, set s(v) {} } = this); + ~ +!!! error TS1136: Property assignment expected. + ~ +!!! error TS1136: Property assignment expected. + ~ +!!! error TS1136: Property assignment expected. + } + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.js b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.js new file mode 100644 index 0000000000000..061c820db6729 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.js @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts] //// + +//// [malformedObjectDestructuringAssignmentESDecorators.ts] +declare function dec(...args: any[]): any; + +@dec +class C { + static { + ({ m() {}, get g() { return 1; }, set s(v) {} } = this); + } +} + + +//// [malformedObjectDestructuringAssignmentESDecorators.js] +"use strict"; +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +let C = (() => { + let _classDecorators = [dec]; + let _classDescriptor; + let _classExtraInitializers = []; + let _classThis; + var C = class { + static { _classThis = this; } + static { + const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + C = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + } + static { + ({ m() { }, get g() { return 1; }, set s(v) { } } = _classThis); + } + static { + __runInitializers(_classThis, _classExtraInitializers); + } + }; + return C = _classThis; +})(); diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.symbols b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.symbols new file mode 100644 index 0000000000000..9df825d579309 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.symbols @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts] //// + +=== malformedObjectDestructuringAssignmentESDecorators.ts === +declare function dec(...args: any[]): any; +>dec : Symbol(dec, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 0, 0)) +>args : Symbol(args, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 0, 21)) + +@dec +>dec : Symbol(dec, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 0, 0)) + +class C { +>C : Symbol(C, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 0, 42)) + + static { + ({ m() {}, get g() { return 1; }, set s(v) {} } = this); +>m : Symbol(m, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 5, 10)) +>g : Symbol(g, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 5, 18)) +>s : Symbol(s, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 5, 41)) +>v : Symbol(v, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 5, 48)) +>this : Symbol(C, Decl(malformedObjectDestructuringAssignmentESDecorators.ts, 0, 42)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.types b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.types new file mode 100644 index 0000000000000..83066daad3da2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.types @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/malformedObjectDestructuringAssignmentESDecorators.ts] //// + +=== malformedObjectDestructuringAssignmentESDecorators.ts === +declare function dec(...args: any[]): any; +>dec : (...args: any[]) => any +>args : any[] + +@dec +>dec : (...args: any[]) => any + +class C { +>C : C + + static { + ({ m() {}, get g() { return 1; }, set s(v) {} } = this); +>({ m() {}, get g() { return 1; }, set s(v) {} } = this) : typeof C +>{ m() {}, get g() { return 1; }, set s(v) {} } = this : typeof C +>{ m() {}, get g() { return 1; }, set s(v) {} } : { m(): void; readonly g: number; s: any; } +>m : () => void +>g : number +>1 : 1 +>s : any +>v : any +>this : typeof C + } +} +