diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 27ec079f614ab..164ee16d71190 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -219,6 +219,7 @@ import { isSpecialPropertyDeclaration, isStatement, isStatementButNotDeclaration, + isStatementsContainer, isStatic, isString, isStringLiteralLike, @@ -3850,7 +3851,7 @@ function isEnumDeclarationWithPreservedEmit(node: Node, options: CompilerOptions } function eachUnreachableRange(node: Node, options: CompilerOptions, cb: (start: Node, last: Node) => void): void { - if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) { + if (isStatement(node) && isExecutableStatement(node) && isStatementsContainer(node.parent)) { const { statements } = node.parent; const slice = sliceAfter(statements, node); getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1])); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 626bcecf9d34a..83d7280ac462d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3321,6 +3321,10 @@ export interface Statement extends Node, JSDocContainer { _statementBrand: any; } +export interface StatementsContainer extends Node { + readonly statements: NodeArray; +} + // Represents a statement that is elided as part of a transformation to emit comments on a // not-emitted node. export interface NotEmittedStatement extends Statement { @@ -3368,9 +3372,8 @@ export type BlockLike = | ModuleBlock | CaseOrDefaultClause; -export interface Block extends Statement, LocalsContainer { +export interface Block extends Statement, LocalsContainer, StatementsContainer { readonly kind: SyntaxKind.Block; - readonly statements: NodeArray; /** @internal */ multiLine?: boolean; } @@ -3477,18 +3480,16 @@ export interface CaseBlock extends Node, LocalsContainer { readonly clauses: NodeArray; } -export interface CaseClause extends Node, JSDocContainer { +export interface CaseClause extends StatementsContainer, JSDocContainer { readonly kind: SyntaxKind.CaseClause; readonly parent: CaseBlock; readonly expression: Expression; - readonly statements: NodeArray; /** @internal */ fallthroughFlowNode?: FlowNode; } -export interface DefaultClause extends Node { +export interface DefaultClause extends StatementsContainer { readonly kind: SyntaxKind.DefaultClause; readonly parent: CaseBlock; - readonly statements: NodeArray; /** @internal */ fallthroughFlowNode?: FlowNode; } @@ -3653,10 +3654,9 @@ export interface JSDocNamespaceDeclaration extends ModuleDeclaration { readonly body?: JSDocNamespaceBody; } -export interface ModuleBlock extends Node, Statement { +export interface ModuleBlock extends StatementsContainer, Statement { readonly kind: SyntaxKind.ModuleBlock; readonly parent: ModuleDeclaration; - readonly statements: NodeArray; } export type ModuleReference = @@ -4316,9 +4316,8 @@ export interface RedirectInfo { export type ResolutionMode = ModuleKind.ESNext | ModuleKind.CommonJS | undefined; // Source files are declarations when they are external modules. -export interface SourceFile extends Declaration, LocalsContainer { +export interface SourceFile extends Declaration, LocalsContainer, StatementsContainer { readonly kind: SyntaxKind.SourceFile; - readonly statements: NodeArray; readonly endOfFileToken: Token; fileName: string; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9805b7ae9b1e4..6bf907900b641 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -533,6 +533,7 @@ import { startsWith, startsWithUseStrict, Statement, + StatementsContainer, StringLiteral, StringLiteralLike, StringLiteralType, @@ -4939,6 +4940,19 @@ export function isNodeWithPossibleHoistedDeclaration(node: Node): node is NodeWi return false; } +/** @internal */ +export function isStatementsContainer(node: Node): node is StatementsContainer { + switch (node.kind) { + case SyntaxKind.Block: + case SyntaxKind.ModuleBlock: + case SyntaxKind.SourceFile: + case SyntaxKind.DefaultClause: + case SyntaxKind.CaseClause: + return true; + } + return false; +} + /** @internal */ export type ValueSignatureDeclaration = | FunctionDeclaration diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ba6404355b3c3..81f038c25b796 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5271,6 +5271,9 @@ declare namespace ts { interface Statement extends Node, JSDocContainer { _statementBrand: any; } + interface StatementsContainer extends Node { + readonly statements: NodeArray; + } interface NotEmittedStatement extends Statement { readonly kind: SyntaxKind.NotEmittedStatement; } @@ -5295,9 +5298,8 @@ declare namespace ts { readonly name?: Identifier; } type BlockLike = SourceFile | Block | ModuleBlock | CaseOrDefaultClause; - interface Block extends Statement, LocalsContainer { + interface Block extends Statement, LocalsContainer, StatementsContainer { readonly kind: SyntaxKind.Block; - readonly statements: NodeArray; } interface VariableStatement extends Statement, FlowContainer { readonly kind: SyntaxKind.VariableStatement; @@ -5373,16 +5375,14 @@ declare namespace ts { readonly parent: SwitchStatement; readonly clauses: NodeArray; } - interface CaseClause extends Node, JSDocContainer { + interface CaseClause extends StatementsContainer, JSDocContainer { readonly kind: SyntaxKind.CaseClause; readonly parent: CaseBlock; readonly expression: Expression; - readonly statements: NodeArray; } - interface DefaultClause extends Node { + interface DefaultClause extends StatementsContainer { readonly kind: SyntaxKind.DefaultClause; readonly parent: CaseBlock; - readonly statements: NodeArray; } type CaseOrDefaultClause = CaseClause | DefaultClause; interface LabeledStatement extends Statement, FlowContainer { @@ -5488,10 +5488,9 @@ declare namespace ts { readonly name: Identifier; readonly body?: JSDocNamespaceBody; } - interface ModuleBlock extends Node, Statement { + interface ModuleBlock extends StatementsContainer, Statement { readonly kind: SyntaxKind.ModuleBlock; readonly parent: ModuleDeclaration; - readonly statements: NodeArray; } type ModuleReference = EntityName | ExternalModuleReference; /** @@ -5913,9 +5912,8 @@ declare namespace ts { getLineAndCharacterOfPosition(pos: number): LineAndCharacter; } type ResolutionMode = ModuleKind.ESNext | ModuleKind.CommonJS | undefined; - interface SourceFile extends Declaration, LocalsContainer { + interface SourceFile extends Declaration, LocalsContainer, StatementsContainer { readonly kind: SyntaxKind.SourceFile; - readonly statements: NodeArray; readonly endOfFileToken: Token; fileName: string; text: string; diff --git a/tests/baselines/reference/reachabilityChecks1.errors.txt b/tests/baselines/reference/reachabilityChecks1.errors.txt index 8220dd8a921b3..5197d5967bc61 100644 --- a/tests/baselines/reference/reachabilityChecks1.errors.txt +++ b/tests/baselines/reference/reachabilityChecks1.errors.txt @@ -3,56 +3,88 @@ reachabilityChecks1.ts(6,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(18,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(30,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(47,5): error TS7027: Unreachable code detected. +reachabilityChecks1.ts(51,1): error TS7027: Unreachable code detected. reachabilityChecks1.ts(60,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected. -==== reachabilityChecks1.ts (7 errors) ==== +==== reachabilityChecks1.ts (8 errors) ==== while (true); var x = 1; ~~~~~~~~~~ -!!! error TS7027: Unreachable code detected. + namespace A { + ~~~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ let x; + ~~~~~~~~~~ ~~~~~~ !!! error TS7027: Unreachable code detected. } + ~ + namespace A1 { + ~~~~~~~~~~~~~~ do {} while(true); + ~~~~~~~~~~~~~~~~~~~~~~ namespace A { + ~~~~~~~~~~~~~~~~~ interface F {} + ~~~~~~~~~~~~~~~~~~~~~~ } + ~~~~~ } + ~ + namespace A2 { + ~~~~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ namespace A { + ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ var x = 1; + ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ } ~~~~~ + ~~~~~ !!! error TS7027: Unreachable code detected. } + ~ + namespace A3 { + ~~~~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ type T = string; + ~~~~~~~~~~~~~~~~~~~~ } + ~ + namespace A4 { + ~~~~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ namespace A { + ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ const enum E { X } + ~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ } ~~~~~ + ~~~~~ !!! error TS7027: Unreachable code detected. } + ~ +!!! error TS7027: Unreachable code detected. function f1(x) { if (x) { @@ -74,10 +106,16 @@ reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected. } namespace B { + ~~~~~~~~~~~~~ for (; ;); + ~~~~~~~~~~~~~~ namespace C { + ~~~~~~~~~~~~~~~~~ } + ~~~~~ } + ~ +!!! error TS7027: Unreachable code detected. function f3() { do { diff --git a/tests/baselines/reference/reachabilityChecks10.errors.txt b/tests/baselines/reference/reachabilityChecks10.errors.txt new file mode 100644 index 0000000000000..2b26eae20f99e --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks10.errors.txt @@ -0,0 +1,11 @@ +reachabilityChecks10.ts(2,1): error TS7027: Unreachable code detected. + + +==== reachabilityChecks10.ts (1 errors) ==== + throw new Error("") + console.log("1") + ~~~~~~~~~~~~~~~~ + console.log("2") + ~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks10.symbols b/tests/baselines/reference/reachabilityChecks10.symbols new file mode 100644 index 0000000000000..6ecc554153996 --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks10.symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/reachabilityChecks10.ts] //// + +=== reachabilityChecks10.ts === +throw new Error("") +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +console.log("1") +>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, --, --)) + +console.log("2") +>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, --, --)) + diff --git a/tests/baselines/reference/reachabilityChecks10.types b/tests/baselines/reference/reachabilityChecks10.types new file mode 100644 index 0000000000000..bfe7b1bfefe8d --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks10.types @@ -0,0 +1,35 @@ +//// [tests/cases/compiler/reachabilityChecks10.ts] //// + +=== reachabilityChecks10.ts === +throw new Error("") +>new Error("") : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +>"" : "" +> : ^^ + +console.log("1") +>console.log("1") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"1" : "1" +> : ^^^ + +console.log("2") +>console.log("2") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"2" : "2" +> : ^^^ + diff --git a/tests/baselines/reference/reachabilityChecks9.errors.txt b/tests/baselines/reference/reachabilityChecks9.errors.txt new file mode 100644 index 0000000000000..75b9cf98dac0e --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks9.errors.txt @@ -0,0 +1,37 @@ +reachabilityChecks9.ts(7,7): error TS7027: Unreachable code detected. +reachabilityChecks9.ts(20,7): error TS7027: Unreachable code detected. + + +==== reachabilityChecks9.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/55562 + + function g(str: string) { + switch (str) { + case "a": + return; + console.log("1"); + ~~~~~~~~~~~~~~~~~ + console.log("2"); + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + case "b": + console.log("3"); + } + } + + function h(str: string) { + switch (str) { + case "a": + console.log("1"); + default: + return; + console.log("2"); + ~~~~~~~~~~~~~~~~~ + console.log("3"); + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + case "b": + console.log("4"); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks9.symbols b/tests/baselines/reference/reachabilityChecks9.symbols new file mode 100644 index 0000000000000..d50f997b9aabc --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks9.symbols @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { +>g : Symbol(g, Decl(reachabilityChecks9.ts, 0, 0)) +>str : Symbol(str, Decl(reachabilityChecks9.ts, 2, 11)) + + switch (str) { +>str : Symbol(str, Decl(reachabilityChecks9.ts, 2, 11)) + + case "a": + return; + console.log("1"); +>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, --, --)) + + console.log("2"); +>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, --, --)) + + case "b": + console.log("3"); +>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, --, --)) + } +} + +function h(str: string) { +>h : Symbol(h, Decl(reachabilityChecks9.ts, 11, 1)) +>str : Symbol(str, Decl(reachabilityChecks9.ts, 13, 11)) + + switch (str) { +>str : Symbol(str, Decl(reachabilityChecks9.ts, 13, 11)) + + case "a": + console.log("1"); +>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, --, --)) + + default: + return; + console.log("2"); +>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, --, --)) + + console.log("3"); +>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, --, --)) + + case "b": + console.log("4"); +>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, --, --)) + } +} + diff --git a/tests/baselines/reference/reachabilityChecks9.types b/tests/baselines/reference/reachabilityChecks9.types new file mode 100644 index 0000000000000..d428078568a98 --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks9.types @@ -0,0 +1,132 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { +>g : (str: string) => void +> : ^ ^^ ^^^^^^^^^ +>str : string +> : ^^^^^^ + + switch (str) { +>str : string +> : ^^^^^^ + + case "a": +>"a" : "a" +> : ^^^ + + return; + console.log("1"); +>console.log("1") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"1" : "1" +> : ^^^ + + console.log("2"); +>console.log("2") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"2" : "2" +> : ^^^ + + case "b": +>"b" : "b" +> : ^^^ + + console.log("3"); +>console.log("3") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"3" : "3" +> : ^^^ + } +} + +function h(str: string) { +>h : (str: string) => void +> : ^ ^^ ^^^^^^^^^ +>str : string +> : ^^^^^^ + + switch (str) { +>str : string +> : ^^^^^^ + + case "a": +>"a" : "a" +> : ^^^ + + console.log("1"); +>console.log("1") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"1" : "1" +> : ^^^ + + default: + return; + console.log("2"); +>console.log("2") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"2" : "2" +> : ^^^ + + console.log("3"); +>console.log("3") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"3" : "3" +> : ^^^ + + case "b": +>"b" : "b" +> : ^^^ + + console.log("4"); +>console.log("4") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"4" : "4" +> : ^^^ + } +} + diff --git a/tests/cases/compiler/reachabilityChecks10.ts b/tests/cases/compiler/reachabilityChecks10.ts new file mode 100644 index 0000000000000..536b3ad5d8b8c --- /dev/null +++ b/tests/cases/compiler/reachabilityChecks10.ts @@ -0,0 +1,7 @@ +// @strict: true +// @allowUnreachableCode: false +// @noEmit: true + +throw new Error("") +console.log("1") +console.log("2") diff --git a/tests/cases/compiler/reachabilityChecks9.ts b/tests/cases/compiler/reachabilityChecks9.ts new file mode 100644 index 0000000000000..4c577a203a29e --- /dev/null +++ b/tests/cases/compiler/reachabilityChecks9.ts @@ -0,0 +1,29 @@ +// @strict: true +// @allowUnreachableCode: false +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { + switch (str) { + case "a": + return; + console.log("1"); + console.log("2"); + case "b": + console.log("3"); + } +} + +function h(str: string) { + switch (str) { + case "a": + console.log("1"); + default: + return; + console.log("2"); + console.log("3"); + case "b": + console.log("4"); + } +}