Skip to content

Commit f55c6a2

Browse files
committed
Report error for function declarations as statement children in strict mode
Fixes #62896 In strict mode, function declarations can only appear at the top level of a script, module, or function body, or inside a block. Code like `if (true) function f() {}` is a syntax error in strict mode but TypeScript was not reporting it. This change adds a check in the binder to detect when a function declaration is a direct child of a statement node (IfStatement, WhileStatement, DoStatement, ForStatement, ForInStatement, ForOfStatement, WithStatement, or LabeledStatement) and reports error TS1256.
1 parent db3ae1b commit f55c6a2

7 files changed

Lines changed: 355 additions & 0 deletions

src/compiler/binder.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,6 +2705,30 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
27052705
file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length, getStrictModeBlockScopeFunctionDeclarationMessage(node)));
27062706
}
27072707
}
2708+
// In strict mode, function declarations are not allowed as the direct child of a statement.
2709+
// For example: `if (true) function f() {}` is a syntax error in strict mode.
2710+
// This applies regardless of the target version.
2711+
if (inStrictMode && isFunctionDeclarationStatementChild(node)) {
2712+
const errorSpan = getErrorSpanForNode(file, node);
2713+
file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length, Diagnostics.In_strict_mode_code_functions_can_only_be_declared_at_top_level_or_inside_a_block));
2714+
}
2715+
}
2716+
2717+
function isFunctionDeclarationStatementChild(node: FunctionDeclaration): boolean {
2718+
const parent = node.parent;
2719+
switch (parent.kind) {
2720+
case SyntaxKind.IfStatement:
2721+
case SyntaxKind.DoStatement:
2722+
case SyntaxKind.WhileStatement:
2723+
case SyntaxKind.ForStatement:
2724+
case SyntaxKind.ForInStatement:
2725+
case SyntaxKind.ForOfStatement:
2726+
case SyntaxKind.WithStatement:
2727+
case SyntaxKind.LabeledStatement:
2728+
return true;
2729+
default:
2730+
return false;
2731+
}
27082732
}
27092733

