Skip to content

Commit

Permalink
Report error when referencing class name from decorator (#57666)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton authored Mar 11, 2024
1 parent ef6a4ab commit 1e982d8
Show file tree
Hide file tree
Showing 9 changed files with 507 additions and 7 deletions.
18 changes: 17 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2834,7 +2834,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
else if (isClassLike(declaration)) {
// still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} })
return !findAncestor(usage, n => isComputedPropertyName(n) && n.parent.parent === declaration);
// or when used within a decorator in the class (e.g. `@dec(A.x) class A { static x = "x" }`),
// except when used in a function that is not an IIFE (e.g., `@dec(() => A.x) class A { ... }`)
const container = findAncestor(usage, n =>
n === declaration ? "quit" :
isComputedPropertyName(n) ? n.parent.parent === declaration :
isDecorator(n) && (n.parent === declaration ||
isMethodDeclaration(n.parent) && n.parent.parent === declaration ||
isGetOrSetAccessorDeclaration(n.parent) && n.parent.parent === declaration ||
isPropertyDeclaration(n.parent) && n.parent.parent === declaration ||
isParameter(n.parent) && n.parent.parent.parent === declaration));
if (!container) {
return true;
}
if (isDecorator(container)) {
return !!findAncestor(usage, n => n === container ? "quit" : isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression(n));
}
return false;
}
else if (isPropertyDeclaration(declaration)) {
// still might be illegal if a self-referencing property initializer (eg private x = this.x)
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/decoratorReferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//// [decoratorReferences.ts]
declare function y(...args: any[]): any;
type T = number;
@y(1 as T, C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
@y(1 as T, () => C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
class C<T> {
@y(null as T) // <-- y should resolve to the function declaration, not the parameter; T should resolve to the type parameter of the class
method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
Expand All @@ -29,7 +29,7 @@ var C = /** @class */ (function () {
__param(0, y)
], C.prototype, "method", null);
C = __decorate([
y(1, C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
y(1, function () { return C; }) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
], C);
return C;
}());
2 changes: 1 addition & 1 deletion tests/baselines/reference/decoratorReferences.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ declare function y(...args: any[]): any;
type T = number;
>T : Symbol(T, Decl(decoratorReferences.ts, 0, 40))

@y(1 as T, C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
@y(1 as T, () => C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
>y : Symbol(y, Decl(decoratorReferences.ts, 0, 0))
>T : Symbol(T, Decl(decoratorReferences.ts, 0, 40))
>C : Symbol(C, Decl(decoratorReferences.ts, 1, 16))
Expand Down
5 changes: 3 additions & 2 deletions tests/baselines/reference/decoratorReferences.types
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ declare function y(...args: any[]): any;
type T = number;
>T : number

@y(1 as T, C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
>y(1 as T, C) : any
@y(1 as T, () => C) // <-- T should be resolved to the type alias, not the type parameter of the class; C should resolve to the class
>y(1 as T, () => C) : any
>y : (...args: any[]) => any
>1 as T : number
>1 : 1
>() => C : () => typeof C
>C : typeof C

class C<T> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
useBeforeDeclaration_classDecorators.1.ts(7,6): error TS2449: Class 'C2' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(10,13): error TS2449: Class 'C3' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(29,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(30,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(31,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(32,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(33,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(35,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(36,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(37,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(38,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(39,10): error TS2449: Class 'C5' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(44,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(45,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(46,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(47,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(48,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(50,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(51,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(52,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(53,17): error TS2449: Class 'C6' used before its declaration.
useBeforeDeclaration_classDecorators.1.ts(54,17): error TS2449: Class 'C6' used before its declaration.


==== useBeforeDeclaration_classDecorators.1.ts (22 errors) ====
declare const dec: any;

// ok
@dec(() => C1) class C1 { }

// error
@dec(C2) class C2 { }
~~
!!! error TS2449: Class 'C2' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:7:16: 'C2' is declared here.

// error
@dec((() => C3)()) class C3 { }
~~
!!! error TS2449: Class 'C3' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:10:26: 'C3' is declared here.

// ok
class C4 {
@dec(() => C4) static method() {}
@dec(() => C4) static get x() { return this.y; }
@dec(() => C4) static set x(v) {}
@dec(() => C4) static y: any;
@dec(() => C4) static accessor z: any;

@dec(() => C4) method() {}
@dec(() => C4) get x() { return this.y; }
@dec(() => C4) set x(v) {}
@dec(() => C4) y: any;
@dec(() => C4) accessor z: any;
}

// error
class C5 {
@dec(C5) static method() {}
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) static get x() { return this.y; }
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) static set x(v) {}
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) static y: any;
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) static accessor z: any;
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.

@dec(C5) method() {}
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) get x() { return this.y; }
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) set x(v) {}
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) y: any;
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
@dec(C5) accessor z: any;
~~
!!! error TS2449: Class 'C5' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:28:7: 'C5' is declared here.
}

// error
class C6 {
@dec((() => C6)()) static method() {}
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) static get x() { return this.y; }
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) static set x(v) {}
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) static y: any;
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) static accessor z: any;
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.

@dec((() => C6)()) method() {}
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) get x() { return this.y; }
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) set x(v) {}
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) y: any;
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
@dec((() => C6)()) accessor z: any;
~~
!!! error TS2449: Class 'C6' used before its declaration.
!!! related TS2728 useBeforeDeclaration_classDecorators.1.ts:43:7: 'C6' is declared here.
}

Loading

0 comments on commit 1e982d8

Please sign in to comment.