From c07da583afbfbb68930448b781832c74b3f713e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 18 Oct 2024 18:24:33 +0200 Subject: [PATCH] Disallow type and interface declarations in statements with blockless bodies (#60183) --- src/compiler/checker.ts | 12 +- .../typeAliasDeclarationEmit3.errors.txt | 30 +++++ ...eDeclarationsInBlockStatements1.errors.txt | 46 +++++++ ...InterfaceDeclarationsInBlockStatements1.js | 65 ++++++++++ ...faceDeclarationsInBlockStatements1.symbols | 67 ++++++++++ ...erfaceDeclarationsInBlockStatements1.types | 114 ++++++++++++++++++ ...InterfaceDeclarationsInBlockStatements1.ts | 33 +++++ 7 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/typeAliasDeclarationEmit3.errors.txt create mode 100644 tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.errors.txt create mode 100644 tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.js create mode 100644 tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.symbols create mode 100644 tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.types create mode 100644 tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ce2f19bbca1..295b8b3c257 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -46774,6 +46774,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkInterfaceDeclaration(node: InterfaceDeclaration) { // Grammar checking if (!checkGrammarModifiers(node)) checkGrammarInterfaceDeclaration(node); + if (!allowBlockDeclarations(node.parent)) { + grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "interface"); + } checkTypeParameters(node.typeParameters); addLazyDiagnostic(() => { @@ -46817,6 +46820,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Grammar checking checkGrammarModifiers(node); checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); + if (!allowBlockDeclarations(node.parent)) { + grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "type"); + } checkExportsOnMergedDeclarations(node); checkTypeParameters(node.typeParameters); if (node.type.kind === SyntaxKind.IntrinsicKeyword) { @@ -52051,7 +52057,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function allowLetAndConstDeclarations(parent: Node): boolean { + function allowBlockDeclarations(parent: Node): boolean { switch (parent.kind) { case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: @@ -52062,14 +52068,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ForOfStatement: return false; case SyntaxKind.LabeledStatement: - return allowLetAndConstDeclarations(parent.parent); + return allowBlockDeclarations(parent.parent); } return true; } function checkGrammarForDisallowedBlockScopedVariableStatement(node: VariableStatement) { - if (!allowLetAndConstDeclarations(node.parent)) { + if (!allowBlockDeclarations(node.parent)) { const blockScopeKind = getCombinedNodeFlagsCached(node.declarationList) & NodeFlags.BlockScoped; if (blockScopeKind) { const keyword = blockScopeKind === NodeFlags.Let ? "let" : diff --git a/tests/baselines/reference/typeAliasDeclarationEmit3.errors.txt b/tests/baselines/reference/typeAliasDeclarationEmit3.errors.txt new file mode 100644 index 00000000000..516b4494910 --- /dev/null +++ b/tests/baselines/reference/typeAliasDeclarationEmit3.errors.txt @@ -0,0 +1,30 @@ +typeAliasDeclarationEmit3.ts(3,14): error TS1156: 'type' declarations can only be declared inside a block. +typeAliasDeclarationEmit3.ts(9,14): error TS1156: 'type' declarations can only be declared inside a block. +typeAliasDeclarationEmit3.ts(15,14): error TS1156: 'type' declarations can only be declared inside a block. + + +==== typeAliasDeclarationEmit3.ts (3 errors) ==== + function f1(): void { + for (let i = 0; i < 1; i++) + type foo = []; + ~~~ +!!! error TS1156: 'type' declarations can only be declared inside a block. + console.log('f1'); + } + + function f2(): void { + while (true) + type foo = []; + ~~~ +!!! error TS1156: 'type' declarations can only be declared inside a block. + console.log('f2'); + } + + function f3(): void { + if (true) + type foo = []; + ~~~ +!!! error TS1156: 'type' declarations can only be declared inside a block. + console.log('f3'); + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.errors.txt b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.errors.txt new file mode 100644 index 00000000000..eca38cd12fd --- /dev/null +++ b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.errors.txt @@ -0,0 +1,46 @@ +typeInterfaceDeclarationsInBlockStatements1.ts(4,18): error TS1156: 'type' declarations can only be declared inside a block. +typeInterfaceDeclarationsInBlockStatements1.ts(12,21): error TS2304: Cannot find name 's'. +typeInterfaceDeclarationsInBlockStatements1.ts(17,15): error TS1156: 'interface' declarations can only be declared inside a block. +typeInterfaceDeclarationsInBlockStatements1.ts(29,21): error TS2304: Cannot find name 's'. + + +==== typeInterfaceDeclarationsInBlockStatements1.ts (4 errors) ==== + // https://github.com/microsoft/TypeScript/issues/60175 + + function f1() { + if (true) type s = string; + ~ +!!! error TS1156: 'type' declarations can only be declared inside a block. + console.log("" as s); + } + + function f2() { + if (true) { + type s = string; + } + console.log("" as s); + ~ +!!! error TS2304: Cannot find name 's'. + } + + function f3() { + if (true) + interface s { + ~ +!!! error TS1156: 'interface' declarations can only be declared inside a block. + length: number; + } + console.log("" as s); + } + + function f4() { + if (true) { + interface s { + length: number; + } + } + console.log("" as s); + ~ +!!! error TS2304: Cannot find name 's'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.js b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.js new file mode 100644 index 00000000000..f636aafa95c --- /dev/null +++ b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.js @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] //// + +//// [typeInterfaceDeclarationsInBlockStatements1.ts] +// https://github.com/microsoft/TypeScript/issues/60175 + +function f1() { + if (true) type s = string; + console.log("" as s); +} + +function f2() { + if (true) { + type s = string; + } + console.log("" as s); +} + +function f3() { + if (true) + interface s { + length: number; + } + console.log("" as s); +} + +function f4() { + if (true) { + interface s { + length: number; + } + } + console.log("" as s); +} + + +//// [typeInterfaceDeclarationsInBlockStatements1.js] +"use strict"; +// https://github.com/microsoft/TypeScript/issues/60175 +function f1() { + if (true) + ; + console.log(""); +} +function f2() { + if (true) { + } + console.log(""); +} +function f3() { + if (true) + ; + console.log(""); +} +function f4() { + if (true) { + } + console.log(""); +} + + +//// [typeInterfaceDeclarationsInBlockStatements1.d.ts] +declare function f1(): void; +declare function f2(): void; +declare function f3(): void; +declare function f4(): void; diff --git a/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.symbols b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.symbols new file mode 100644 index 00000000000..7c3ee3a47ae --- /dev/null +++ b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.symbols @@ -0,0 +1,67 @@ +//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] //// + +=== typeInterfaceDeclarationsInBlockStatements1.ts === +// https://github.com/microsoft/TypeScript/issues/60175 + +function f1() { +>f1 : Symbol(f1, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 0, 0)) + + if (true) type s = string; +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11)) + + console.log("" as s); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11)) +} + +function f2() { +>f2 : Symbol(f2, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 5, 1)) + + if (true) { + type s = string; +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 8, 13)) + } + console.log("" as s); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>s : Symbol(s) +} + +function f3() { +>f3 : Symbol(f3, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 12, 1)) + + if (true) + interface s { +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11)) + + length: number; +>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 16, 17)) + } + console.log("" as s); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11)) +} + +function f4() { +>f4 : Symbol(f4, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 20, 1)) + + if (true) { + interface s { +>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 23, 13)) + + length: number; +>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 24, 17)) + } + } + console.log("" as s); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>s : Symbol(s) +} + diff --git a/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.types b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.types new file mode 100644 index 00000000000..1a9b2cadab3 --- /dev/null +++ b/tests/baselines/reference/typeInterfaceDeclarationsInBlockStatements1.types @@ -0,0 +1,114 @@ +//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] //// + +=== typeInterfaceDeclarationsInBlockStatements1.ts === +// https://github.com/microsoft/TypeScript/issues/60175 + +function f1() { +>f1 : () => void +> : ^^^^^^^^^^ + + if (true) type s = string; +>true : true +> : ^^^^ +>s : string +> : ^^^^^^ + + console.log("" as s); +>console.log("" as s) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"" as s : string +> : ^^^^^^ +>"" : "" +> : ^^ +} + +function f2() { +>f2 : () => void +> : ^^^^^^^^^^ + + if (true) { +>true : true +> : ^^^^ + + type s = string; +>s : string +> : ^^^^^^ + } + console.log("" as s); +>console.log("" as s) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"" as s : s +> : ^ +>"" : "" +> : ^^ +} + +function f3() { +>f3 : () => void +> : ^^^^^^^^^^ + + if (true) +>true : true +> : ^^^^ + + interface s { + length: number; +>length : number +> : ^^^^^^ + } + console.log("" as s); +>console.log("" as s) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"" as s : s +> : ^ +>"" : "" +> : ^^ +} + +function f4() { +>f4 : () => void +> : ^^^^^^^^^^ + + if (true) { +>true : true +> : ^^^^ + + interface s { + length: number; +>length : number +> : ^^^^^^ + } + } + console.log("" as s); +>console.log("" as s) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"" as s : s +> : ^ +>"" : "" +> : ^^ +} + diff --git a/tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts b/tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts new file mode 100644 index 00000000000..27d698a4835 --- /dev/null +++ b/tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts @@ -0,0 +1,33 @@ +// @strict: true +// @declaration: true + +// https://github.com/microsoft/TypeScript/issues/60175 + +function f1() { + if (true) type s = string; + console.log("" as s); +} + +function f2() { + if (true) { + type s = string; + } + console.log("" as s); +} + +function f3() { + if (true) + interface s { + length: number; + } + console.log("" as s); +} + +function f4() { + if (true) { + interface s { + length: number; + } + } + console.log("" as s); +}