27102734
function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,10 @@
823823
"category": "Error",
824824
"code": 1255
825825
},
826+
"In strict mode code, functions can only be declared at top level or inside a block.": {
827+
"category": "Error",
828+
"code": 1256
829+
},
826830
"A required element cannot follow an optional element.": {
827831
"category": "Error",
828832
"code": 1257
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
functionDeclarationAsStatementInStrictMode.ts(2,20): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
2+
functionDeclarationAsStatementInStrictMode.ts(3,23): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
3+
functionDeclarationAsStatementInStrictMode.ts(4,13): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
4+
functionDeclarationAsStatementInStrictMode.ts(5,19): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
5+
functionDeclarationAsStatementInStrictMode.ts(6,28): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
6+
functionDeclarationAsStatementInStrictMode.ts(7,28): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
7+
functionDeclarationAsStatementInStrictMode.ts(8,1): error TS1344: 'A label is not allowed here.
8+
functionDeclarationAsStatementInStrictMode.ts(8,17): error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
9+
10+
11+
==== functionDeclarationAsStatementInStrictMode.ts (8 errors) ====
12+
// Error cases - function declarations as direct children of statements in strict mode
13+
if (true) function f1() {}
14+
~~
15+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
16+
while (true) function f2() {}
17+
~~
18+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
19+
do function f3() {} while (false);
20+
~~
21+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
22+
for (;;) function f4() {}
23+
~~
24+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
25+
for (let x in {}) function f5() {}
26+
~~
27+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
28+
for (let x of []) function f6() {}
29+
~~
30+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
31+
label: function f7() {}
32+
~~~~~
33+
!!! error TS1344: 'A label is not allowed here.
34+
~~
35+
!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block.
36+
37+
// Valid cases - function declarations inside blocks
38+
if (true) { function g1() {} }
39+
while (true) { function g2() {} }
40+
do { function g3() {} } while (false);
41+
for (;;) { function g4() {} }
42+
for (let x in {}) { function g5() {} }
43+
for (let x of []) { function g6() {} }
44+
label: { function g7() {} }
45+
46+
// Valid - top level
47+
function topLevel() {}
48+
49+
// Valid - inside function body
50+
function outer() {
51+
function inner() {}
52+
}
53+
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] ////
2+
3+
//// [functionDeclarationAsStatementInStrictMode.ts]
4+
// Error cases - function declarations as direct children of statements in strict mode
5+
if (true) function f1() {}
6+
while (true) function f2() {}
7+
do function f3() {} while (false);
8+
for (;;) function f4() {}
9+
for (let x in {}) function f5() {}
10+
for (let x of []) function f6() {}
11+
label: function f7() {}
12+
13+
// Valid cases - function declarations inside blocks
14+
if (true) { function g1() {} }
15+
while (true) { function g2() {} }
16+
do { function g3() {} } while (false);
17+
for (;;) { function g4() {} }
18+
for (let x in {}) { function g5() {} }
19+
for (let x of []) { function g6() {} }
20+
label: { function g7() {} }
21+
22+
// Valid - top level
23+
function topLevel() {}
24+
25+
// Valid - inside function body
26+
function outer() {
27+
function inner() {}
28+
}
29+
30+
31+
//// [functionDeclarationAsStatementInStrictMode.js]
32+
"use strict";
33+
// Error cases - function declarations as direct children of statements in strict mode
34+
if (true)
35+
function f1() { }
36+
while (true)
37+
function f2() { }
38+
do
39+
function f3() { }
40+
while (false);
41+
for (;;)
42+
function f4() { }
43+
for (let x in {})
44+
function f5() { }
45+
for (let x of [])
46+
function f6() { }
47+
label: function f7() { }
48+
// Valid cases - function declarations inside blocks
49+
if (true) {
50+
function g1() { }
51+
}
52+
while (true) {
53+
function g2() { }
54+
}
55+
do {
56+
function g3() { }
57+
} while (false);
58+
for (;;) {
59+
function g4() { }
60+
}
61+
for (let x in {}) {
62+
function g5() { }
63+
}
64+
for (let x of []) {
65+
function g6() { }
66+
}
67+
label: {
68+
function g7() { }
69+
}
70+
// Valid - top level
71+
function topLevel() { }
72+
// Valid - inside function body
73+
function outer() {
74+
function inner() { }
75+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] ////
2+
3+
=== functionDeclarationAsStatementInStrictMode.ts ===
4+
// Error cases - function declarations as direct children of statements in strict mode
5+
if (true) function f1() {}
6+
>f1 : Symbol(f1, Decl(functionDeclarationAsStatementInStrictMode.ts, 1, 9))
7+
8+
while (true) function f2() {}
9+
>f2 : Symbol(f2, Decl(functionDeclarationAsStatementInStrictMode.ts, 2, 12))
10+
11+
do function f3() {} while (false);
12+
>f3 : Symbol(f3, Decl(functionDeclarationAsStatementInStrictMode.ts, 3, 2))
13+
14+
for (;;) function f4() {}
15+
>f4 : Symbol(f4, Decl(functionDeclarationAsStatementInStrictMode.ts, 4, 8))
16+
17+
for (let x in {}) function f5() {}
18+
>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 5, 8))
19+
>f5 : Symbol(f5, Decl(functionDeclarationAsStatementInStrictMode.ts, 5, 17))
20+
21+
for (let x of []) function f6() {}
22+
>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 6, 8))
23+
>f6 : Symbol(f6, Decl(functionDeclarationAsStatementInStrictMode.ts, 6, 17))
24+
25+
label: function f7() {}
26+
>f7 : Symbol(f7, Decl(functionDeclarationAsStatementInStrictMode.ts, 7, 6))
27+
28+
// Valid cases - function declarations inside blocks
29+
if (true) { function g1() {} }
30+
>g1 : Symbol(g1, Decl(functionDeclarationAsStatementInStrictMode.ts, 10, 11))
31+
32+
while (true) { function g2() {} }
33+
>g2 : Symbol(g2, Decl(functionDeclarationAsStatementInStrictMode.ts, 11, 14))
34+
35+
do { function g3() {} } while (false);
36+
>g3 : Symbol(g3, Decl(functionDeclarationAsStatementInStrictMode.ts, 12, 4))
37+
38+
for (;;) { function g4() {} }
39+
>g4 : Symbol(g4, Decl(functionDeclarationAsStatementInStrictMode.ts, 13, 10))
40+
41+
for (let x in {}) { function g5() {} }
42+
>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 14, 8))
43+
>g5 : Symbol(g5, Decl(functionDeclarationAsStatementInStrictMode.ts, 14, 19))
44+
45+
for (let x of []) { function g6() {} }
46+
>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 15, 8))
47+
>g6 : Symbol(g6, Decl(functionDeclarationAsStatementInStrictMode.ts, 15, 19))
48+
49+
label: { function g7() {} }
50+
>g7 : Symbol(g7, Decl(functionDeclarationAsStatementInStrictMode.ts, 16, 8))
51+
52+
// Valid - top level
53+
function topLevel() {}
54+
>topLevel : Symbol(topLevel, Decl(functionDeclarationAsStatementInStrictMode.ts, 16, 27))
55+
56+
// Valid - inside function body
57+
function outer() {
58+
>outer : Symbol(outer, Decl(functionDeclarationAsStatementInStrictMode.ts, 19, 22))
59+
60+
function inner() {}
61+
>inner : Symbol(inner, Decl(functionDeclarationAsStatementInStrictMode.ts, 22, 18))
62+
}
63+
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] ////
2+
3+
=== functionDeclarationAsStatementInStrictMode.ts ===
4+
// Error cases - function declarations as direct children of statements in strict mode
5+
if (true) function f1() {}
6+
>true : true
7+
> : ^^^^
8+
>f1 : () => void
9+
> : ^^^^^^^^^^
10+
11+
while (true) function f2() {}
12+
>true : true
13+
> : ^^^^
14+
>f2 : () => void
15+
> : ^^^^^^^^^^
16+
17+
do function f3() {} while (false);
18+
>f3 : () => void
19+
> : ^^^^^^^^^^
20+
>false : false
21+
> : ^^^^^
22+
23+
for (;;) function f4() {}
24+
>f4 : () => void
25+
> : ^^^^^^^^^^
26+
27+
for (let x in {}) function f5() {}
28+
>x : string
29+
> : ^^^^^^
30+
>{} : {}
31+
> : ^^
32+
>f5 : () => void
33+
> : ^^^^^^^^^^
34+
35+
for (let x of []) function f6() {}
36+
>x : never
37+
> : ^^^^^
38+
>[] : never[]
39+
> : ^^^^^^^
40+
>f6 : () => void
41+
> : ^^^^^^^^^^
42+
43+
label: function f7() {}
44+
>label : any
45+
> : ^^^
46+
>f7 : () => void
47+
> : ^^^^^^^^^^
48+
49+
// Valid cases - function declarations inside blocks
50+
if (true) { function g1() {} }
51+
>true : true
52+
> : ^^^^
53+
>g1 : () => void
54+
> : ^^^^^^^^^^
55+
56+
while (true) { function g2() {} }
57+
>true : true
58+
> : ^^^^
59+
>g2 : () => void
60+
> : ^^^^^^^^^^
61+
62+
do { function g3() {} } while (false);
63+
>g3 : () => void
64+
> : ^^^^^^^^^^
65+
>false : false
66+
> : ^^^^^
67+
68+
for (;;) { function g4() {} }
69+
>g4 : () => void
70+
> : ^^^^^^^^^^
71+
72+
for (let x in {}) { function g5() {} }
73+
>x : string
74+
> : ^^^^^^
75+
>{} : {}
76+
> : ^^
77+
>g5 : () => void
78+
> : ^^^^^^^^^^
79+
80+
for (let x of []) { function g6() {} }
81+
>x : never
82+
> : ^^^^^
83+
>[] : never[]
84+
> : ^^^^^^^
85+
>g6 : () => void
86+
> : ^^^^^^^^^^
87+
88+
label: { function g7() {} }
89+
>label : any
90+
> : ^^^
91+
>g7 : () => void
92+
> : ^^^^^^^^^^
93+
94+
// Valid - top level
95+
function topLevel() {}
96+
>topLevel : () => void
97+
> : ^^^^^^^^^^
98+
99+
// Valid - inside function body
100+
function outer() {
101+
>outer : () => void
102+
> : ^^^^^^^^^^
103+
104+
function inner() {}
105+
>inner : () => void
106+
> : ^^^^^^^^^^
107+
}
108+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @strict: true
2+
// @target: ES2020
3+
4+
// Error cases - function declarations as direct children of statements in strict mode
5+
if (true) function f1() {}
6+
while (true) function f2() {}
7+
do function f3() {} while (false);
8+
for (;;) function f4() {}
9+
for (let x in {}) function f5() {}
10+
for (let x of []) function f6() {}
11+
label: function f7() {}
12+
13+
// Valid cases - function declarations inside blocks
14+
if (true) { function g1() {} }
15+
while (true) { function g2() {} }
16+
do { function g3() {} } while (false);
17+
for (;;) { function g4() {} }
18+
for (let x in {}) { function g5() {} }
19+
for (let x of []) { function g6() {} }
20+
label: { function g7() {} }
21+
22+
// Valid - top level
23+
function topLevel() {}
24+
25+
// Valid - inside function body
26+
function outer() {
27+
function inner() {}
28+
}

0 commit comments

Comments
 (0)