Skip to content

Commit 5e36778

Browse files
Zzzensandersn
andauthored
check usage before declaration for decorators (#50372)
Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
1 parent e37ca49 commit 5e36778

11 files changed

+1194
-3
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3068,13 +3068,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
30683068
return isForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.expression, declContainer);
30693069
}
30703070

3071-
function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node): boolean {
3071+
function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node) {
3072+
return isUsedInFunctionOrInstancePropertyWorker(usage, declaration);
3073+
}
3074+
3075+
function isUsedInFunctionOrInstancePropertyWorker(usage: Node, declaration: Node): boolean {
30723076
return !!findAncestor(usage, current => {
30733077
if (current === declContainer) {
30743078
return "quit";
30753079
}
30763080
if (isFunctionLike(current)) {
3077-
return true;
3081+
return !getImmediatelyInvokedFunctionExpression(current);
30783082
}
30793083
if (isClassStaticBlockDeclaration(current)) {
30803084
return declaration.pos < usage.pos;
@@ -3107,6 +3111,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
31073111
}
31083112
}
31093113
}
3114+
3115+
const decorator = tryCast(current.parent, isDecorator);
3116+
if (decorator && decorator.expression === current) {
3117+
if (isParameter(decorator.parent)) {
3118+
return isUsedInFunctionOrInstancePropertyWorker(decorator.parent.parent.parent, declaration) ? true : "quit";
3119+
}
3120+
if (isMethodDeclaration(decorator.parent)) {
3121+
return isUsedInFunctionOrInstancePropertyWorker(decorator.parent.parent, declaration) ? true : "quit";
3122+
}
3123+
}
3124+
31103125
return false;
31113126
});
31123127
}

tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448: Block-scoped variable
55
blockScopedVariablesUseBeforeDef.ts(111,28): error TS2448: Block-scoped variable 'a' used before its declaration.
66
blockScopedVariablesUseBeforeDef.ts(112,21): error TS2448: Block-scoped variable 'a' used before its declaration.
77
blockScopedVariablesUseBeforeDef.ts(122,22): error TS2448: Block-scoped variable 'a' used before its declaration.
8+
blockScopedVariablesUseBeforeDef.ts(128,9): error TS2448: Block-scoped variable 'foo' used before its declaration.
9+
blockScopedVariablesUseBeforeDef.ts(131,9): error TS2448: Block-scoped variable 'foo' used before its declaration.
10+
blockScopedVariablesUseBeforeDef.ts(153,20): error TS2450: Enum 'Enum' used before its declaration.
811

912

10-
==== blockScopedVariablesUseBeforeDef.ts (7 errors) ====
13+
==== blockScopedVariablesUseBeforeDef.ts (10 errors) ====
1114
function foo0() {
1215
let a = x;
1316
~
@@ -157,9 +160,15 @@ blockScopedVariablesUseBeforeDef.ts(122,22): error TS2448: Block-scoped variable
157160
const promise = (async () => {
158161
promise
159162
foo
163+
~~~
164+
!!! error TS2448: Block-scoped variable 'foo' used before its declaration.
165+
!!! related TS2728 blockScopedVariablesUseBeforeDef.ts:134:11: 'foo' is declared here.
160166
await null
161167
promise
162168
foo
169+
~~~
170+
!!! error TS2448: Block-scoped variable 'foo' used before its declaration.
171+
!!! related TS2728 blockScopedVariablesUseBeforeDef.ts:134:11: 'foo' is declared here.
163172
})()
164173

165174
const foo = 1;
@@ -179,4 +188,15 @@ blockScopedVariablesUseBeforeDef.ts(122,22): error TS2448: Block-scoped variable
179188
yield 1;
180189
})();
181190
}
191+
192+
function foo18() {
193+
let a = (() => Enum.Yes)();
194+
~~~~
195+
!!! error TS2450: Enum 'Enum' used before its declaration.
196+
!!! related TS2728 blockScopedVariablesUseBeforeDef.ts:154:10: 'Enum' is declared here.
197+
enum Enum {
198+
No = 0,
199+
Yes = 1,
200+
}
201+
}
182202

tests/baselines/reference/blockScopedVariablesUseBeforeDef.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ function wrapI2() {
151151
yield 1;
152152
})();
153153
}
154+
155+
function foo18() {
156+
let a = (() => Enum.Yes)();
157+
enum Enum {
158+
No = 0,
159+
Yes = 1,
160+
}
161+
}
154162

