Move unreachable checks to checker (#62751)

This commit is contained in:
Jake Bailey 2025-11-13 17:50:42 -08:00 committed by GitHub
parent ea48dedd7d
commit 09e31cf04a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1273 additions and 156 deletions

View File

@ -46,7 +46,6 @@ import {
DeleteExpression,
DestructuringAssignment,
DiagnosticArguments,
DiagnosticCategory,
DiagnosticMessage,
DiagnosticRelatedInformation,
Diagnostics,
@ -87,7 +86,6 @@ import {
getAssignmentDeclarationKind,
getAssignmentDeclarationPropertyAccessKind,
getCombinedModifierFlags,
getCombinedNodeFlags,
getContainingClass,
getEffectiveContainerForJSDocTemplateTag,
getElementOrPropertyAccessName,
@ -106,7 +104,6 @@ import {
getNameOfDeclaration,
getNameOrArgument,
getNodeId,
getRangesWhere,
getRightMostAssignedExpression,
getSourceFileOfNode,
getSourceTextOfNodeFromSourceFile,
@ -115,10 +112,8 @@ import {
getSymbolNameForPrivateIdentifier,
getTextOfIdentifierOrLiteral,
getThisContainer,
getTokenPosOfNode,
HasContainerFlags,
hasDynamicName,
HasFlowNode,
hasJSDocNodes,
HasLocals,
hasSyntacticModifier,
@ -164,7 +159,6 @@ import {
isExternalModule,
isExternalOrCommonJsModule,
isForInOrOfStatement,
isFunctionDeclaration,
isFunctionLike,
isFunctionLikeDeclaration,
isFunctionLikeOrClassStaticBlockDeclaration,
@ -204,6 +198,7 @@ import {
isParenthesizedExpression,
isPartOfParameterDeclaration,
isPartOfTypeQuery,
isPotentiallyExecutableNode,
isPrefixUnaryExpression,
isPrivateIdentifier,
isPrologueDirective,
@ -217,8 +212,6 @@ import {
isSignedNumericLiteral,
isSourceFile,
isSpecialPropertyDeclaration,
isStatement,
isStatementButNotDeclaration,
isStatic,
isString,
isStringLiteralLike,
@ -286,10 +279,8 @@ import {
setParentRecursive,
setValueDeclaration,
ShorthandPropertyAssignment,
shouldPreserveConstEnums,
SignatureDeclaration,
skipParentheses,
sliceAfter,
some,
SourceFile,
SpreadElement,
@ -302,7 +293,6 @@ import {
symbolName,
SymbolTable,
SyntaxKind,
TextRange,
ThisExpression,
ThrowStatement,
tokenToString,
@ -315,8 +305,6 @@ import {
TypeOfExpression,
TypeParameterDeclaration,
unescapeLeadingUnderscores,
unreachableCodeIsError,
unusedLabelIsError,
VariableDeclaration,
WhileStatement,
WithStatement,
@ -565,7 +553,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
var classifiableNames: Set<__String>;
var unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
var reportedUnreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
var bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
/* eslint-enable no-var */
@ -592,7 +579,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
// Attach debugging information if necessary
Debug.attachFlowNodeDebugInfo(unreachableFlow);
Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
if (!file.locals) {
tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
@ -1104,18 +1090,24 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
// Most nodes aren't valid in an assignment pattern, so we clear the value here
// and set it before we descend into nodes that could actually be part of an assignment pattern.
inAssignmentPattern = false;
if (checkUnreachable(node)) {
if (canHaveFlowNode(node) && node.flowNode) {
if (currentFlow === unreachableFlow) {
if (canHaveFlowNode(node)) {
node.flowNode = undefined;
}
if (isPotentiallyExecutableNode(node)) {
(node as Mutable<Node>).flags |= NodeFlags.Unreachable;
}
bindEachChild(node);
bindJSDoc(node);
inAssignmentPattern = saveInAssignmentPattern;
return;
}
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && (!options.allowUnreachableCode || node.kind === SyntaxKind.ReturnStatement)) {
(node as HasFlowNode).flowNode = currentFlow;
if (SyntaxKind.FirstStatement <= node.kind && node.kind <= SyntaxKind.LastStatement && canHaveFlowNode(node)) {
node.flowNode = currentFlow;
}
switch (node.kind) {
case SyntaxKind.WhileStatement:
bindWhileStatement(node as WhileStatement);
@ -1793,8 +1785,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
};
bind(node.label);
bind(node.statement);
if (!activeLabelList.referenced && !options.allowUnusedLabels) {
errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
if (!activeLabelList.referenced) {
(node.label as Mutable<Node>).flags |= NodeFlags.Unreachable;
}
activeLabelList = activeLabelList.next;
addAntecedent(postStatementLabel, currentFlow);
@ -2743,24 +2735,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, ...args));
}
function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
errorOrSuggestionOnRange(isError, node, node, message);
}
function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
}
function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
if (isError) {
file.bindDiagnostics.push(diag);
}
else {
file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
}
}
function bind(node: Node | undefined): void {
if (!node) {
return;
@ -3793,93 +3767,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
}
// reachability checks
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
const instanceState = getModuleInstanceState(node);
return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options));
}
function checkUnreachable(node: Node): boolean {
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
return false;
}
if (currentFlow === unreachableFlow) {
const reportError =
// report error on all statements except empty ones
(isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
// report error on class declarations
node.kind === SyntaxKind.ClassDeclaration ||
// report errors on enums with preserved emit
isEnumDeclarationWithPreservedEmit(node, options) ||
// report error on instantiated modules
(node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration));
if (reportError) {
currentFlow = reportedUnreachableFlow;
if (!options.allowUnreachableCode) {
// unreachable code is reported if
// - user has explicitly asked about it AND
// - statement is in not ambient context (statements in ambient context is already an error
// so we should not report extras) AND
// - node is not variable statement OR
// - node is block scoped variable statement OR
// - node is not block scoped variable statement and at least one variable declaration has initializer
// Rationale: we don't want to report errors on non-initialized var's since they are hoisted
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
const isError = unreachableCodeIsError(options) &&
!(node.flags & NodeFlags.Ambient) &&
(
!isVariableStatement(node) ||
!!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
node.declarationList.declarations.some(d => !!d.initializer)
);
eachUnreachableRange(node, options, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
}
}
}
return true;
}
}
function isEnumDeclarationWithPreservedEmit(node: Node, options: CompilerOptions): boolean {
return node.kind === SyntaxKind.EnumDeclaration && (!isEnumConst(node as EnumDeclaration) || shouldPreserveConstEnums(options));
}
function eachUnreachableRange(node: Node, options: CompilerOptions, cb: (start: Node, last: Node) => void): void {
if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
const { statements } = node.parent;
const slice = sliceAfter(statements, node);
getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
}
else {
cb(node, node);
}
// As opposed to a pure declaration like an `interface`
function isExecutableStatement(s: Statement): boolean {
// Don't remove statements that can validly be used before they appear.
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
// `var x;` may declare a variable used above
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.BlockScoped)) && s.declarationList.declarations.some(d => !d.initializer));
}
function isPurelyTypeDeclaration(s: Statement): boolean {
switch (s.kind) {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
return true;
case SyntaxKind.ModuleDeclaration:
return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
case SyntaxKind.EnumDeclaration:
return !isEnumDeclarationWithPreservedEmit(s, options);
default:
return false;
}
}
}
/** @internal */

View File

@ -62,6 +62,7 @@ import {
canHaveLocals,
canHaveModifiers,
canHaveModuleSpecifier,
canHaveStatements,
canHaveSymbol,
canIncludeBindAndCheckDiagnostics,
canUsePropertyAccess,
@ -708,6 +709,7 @@ import {
isPartOfTypeOnlyImportOrExportDeclaration,
isPartOfTypeQuery,
isPlainJsFile,
isPotentiallyExecutableNode,
isPrefixUnaryExpression,
isPrivateIdentifier,
isPrivateIdentifierClassElementDeclaration,
@ -1512,6 +1514,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
var currentNode: Node | undefined;
var varianceTypeParameter: TypeParameter | undefined;
var isInferencePartiallyBlocked = false;
var withinUnreachableCode = false;
var reportedUnreachableNodes: Set<Node> | undefined;
var emptySymbols = createSymbolTable();
var arrayVariances = [VarianceFlags.Covariant];
@ -46603,6 +46607,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
});
}
if (node.label.flags & NodeFlags.Unreachable && compilerOptions.allowUnusedLabels !== true) {
errorOrSuggestion(compilerOptions.allowUnusedLabels === false, node.label, Diagnostics.Unused_label);
}
// ensure that label is unique
checkSourceElement(node.statement);
}
@ -48967,10 +48975,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkSourceElement(node: Node | undefined): void {
if (node) {
const saveCurrentNode = currentNode;
const saveWithinUnreachableCode = withinUnreachableCode;
currentNode = node;
instantiationCount = 0;
checkSourceElementWorker(node);
currentNode = saveCurrentNode;
withinUnreachableCode = saveWithinUnreachableCode;
}
}
@ -49003,8 +49013,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
cancellationToken.throwIfCancellationRequested();
}
}
if (kind >= SyntaxKind.FirstStatement && kind <= SyntaxKind.LastStatement && canHaveFlowNode(node) && node.flowNode && !isReachableFlowNode(node.flowNode)) {
errorOrSuggestion(compilerOptions.allowUnreachableCode === false, node, Diagnostics.Unreachable_code_detected);
if (compilerOptions.allowUnreachableCode !== true && !withinUnreachableCode) {
if (checkSourceElementUnreachable(node)) {
withinUnreachableCode = true;
}
}
// If editing this, keep `isSourceElement` in utilities up to date.
@ -49185,6 +49198,86 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
function checkSourceElementUnreachable(node: Node): boolean {
if (!isPotentiallyExecutableNode(node)) {
return false;
}
if (reportedUnreachableNodes?.has(node)) {
return true;
}
if (!isSourceElementUnreachable(node)) {
return false;
}
(reportedUnreachableNodes ??= new Set()).add(node);
const sourceFile = getSourceFileOfNode(node);
let start = node.pos;
let end = node.end;
const parent = node.parent;
if (canHaveStatements(parent)) {
const statements = parent.statements;
const offset = statements.indexOf(node as Statement);
if (offset >= 0) {
// Scan backwards to find the first unreachable unreported node;
// this may happen when producing region diagnostics where not all nodes
// will have been visited.
let first = offset;
for (let i = offset - 1; i >= 0; i--) {
const prevNode = statements[i];
if (!isPotentiallyExecutableNode(prevNode) || reportedUnreachableNodes.has(prevNode) || !isSourceElementUnreachable(prevNode)) {
break;
}
first = i;
reportedUnreachableNodes.add(prevNode);
}
let last = offset;
for (let i = offset + 1; i < statements.length; i++) {
const nextNode = statements[i];
if (!isPotentiallyExecutableNode(nextNode) || !isSourceElementUnreachable(nextNode)) {
break;
}
last = i;
reportedUnreachableNodes.add(nextNode);
}
start = statements[first].pos;
end = statements[last].end;
}
}
start = skipTrivia(sourceFile.text, start);
addErrorOrSuggestion(compilerOptions.allowUnreachableCode === false, createFileDiagnostic(sourceFile, start, end - start, Diagnostics.Unreachable_code_detected));
return true;
}
function isSourceElementUnreachable(node: Node): boolean {
// Precondition: isPotentiallyExecutableNode is true
if (node.flags & NodeFlags.Unreachable) {
// The binder has determined that this code is unreachable.
// Ignore const enums unless preserveConstEnums is set.
switch (node.kind) {
case SyntaxKind.EnumDeclaration:
return !isEnumConst(node as EnumDeclaration) || shouldPreserveConstEnums(compilerOptions);
case SyntaxKind.ModuleDeclaration:
return isInstantiatedModule(node as ModuleDeclaration, shouldPreserveConstEnums(compilerOptions));
default:
return true;
}
}
else if (canHaveFlowNode(node) && node.flowNode) {
// For code the binder doesn't know is unreachable, use control flow / types.
return !isReachableFlowNode(node.flowNode);
}
return false;
}
function checkJSDocCommentWorker(node: string | readonly JSDocComment[] | undefined) {
if (isArray(node)) {
forEach(node, tag => {
@ -49383,6 +49476,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
performance.mark(afterMark);
performance.measure("Check", beforeMark, afterMark);
tracing?.pop();
reportedUnreachableNodes = undefined;
}
function unusedIsError(kind: UnusedKind, isAmbient: boolean): boolean {

View File

@ -823,6 +823,7 @@ export const enum NodeFlags {
JsonFile = 1 << 27, // If node was parsed in a Json
/** @internal */ TypeCached = 1 << 28, // If a type was cached for node at any point
/** @internal */ Deprecated = 1 << 29, // If has '@deprecated' JSDoc tag
/** @internal */ Unreachable = 1 << 30, // If node is unreachable according to the binder
BlockScoped = Let | Const | Using,
Constant = Const | Using,

View File

@ -261,7 +261,9 @@ import {
isBinaryExpression,
isBindingElement,
isBindingPattern,
isBlock,
isCallExpression,
isCaseClause,
isClassDeclaration,
isClassElement,
isClassExpression,
@ -274,6 +276,7 @@ import {
isDeclaration,
isDeclarationFileName,
isDecorator,
isDefaultClause,
isElementAccessExpression,
isEnumDeclaration,
isEnumMember,
@ -332,6 +335,7 @@ import {
isMethodDeclaration,
isMethodOrAccessor,
isModifierLike,
isModuleBlock,
isModuleDeclaration,
isModuleOrEnumDeclaration,
isNamedDeclaration,
@ -9273,16 +9277,6 @@ export function hasJsonModuleEmitEnabled(options: CompilerOptions): boolean {
return true;
}
/** @internal */
export function unreachableCodeIsError(options: CompilerOptions): boolean {
return options.allowUnreachableCode === false;
}
/** @internal */
export function unusedLabelIsError(options: CompilerOptions): boolean {
return options.allowUnusedLabels === false;
}
/** @internal */
export function moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution: ModuleResolutionKind): boolean {
return moduleResolution >= ModuleResolutionKind.Node16 && moduleResolution <= ModuleResolutionKind.NodeNext
@ -12372,3 +12366,22 @@ function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node
function getFirstChild(node: Node): Node | undefined {
return forEachChild(node, child => child);
}
/** @internal */
export function canHaveStatements(node: Node): node is Block | ModuleBlock | SourceFile | CaseClause | DefaultClause {
return isBlock(node) || isModuleBlock(node) || isSourceFile(node) || isCaseClause(node) || isDefaultClause(node);
}
/** @internal */
export function isPotentiallyExecutableNode(node: Node): boolean {
if (SyntaxKind.FirstStatement <= node.kind && node.kind <= SyntaxKind.LastStatement) {
if (isVariableStatement(node)) {
if (getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) {
return true;
}
return some(node.declarationList.declarations, d => d.initializer !== undefined);
}
return true;
}
return isClassDeclaration(node) || isEnumDeclaration(node) || isModuleDeclaration(node);
}

View File

@ -13,7 +13,6 @@ neverReturningFunctions1.ts(101,13): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(103,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(105,5): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(111,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(112,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(122,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(127,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(129,5): error TS7027: Unreachable code detected.
@ -23,7 +22,7 @@ neverReturningFunctions1.ts(148,9): error TS7027: Unreachable code detected.
neverReturningFunctions1.ts(153,5): error TS7027: Unreachable code detected.
==== neverReturningFunctions1.ts (23 errors) ====
==== neverReturningFunctions1.ts (22 errors) ====
function fail(message?: string): never {
throw new Error(message);
}
@ -163,10 +162,9 @@ neverReturningFunctions1.ts(153,5): error TS7027: Unreachable code detected.
if (typeof x.a === "string") {
fail();
x; // Unreachable
~~
!!! error TS7027: Unreachable code detected.
~~~~~~~~~~~~~~~~~~~~
x.a; // Unreachable
~~~~
~~~~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
x; // { a: string | number }

View File

@ -1,58 +1,77 @@
reachabilityChecks1.ts(2,1): error TS7027: Unreachable code detected.
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 (5 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 +93,16 @@ reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected.
}
namespace B {
~~~~~~~~~~~~~
for (; ;);
~~~~~~~~~~~~~~
namespace C {
~~~~~~~~~~~~~~~~~
}
~~~~~
}
~
!!! error TS7027: Unreachable code detected.
function f3() {
do {

View File

@ -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.

View File

@ -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, --, --))

View File

@ -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"
> : ^^^

View File

@ -0,0 +1,104 @@
reachabilityChecks11.ts(6,5): error TS7027: Unreachable code detected.
reachabilityChecks11.ts(18,5): error TS7027: Unreachable code detected.
reachabilityChecks11.ts(30,5): error TS7027: Unreachable code detected.
reachabilityChecks11.ts(47,5): error TS7027: Unreachable code detected.
reachabilityChecks11.ts(60,5): error TS7027: Unreachable code detected.
reachabilityChecks11.ts(69,5): error TS7027: Unreachable code detected.
==== reachabilityChecks11.ts (6 errors) ====
// while (true);
var x = 1;
module A {
while (true);
let x;
~~~~~~
!!! error TS7027: Unreachable code detected.
}
module A1 {
do {} while(true);
module A {
interface F {}
}
}
module A2 {
while (true);
module A {
~~~~~~~~~~
var x = 1;
~~~~~~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}
module A3 {
while (true);
type T = string;
}
module A4 {
while (true);
module A {
~~~~~~~~~~
const enum E { X }
~~~~~~~~~~~~~~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}
function f1(x) {
if (x) {
return;
}
else {
throw new Error("123");
}
var x;
}
function f2() {
return;
class A {
~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}
module B {
for (; ;);
module C {
}
}
function f3() {
do {
} while (true);
enum E {
~~~~~~~~
X = 1
~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}
function f4() {
if (true) {
throw new Error();
}
const enum E {
~~~~~~~~~~~~~~
X = 1
~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}

View File

@ -0,0 +1,156 @@
//// [tests/cases/compiler/reachabilityChecks11.ts] ////
//// [reachabilityChecks11.ts]
// while (true);
var x = 1;
module A {
while (true);
let x;
}
module A1 {
do {} while(true);
module A {
interface F {}
}
}
module A2 {
while (true);
module A {
var x = 1;
}
}
module A3 {
while (true);
type T = string;
}
module A4 {
while (true);
module A {
const enum E { X }
}
}
function f1(x) {
if (x) {
return;
}
else {
throw new Error("123");
}
var x;
}
function f2() {
return;
class A {
}
}
module B {
for (; ;);
module C {
}
}
function f3() {
do {
} while (true);
enum E {
X = 1
}
}
function f4() {
if (true) {
throw new Error();
}
const enum E {
X = 1
}
}
//// [reachabilityChecks11.js]
// while (true);
var x = 1;
var A;
(function (A) {
while (true)
;
var x;
})(A || (A = {}));
var A1;
(function (A1) {
do { } while (true);
})(A1 || (A1 = {}));
var A2;
(function (A2) {
while (true)
;
var A;
(function (A) {
var x = 1;
})(A || (A = {}));
})(A2 || (A2 = {}));
var A3;
(function (A3) {
while (true)
;
})(A3 || (A3 = {}));
var A4;
(function (A4) {
while (true)
;
var A;
(function (A) {
var E;
(function (E) {
E[E["X"] = 0] = "X";
})(E || (E = {}));
})(A || (A = {}));
})(A4 || (A4 = {}));
function f1(x) {
if (x) {
return;
}
else {
throw new Error("123");
}
var x;
}
function f2() {
return;
var A = /** @class */ (function () {
function A() {
}
return A;
}());
}
var B;
(function (B) {
for (;;)
;
})(B || (B = {}));
function f3() {
do {
} while (true);
var E;
(function (E) {
E[E["X"] = 1] = "X";
})(E || (E = {}));
}
function f4() {
if (true) {
throw new Error();
}
var E;
(function (E) {
E[E["X"] = 1] = "X";
})(E || (E = {}));
}

View File

@ -0,0 +1,124 @@
//// [tests/cases/compiler/reachabilityChecks11.ts] ////
=== reachabilityChecks11.ts ===
// while (true);
var x = 1;
>x : Symbol(x, Decl(reachabilityChecks11.ts, 1, 3))
module A {
>A : Symbol(A, Decl(reachabilityChecks11.ts, 1, 10))
while (true);
let x;
>x : Symbol(x, Decl(reachabilityChecks11.ts, 5, 7))
}
module A1 {
>A1 : Symbol(A1, Decl(reachabilityChecks11.ts, 6, 1))
do {} while(true);
module A {
>A : Symbol(A, Decl(reachabilityChecks11.ts, 9, 22))
interface F {}
>F : Symbol(F, Decl(reachabilityChecks11.ts, 10, 14))
}
}
module A2 {
>A2 : Symbol(A2, Decl(reachabilityChecks11.ts, 13, 1))
while (true);
module A {
>A : Symbol(A, Decl(reachabilityChecks11.ts, 16, 17))
var x = 1;
>x : Symbol(x, Decl(reachabilityChecks11.ts, 18, 11))
}
}
module A3 {
>A3 : Symbol(A3, Decl(reachabilityChecks11.ts, 20, 1))
while (true);
type T = string;
>T : Symbol(T, Decl(reachabilityChecks11.ts, 23, 17))
}
module A4 {
>A4 : Symbol(A4, Decl(reachabilityChecks11.ts, 25, 1))
while (true);
module A {
>A : Symbol(A, Decl(reachabilityChecks11.ts, 28, 17))
const enum E { X }
>E : Symbol(E, Decl(reachabilityChecks11.ts, 29, 14))
>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 30, 22))
}
}
function f1(x) {
>f1 : Symbol(f1, Decl(reachabilityChecks11.ts, 32, 1))
>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7))
if (x) {
>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7))
return;
}
else {
throw new Error("123");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
var x;
>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7))
}
function f2() {
>f2 : Symbol(f2, Decl(reachabilityChecks11.ts, 42, 1))
return;
class A {
>A : Symbol(A, Decl(reachabilityChecks11.ts, 45, 11))
}
}
module B {
>B : Symbol(B, Decl(reachabilityChecks11.ts, 48, 1))
for (; ;);
module C {
>C : Symbol(C, Decl(reachabilityChecks11.ts, 51, 14))
}
}
function f3() {
>f3 : Symbol(f3, Decl(reachabilityChecks11.ts, 54, 1))
do {
} while (true);
enum E {
>E : Symbol(E, Decl(reachabilityChecks11.ts, 58, 19))
X = 1
>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 59, 12))
}
}
function f4() {
>f4 : Symbol(f4, Decl(reachabilityChecks11.ts, 62, 1))
if (true) {
throw new Error();
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
const enum E {
>E : Symbol(E, Decl(reachabilityChecks11.ts, 67, 5))
X = 1
>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 68, 18))
}
}

View File

@ -0,0 +1,180 @@
//// [tests/cases/compiler/reachabilityChecks11.ts] ////
=== reachabilityChecks11.ts ===
// while (true);
var x = 1;
>x : number
> : ^^^^^^
>1 : 1
> : ^
module A {
>A : typeof A
> : ^^^^^^^^
while (true);
>true : true
> : ^^^^
let x;
>x : any
> : ^^^
}
module A1 {
>A1 : typeof A1
> : ^^^^^^^^^
do {} while(true);
>true : true
> : ^^^^
module A {
interface F {}
}
}
module A2 {
>A2 : typeof A2
> : ^^^^^^^^^
while (true);
>true : true
> : ^^^^
module A {
>A : typeof A
> : ^^^^^^^^
var x = 1;
>x : number
> : ^^^^^^
>1 : 1
> : ^
}
}
module A3 {
>A3 : typeof A3
> : ^^^^^^^^^
while (true);
>true : true
> : ^^^^
type T = string;
>T : string
> : ^^^^^^
}
module A4 {
>A4 : typeof A4
> : ^^^^^^^^^
while (true);
>true : true
> : ^^^^
module A {
const enum E { X }
>E : E
> : ^
>X : E.X
> : ^^^
}
}
function f1(x) {
>f1 : (x: any) => void
> : ^ ^^^^^^^^^^^^^^
>x : any
> : ^^^
if (x) {
>x : any
> : ^^^
return;
}
else {
throw new Error("123");
>new Error("123") : Error
> : ^^^^^
>Error : ErrorConstructor
> : ^^^^^^^^^^^^^^^^
>"123" : "123"
> : ^^^^^
}
var x;
>x : any
> : ^^^
}
function f2() {
>f2 : () => void
> : ^^^^^^^^^^
return;
class A {
>A : A
> : ^
}
}
module B {
>B : typeof B
> : ^^^^^^^^
for (; ;);
module C {
}
}
function f3() {
>f3 : () => void
> : ^^^^^^^^^^
do {
} while (true);
>true : true
> : ^^^^
enum E {
>E : E
> : ^
X = 1
>X : E.X
> : ^^^
>1 : 1
> : ^
}
}
function f4() {
>f4 : () => void
> : ^^^^^^^^^^
if (true) {
>true : true
> : ^^^^
throw new Error();
>new Error() : Error
> : ^^^^^
>Error : ErrorConstructor
> : ^^^^^^^^^^^^^^^^
}
const enum E {
>E : E
> : ^
X = 1
>X : E.X
> : ^^^
>1 : 1
> : ^
}
}

View File

@ -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");
}
}

View File

@ -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, --, --))
}
}

View File

@ -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"
> : ^^^
}
}

View File

@ -0,0 +1,28 @@
//// [tests/cases/compiler/reachabilityChecksIgnored.ts] ////
//// [reachabilityChecksIgnored.ts]
function a() {
throw new Error("");
// @ts-ignore
console.log("unreachable");
}
function b() {
throw new Error("");
// @ts-expect-error
console.log("unreachable");
}
//// [reachabilityChecksIgnored.js]
function a() {
throw new Error("");
// @ts-ignore
console.log("unreachable");
}
function b() {
throw new Error("");
// @ts-expect-error
console.log("unreachable");
}

View File

@ -0,0 +1,28 @@
//// [tests/cases/compiler/reachabilityChecksIgnored.ts] ////
=== reachabilityChecksIgnored.ts ===
function a() {
>a : Symbol(a, Decl(reachabilityChecksIgnored.ts, 0, 0))
throw new Error("");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
// @ts-ignore
console.log("unreachable");
>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 b() {
>b : Symbol(b, Decl(reachabilityChecksIgnored.ts, 5, 1))
throw new Error("");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
// @ts-expect-error
console.log("unreachable");
>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, --, --))
}

View File

@ -0,0 +1,54 @@
//// [tests/cases/compiler/reachabilityChecksIgnored.ts] ////
=== reachabilityChecksIgnored.ts ===
function a() {
>a : () => void
> : ^^^^^^^^^^
throw new Error("");
>new Error("") : Error
> : ^^^^^
>Error : ErrorConstructor
> : ^^^^^^^^^^^^^^^^
>"" : ""
> : ^^
// @ts-ignore
console.log("unreachable");
>console.log("unreachable") : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"unreachable" : "unreachable"
> : ^^^^^^^^^^^^^
}
function b() {
>b : () => void
> : ^^^^^^^^^^
throw new Error("");
>new Error("") : Error
> : ^^^^^
>Error : ErrorConstructor
> : ^^^^^^^^^^^^^^^^
>"" : ""
> : ^^
// @ts-expect-error
console.log("unreachable");
>console.log("unreachable") : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"unreachable" : "unreachable"
> : ^^^^^^^^^^^^^
}

View File

@ -0,0 +1,7 @@
// @strict: true
// @noEmit: true
// @allowUnreachableCode: false
throw new Error("")
console.log("1")
console.log("2")

View File

@ -0,0 +1,76 @@
// @allowUnreachableCode: false
// @preserveConstEnums: true
// while (true);
var x = 1;
module A {
while (true);
let x;
}
module A1 {
do {} while(true);
module A {
interface F {}
}
}
module A2 {
while (true);
module A {
var x = 1;
}
}
module A3 {
while (true);
type T = string;
}
module A4 {
while (true);
module A {
const enum E { X }
}
}
function f1(x) {
if (x) {
return;
}
else {
throw new Error("123");
}
var x;
}
function f2() {
return;
class A {
}
}
module B {
for (; ;);
module C {
}
}
function f3() {
do {
} while (true);
enum E {
X = 1
}
}
function f4() {
if (true) {
throw new Error();
}
const enum E {
X = 1
}
}

View File

@ -0,0 +1,29 @@
// @strict: true
// @noEmit: true
// @allowUnreachableCode: false
// 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");
}
}

View File

@ -0,0 +1,17 @@
// @allowUnreachableCode: false
// @preserveConstEnums: true
function a() {
throw new Error("");
// @ts-ignore
console.log("unreachable");
}
function b() {
throw new Error("");
// @ts-expect-error
console.log("unreachable");
}

View File

@ -2,6 +2,6 @@
// @allowUnreachableCode: true
////if (false) 0;
////if (false) [|0;|]
verify.getSuggestionDiagnostics([]);

View File

@ -2,6 +2,6 @@
// @allowUnusedLabels: true
////foo: while (true) {}
////[|foo|]: while (true) {}
verify.getSuggestionDiagnostics([]);