Skip to content

Commit 1cccdf2

Browse files
committed
Exclude var variables and exported/global variables
1 parent 9a1faff commit 1cccdf2

File tree

2 files changed

+72
-40
lines changed

2 files changed

+72
-40
lines changed

src/compiler/checker.ts

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ import {
475475
isCallLikeOrFunctionLikeExpression,
476476
isCallOrNewExpression,
477477
isCallSignatureDeclaration,
478+
isCatchClause,
478479
isCatchClauseVariableDeclarationOrBindingElement,
479480
isCheckJsEnabledForFile,
480481
isClassDeclaration,
@@ -27449,7 +27450,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2744927450
case SyntaxKind.Identifier:
2745027451
if (!isThisInTypeQuery(node)) {
2745127452
const symbol = getResolvedSymbol(node as Identifier);
27452-
return isConstantVariable(symbol) || isParameterOrMutableVariable(symbol) && !isSymbolAssigned(symbol);
27453+
return isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol);
2745327454
}
2745427455
break;
2745527456
case SyntaxKind.PropertyAccessExpression:
@@ -28769,25 +28770,50 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2876928770
}
2877028771

2877128772
// For all assignments within the given root node, record the last assignment source position for all
28772-
// referenced parameters, let variables, and catch variables. When assignments occur in nested functions,
28773-
// record Number.MAX_VALUE as the assignment position. When assignments occur in compound statements,
28774-
// record the ending source position of the compound statement as the assignment position (this is more
28775-
// conservative than full control flow analysis, but requires only a single walk over the AST).
28773+
// referenced parameters and mutable local variables. When assignments occur in nested functions or
28774+
// references occur in export specifiers, record Number.MAX_VALUE as the assignment position. When
28775+
// assignments occur in compound statements, record the ending source position of the compound statement
28776+
// as the assignment position (this is more conservative than full control flow analysis, but requires
28777+
// only a single walk over the AST).
2877628778
function markNodeAssignments(node: Node) {
28777-
let statementEnd: number | undefined;
28778-
markAssignments(node);
28779-
function markAssignments(node: Node) {
28780-
switch (node.kind) {
28781-
case SyntaxKind.Identifier:
28782-
if (isAssignmentTarget(node)) {
28783-
const symbol = getResolvedSymbol(node as Identifier);
28784-
if (isParameterOrMutableVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) {
28785-
const referencingFunction = findAncestor(node, isFunctionOrSourceFile);
28786-
const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile);
28787-
symbol.lastAssignmentPos = referencingFunction === declaringFunction ? statementEnd ?? node.pos : Number.MAX_VALUE;
28788-
}
28779+
switch (node.kind) {
28780+
case SyntaxKind.Identifier:
28781+
if (isAssignmentTarget(node)) {
28782+
const symbol = getResolvedSymbol(node as Identifier);
28783+
if (isParameterOrMutableLocalVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) {
28784+
const referencingFunction = findAncestor(node, isFunctionOrSourceFile);
28785+
const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile);
28786+
symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE;
2878928787
}
28790-
return;
28788+
}
28789+
return;
28790+
case SyntaxKind.ExportSpecifier:
28791+
const exportDeclaration = (node as ExportSpecifier).parent.parent;
28792+
if (!(node as ExportSpecifier).isTypeOnly && !exportDeclaration.isTypeOnly && !exportDeclaration.moduleSpecifier) {
28793+
const symbol = resolveEntityName((node as ExportSpecifier).propertyName || (node as ExportSpecifier).name, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ true);
28794+
if (symbol && isParameterOrMutableLocalVariable(symbol)) {
28795+
symbol.lastAssignmentPos = Number.MAX_VALUE;
28796+
}
28797+
}
28798+
return;
28799+
case SyntaxKind.InterfaceDeclaration:
28800+
case SyntaxKind.TypeAliasDeclaration:
28801+
case SyntaxKind.EnumDeclaration:
28802+
return;
28803+
}
28804+
if (isTypeNode(node)) {
28805+
return;
28806+
}
28807+
forEachChild(node, markNodeAssignments);
28808+
}
28809+
28810+
// Extend the position of the given assignment target node to the end of any intervening variable statement,
28811+
// expression statement, compound statement, or class declaration occurring between the node and the given
28812+
// declaration node.
28813+
function extendAssignmentPosition(node: Node, declaration: Declaration) {
28814+
let pos = node.pos;
28815+
while (node && node.pos > declaration.pos) {
28816+
switch (node.kind) {
2879128817
case SyntaxKind.VariableStatement:
2879228818
case SyntaxKind.ExpressionStatement:
2879328819
case SyntaxKind.IfStatement:
@@ -28800,30 +28826,30 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2880028826
case SyntaxKind.SwitchStatement:
2880128827
case SyntaxKind.TryStatement:
2880228828
case SyntaxKind.ClassDeclaration:
28803-
const saveStatementEnd = statementEnd;
28804-
statementEnd ??= node.end;
28805-
forEachChild(node, markAssignments);
28806-
statementEnd = saveStatementEnd;
28807-
return;
28808-
case SyntaxKind.InterfaceDeclaration:
28809-
case SyntaxKind.TypeAliasDeclaration:
28810-
case SyntaxKind.EnumDeclaration:
28811-
return;
28812-
}
28813-
if (!isTypeNode(node)) {
28814-
forEachChild(node, markAssignments);
28829+
pos = node.end;
2881528830
}
28831+
node = node.parent;
2881628832
}
28833+
return pos;
2881728834
}
2881828835

2881928836
function isConstantVariable(symbol: Symbol) {
2882028837
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Constant) !== 0;
2882128838
}
2882228839