155163

156164
//// [blockScopedVariablesUseBeforeDef.js]
@@ -389,3 +397,11 @@ function wrapI2() {
389397
});
390398
})();
391399
}
400+
function foo18() {
401+
var a = (function () { return Enum.Yes; })();
402+
var Enum;
403+
(function (Enum) {
404+
Enum[Enum["No"] = 0] = "No";
405+
Enum[Enum["Yes"] = 1] = "Yes";
406+
})(Enum || (Enum = {}));
407+
}

tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,23 @@ function wrapI2() {
312312
})();
313313
}
314314

315+
function foo18() {
316+
>foo18 : Symbol(foo18, Decl(blockScopedVariablesUseBeforeDef.ts, 149, 1))
317+
318+
let a = (() => Enum.Yes)();
319+
>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 152, 7))
320+
>Enum.Yes : Symbol(Enum.Yes, Decl(blockScopedVariablesUseBeforeDef.ts, 154, 15))
321+
>Enum : Symbol(Enum, Decl(blockScopedVariablesUseBeforeDef.ts, 152, 31))
322+
>Yes : Symbol(Enum.Yes, Decl(blockScopedVariablesUseBeforeDef.ts, 154, 15))
323+
324+
enum Enum {
325+
>Enum : Symbol(Enum, Decl(blockScopedVariablesUseBeforeDef.ts, 152, 31))
326+
327+
No = 0,
328+
>No : Symbol(Enum.No, Decl(blockScopedVariablesUseBeforeDef.ts, 153, 15))
329+
330+
Yes = 1,
331+
>Yes : Symbol(Enum.Yes, Decl(blockScopedVariablesUseBeforeDef.ts, 154, 15))
332+
}
333+
}
334+

tests/baselines/reference/blockScopedVariablesUseBeforeDef.types

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,41 @@ function wrapI2() {
510510
})();
511511
}
512512

