Skip to content
Draft
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
32 changes: 31 additions & 1 deletion tsc/internal/transformers/tstransforms/legacydecorators.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tstransforms

import (
"slices"

"github.com/microsoft/TypeScript/tsc/internal/ast"
"github.com/microsoft/TypeScript/tsc/internal/binder"
"github.com/microsoft/TypeScript/tsc/internal/collections"
Expand All @@ -20,6 +22,8 @@ type LegacyDecoratorsTransformer struct {
*/
classAliases map[*ast.Node]*ast.Node
enclosingClasses []*ast.ClassDeclaration
// Class aliases that should not be substituted while visiting a direct computed member name.
excludedClassAliases []*ast.Node
}

func NewLegacyDecoratorsTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
Expand Down Expand Up @@ -60,10 +64,12 @@ func (tx *LegacyDecoratorsTransformer) visit(node *ast.Node) *ast.Node {
case ast.KindSourceFile:
tx.classAliases = make(map[*ast.Node]*ast.Node)
tx.enclosingClasses = nil
tx.excludedClassAliases = nil
result := tx.Visitor().VisitEachChild(node)
tx.EmitContext().AddEmitHelper(result, tx.EmitContext().ReadEmitHelpers()...)
tx.classAliases = nil
tx.enclosingClasses = nil
tx.excludedClassAliases = nil
return result
default:
return tx.Visitor().VisitEachChild(node)
Expand All @@ -73,7 +79,7 @@ func (tx *LegacyDecoratorsTransformer) visit(node *ast.Node) *ast.Node {
func (tx *LegacyDecoratorsTransformer) visitIdentifier(node *ast.Identifier) *ast.Node {
// takes the place of `substituteIdentifier` in the strada transform
for _, d := range tx.enclosingClasses {
if _, ok := tx.classAliases[d.AsNode()]; ok && tx.referenceResolver.GetReferencedValueDeclaration(tx.EmitContext().MostOriginal(node.AsNode())) == tx.EmitContext().MostOriginal(d.AsNode()) {
if _, ok := tx.classAliases[d.AsNode()]; ok && !slices.Contains(tx.excludedClassAliases, d.AsNode()) && tx.referenceResolver.GetReferencedValueDeclaration(tx.EmitContext().MostOriginal(node.AsNode())) == tx.EmitContext().MostOriginal(d.AsNode()) {
return tx.classAliases[d.AsNode()]
}
}
Expand Down Expand Up @@ -152,6 +158,20 @@ func (tx *LegacyDecoratorsTransformer) visitParamerDeclaration(node *ast.Paramet
// with decorators, a temporary value is stored for later use.
func (tx *LegacyDecoratorsTransformer) visitPropertyNameOfClassElement(member *ast.Node) *ast.Node {
name := member.Name()
if ast.IsComputedPropertyName(name) {
// A direct computed name is evaluated before its class alias is assigned. Exclude only
// that class so references to aliases of enclosing classes are still substituted.
for i := len(tx.enclosingClasses) - 1; i >= 0; i-- {
class := tx.enclosingClasses[i]
if slices.Contains(class.Members.Nodes, member) {
tx.excludedClassAliases = append(tx.excludedClassAliases, class.AsNode())
defer func() {
tx.excludedClassAliases = tx.excludedClassAliases[:len(tx.excludedClassAliases)-1]
}()
break
}
}
}
if ast.IsComputedPropertyName(name) && ast.HasDecorators(member) {
expression := tx.Visitor().VisitNode(name.AsComputedPropertyName().Expression)
innerExpression := ast.SkipPartiallyEmittedExpressions(expression)
Expand Down Expand Up @@ -511,8 +531,18 @@ func (tx *LegacyDecoratorsTransformer) transformClassDeclarationWithClassDecorat

func (tx *LegacyDecoratorsTransformer) hasInternalStaticReference(node *ast.ClassDeclaration) bool {
classNode := tx.EmitContext().MostOriginal(node.AsNode())
computedNames := make(map[*ast.Node]struct{})
for _, member := range node.Members.Nodes {
if name := member.Name(); name != nil && ast.IsComputedPropertyName(name) {
// Direct computed names are evaluated before the class alias can be assigned.
computedNames[name] = struct{}{}
}
}
var isOrContainsStaticSelfReference func(n *ast.Node) bool
isOrContainsStaticSelfReference = func(n *ast.Node) bool {
if _, ok := computedNames[n]; ok {
return false
}
if ast.IsIdentifier(n) && tx.referenceResolver.GetReferencedValueDeclaration(tx.EmitContext().MostOriginal(n)) == classNode {
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
legacyDecoratorClassAliasComputedNames.ts(6,6): error TS2449: Class 'C' used before its declaration.
legacyDecoratorClassAliasComputedNames.ts(7,10): error TS2449: Class 'C' used before its declaration.
legacyDecoratorClassAliasComputedNames.ts(8,10): error TS2449: Class 'C' used before its declaration.
legacyDecoratorClassAliasComputedNames.ts(14,13): error TS2449: Class 'D' used before its declaration.


==== legacyDecoratorClassAliasComputedNames.ts (4 errors) ====
declare function dec(...args: any[]): any;

@dec
class C {
constructor() {}
[C.name]() {}
~
!!! error TS2449: Class 'C' used before its declaration.
!!! related TS2728 legacyDecoratorClassAliasComputedNames.ts:4:7: 'C' is declared here.
get [C.name]() { return 1; }
~
!!! error TS2449: Class 'C' used before its declaration.
!!! related TS2728 legacyDecoratorClassAliasComputedNames.ts:4:7: 'C' is declared here.
set [C.name](value: number) {}
~
!!! error TS2449: Class 'C' used before its declaration.
!!! related TS2728 legacyDecoratorClassAliasComputedNames.ts:4:7: 'C' is declared here.
}

@dec
class D {
@dec
static [D.name] = 1;
~
!!! error TS2449: Class 'D' used before its declaration.
!!! related TS2728 legacyDecoratorClassAliasComputedNames.ts:12:7: 'D' is declared here.
static getSelf() {
return D;
}
}

@dec
class Outer {
method() {
return class {
[Outer.name]() {}
};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//// [tests/cases/compiler/legacyDecoratorClassAliasComputedNames.ts] ////

//// [legacyDecoratorClassAliasComputedNames.ts]
declare function dec(...args: any[]): any;

@dec
class C {
constructor() {}
[C.name]() {}
get [C.name]() { return 1; }
set [C.name](value: number) {}
}

@dec
class D {
@dec
static [D.name] = 1;
static getSelf() {
return D;
}
}

@dec
class Outer {
method() {
return class {
[Outer.name]() {}
};
}
}


//// [legacyDecoratorClassAliasComputedNames.js]
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var D_1, _a, Outer_1;
let C = class C {
constructor() { }
[C.name]() { }
get [C.name]() { return 1; }
set [C.name](value) { }
};
C = __decorate([
dec
], C);
let D = D_1 = class D {
static getSelf() {
return D_1;
}
};
_a = D.name;
D[_a] = 1;
__decorate([
dec
], D, _a, void 0);
D = D_1 = __decorate([
dec
], D);
let Outer = Outer_1 = class Outer {
method() {
return class {
[Outer_1.name]() { }
};
}
};
Outer = Outer_1 = __decorate([
dec
], Outer);
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//// [tests/cases/compiler/legacyDecoratorClassAliasComputedNames.ts] ////

=== legacyDecoratorClassAliasComputedNames.ts ===
declare function dec(...args: any[]): any;
>dec : Symbol(dec, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 0))
>args : Symbol(args, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 21))

@dec
>dec : Symbol(dec, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 0))

class C {
>C : Symbol(C, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 42))

constructor() {}
[C.name]() {}
>[C.name] : Symbol(C[C.name], Decl(legacyDecoratorClassAliasComputedNames.ts, 4, 20))
>C.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>C : Symbol(C, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 42))
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))

get [C.name]() { return 1; }
>[C.name] : Symbol(C[C.name], Decl(legacyDecoratorClassAliasComputedNames.ts, 5, 17))
>C.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>C : Symbol(C, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 42))
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))

set [C.name](value: number) {}
>[C.name] : Symbol(C[C.name], Decl(legacyDecoratorClassAliasComputedNames.ts, 6, 32))
>C.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>C : Symbol(C, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 42))
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>value : Symbol(value, Decl(legacyDecoratorClassAliasComputedNames.ts, 7, 17))
}

@dec
>dec : Symbol(dec, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 0))

class D {
>D : Symbol(D, Decl(legacyDecoratorClassAliasComputedNames.ts, 8, 1))

@dec
>dec : Symbol(dec, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 0))

static [D.name] = 1;
>[D.name] : Symbol(D[D.name], Decl(legacyDecoratorClassAliasComputedNames.ts, 11, 9))
>D.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>D : Symbol(D, Decl(legacyDecoratorClassAliasComputedNames.ts, 8, 1))
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))