28823-
function isParameterOrMutableVariable(symbol: Symbol) {
28840+
function isParameterOrMutableLocalVariable(symbol: Symbol) {
28841+
// Return true if symbol is a parameter, a catch clause variable, or a mutable local variable
2882428842
const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration);
28825-
return !!(declaration && (isParameter(declaration) ||
28826-
isVariableDeclaration(declaration) && (declaration.parent.kind === SyntaxKind.CatchClause || !(declaration.parent.flags & NodeFlags.Constant))));
28843+
return !!declaration && (
28844+
isParameter(declaration) ||
28845+
isVariableDeclaration(declaration) && (isCatchClause(declaration.parent) || isMutableLocalVariableDeclaration(declaration)));
28846+
}
28847+
28848+
function isMutableLocalVariableDeclaration(declaration: VariableDeclaration) {
28849+
// Return true if symbol is a non-exported and non-global `let` variable
28850+
return !!(declaration.parent.flags & NodeFlags.Let) && !(
28851+
getCombinedModifierFlags(declaration) & ModifierFlags.Export ||
28852+
declaration.parent.parent.kind === SyntaxKind.VariableStatement && isGlobalSourceFile(declaration.parent.parent.parent));
2882728853
}
2882828854

2882928855
function parameterInitializerContainsUndefined(declaration: ParameterDeclaration): boolean {
@@ -29176,13 +29202,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2917629202
const isModuleExports = symbol.flags & SymbolFlags.ModuleExports;
2917729203
const typeIsAutomatic = type === autoType || type === autoArrayType;
2917829204
const isAutomaticTypeInNonNull = typeIsAutomatic && node.parent.kind === SyntaxKind.NonNullExpression;
29179-
// When the control flow originates in a function expression or arrow function and we are referencing
29180-
// a const variable or parameter from an outer function, we extend the origin of the control flow
29181-
// analysis to include the immediately enclosing function.
29205+
// When the control flow originates in a function expression, arrow function, method, or accessor, and
29206+
// we are referencing a closed-over const variable or parameter or mutable local variable past its last
29207+
// assignment, we extend the origin of the control flow analysis to include the immediately enclosing
29208+
// control flow container.
2918229209
while (
29183-
flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
29184-
flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) &&
29185-
(isConstantVariable(localOrExportSymbol) && type !== autoArrayType || (isParameterOrMutableVariable(localOrExportSymbol)) && isPastLastAssignment(localOrExportSymbol, node))
29210+
flowContainer !== declarationContainer && (
29211+
flowContainer.kind === SyntaxKind.FunctionExpression ||
29212+
flowContainer.kind === SyntaxKind.ArrowFunction ||
29213+
isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)
29214+
) && (
29215+
isConstantVariable(localOrExportSymbol) && type !== autoArrayType ||
29216+
isParameterOrMutableLocalVariable(localOrExportSymbol) && isPastLastAssignment(localOrExportSymbol, node)
29217+
)
2918629218
) {
2918729219
flowContainer = getControlFlowContainer(flowContainer);
2918829220
}

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5825,8 +5825,8 @@ export interface Symbol {
58255825
/** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
58265826
/** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums
58275827
/** @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter.
5828+
/** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol
58285829
/** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
5829-
/** @internal */ lastAssignmentPos?: number; // Last node that assigns value to symbol
58305830
/** @internal */ assignmentDeclarationMembers?: Map<number, Declaration>; // detected late-bound assignment declarations associated with the symbol
58315831
}
58325832

0 commit comments

Comments
 (0)