513+
function foo18() {
514+
>foo18 : () => void
515+
> : ^^^^^^^^^^
516+
517+
let a = (() => Enum.Yes)();
518+
>a : Enum
519+
> : ^^^^
520+
>(() => Enum.Yes)() : Enum
521+
> : ^^^^
522+
>(() => Enum.Yes) : () => Enum
523+
> : ^^^^^^^^^^
524+
>() => Enum.Yes : () => Enum
525+
> : ^^^^^^^^^^
526+
>Enum.Yes : Enum.Yes
527+
> : ^^^^^^^^
528+
>Enum : typeof Enum
529+
> : ^^^^^^^^^^^
530+
>Yes : Enum.Yes
531+
> : ^^^^^^^^
532+
533+
enum Enum {
534+
>Enum : Enum
535+
> : ^^^^
536+
537+
No = 0,
538+
>No : Enum.No
539+
> : ^^^^^^^
540+
>0 : 0
541+
> : ^
542+
543+
Yes = 1,
544+
>Yes : Enum.Yes
545+
> : ^^^^^^^^
546+
>1 : 1
547+
> : ^
548+
}
549+
}
550+
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
decoratorUsedBeforeDeclaration.ts(1,2): error TS2448: Block-scoped variable 'lambda' used before its declaration.
2+
decoratorUsedBeforeDeclaration.ts(1,9): error TS2450: Enum 'Enum' used before its declaration.
3+
decoratorUsedBeforeDeclaration.ts(2,7): error TS2450: Enum 'Enum' used before its declaration.
4+
decoratorUsedBeforeDeclaration.ts(4,4): error TS2448: Block-scoped variable 'lambda' used before its declaration.
5+
decoratorUsedBeforeDeclaration.ts(4,11): error TS2450: Enum 'Enum' used before its declaration.
6+
decoratorUsedBeforeDeclaration.ts(4,16): error TS2729: Property 'No' is used before its initialization.
7+
decoratorUsedBeforeDeclaration.ts(5,9): error TS2450: Enum 'Enum' used before its declaration.
8+
decoratorUsedBeforeDeclaration.ts(5,14): error TS2729: Property 'No' is used before its initialization.
9+
decoratorUsedBeforeDeclaration.ts(12,4): error TS2448: Block-scoped variable 'lambda' used before its declaration.
10+
decoratorUsedBeforeDeclaration.ts(12,11): error TS2450: Enum 'Enum' used before its declaration.
11+
decoratorUsedBeforeDeclaration.ts(13,9): error TS2450: Enum 'Enum' used before its declaration.
12+
decoratorUsedBeforeDeclaration.ts(18,4): error TS2448: Block-scoped variable 'lambda' used before its declaration.
13+
decoratorUsedBeforeDeclaration.ts(24,11): error TS2448: Block-scoped variable 'lambda' used before its declaration.
14+
decoratorUsedBeforeDeclaration.ts(24,18): error TS2450: Enum 'Enum' used before its declaration.
15+
decoratorUsedBeforeDeclaration.ts(24,33): error TS2450: Enum 'Enum' used before its declaration.
16+
decoratorUsedBeforeDeclaration.ts(28,11): error TS2448: Block-scoped variable 'lambda' used before its declaration.
17+
18+
19+
==== decoratorUsedBeforeDeclaration.ts (16 errors) ====
20+
@lambda(Enum.No)
21+
~~~~~~
22+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
23+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
24+
~~~~
25+
!!! error TS2450: Enum 'Enum' used before its declaration.
26+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
27+
@deco(Enum.No)
28+
~~~~
29+
!!! error TS2450: Enum 'Enum' used before its declaration.
30+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
31+
class Greeter {
32+
@lambda(Enum.No)
33+
~~~~~~
34+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
35+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
36+
~~~~
37+
!!! error TS2450: Enum 'Enum' used before its declaration.
38+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
39+
~~
40+
!!! error TS2729: Property 'No' is used before its initialization.
41+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:36:3: 'No' is declared here.
42+
@deco(Enum.No)
43+
~~~~
44+
!!! error TS2450: Enum 'Enum' used before its declaration.
45+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
46+
~~
47+
!!! error TS2729: Property 'No' is used before its initialization.
48+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:36:3: 'No' is declared here.
49+
greeting: string;
50+
51+
constructor(message: string) {
52+
this.greeting = message;
53+
}
54+
55+
@lambda(Enum.No)
56+
~~~~~~
57+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
58+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
59+
~~~~
60+
!!! error TS2450: Enum 'Enum' used before its declaration.
61+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
62+
@deco(Enum.No)
63+
~~~~
64+
!!! error TS2450: Enum 'Enum' used before its declaration.
65+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
66+
greet() {
67+
return "Hello, " + this.greeting;
68+
}
69+
70+
@lambda
71+
~~~~~~
72+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
73+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
74+
@deco
75+
greet1() {
76+
return "Hello, " + this.greeting;
77+
}
78+
79+
greet2(@lambda(Enum.No) @deco(Enum.No) param) {
80+
~~~~~~
81+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
82+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
83+
~~~~
84+
!!! error TS2450: Enum 'Enum' used before its declaration.
85+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
86+
~~~~
87+
!!! error TS2450: Enum 'Enum' used before its declaration.
88+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:35:6: 'Enum' is declared here.
89+
return "Hello, " + this.greeting;
90+
}
91+
92+
greet3(@lambda @deco param) {
93+
~~~~~~
94+
!!! error TS2448: Block-scoped variable 'lambda' used before its declaration.
95+
!!! related TS2728 decoratorUsedBeforeDeclaration.ts:40:7: 'lambda' is declared here.
96+
return "Hello, " + this.greeting;
97+
}
98+
}
99+
100+
function deco(...args: any[]): any {}
101+
102+
enum Enum {
103+
No = 0,
104+
Yes = 1,
105+
}
106+
107+
const lambda = (...args: any[]): any => {};
108+
109+
@lambda(Enum.No)
110+
@deco(Enum.No)
111+
class Greeter1 {
112+
@lambda(Enum.No)
113+
@deco(Enum.No)
114+
greeting: string;
115+
116+
constructor(message: string) {
117+
this.greeting = message;
118+
}
119+
120+
@lambda(Enum.No)
121+
@deco(Enum.No)
122+
greet() {
123+
return "Hello, " + this.greeting;
124+
}
125+
126+
@lambda
127+
@deco
128+
greet1() {
129+
return "Hello, " + this.greeting;
130+
}
131+
132+
greet2(@lambda(Enum.No) @deco(Enum.No) param) {
133+
return "Hello, " + this.greeting;
134+
}
135+
136+
greet3(@lambda @deco param) {
137+
return "Hello, " + this.greeting;
138+
}
139+
}
140+

0 commit comments

Comments
 (0)