Skip to content

Commit 09e31cf

Browse files
authored
Move unreachable checks to checker (#62751)
1 parent ea48ded commit 09e31cf

25 files changed

+1273
-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,
@@ -106,7 +104,6 @@ import {
106104
getNameOfDeclaration,
107105
getNameOrArgument,
108106
getNodeId,
109-
getRangesWhere,
110107
getRightMostAssignedExpression,
111108
getSourceFileOfNode,
112109
getSourceTextOfNodeFromSourceFile,
@@ -115,10 +112,8 @@ import {
115112
getSymbolNameForPrivateIdentifier,
116113
getTextOfIdentifierOrLiteral,
117114
getThisContainer,
118-
getTokenPosOfNode,
119115
HasContainerFlags,
120116
hasDynamicName,
121-
HasFlowNode,
122117
hasJSDocNodes,
123118
HasLocals,
124119
hasSyntacticModifier,
@@ -164,7 +159,6 @@ import {
164159
isExternalModule,
165160
isExternalOrCommonJsModule,
166161
isForInOrOfStatement,
167-
isFunctionDeclaration,
168162
isFunctionLike,
169163
isFunctionLikeDeclaration,
170164
isFunctionLikeOrClassStaticBlockDeclaration,
@@ -204,6 +198,7 @@ import {
204198
isParenthesizedExpression,
205199
isPartOfParameterDeclaration,
206200
isPartOfTypeQuery,
201+
isPotentiallyExecutableNode,
207202
isPrefixUnaryExpression,
208203
isPrivateIdentifier,
209204
isPrologueDirective,
@@ -217,8 +212,6 @@ import {
217212
isSignedNumericLiteral,
218213
isSourceFile,
219214
isSpecialPropertyDeclaration,
220-
isStatement,
221-
isStatementButNotDeclaration,
222215
isStatic,
223216
isString,
224217
isStringLiteralLike,
@@ -286,10 +279,8 @@ import {
286279
setParentRecursive,
287280
setValueDeclaration,
288281
ShorthandPropertyAssignment,
289-
shouldPreserveConstEnums,
290282
SignatureDeclaration,
291283
skipParentheses,
292-
sliceAfter,
293284
some,
294285
SourceFile,
295286
SpreadElement,
@@ -302,7 +293,6 @@ import {
302293
symbolName,
303294
SymbolTable,
304295
SyntaxKind,
305-
TextRange,
306296
ThisExpression,
307297
ThrowStatement,
308298
tokenToString,
@@ -315,8 +305,6 @@ import {
315305
TypeOfExpression,
316306
TypeParameterDeclaration,
317307
unescapeLeadingUnderscores,
318-
unreachableCodeIsError,
319-
unusedLabelIsError,
320308
VariableDeclaration,
321309
WhileStatement,
322310
WithStatement,
@@ -565,7 +553,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
565553
var classifiableNames: Set<__String>;
566554

567555
var unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
568-
var reportedUnreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
569556
var bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
570557
/* eslint-enable no-var */
571558

@@ -592,7 +579,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
592579

593580
// Attach debugging information if necessary
594581
Debug.attachFlowNodeDebugInfo(unreachableFlow);
595-
Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
596582

597583
if (!file.locals) {
598584
tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
@@ -1104,18 +1090,24 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
11041090
// Most nodes aren't valid in an assignment pattern, so we clear the value here
11051091
// and set it before we descend into nodes that could actually be part of an assignment pattern.
11061092
inAssignmentPattern = false;
1107-
if (checkUnreachable(node)) {
1108-
if (canHaveFlowNode(node) && node.flowNode) {
1093+
1094+
if (currentFlow === unreachableFlow) {
1095+
if (canHaveFlowNode(node)) {
11091096
node.flowNode = undefined;
11101097
}
1098+
if (isPotentiallyExecutableNode(node)) {
1099+
(node as Mutable<Node>).flags |= NodeFlags.Unreachable;
1100+
}
11111101
bindEachChild(node);
11121102
bindJSDoc(node);
11131103
inAssignmentPattern = saveInAssignmentPattern;
11141104
return;
11151105
}
1116-
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && (!options.allowUnreachableCode || node.kind === SyntaxKind.ReturnStatement)) {
1117-
(node as HasFlowNode).flowNode = currentFlow;
1106+
1107+
if (SyntaxKind.FirstStatement <= node.kind && node.kind <= SyntaxKind.LastStatement && canHaveFlowNode(node)) {
1108+
node.flowNode = currentFlow;
11181109
}
1110+
11191111
switch (node.kind) {
11201112
case SyntaxKind.WhileStatement:
11211113
bindWhileStatement(node as WhileStatement);
@@ -1793,8 +1785,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
17931785
};
17941786
bind(node.label);
17951787
bind(node.statement);
1796-
if (!activeLabelList.referenced && !options.allowUnusedLabels) {
1797-
errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
1788+
if (!activeLabelList.referenced) {
1789+
(node.label as Mutable<Node>).flags |= NodeFlags.Unreachable;
17981790
}
17991791
activeLabelList = activeLabelList.next;
18001792
addAntecedent(postStatementLabel, currentFlow);
@@ -2743,24 +2735,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
27432735
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, ...args));
27442736
}
27452737

2746-
function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
2747-
errorOrSuggestionOnRange(isError, node, node, message);
2748-
}
2749-
2750-
function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
2751-
addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
2752-
}
2753-
2754-
function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
2755-
const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
2756-
if (isError) {
2757-
file.bindDiagnostics.push(diag);
2758-
}
2759-
else {
2760-
file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
2761-
}
2762-
}
2763-
27642738
function bind(node: Node | undefined): void {
27652739
if (!node) {
27662740
return;
@@ -3793,93 +3767,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
37933767
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
37943768
}
37953769
}
3796-
3797-
// reachability checks
3798-
3799-
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
3800-
const instanceState = getModuleInstanceState(node);
3801-
return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options));
3802-
}
3803-
3804-
function checkUnreachable(node: Node): boolean {
3805-
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
3806-
return false;
3807-
}
3808-
if (currentFlow === unreachableFlow) {
3809-
const reportError =
3810-
// report error on all statements except empty ones
3811-
(isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
3812-
// report error on class declarations
3813-
node.kind === SyntaxKind.ClassDeclaration ||
3814-
// report errors on enums with preserved emit
3815-
isEnumDeclarationWithPreservedEmit(node, options) ||
3816-
// report error on instantiated modules
3817-
(node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration));
3818-
3819-
if (reportError) {
3820-
currentFlow = reportedUnreachableFlow;
3821-
3822-
if (!options.allowUnreachableCode) {
3823-
// unreachable code is reported if
3824-
// - user has explicitly asked about it AND
3825-
// - statement is in not ambient context (statements in ambient context is already an error
3826-
// so we should not report extras) AND
3827-
// - node is not variable statement OR
3828-
// - node is block scoped variable statement OR
3829-
// - node is not block scoped variable statement and at least one variable declaration has initializer
3830-
// Rationale: we don't want to report errors on non-initialized var's since they are hoisted
3831-
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
3832-
const isError = unreachableCodeIsError(options) &&
3833-
!(node.flags & NodeFlags.Ambient) &&
3834-
(
3835-
!isVariableStatement(node) ||
3836-
!!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
3837-
node.declarationList.declarations.some(d => !!d.initializer)
3838-
);
3839-
3840-
eachUnreachableRange(node, options, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
3841-
}
3842-
}
3843-
}
3844-
return true;
3845-
}
3846-
}
3847-
3848-
function isEnumDeclarationWithPreservedEmit(node: Node, options: CompilerOptions): boolean {
3849-
return node.kind === SyntaxKind.EnumDeclaration && (!isEnumConst(node as EnumDeclaration) || shouldPreserveConstEnums(options));
3850-
}
3851-
3852-
function eachUnreachableRange(node: Node, options: CompilerOptions, cb: (start: Node, last: Node) => void): void {
3853-
if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
3854-
const { statements } = node.parent;
3855-
const slice = sliceAfter(statements, node);
3856-
getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
3857-
}
3858-
else {
3859-
cb(node, node);
3860-
}
3861-
3862-
// As opposed to a pure declaration like an `interface`
3863-
function isExecutableStatement(s: Statement): boolean {
3864-
// Don't remove statements that can validly be used before they appear.
3865-
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
3866-
// `var x;` may declare a variable used above
3867-
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.BlockScoped)) && s.declarationList.declarations.some(d => !d.initializer));
3868-
}
3869-
3870-
function isPurelyTypeDeclaration(s: Statement): boolean {
3871-
switch (s.kind) {
3872-
case SyntaxKind.InterfaceDeclaration:
3873-
case SyntaxKind.TypeAliasDeclaration:
3874-
return true;
3875-
case SyntaxKind.ModuleDeclaration:
3876-
return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
3877-
case SyntaxKind.EnumDeclaration:
3878-
return !isEnumDeclarationWithPreservedEmit(s, options);
3879-
default:
3880-
return false;
3881-
}
3882-
}
38833770
}
38843771

38853772
/** @internal */

0 commit comments

Comments
 (0)