Skip to content

Commit b40893d

Browse files
fix(parser): reject using/await using in switch case/default clauses
Fixes #62708 The ECMAScript spec was updated to disallow using and await using declarations directly in switch case/default clauses. This makes resource declaration count statically knowable. Added parser validation that checks parsing context using bitfields to distinguish direct nesting (error) from nested blocks (valid). Block-wrapped declarations remain allowed. Test coverage: - usingDeclarations.1.ts: 3 errors caught - awaitUsingDeclarations.1.ts: 3 errors caught - New comprehensive tests: error cases + edge cases - Full suite: 2087 tests passing
1 parent 6f4fb01 commit b40893d

18 files changed

+2371
-86
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8332,6 +8332,10 @@
83328332
"category": "Message",
83338333
"code": 95197
83348334
},
8335+
"A '{0}' declaration cannot be placed within a 'case' or 'default' clause.": {
8336+
"category": "Error",
8337+
"code": 95198
8338+
},
83358339

83368340
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
83378341
"category": "Error",

src/compiler/parser.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7678,12 +7678,20 @@ namespace Parser {
76787678
break;
76797679
case SyntaxKind.UsingKeyword:
76807680
flags |= NodeFlags.Using;
7681+
if ((parsingContext & (1 << ParsingContext.SwitchClauseStatements)) &&
7682+
!(parsingContext & (1 << ParsingContext.BlockStatements))) {
7683+
parseErrorAtCurrentToken(Diagnostics.A_0_declaration_cannot_be_placed_within_a_case_or_default_clause, "using");
7684+
}
76817685
break;
76827686
case SyntaxKind.AwaitKeyword:
76837687
if (!isAwaitUsingDeclaration()) {
76847688
break;
76857689
}
76867690
flags |= NodeFlags.AwaitUsing;
7691+
if ((parsingContext & (1 << ParsingContext.SwitchClauseStatements)) &&
7692+
!(parsingContext & (1 << ParsingContext.BlockStatements))) {
7693+
parseErrorAtCurrentToken(Diagnostics.A_0_declaration_cannot_be_placed_within_a_case_or_default_clause, "await using");
7694+
}
76877695
nextToken();
76887696
break;
76897697
default:
Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
1-
awaitUsingDeclarations.1.ts(1,1): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
2-
awaitUsingDeclarations.1.ts(36,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
3-
awaitUsingDeclarations.1.ts(41,9): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
4-
awaitUsingDeclarations.1.ts(45,9): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
5-
awaitUsingDeclarations.1.ts(52,13): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
6-
awaitUsingDeclarations.1.ts(57,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
7-
awaitUsingDeclarations.1.ts(60,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
8-
awaitUsingDeclarations.1.ts(63,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
9-
awaitUsingDeclarations.1.ts(67,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
10-
awaitUsingDeclarations.1.ts(70,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
11-
awaitUsingDeclarations.1.ts(74,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
12-
awaitUsingDeclarations.1.ts(79,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
13-
awaitUsingDeclarations.1.ts(85,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
14-
awaitUsingDeclarations.1.ts(90,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
15-
awaitUsingDeclarations.1.ts(94,5): error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
1+
awaitUsingDeclarations.1.ts(41,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
2+
awaitUsingDeclarations.1.ts(45,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
3+
awaitUsingDeclarations.1.ts(52,13): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
164

175

18-
==== awaitUsingDeclarations.1.ts (15 errors) ====
6+
==== awaitUsingDeclarations.1.ts (3 errors) ====
197
await using d1 = { async [Symbol.asyncDispose]() {} };
20-
~~~~~
21-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
228

239
async function af() {
2410
await using d3 = { async [Symbol.asyncDispose]() {} };
@@ -54,21 +40,19 @@ awaitUsingDeclarations.1.ts(94,5): error TS2854: Top-level 'await using' stateme
5440

5541
{
5642
await using d19 = { async [Symbol.asyncDispose]() {} };
57-
~~~~~
58-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
5943
}
6044

6145
switch (Math.random()) {
6246
case 0:
6347
await using d20 = { async [Symbol.asyncDispose]() {} };
6448
~~~~~
65-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
49+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
6650
break;
6751

6852
case 1:
6953
await using d21 = { async [Symbol.asyncDispose]() {} };
7054
~~~~~
71-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
55+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
7256
break;
7357
}
7458

@@ -77,69 +61,49 @@ awaitUsingDeclarations.1.ts(94,5): error TS2854: Top-level 'await using' stateme
7761
case 0:
7862
await using d22 = { async [Symbol.asyncDispose]() {} };
7963
~~~~~
80-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
64+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
8165
break;
8266
}
8367

8468
try {
8569
await using d23 = { async [Symbol.asyncDispose]() {} };
86-
~~~~~
87-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
8870
}
8971
catch {
9072
await using d24 = { async [Symbol.asyncDispose]() {} };
91-
~~~~~
92-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
9373
}
9474
finally {
9575
await using d25 = { async [Symbol.asyncDispose]() {} };
96-
~~~~~
97-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
9876
}
9977

10078
if (true) {
10179
await using d26 = { async [Symbol.asyncDispose]() {} };
102-
~~~~~
103-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
10480
}
10581
else {
10682
await using d27 = { async [Symbol.asyncDispose]() {} };
107-
~~~~~
108-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
10983
}
11084

11185
while (true) {
11286
await using d28 = { async [Symbol.asyncDispose]() {} };
113-
~~~~~
114-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
11587
break;
11688
}
11789

11890
do {
11991
await using d29 = { async [Symbol.asyncDispose]() {} };
120-
~~~~~
121-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
12292
break;
12393
}
12494
while (true);
12595

12696
for (;;) {
12797
await using d30 = { async [Symbol.asyncDispose]() {} };
128-
~~~~~
129-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
13098
break;
13199
}
132100

133101
for (const x in {}) {
134102
await using d31 = { async [Symbol.asyncDispose]() {} };
135-
~~~~~
136-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
137103
}
138104

139105
for (const x of []) {
140106
await using d32 = { async [Symbol.asyncDispose]() {} };
141-
~~~~~
142-
!!! error TS2854: Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
143107
}
144108

145109
export {};
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
awaitUsingDeclarations.1.ts(41,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
2+
awaitUsingDeclarations.1.ts(45,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
3+
awaitUsingDeclarations.1.ts(52,13): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
4+
5+
6+
==== awaitUsingDeclarations.1.ts (3 errors) ====
7+
await using d1 = { async [Symbol.asyncDispose]() {} };
8+
9+
async function af() {
10+
await using d3 = { async [Symbol.asyncDispose]() {} };
11+
await null;
12+
}
13+
14+
async function * ag() {
15+
await using d5 = { async [Symbol.asyncDispose]() {} };
16+
yield;
17+
await null;
18+
}
19+
20+
const a = async () => {
21+
await using d6 = { async [Symbol.asyncDispose]() {} };
22+
};
23+
24+
class C1 {
25+
a = async () => {
26+
await using d7 = { async [Symbol.asyncDispose]() {} };
27+
};
28+
29+
async am() {
30+
await using d13 = { async [Symbol.asyncDispose]() {} };
31+
await null;
32+
}
33+
34+
async * ag() {
35+
await using d15 = { async [Symbol.asyncDispose]() {} };
36+
yield;
37+
await null;
38+
}
39+
}
40+
41+
{
42+
await using d19 = { async [Symbol.asyncDispose]() {} };
43+
}
44+
45+
switch (Math.random()) {
46+
case 0:
47+
await using d20 = { async [Symbol.asyncDispose]() {} };
48+
~~~~~
49+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
50+
break;
51+
52+
case 1:
53+
await using d21 = { async [Symbol.asyncDispose]() {} };
54+
~~~~~
55+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
56+
break;
57+
}
58+
59+
if (true)
60+
switch (0) {
61+
case 0:
62+
await using d22 = { async [Symbol.asyncDispose]() {} };
63+
~~~~~
64+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
65+
break;
66+
}
67+
68+
try {
69+
await using d23 = { async [Symbol.asyncDispose]() {} };
70+
}
71+
catch {
72+
await using d24 = { async [Symbol.asyncDispose]() {} };
73+
}
74+
finally {
75+
await using d25 = { async [Symbol.asyncDispose]() {} };
76+
}
77+
78+
if (true) {
79+
await using d26 = { async [Symbol.asyncDispose]() {} };
80+
}
81+
else {
82+
await using d27 = { async [Symbol.asyncDispose]() {} };
83+
}
84+
85+
while (true) {
86+
await using d28 = { async [Symbol.asyncDispose]() {} };
87+
break;
88+
}
89+
90+
do {
91+
await using d29 = { async [Symbol.asyncDispose]() {} };
92+
break;
93+
}
94+
while (true);
95+
96+
for (;;) {
97+
await using d30 = { async [Symbol.asyncDispose]() {} };
98+
break;
99+
}
100+
101+
for (const x in {}) {
102+
await using d31 = { async [Symbol.asyncDispose]() {} };
103+
}
104+
105+
for (const x of []) {
106+
await using d32 = { async [Symbol.asyncDispose]() {} };
107+
}
108+
109+
export {};
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
awaitUsingDeclarations.1.ts(41,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
2+
awaitUsingDeclarations.1.ts(45,9): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
3+
awaitUsingDeclarations.1.ts(52,13): error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
4+
5+
6+
==== awaitUsingDeclarations.1.ts (3 errors) ====
7+
await using d1 = { async [Symbol.asyncDispose]() {} };
8+
9+
async function af() {
10+
await using d3 = { async [Symbol.asyncDispose]() {} };
11+
await null;
12+
}
13+
14+
async function * ag() {
15+
await using d5 = { async [Symbol.asyncDispose]() {} };
16+
yield;
17+
await null;
18+
}
19+
20+
const a = async () => {
21+
await using d6 = { async [Symbol.asyncDispose]() {} };
22+
};
23+
24+
class C1 {
25+
a = async () => {
26+
await using d7 = { async [Symbol.asyncDispose]() {} };
27+
};
28+
29+
async am() {
30+
await using d13 = { async [Symbol.asyncDispose]() {} };
31+
await null;
32+
}
33+
34+
async * ag() {
35+
await using d15 = { async [Symbol.asyncDispose]() {} };
36+
yield;
37+
await null;
38+
}
39+
}
40+
41+
{
42+
await using d19 = { async [Symbol.asyncDispose]() {} };
43+
}
44+
45+
switch (Math.random()) {
46+
case 0:
47+
await using d20 = { async [Symbol.asyncDispose]() {} };
48+
~~~~~
49+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
50+
break;
51+
52+
case 1:
53+
await using d21 = { async [Symbol.asyncDispose]() {} };
54+
~~~~~
55+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
56+
break;
57+
}
58+
59+
if (true)
60+
switch (0) {
61+
case 0:
62+
await using d22 = { async [Symbol.asyncDispose]() {} };
63+
~~~~~
64+
!!! error TS95198: A 'await using' declaration cannot be placed within a 'case' or 'default' clause.
65+
break;
66+
}
67+
68+
try {
69+
await using d23 = { async [Symbol.asyncDispose]() {} };
70+
}
71+
catch {
72+
await using d24 = { async [Symbol.asyncDispose]() {} };
73+
}
74+
finally {
75+
await using d25 = { async [Symbol.asyncDispose]() {} };
76+
}
77+
78+
if (true) {
79+
await using d26 = { async [Symbol.asyncDispose]() {} };
80+
}
81+
else {
82+
await using d27 = { async [Symbol.asyncDispose]() {} };
83+
}
84+
85+
while (true) {
86+
await using d28 = { async [Symbol.asyncDispose]() {} };
87+
break;
88+
}
89+
90+
do {
91+
await using d29 = { async [Symbol.asyncDispose]() {} };
92+
break;
93+
}
94+
while (true);
95+
96+
for (;;) {
97+
await using d30 = { async [Symbol.asyncDispose]() {} };
98+
break;
99+
}
100+
101+
for (const x in {}) {
102+
await using d31 = { async [Symbol.asyncDispose]() {} };
103+
}
104+
105+
for (const x of []) {
106+
await using d32 = { async [Symbol.asyncDispose]() {} };
107+
}
108+
109+
export {};

0 commit comments

Comments
 (0)