Skip to content

Commit 54d464b

Browse files
🤖 Pick PR #62751 (Move unreachable checks to checker) into tsgo-port (#62760)
Co-authored-by: Jake Bailey <[email protected]>
1 parent 9e8eaa1 commit 54d464b

25 files changed

+1270
-156
lines changed

‎src/compiler/binder.ts‎

Lines changed: 13 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import {
4646
DeleteExpression,
4747
DestructuringAssignment,
4848
DiagnosticArguments,
49-
DiagnosticCategory,
5049
DiagnosticMessage,
5150
DiagnosticRelatedInformation,
5251
Diagnostics,
@@ -87,7 +86,6 @@ import {
8786
getAssignmentDeclarationKind,
8887
getAssignmentDeclarationPropertyAccessKind,
8988
getCombinedModifierFlags,
90-
getCombinedNodeFlags,
9189
getContainingClass,
9290
getEffectiveContainerForJSDocTemplateTag,
9391
getElementOrPropertyAccessName,
@@ -105,7 +103,6 @@ import {
105103
getNameOfDeclaration,
106104
getNameOrArgument,
107105
getNodeId,
108-
getRangesWhere,
109106
getRightMostAssignedExpression,
110107
getSourceFileOfNode,
111108
getSourceTextOfNodeFromSourceFile,
@@ -114,10 +111,8 @@ import {
114111
getSymbolNameForPrivateIdentifier,
115112
getTextOfIdentifierOrLiteral,
116113
getThisContainer,
117-
getTokenPosOfNode,
118114
HasContainerFlags,
119115
hasDynamicName,
120-
HasFlowNode,
121116
hasJSDocNodes,
122117
HasLocals,
123118
hasSyntacticModifier,
@@ -163,7 +158,6 @@ import {
163158
isExternalModule,
164159
isExternalOrCommonJsModule,
165160
isForInOrOfStatement,
166-
isFunctionDeclaration,
167161
isFunctionLike,
168162
isFunctionLikeDeclaration,
169163
isFunctionLikeOrClassStaticBlockDeclaration,
@@ -203,6 +197,7 @@ import {
203197
isParenthesizedExpression,
204198
isPartOfParameterDeclaration,
205199
isPartOfTypeQuery,
200+
isPotentiallyExecutableNode,
206201
isPrefixUnaryExpression,
207202
isPrivateIdentifier,
208203
isPrologueDirective,
@@ -216,8 +211,6 @@ import {
216211
isSignedNumericLiteral,
217212
isSourceFile,
218213
isSpecialPropertyDeclaration,
219-
isStatement,
220-
isStatementButNotDeclaration,
221214
isStatic,
222215
isString,
223216
isStringLiteralLike,
@@ -284,10 +277,8 @@ import {
284277
setParentRecursive,
285278
setValueDeclaration,
286279
ShorthandPropertyAssignment,
287-
shouldPreserveConstEnums,
288280
SignatureDeclaration,
289281
skipParentheses,
290-
sliceAfter,
291282
some,
292283
SourceFile,
293284
SpreadElement,
@@ -300,7 +291,6 @@ import {
300291
symbolName,
301292
SymbolTable,
302293
SyntaxKind,
303-
TextRange,
304294
ThisExpression,
305295
ThrowStatement,
306296
tokenToString,
@@ -313,8 +303,6 @@ import {
313303
TypeOfExpression,
314304
TypeParameterDeclaration,
315305
unescapeLeadingUnderscores,
316-
unreachableCodeIsError,
317-
unusedLabelIsError,
318306
VariableDeclaration,
319307
WhileStatement,
320308
WithStatement,
@@ -562,7 +550,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
562550
var classifiableNames: Set<__String>;
563551

564552
var unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
565-
var reportedUnreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
566553
var bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
567554
/* eslint-enable no-var */
568555

@@ -588,7 +575,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
588575

589576
// Attach debugging information if necessary
590577
Debug.attachFlowNodeDebugInfo(unreachableFlow);
591-
Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
592578

593579
if (!file.locals) {
594580
tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
@@ -1099,18 +1085,24 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
10991085
// Most nodes aren't valid in an assignment pattern, so we clear the value here
11001086
// and set it before we descend into nodes that could actually be part of an assignment pattern.
11011087
inAssignmentPattern = false;
1102-
if (checkUnreachable(node)) {
1103-
if (canHaveFlowNode(node) && node.flowNode) {
1088+
1089+
if (currentFlow === unreachableFlow) {
1090+
if (canHaveFlowNode(node)) {
11041091
node.flowNode = undefined;
11051092
}
1093+
if (isPotentiallyExecutableNode(node)) {
1094+
(node as Mutable<Node>).flags |= NodeFlags.Unreachable;
1095+
}
11061096
bindEachChild(node);
11071097
bindJSDoc(node);
11081098
inAssignmentPattern = saveInAssignmentPattern;
11091099
return;
11101100
}
1111-
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && (!options.allowUnreachableCode || node.kind === SyntaxKind.ReturnStatement)) {
1112-
(node as HasFlowNode).flowNode = currentFlow;
1101+
1102+
if (SyntaxKind.FirstStatement <= node.kind && node.kind <= SyntaxKind.LastStatement && canHaveFlowNode(node)) {
1103+
node.flowNode = currentFlow;
11131104
}
1105+
11141106
switch (node.kind) {
11151107
case SyntaxKind.WhileStatement:
11161108
bindWhileStatement(node as WhileStatement);
@@ -1788,8 +1780,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
17881780
};
17891781
bind(node.label);
17901782
bind(node.statement);
1791-
if (!activeLabelList.referenced && !options.allowUnusedLabels) {
1792-
errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
1783+
if (!activeLabelList.referenced) {
1784+
(node.label as Mutable<Node>).flags |= NodeFlags.Unreachable;
17931785
}
17941786
activeLabelList = activeLabelList.next;
17951787
addAntecedent(postStatementLabel, currentFlow);
@@ -2708,24 +2700,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
27082700
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, ...args));
27092701
}
27102702

2711-
function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
2712-
errorOrSuggestionOnRange(isError, node, node, message);
2713-
}
2714-
2715-
function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
2716-
addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
2717-
}
2718-
2719-
function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
2720-
const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
2721-
if (isError) {
2722-
file.bindDiagnostics.push(diag);
2723-
}
2724-
else {
2725-
file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
2726-
}
2727-
}
2728-
27292703
function bind(node: Node | undefined): void {
27302704
if (!node) {
27312705
return;
@@ -3757,93 +3731,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
37573731
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
37583732
}
37593733
}
3760-
3761-
// reachability checks
3762-
3763-
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
3764-
const instanceState = getModuleInstanceState(node);
3765-
return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options));
3766-
}
3767-
3768-
function checkUnreachable(node: Node): boolean {
3769-
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
3770-
return false;
3771-
}
3772-
if (currentFlow === unreachableFlow) {
3773-
const reportError =
3774-
// report error on all statements except empty ones
3775-
(isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
3776-
// report error on class declarations
3777-
node.kind === SyntaxKind.ClassDeclaration ||
3778-
// report errors on enums with preserved emit
3779-
isEnumDeclarationWithPreservedEmit(node, options) ||
3780-
// report error on instantiated modules
3781-
(node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration));
3782-
3783-
if (reportError) {
3784-
currentFlow = reportedUnreachableFlow;
3785-
3786-
if (!options.allowUnreachableCode) {
3787-
// unreachable code is reported if
3788-
// - user has explicitly asked about it AND
3789-
// - statement is in not ambient context (statements in ambient context is already an error
3790-
// so we should not report extras) AND
3791-
// - node is not variable statement OR
3792-
// - node is block scoped variable statement OR
3793-
// - node is not block scoped variable statement and at least one variable declaration has initializer
3794-
// Rationale: we don't want to report errors on non-initialized var's since they are hoisted
3795-
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
3796-
const isError = unreachableCodeIsError(options) &&
3797-
!(node.flags & NodeFlags.Ambient) &&
3798-
(
3799-
!isVariableStatement(node) ||
3800-
!!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
3801-
node.declarationList.declarations.some(d => !!d.initializer)
3802-
);
3803-
3804-
eachUnreachableRange(node, options, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
3805-
}
3806-
}
3807-
}
3808-
return true;
3809-
}
3810-
}
3811-
3812-
function isEnumDeclarationWithPreservedEmit(node: Node, options: CompilerOptions): boolean {
3813-
return node.kind === SyntaxKind.EnumDeclaration && (!isEnumConst(node as EnumDeclaration) || shouldPreserveConstEnums(options));
3814-
}
3815-
3816-
function eachUnreachableRange(node: Node, options: CompilerOptions, cb: (start: Node, last: Node) => void): void {
3817-
if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
3818-
const { statements } = node.parent;
3819-
const slice = sliceAfter(statements, node);
3820-
getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
3821-
}
3822-
else {
3823-
cb(node, node);
3824-
}
3825-
3826-
// As opposed to a pure declaration like an `interface`
3827-
function isExecutableStatement(s: Statement): boolean {
3828-
// Don't remove statements that can validly be used before they appear.
3829-
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
3830-
// `var x;` may declare a variable used above
3831-
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.BlockScoped)) && s.declarationList.declarations.some(d => !d.initializer));
3832-
}
3833-
3834-
function isPurelyTypeDeclaration(s: Statement): boolean {
3835-
switch (s.kind) {
3836-
case SyntaxKind.InterfaceDeclaration:
3837-
case SyntaxKind.TypeAliasDeclaration:
3838-
return true;
3839-
case SyntaxKind.ModuleDeclaration:
3840-
return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
3841-
case SyntaxKind.EnumDeclaration:
3842-
return !isEnumDeclarationWithPreservedEmit(s, options);
3843-
default:
3844-
return false;
3845-
}
3846-
}
38473734
}
38483735

38493736
/** @internal */

0 commit comments

Comments
 (0)