diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b01352968eb42..1ab005dd99c73 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2707,6 +2707,28 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { } } + // Function declarations are not allowed as the direct child of a statement in strict mode. + // For example: `if (true) function f() {}` is a syntax error in strict mode. + function checkStrictModeFunctionDeclarationAsStatementChild(node: FunctionDeclaration) { + if (!inStrictMode) { + return; + } + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.LabeledStatement: + const errorSpan = getErrorSpanForNode(file, node); + 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)); + break; + } + } + function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) { // Grammar checking // The identifier eval or arguments may not appear as the LeftHandSideExpression of an @@ -3713,6 +3735,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { } checkStrictModeFunctionName(node); + checkStrictModeFunctionDeclarationAsStatementChild(node); if (inStrictMode) { checkStrictModeFunctionDeclaration(node); bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 03f5f19a69877..43b087c9a4a06 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -823,6 +823,10 @@ "category": "Error", "code": 1255 }, + "In strict mode code, functions can only be declared at top level or inside a block.": { + "category": "Error", + "code": 1256 + }, "A required element cannot follow an optional element.": { "category": "Error", "code": 1257 diff --git a/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.errors.txt b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.errors.txt new file mode 100644 index 0000000000000..8bb0f2a99ed24 --- /dev/null +++ b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.errors.txt @@ -0,0 +1,53 @@ +functionDeclarationAsStatementInStrictMode.ts(2,20): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(3,23): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(4,13): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(5,19): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(6,28): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(7,28): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. +functionDeclarationAsStatementInStrictMode.ts(8,1): error TS1344: 'A label is not allowed here. +functionDeclarationAsStatementInStrictMode.ts(8,17): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + + +==== functionDeclarationAsStatementInStrictMode.ts (8 errors) ==== + // Error cases - function declarations as direct children of statements in strict mode + if (true) function f1() {} + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + while (true) function f2() {} + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + do function f3() {} while (false); + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + for (;;) function f4() {} + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + for (let x in {}) function f5() {} + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + for (let x of []) function f6() {} + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + label: function f7() {} + ~~~~~ +!!! error TS1344: 'A label is not allowed here. + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. + + // Valid cases - function declarations inside blocks + if (true) { function g1() {} } + while (true) { function g2() {} } + do { function g3() {} } while (false); + for (;;) { function g4() {} } + for (let x in {}) { function g5() {} } + for (let x of []) { function g6() {} } + label: { function g7() {} } + + // Valid - top level + function topLevel() {} + + // Valid - inside function body + function outer() { + function inner() {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.js b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.js new file mode 100644 index 0000000000000..b79687c2c7dd6 --- /dev/null +++ b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.js @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] //// + +//// [functionDeclarationAsStatementInStrictMode.ts] +// Error cases - function declarations as direct children of statements in strict mode +if (true) function f1() {} +while (true) function f2() {} +do function f3() {} while (false); +for (;;) function f4() {} +for (let x in {}) function f5() {} +for (let x of []) function f6() {} +label: function f7() {} + +// Valid cases - function declarations inside blocks +if (true) { function g1() {} } +while (true) { function g2() {} } +do { function g3() {} } while (false); +for (;;) { function g4() {} } +for (let x in {}) { function g5() {} } +for (let x of []) { function g6() {} } +label: { function g7() {} } + +// Valid - top level +function topLevel() {} + +// Valid - inside function body +function outer() { + function inner() {} +} + + +//// [functionDeclarationAsStatementInStrictMode.js] +"use strict"; +// Error cases - function declarations as direct children of statements in strict mode +if (true) + function f1() { } +while (true) + function f2() { } +do + function f3() { } +while (false); +for (;;) + function f4() { } +for (let x in {}) + function f5() { } +for (let x of []) + function f6() { } +label: function f7() { } +// Valid cases - function declarations inside blocks +if (true) { + function g1() { } +} +while (true) { + function g2() { } +} +do { + function g3() { } +} while (false); +for (;;) { + function g4() { } +} +for (let x in {}) { + function g5() { } +} +for (let x of []) { + function g6() { } +} +label: { + function g7() { } +} +// Valid - top level +function topLevel() { } +// Valid - inside function body +function outer() { + function inner() { } +} diff --git a/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.symbols b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.symbols new file mode 100644 index 0000000000000..1f408d25375d2 --- /dev/null +++ b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.symbols @@ -0,0 +1,63 @@ +//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] //// + +=== functionDeclarationAsStatementInStrictMode.ts === +// Error cases - function declarations as direct children of statements in strict mode +if (true) function f1() {} +>f1 : Symbol(f1, Decl(functionDeclarationAsStatementInStrictMode.ts, 1, 9)) + +while (true) function f2() {} +>f2 : Symbol(f2, Decl(functionDeclarationAsStatementInStrictMode.ts, 2, 12)) + +do function f3() {} while (false); +>f3 : Symbol(f3, Decl(functionDeclarationAsStatementInStrictMode.ts, 3, 2)) + +for (;;) function f4() {} +>f4 : Symbol(f4, Decl(functionDeclarationAsStatementInStrictMode.ts, 4, 8)) + +for (let x in {}) function f5() {} +>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 5, 8)) +>f5 : Symbol(f5, Decl(functionDeclarationAsStatementInStrictMode.ts, 5, 17)) + +for (let x of []) function f6() {} +>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 6, 8)) +>f6 : Symbol(f6, Decl(functionDeclarationAsStatementInStrictMode.ts, 6, 17)) + +label: function f7() {} +>f7 : Symbol(f7, Decl(functionDeclarationAsStatementInStrictMode.ts, 7, 6)) + +// Valid cases - function declarations inside blocks +if (true) { function g1() {} } +>g1 : Symbol(g1, Decl(functionDeclarationAsStatementInStrictMode.ts, 10, 11)) + +while (true) { function g2() {} } +>g2 : Symbol(g2, Decl(functionDeclarationAsStatementInStrictMode.ts, 11, 14)) + +do { function g3() {} } while (false); +>g3 : Symbol(g3, Decl(functionDeclarationAsStatementInStrictMode.ts, 12, 4)) + +for (;;) { function g4() {} } +>g4 : Symbol(g4, Decl(functionDeclarationAsStatementInStrictMode.ts, 13, 10)) + +for (let x in {}) { function g5() {} } +>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 14, 8)) +>g5 : Symbol(g5, Decl(functionDeclarationAsStatementInStrictMode.ts, 14, 19)) + +for (let x of []) { function g6() {} } +>x : Symbol(x, Decl(functionDeclarationAsStatementInStrictMode.ts, 15, 8)) +>g6 : Symbol(g6, Decl(functionDeclarationAsStatementInStrictMode.ts, 15, 19)) + +label: { function g7() {} } +>g7 : Symbol(g7, Decl(functionDeclarationAsStatementInStrictMode.ts, 16, 8)) + +// Valid - top level +function topLevel() {} +>topLevel : Symbol(topLevel, Decl(functionDeclarationAsStatementInStrictMode.ts, 16, 27)) + +// Valid - inside function body +function outer() { +>outer : Symbol(outer, Decl(functionDeclarationAsStatementInStrictMode.ts, 19, 22)) + + function inner() {} +>inner : Symbol(inner, Decl(functionDeclarationAsStatementInStrictMode.ts, 22, 18)) +} + diff --git a/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.types b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.types new file mode 100644 index 0000000000000..c5cbe73becb95 --- /dev/null +++ b/tests/baselines/reference/functionDeclarationAsStatementInStrictMode.types @@ -0,0 +1,108 @@ +//// [tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts] //// + +=== functionDeclarationAsStatementInStrictMode.ts === +// Error cases - function declarations as direct children of statements in strict mode +if (true) function f1() {} +>true : true +> : ^^^^ +>f1 : () => void +> : ^^^^^^^^^^ + +while (true) function f2() {} +>true : true +> : ^^^^ +>f2 : () => void +> : ^^^^^^^^^^ + +do function f3() {} while (false); +>f3 : () => void +> : ^^^^^^^^^^ +>false : false +> : ^^^^^ + +for (;;) function f4() {} +>f4 : () => void +> : ^^^^^^^^^^ + +for (let x in {}) function f5() {} +>x : string +> : ^^^^^^ +>{} : {} +> : ^^ +>f5 : () => void +> : ^^^^^^^^^^ + +for (let x of []) function f6() {} +>x : never +> : ^^^^^ +>[] : never[] +> : ^^^^^^^ +>f6 : () => void +> : ^^^^^^^^^^ + +label: function f7() {} +>label : any +> : ^^^ +>f7 : () => void +> : ^^^^^^^^^^ + +// Valid cases - function declarations inside blocks +if (true) { function g1() {} } +>true : true +> : ^^^^ +>g1 : () => void +> : ^^^^^^^^^^ + +while (true) { function g2() {} } +>true : true +> : ^^^^ +>g2 : () => void +> : ^^^^^^^^^^ + +do { function g3() {} } while (false); +>g3 : () => void +> : ^^^^^^^^^^ +>false : false +> : ^^^^^ + +for (;;) { function g4() {} } +>g4 : () => void +> : ^^^^^^^^^^ + +for (let x in {}) { function g5() {} } +>x : string +> : ^^^^^^ +>{} : {} +> : ^^ +>g5 : () => void +> : ^^^^^^^^^^ + +for (let x of []) { function g6() {} } +>x : never +> : ^^^^^ +>[] : never[] +> : ^^^^^^^ +>g6 : () => void +> : ^^^^^^^^^^ + +label: { function g7() {} } +>label : any +> : ^^^ +>g7 : () => void +> : ^^^^^^^^^^ + +// Valid - top level +function topLevel() {} +>topLevel : () => void +> : ^^^^^^^^^^ + +// Valid - inside function body +function outer() { +>outer : () => void +> : ^^^^^^^^^^ + + function inner() {} +>inner : () => void +> : ^^^^^^^^^^ +} + diff --git a/tests/baselines/reference/labeledStatementWithLabel_strict.errors.txt b/tests/baselines/reference/labeledStatementWithLabel_strict.errors.txt index 4aaa73d420b0e..be2b820a17341 100644 --- a/tests/baselines/reference/labeledStatementWithLabel_strict.errors.txt +++ b/tests/baselines/reference/labeledStatementWithLabel_strict.errors.txt @@ -1,6 +1,9 @@ labeledStatementWithLabel_strict.ts(2,1): error TS1344: 'A label is not allowed here. +labeledStatementWithLabel_strict.ts(2,17): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. labeledStatementWithLabel_strict.ts(3,1): error TS1344: 'A label is not allowed here. +labeledStatementWithLabel_strict.ts(3,18): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. labeledStatementWithLabel_strict.ts(4,1): error TS1344: 'A label is not allowed here. +labeledStatementWithLabel_strict.ts(4,23): error TS1256: In strict mode code, functions can only be declared at top level or inside a block. labeledStatementWithLabel_strict.ts(5,1): error TS1344: 'A label is not allowed here. labeledStatementWithLabel_strict.ts(6,1): error TS1344: 'A label is not allowed here. labeledStatementWithLabel_strict.ts(7,1): error TS1344: 'A label is not allowed here. @@ -14,17 +17,23 @@ labeledStatementWithLabel_strict.ts(13,8): error TS1235: A namespace declaration labeledStatementWithLabel_strict.ts(14,1): error TS1344: 'A label is not allowed here. -==== labeledStatementWithLabel_strict.ts (14 errors) ==== +==== labeledStatementWithLabel_strict.ts (17 errors) ==== "use strict" label: function fn() { } ~~~~~ !!! error TS1344: 'A label is not allowed here. + ~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. label: function* gen() { } ~~~~~ !!! error TS1344: 'A label is not allowed here. + ~~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. label: async function gen1() { } ~~~~~ !!! error TS1344: 'A label is not allowed here. + ~~~~ +!!! error TS1256: In strict mode code, functions can only be declared at top level or inside a block. label: enum E {} ~~~~~ !!! error TS1344: 'A label is not allowed here. diff --git a/tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts b/tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts new file mode 100644 index 0000000000000..83f8eb951e42d --- /dev/null +++ b/tests/cases/compiler/functionDeclarationAsStatementInStrictMode.ts @@ -0,0 +1,28 @@ +// @strict: true +// @target: ES2020 + +// Error cases - function declarations as direct children of statements in strict mode +if (true) function f1() {} +while (true) function f2() {} +do function f3() {} while (false); +for (;;) function f4() {} +for (let x in {}) function f5() {} +for (let x of []) function f6() {} +label: function f7() {} + +// Valid cases - function declarations inside blocks +if (true) { function g1() {} } +while (true) { function g2() {} } +do { function g3() {} } while (false); +for (;;) { function g4() {} } +for (let x in {}) { function g5() {} } +for (let x of []) { function g6() {} } +label: { function g7() {} } + +// Valid - top level +function topLevel() {} + +// Valid - inside function body +function outer() { + function inner() {} +}