static getSelf() {
>getSelf : Symbol(D.getSelf, Decl(legacyDecoratorClassAliasComputedNames.ts, 13, 24))

return D;
>D : Symbol(D, Decl(legacyDecoratorClassAliasComputedNames.ts, 8, 1))
}
}

@dec
>dec : Symbol(dec, Decl(legacyDecoratorClassAliasComputedNames.ts, 0, 0))

class Outer {
>Outer : Symbol(Outer, Decl(legacyDecoratorClassAliasComputedNames.ts, 17, 1))

method() {
>method : Symbol(Outer.method, Decl(legacyDecoratorClassAliasComputedNames.ts, 20, 13))

return class {
[Outer.name]() {}
>[Outer.name] : Symbol((Anonymous class)[Outer.name], Decl(legacyDecoratorClassAliasComputedNames.ts, 22, 22))
>Outer.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
>Outer : Symbol(Outer, Decl(legacyDecoratorClassAliasComputedNames.ts, 17, 1))
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))

};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//// [tests/cases/compiler/legacyDecoratorClassAliasComputedNames.ts] ////

=== legacyDecoratorClassAliasComputedNames.ts ===
declare function dec(...args: any[]): any;
>dec : (...args: any[]) => any
>args : any[]

@dec
>dec : (...args: any[]) => any

class C {
>C : C

constructor() {}
[C.name]() {}
>[C.name] : () => void
>C.name : string
>C : typeof C
>name : string

get [C.name]() { return 1; }
>[C.name] : number
>C.name : string
>C : typeof C
>name : string
>1 : 1

set [C.name](value: number) {}
>[C.name] : number
>C.name : string
>C : typeof C
>name : string
>value : number
}

@dec
>dec : (...args: any[]) => any

class D {
>D : D

@dec
>dec : (...args: any[]) => any

static [D.name] = 1;
>[D.name] : number
>D.name : string
>D : typeof D
>name : string
>1 : 1

static getSelf() {
>getSelf : () => typeof D

return D;
>D : typeof D
}
}

@dec
>dec : (...args: any[]) => any

class Outer {
>Outer : Outer

method() {
>method : () => typeof (Anonymous class)

return class {
>class { [Outer.name]() {} } : typeof (Anonymous class)

[Outer.name]() {}
>[Outer.name] : () => void
>Outer.name : string
>Outer : typeof Outer
>name : string

};
}
}

Loading