From ecf36a7c248e3c04426178e3d8299b3189af1ec9 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Wed, 27 Sep 2023 14:14:07 +0800 Subject: [PATCH 01/22] error on variables that are used but never uninitialized --- src/compiler/checker.ts | 131 ++++++--- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 1 + .../typeGuardsAsAssertions.errors.txt | 135 ++++++++++ .../unusedLocalsInMethod4.errors.txt | 116 ++++++++ .../reference/unusedLocalsInMethod4.js | 140 ++++++++++ .../reference/unusedLocalsInMethod4.symbols | 237 ++++++++++++++++ .../reference/unusedLocalsInMethod4.types | 255 ++++++++++++++++++ tests/cases/compiler/unusedLocalsInMethod4.ts | 72 +++++ 9 files changed, 1048 insertions(+), 40 deletions(-) create mode 100644 tests/baselines/reference/typeGuardsAsAssertions.errors.txt create mode 100644 tests/baselines/reference/unusedLocalsInMethod4.errors.txt create mode 100644 tests/baselines/reference/unusedLocalsInMethod4.js create mode 100644 tests/baselines/reference/unusedLocalsInMethod4.symbols create mode 100644 tests/baselines/reference/unusedLocalsInMethod4.types create mode 100644 tests/cases/compiler/unusedLocalsInMethod4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f4c2dd1d9094..908b4d4870f36 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1802,11 +1802,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked)); diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName)); - checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => { + checkUnusedOrUninitializedIdentifiers(getPotentiallyUnusedOrUninitializedIdentifiers(file), (containingNode, kind, diag) => { if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion }); } - }); + }, /*checkUnused*/ true); return diagnostics || emptyArray; } @@ -2147,7 +2147,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var deferredGlobalClassAccessorDecoratorResultType: GenericType | undefined; var deferredGlobalClassFieldDecoratorContextType: GenericType | undefined; - var allPotentiallyUnusedIdentifiers = new Map(); // key is file name + var allPotentiallyUnusedOrUninitializedIdentifiers = new Map(); // key is file name var flowLoopStart = 0; var flowLoopCount = 0; @@ -3027,8 +3027,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isUse: boolean, excludeGlobals = false, getSpellingSuggestions = true, + isWritten = false, ): Symbol | undefined { - return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, getSymbol); + return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, isWritten, getSymbol); } function resolveNameHelper( @@ -3040,6 +3041,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isUse: boolean, excludeGlobals: boolean, getSpellingSuggestions: boolean, + isWritten: boolean, lookup: typeof getSymbol, ): Symbol | undefined { const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location @@ -3394,6 +3396,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result.isReferenced! |= meaning; } + if (isWritten && result) { + result.isInitialized = true; + } + if (!result) { if (lastLocation) { Debug.assertNode(lastLocation, isSourceFile); @@ -25827,6 +25833,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { node, !isWriteOnlyAccess(node), /*excludeGlobals*/ false, + /*getSpellingSuggestions*/ undefined, + isWriteAccess(node), ) || unknownSymbol; } return links.resolvedSymbol; @@ -32487,7 +32495,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined { Debug.assert(outerName !== undefined, "outername should always be defined"); - const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, /*getSpellingSuggestions*/ true, (symbols, name, meaning) => { + const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, /*getSpellingSuggestions*/ true, /*isWritten*/ false, (symbols, name, meaning) => { Debug.assertEqual(outerName, name, "name should equal outerName"); const symbol = getSymbol(symbols, name, meaning); // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function @@ -38934,7 +38942,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.JSDocFunctionType) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } } @@ -39626,7 +39634,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } function checkTemplateLiteralType(node: TemplateLiteralTypeNode) { @@ -41036,32 +41044,34 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function registerForUnusedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void { - addLazyDiagnostic(registerForUnusedIdentifiersCheckDiagnostics); + function registerForUnusedOrUninitializedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void { + addLazyDiagnostic(registerForUnusedOrUninitializedIdentifiersCheckDiagnostics); - function registerForUnusedIdentifiersCheckDiagnostics() { + function registerForUnusedOrUninitializedIdentifiersCheckDiagnostics() { // May be in a call such as getTypeOfNode that happened to call this. But potentiallyUnusedIdentifiers is only defined in the scope of `checkSourceFile`. const sourceFile = getSourceFileOfNode(node); - let potentiallyUnusedIdentifiers = allPotentiallyUnusedIdentifiers.get(sourceFile.path); - if (!potentiallyUnusedIdentifiers) { - potentiallyUnusedIdentifiers = []; - allPotentiallyUnusedIdentifiers.set(sourceFile.path, potentiallyUnusedIdentifiers); + let potentiallyUnusedOrUninitializedIdentifiers = allPotentiallyUnusedOrUninitializedIdentifiers.get(sourceFile.path); + if (!potentiallyUnusedOrUninitializedIdentifiers) { + potentiallyUnusedOrUninitializedIdentifiers = []; + allPotentiallyUnusedOrUninitializedIdentifiers.set(sourceFile.path, potentiallyUnusedOrUninitializedIdentifiers); } // TODO: GH#22580 // Debug.assert(addToSeen(seenPotentiallyUnusedIdentifiers, getNodeId(node)), "Adding potentially-unused identifier twice"); - potentiallyUnusedIdentifiers.push(node); + potentiallyUnusedOrUninitializedIdentifiers.push(node); } } type PotentiallyUnusedIdentifier = SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration | Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement | Exclude | TypeAliasDeclaration | InferTypeNode; - function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: readonly PotentiallyUnusedIdentifier[], addDiagnostic: AddUnusedDiagnostic) { + function checkUnusedOrUninitializedIdentifiers(potentiallyUnusedIdentifiers: readonly PotentiallyUnusedIdentifier[], addDiagnostic: AddUnusedDiagnostic, checkUnused?: boolean, checkUninitialized?: boolean) { for (const node of potentiallyUnusedIdentifiers) { switch (node.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: - checkUnusedClassMembers(node, addDiagnostic); - checkUnusedTypeParameters(node, addDiagnostic); + if (checkUninitialized) { + checkUnusedClassMembers(node, addDiagnostic); + checkUnusedTypeParameters(node, addDiagnostic); + } break; case SyntaxKind.SourceFile: case SyntaxKind.ModuleDeclaration: @@ -41070,7 +41080,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: - checkUnusedLocalsAndParameters(node, addDiagnostic); + if (checkUnused) { + checkUnusedLocalsAndParameters(node, addDiagnostic); + } + if (checkUninitialized) { + checkUninitializedLocals(node); + } break; case SyntaxKind.Constructor: case SyntaxKind.FunctionExpression: @@ -41079,10 +41094,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - if (node.body) { // Don't report unused parameters in overloads - checkUnusedLocalsAndParameters(node, addDiagnostic); + if (checkUnused) { + if (node.body) { // Don't report unused parameters in overloads + checkUnusedLocalsAndParameters(node, addDiagnostic); + } + checkUnusedTypeParameters(node, addDiagnostic); + } + if (checkUninitialized) { + checkUninitializedLocals(node); } - checkUnusedTypeParameters(node, addDiagnostic); break; case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: @@ -41091,10 +41111,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ConstructorType: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.InterfaceDeclaration: - checkUnusedTypeParameters(node, addDiagnostic); + if (checkUnused) { + checkUnusedTypeParameters(node, addDiagnostic); + } break; case SyntaxKind.InferType: - checkUnusedInferTypeParameter(node, addDiagnostic); + if (checkUnused) { + checkUnusedInferTypeParameter(node, addDiagnostic); + } break; default: Debug.assertNever(node, "Node should not have been registered for unused identifiers check"); @@ -41341,6 +41365,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); } + function checkUninitializedLocals(nodeWithLocals: HasLocals) { + nodeWithLocals.locals!.forEach(local => { + if (!(local.flags & SymbolFlags.Variable) || local.isInitialized || !local.isReferenced) { + return; + } + const declaration = local.valueDeclaration; + if (!declaration) { + return; + } + if (isVariableDeclaration(declaration)) { + if ( + getCombinedNodeFlagsCached(declaration) & NodeFlags.Ambient || + declaration.exclamationToken || + declaration.initializer || + !declaration.type + ) { + return; + } + const type = getTypeFromTypeNode(declaration.type); + if (!(type.flags & TypeFlags.AnyOrUnknown) && !containsUndefinedType(type)) { + error(declaration, Diagnostics.Variable_0_is_used_before_being_assigned, idText(declaration.name as Identifier)); + } + } + }) + } + function checkPotentialUncheckedRenamedBindingElementsInTypes() { for (const node of potentialUnusedRenamedBindingElementsInTypes) { if (!getSymbolOfDeclaration(node)?.isReferenced) { @@ -41393,7 +41443,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { forEach(node.statements, checkSourceElement); } if (node.locals) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -42114,7 +42164,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (node.incrementor) checkExpression(node.incrementor); checkSourceElement(node.statement); if (node.locals) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -42178,7 +42228,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkSourceElement(node.statement); if (node.locals) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -42230,7 +42280,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkSourceElement(node.statement); if (node.locals) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -43201,7 +43251,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } }); if (node.caseBlock.locals) { - registerForUnusedIdentifiersCheck(node.caseBlock); + registerForUnusedOrUninitializedIdentifiersCheck(node.caseBlock); } } @@ -43610,7 +43660,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkClassExpressionDeferred(node: ClassExpression) { forEach(node.members, checkSourceElement); - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } function checkClassDeclaration(node: ClassDeclaration) { @@ -43624,7 +43674,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkClassLikeDeclaration(node); forEach(node.members, checkSourceElement); - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } function checkClassLikeDeclaration(node: ClassLikeDeclaration) { @@ -44360,7 +44410,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addLazyDiagnostic(() => { checkTypeForDuplicateIndexSignatures(node); - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); }); } @@ -44377,7 +44427,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } else { checkSourceElement(node.type); - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -44680,7 +44730,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (node.body) { checkSourceElement(node.body); if (!isGlobalScopeAugmentation(node)) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } } @@ -45908,8 +45958,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getPotentiallyUnusedIdentifiers(sourceFile: SourceFile): readonly PotentiallyUnusedIdentifier[] { - return allPotentiallyUnusedIdentifiers.get(sourceFile.path) || emptyArray; + function getPotentiallyUnusedOrUninitializedIdentifiers(sourceFile: SourceFile): readonly PotentiallyUnusedIdentifier[] { + return allPotentiallyUnusedOrUninitializedIdentifiers.get(sourceFile.path) || emptyArray; } // Fully type check a source file and collect the relevant diagnostics. @@ -45935,17 +45985,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkDeferredNodes(node); if (isExternalOrCommonJsModule(node)) { - registerForUnusedIdentifiersCheck(node); + registerForUnusedOrUninitializedIdentifiersCheck(node); } addLazyDiagnostic(() => { // This relies on the results of other lazy diagnostics, so must be computed after them - if (!node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters)) { - checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(node), (containingNode, kind, diag) => { + const checkUnused = !node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters); + if (checkUnused || strictNullChecks) { + checkUnusedOrUninitializedIdentifiers(getPotentiallyUnusedOrUninitializedIdentifiers(node), (containingNode, kind, diag) => { if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { diagnostics.add(diag); } - }); + }, checkUnused, strictNullChecks); } if (!node.isDeclarationFile) { checkPotentialUncheckedRenamedBindingElementsInTypes(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 860b900251f7f..208213ea136e0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5748,6 +5748,7 @@ export interface Symbol { /** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @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. + /** @internal */ isInitialized?: boolean; // True if the symbol is ever initialized. /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bf5223d610f92..1382b8862a0a5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8133,6 +8133,7 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.exportSymbol = undefined; this.constEnumOnlyModule = undefined; this.isReferenced = undefined; + this.isInitialized = undefined; this.isAssigned = undefined; (this as any).links = undefined; // used by TransientSymbol } diff --git a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt new file mode 100644 index 0000000000000..2da9e1dede086 --- /dev/null +++ b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt @@ -0,0 +1,135 @@ +typeGuardsAsAssertions.ts(3,5): error TS2454: Variable 'cond' is used before being assigned. +typeGuardsAsAssertions.ts(123,9): error TS2454: Variable 'x' is used before being assigned. + + +==== typeGuardsAsAssertions.ts (2 errors) ==== + // Repro from #8513 + + let cond: boolean; + ~~~~ +!!! error TS2454: Variable 'cond' is used before being assigned. + + export type Optional = Some | None; + + export interface None { readonly none: string; } + export interface Some { readonly some: a; } + + export const none : None = { none: '' }; + + export function isSome(value: Optional): value is Some { + return 'some' in value; + } + + function someFrom(some: a) { + return { some }; + } + + export function fn(makeSome: () => r): void { + let result: Optional = none; + result; // None + while (cond) { + result; // Some | None + result = someFrom(isSome(result) ? result.some : makeSome()); + result; // Some + } + } + + function foo1() { + let x: string | number | boolean = 0; + x; // number + while (cond) { + x; // number, then string | number + x = typeof x === "string" ? x.slice() : "abc"; + x; // string + } + x; + } + + function foo2() { + let x: string | number | boolean = 0; + x; // number + while (cond) { + x; // number, then string | number + if (typeof x === "string") { + x = x.slice(); + } + else { + x = "abc"; + } + x; // string + } + x; + } + + // Type guards as assertions + + function f1() { + let x: string | number | undefined = undefined; + x; // undefined + if (x) { + x; // string | number (guard as assertion) + } + x; // string | number | undefined + } + + function f2() { + let x: string | number | undefined = undefined; + x; // undefined + if (typeof x === "string") { + x; // string (guard as assertion) + } + x; // string | undefined + } + + function f3() { + let x: string | number | undefined = undefined; + x; // undefined + if (!x) { + return; + } + x; // string | number (guard as assertion) + } + + function f4() { + let x: string | number | undefined = undefined; + x; // undefined + if (typeof x === "boolean") { + x; // nothing (boolean not in declared type) + } + x; // undefined + } + + function f5(x: string | number) { + if (typeof x === "string" && typeof x === "number") { + x; // number (guard as assertion) + } + else { + x; // string | number + } + x; // string | number + } + + function f6() { + let x: string | undefined | null; + x!.slice(); + x = ""; + x!.slice(); + x = undefined; + x!.slice(); + x = null; + x!.slice(); + x = undefined; + x!.slice(); + x = ""; + x!.slice(); + x = ""; + x!.slice(); + } + + function f7() { + let x: string; + ~ +!!! error TS2454: Variable 'x' is used before being assigned. + x!.slice(); + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt new file mode 100644 index 0000000000000..f846cffd1ed14 --- /dev/null +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -0,0 +1,116 @@ +unusedLocalsInMethod4.ts(2,9): error TS2454: Variable 'x1' is used before being assigned. +unusedLocalsInMethod4.ts(3,9): error TS2454: Variable 'x2' is used before being assigned. +unusedLocalsInMethod4.ts(9,9): error TS2454: Variable 'x8' is used before being assigned. +unusedLocalsInMethod4.ts(10,9): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(27,9): error TS2454: Variable 'x1' is used before being assigned. +unusedLocalsInMethod4.ts(28,9): error TS2454: Variable 'x2' is used before being assigned. +unusedLocalsInMethod4.ts(34,9): error TS2454: Variable 'x8' is used before being assigned. +unusedLocalsInMethod4.ts(35,9): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(37,17): error TS2454: Variable 'x1' is used before being assigned. +unusedLocalsInMethod4.ts(38,17): error TS2454: Variable 'x2' is used before being assigned. +unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before being assigned. +unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(49,9): error TS2454: Variable 'x' is used before being assigned. +unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being assigned. + + +==== unusedLocalsInMethod4.ts (14 errors) ==== + function f() { + let x1: number[]; // should error + ~~ +!!! error TS2454: Variable 'x1' is used before being assigned. + let x2: number[] | null; // should error + ~~ +!!! error TS2454: Variable 'x2' is used before being assigned. + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + ~~ +!!! error TS2454: Variable 'x8' is used before being assigned. + let x9: NonNull; // should error + ~~ +!!! error TS2454: Variable 'x9' is used before being assigned. + + function foo() { + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); + } + foo(); + } + + function f2() { + let x1: number[]; // should error + ~~ +!!! error TS2454: Variable 'x1' is used before being assigned. + let x2: number[] | null; // should error + ~~ +!!! error TS2454: Variable 'x2' is used before being assigned. + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + ~~ +!!! error TS2454: Variable 'x8' is used before being assigned. + let x9: NonNull; // should error + ~~ +!!! error TS2454: Variable 'x9' is used before being assigned. + + console.log(x1); + ~~ +!!! error TS2454: Variable 'x1' is used before being assigned. + console.log(x2); + ~~ +!!! error TS2454: Variable 'x2' is used before being assigned. + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + ~~ +!!! error TS2454: Variable 'x8' is used before being assigned. + console.log(x9); + ~~ +!!! error TS2454: Variable 'x9' is used before being assigned. + } + + function f3() { + let x: number[]; + ~ +!!! error TS2454: Variable 'x' is used before being assigned. + function foo() { + x.toString(); + } + foo(); + } + + function f4() { + let x: number; + ~ +!!! error TS2454: Variable 'x' is used before being assigned. + return { + foo() { + return x.toString(); + } + }; + } + + declare let x: number; + function f5() { + x.toString(); + } + export default {}; + + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js new file mode 100644 index 0000000000000..e9884de0de193 --- /dev/null +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -0,0 +1,140 @@ +//// [tests/cases/compiler/unusedLocalsInMethod4.ts] //// + +//// [unusedLocalsInMethod4.ts] +function f() { + let x1: number[]; // should error + let x2: number[] | null; // should error + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + let x9: NonNull; // should error + + function foo() { + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); + } + foo(); +} + +function f2() { + let x1: number[]; // should error + let x2: number[] | null; // should error + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + let x9: NonNull; // should error + + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); +} + +function f3() { + let x: number[]; + function foo() { + x.toString(); + } + foo(); +} + +function f4() { + let x: number; + return { + foo() { + return x.toString(); + } + }; +} + +declare let x: number; +function f5() { + x.toString(); +} +export default {}; + + + +//// [unusedLocalsInMethod4.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function f() { + var x1; // should error + var x2; // should error + var x3; // should not error + var x4; // should not error + var x5; // should not error + var x6; // should not error + var x7; // should not error + var x8; // should error + var x9; // should error + function foo() { + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); + } + foo(); +} +function f2() { + var x1; // should error + var x2; // should error + var x3; // should not error + var x4; // should not error + var x5; // should not error + var x6; // should not error + var x7; // should not error + var x8; // should error + var x9; // should error + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); +} +function f3() { + var x; + function foo() { + x.toString(); + } + foo(); +} +function f4() { + var x; + return { + foo: function () { + return x.toString(); + } + }; +} +function f5() { + x.toString(); +} +exports.default = {}; diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols new file mode 100644 index 0000000000000..e6c945d106d9c --- /dev/null +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -0,0 +1,237 @@ +//// [tests/cases/compiler/unusedLocalsInMethod4.ts] //// + +=== unusedLocalsInMethod4.ts === +function f() { +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 0, 0)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 0, 11)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) + + let x1: number[]; // should error +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 1, 7)) + + let x2: number[] | null; // should error +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 2, 7)) + + let x3: number[] | undefined; // should not error +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 3, 7)) + + let x4: number[] | undefined | null; // should not error +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 4, 7)) + + let x5!: number[]; // should not error +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 5, 7)) + + let x6: any; // should not error +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 6, 7)) + + let x7: unknown; // should not error +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 7, 7)) + + let x8: T; // should error +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 8, 7)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 0, 11)) + + let x9: NonNull; // should error +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 9, 7)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) + + function foo() { +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 9, 20)) + + console.log(x1); +>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, --, --)) +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 1, 7)) + + console.log(x2); +>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, --, --)) +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 2, 7)) + + console.log(x3); +>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, --, --)) +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 3, 7)) + + console.log(x4); +>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, --, --)) +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 4, 7)) + + console.log(x5); +>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, --, --)) +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 5, 7)) + + console.log(x6); +>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, --, --)) +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 6, 7)) + + console.log(x7); +>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, --, --)) +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 7, 7)) + + console.log(x8); +>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, --, --)) +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 8, 7)) + + console.log(x9); +>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, --, --)) +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 9, 7)) + } + foo(); +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 9, 20)) +} + +function f2() { +>f2 : Symbol(f2, Decl(unusedLocalsInMethod4.ts, 23, 1)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 25, 12)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 25, 14)) + + let x1: number[]; // should error +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 26, 7)) + + let x2: number[] | null; // should error +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 27, 7)) + + let x3: number[] | undefined; // should not error +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 28, 7)) + + let x4: number[] | undefined | null; // should not error +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 29, 7)) + + let x5!: number[]; // should not error +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 30, 7)) + + let x6: any; // should not error +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 31, 7)) + + let x7: unknown; // should not error +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 32, 7)) + + let x8: T; // should error +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 33, 7)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 25, 12)) + + let x9: NonNull; // should error +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 34, 7)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 25, 14)) + + console.log(x1); +>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, --, --)) +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 26, 7)) + + console.log(x2); +>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, --, --)) +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 27, 7)) + + console.log(x3); +>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, --, --)) +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 28, 7)) + + console.log(x4); +>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, --, --)) +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 29, 7)) + + console.log(x5); +>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, --, --)) +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 30, 7)) + + console.log(x6); +>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, --, --)) +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 31, 7)) + + console.log(x7); +>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, --, --)) +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 32, 7)) + + console.log(x8); +>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, --, --)) +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 33, 7)) + + console.log(x9); +>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, --, --)) +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 34, 7)) +} + +function f3() { +>f3 : Symbol(f3, Decl(unusedLocalsInMethod4.ts, 45, 1)) + + let x: number[]; +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 48, 7)) + + function foo() { +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 48, 20)) + + x.toString(); +>x.toString : Symbol(Array.toString, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 48, 7)) +>toString : Symbol(Array.toString, Decl(lib.es5.d.ts, --, --)) + } + foo(); +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 48, 20)) +} + +function f4() { +>f4 : Symbol(f4, Decl(unusedLocalsInMethod4.ts, 53, 1)) + + let x: number; +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 56, 7)) + + return { + foo() { +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 57, 12)) + + return x.toString(); +>x.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 56, 7)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + } + }; +} + +declare let x: number; +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 64, 11)) + +function f5() { +>f5 : Symbol(f5, Decl(unusedLocalsInMethod4.ts, 64, 22)) + + x.toString(); +>x.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 64, 11)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +} +export default {}; + + diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types new file mode 100644 index 0000000000000..32ba6c3de0217 --- /dev/null +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -0,0 +1,255 @@ +//// [tests/cases/compiler/unusedLocalsInMethod4.ts] //// + +=== unusedLocalsInMethod4.ts === +function f() { +>f : () => void + + let x1: number[]; // should error +>x1 : number[] + + let x2: number[] | null; // should error +>x2 : number[] | null + + let x3: number[] | undefined; // should not error +>x3 : number[] | undefined + + let x4: number[] | undefined | null; // should not error +>x4 : number[] | null | undefined + + let x5!: number[]; // should not error +>x5 : number[] + + let x6: any; // should not error +>x6 : any + + let x7: unknown; // should not error +>x7 : unknown + + let x8: T; // should error +>x8 : T + + let x9: NonNull; // should error +>x9 : NonNull + + function foo() { +>foo : () => void + + console.log(x1); +>console.log(x1) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x1 : number[] + + console.log(x2); +>console.log(x2) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x2 : number[] | null + + console.log(x3); +>console.log(x3) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x3 : number[] | undefined + + console.log(x4); +>console.log(x4) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x4 : number[] | null | undefined + + console.log(x5); +>console.log(x5) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x5 : number[] + + console.log(x6); +>console.log(x6) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x6 : any + + console.log(x7); +>console.log(x7) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x7 : unknown + + console.log(x8); +>console.log(x8) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x8 : T + + console.log(x9); +>console.log(x9) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x9 : NonNull + } + foo(); +>foo() : void +>foo : () => void +} + +function f2() { +>f2 : () => void + + let x1: number[]; // should error +>x1 : number[] + + let x2: number[] | null; // should error +>x2 : number[] | null + + let x3: number[] | undefined; // should not error +>x3 : number[] | undefined + + let x4: number[] | undefined | null; // should not error +>x4 : number[] | null | undefined + + let x5!: number[]; // should not error +>x5 : number[] + + let x6: any; // should not error +>x6 : any + + let x7: unknown; // should not error +>x7 : unknown + + let x8: T; // should error +>x8 : T + + let x9: NonNull; // should error +>x9 : NonNull + + console.log(x1); +>console.log(x1) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x1 : number[] + + console.log(x2); +>console.log(x2) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x2 : number[] | null + + console.log(x3); +>console.log(x3) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x3 : number[] | undefined + + console.log(x4); +>console.log(x4) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x4 : number[] | null | undefined + + console.log(x5); +>console.log(x5) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x5 : number[] + + console.log(x6); +>console.log(x6) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x6 : any + + console.log(x7); +>console.log(x7) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x7 : unknown + + console.log(x8); +>console.log(x8) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x8 : T + + console.log(x9); +>console.log(x9) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x9 : NonNull +} + +function f3() { +>f3 : () => void + + let x: number[]; +>x : number[] + + function foo() { +>foo : () => void + + x.toString(); +>x.toString() : string +>x.toString : () => string +>x : number[] +>toString : () => string + } + foo(); +>foo() : void +>foo : () => void +} + +function f4() { +>f4 : () => { foo(): string; } + + let x: number; +>x : number + + return { +>{ foo() { return x.toString(); } } : { foo(): string; } + + foo() { +>foo : () => string + + return x.toString(); +>x.toString() : string +>x.toString : (radix?: number | undefined) => string +>x : number +>toString : (radix?: number | undefined) => string + } + }; +} + +declare let x: number; +>x : number + +function f5() { +>f5 : () => void + + x.toString(); +>x.toString() : string +>x.toString : (radix?: number | undefined) => string +>x : number +>toString : (radix?: number | undefined) => string +} +export default {}; +>{} : {} + + diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts new file mode 100644 index 0000000000000..46c94d2d40cce --- /dev/null +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -0,0 +1,72 @@ +// @strict: true + +function f() { + let x1: number[]; // should error + let x2: number[] | null; // should error + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + let x9: NonNull; // should error + + function foo() { + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); + } + foo(); +} + +function f2() { + let x1: number[]; // should error + let x2: number[] | null; // should error + let x3: number[] | undefined; // should not error + let x4: number[] | undefined | null; // should not error + let x5!: number[]; // should not error + let x6: any; // should not error + let x7: unknown; // should not error + let x8: T; // should error + let x9: NonNull; // should error + + console.log(x1); + console.log(x2); + console.log(x3); + console.log(x4); + console.log(x5); + console.log(x6); + console.log(x7); + console.log(x8); + console.log(x9); +} + +function f3() { + let x: number[]; // should error + function foo() { + x.toString(); + } + foo(); +} + +function f4() { + let x: number; // should error + return { + foo() { + return x.toString(); + } + }; +} + +declare let x: number; // should error +function f5() { + x.toString(); +} +export default {}; + From 8416f983f41074e5b9c0cb758bfb9e36b9fe0083 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Wed, 27 Sep 2023 15:06:08 +0800 Subject: [PATCH 02/22] format --- src/compiler/checker.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 908b4d4870f36..baad33104b7d0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -41388,7 +41388,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(declaration, Diagnostics.Variable_0_is_used_before_being_assigned, idText(declaration.name as Identifier)); } } - }) + }); } function checkPotentialUncheckedRenamedBindingElementsInTypes() { @@ -45992,11 +45992,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // This relies on the results of other lazy diagnostics, so must be computed after them const checkUnused = !node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters); if (checkUnused || strictNullChecks) { - checkUnusedOrUninitializedIdentifiers(getPotentiallyUnusedOrUninitializedIdentifiers(node), (containingNode, kind, diag) => { - if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { - diagnostics.add(diag); - } - }, checkUnused, strictNullChecks); + checkUnusedOrUninitializedIdentifiers( + getPotentiallyUnusedOrUninitializedIdentifiers(node), + (containingNode, kind, diag) => { + if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { + diagnostics.add(diag); + } + }, + checkUnused, + strictNullChecks, + ); } if (!node.isDeclarationFile) { checkPotentialUncheckedRenamedBindingElementsInTypes(); From ff025b3f013ed15439c75a758e864e3f6d39f372 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Wed, 27 Sep 2023 15:56:44 +0800 Subject: [PATCH 03/22] fix tests --- src/compiler/checker.ts | 2 +- .../reference/unusedLocalsInMethod4.errors.txt | 6 +++--- tests/baselines/reference/unusedLocalsInMethod4.js | 10 +++++----- .../baselines/reference/unusedLocalsInMethod4.symbols | 6 +++--- tests/baselines/reference/unusedLocalsInMethod4.types | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c121046d05ffc..37db050ad00eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -41191,7 +41191,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { switch (node.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: - if (checkUninitialized) { + if (checkUnused) { checkUnusedClassMembers(node, addDiagnostic); checkUnusedTypeParameters(node, addDiagnostic); } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index f846cffd1ed14..e3891da89bb34 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -87,7 +87,7 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being } function f3() { - let x: number[]; + let x: number[]; // should error ~ !!! error TS2454: Variable 'x' is used before being assigned. function foo() { @@ -97,7 +97,7 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being } function f4() { - let x: number; + let x: number; // should error ~ !!! error TS2454: Variable 'x' is used before being assigned. return { @@ -107,7 +107,7 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being }; } - declare let x: number; + declare let x: number; // should error function f5() { x.toString(); } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index e9884de0de193..d6a90331d694b 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -49,7 +49,7 @@ function f2() { } function f3() { - let x: number[]; + let x: number[]; // should error function foo() { x.toString(); } @@ -57,7 +57,7 @@ function f3() { } function f4() { - let x: number; + let x: number; // should error return { foo() { return x.toString(); @@ -65,7 +65,7 @@ function f4() { }; } -declare let x: number; +declare let x: number; // should error function f5() { x.toString(); } @@ -120,14 +120,14 @@ function f2() { console.log(x9); } function f3() { - var x; + var x; // should error function foo() { x.toString(); } foo(); } function f4() { - var x; + var x; // should error return { foo: function () { return x.toString(); diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index e6c945d106d9c..575bc5fa397a0 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -188,7 +188,7 @@ function f2() { function f3() { >f3 : Symbol(f3, Decl(unusedLocalsInMethod4.ts, 45, 1)) - let x: number[]; + let x: number[]; // should error >x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 48, 7)) function foo() { @@ -206,7 +206,7 @@ function f3() { function f4() { >f4 : Symbol(f4, Decl(unusedLocalsInMethod4.ts, 53, 1)) - let x: number; + let x: number; // should error >x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 56, 7)) return { @@ -221,7 +221,7 @@ function f4() { }; } -declare let x: number; +declare let x: number; // should error >x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 64, 11)) function f5() { diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index 32ba6c3de0217..c1bf37a6a7774 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -199,7 +199,7 @@ function f2() { function f3() { >f3 : () => void - let x: number[]; + let x: number[]; // should error >x : number[] function foo() { @@ -219,7 +219,7 @@ function f3() { function f4() { >f4 : () => { foo(): string; } - let x: number; + let x: number; // should error >x : number return { @@ -237,7 +237,7 @@ function f4() { }; } -declare let x: number; +declare let x: number; // should error >x : number function f5() { From 7bb240fc676e967bce9cb81ca8a5c0adc9b921ca Mon Sep 17 00:00:00 2001 From: Zzzen Date: Thu, 28 Sep 2023 15:48:41 +0800 Subject: [PATCH 04/22] fix ForInOrOfStatement --- src/compiler/utilities.ts | 5 +++ ...indingElementPropertyName06.baseline.jsonc | 10 ++--- .../unusedLocalsInMethod4.errors.txt | 14 +++++++ .../reference/unusedLocalsInMethod4.js | 27 +++++++++++++ .../reference/unusedLocalsInMethod4.symbols | 34 +++++++++++++++++ .../reference/unusedLocalsInMethod4.types | 38 +++++++++++++++++++ tests/cases/compiler/unusedLocalsInMethod4.ts | 14 +++++++ 7 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 117c93592b174..fd7ea9cc6e6d4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -152,6 +152,8 @@ import { forEachChild, forEachChildRecursively, ForInOrOfStatement, + ForInStatement, + ForOfStatement, ForStatement, FunctionBody, FunctionDeclaration, @@ -7753,6 +7755,9 @@ function accessKind(node: Node): AccessKind { return node === (parent as ShorthandPropertyAssignment).objectAssignmentInitializer ? AccessKind.Read : accessKind(parent.parent); case SyntaxKind.ArrayLiteralExpression: return accessKind(parent); + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + return node === (parent as ForInStatement | ForOfStatement).initializer ? AccessKind.Write : AccessKind.Read; default: return AccessKind.Read; } diff --git a/tests/baselines/reference/findAllRefsObjectBindingElementPropertyName06.baseline.jsonc b/tests/baselines/reference/findAllRefsObjectBindingElementPropertyName06.baseline.jsonc index 5051e0492a750..3dd00d3b7f57c 100644 --- a/tests/baselines/reference/findAllRefsObjectBindingElementPropertyName06.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsObjectBindingElementPropertyName06.baseline.jsonc @@ -13,7 +13,7 @@ // for (<|var { [|property1|]: p1 } of elems|>) { // } // var p2; -// for (<|{ [|{| isWriteAccess: true |}property1|] : p2 } of elems|>) { +// for (<|{ [|property1|] : p2 } of elems|>) { // } // === Definitions === @@ -94,7 +94,7 @@ // for (<|var { [|property1|]: p1 } of elems|>) { // } // var p2; -// for (<|{ [|{| isWriteAccess: true |}property1|] : p2 } of elems|>) { +// for (<|{ [|property1|] : p2 } of elems|>) { // } // === Definitions === @@ -180,7 +180,7 @@ // for (<|var { /*FIND ALL REFS*/[|property1|]: p1 } of elems|>) { // } // var p2; -// for (<|{ [|{| isWriteAccess: true |}property1|] : p2 } of elems|>) { +// for (<|{ [|property1|] : p2 } of elems|>) { // } // === Definitions === @@ -270,7 +270,7 @@ // for (<|var { [|property1|]: p1 } of elems|>) { // } // var p2; -// for (<|{ /*FIND ALL REFS*/[|{| isWriteAccess: true, isDefinition: true |}property1|] : p2 } of elems|>) { +// for (<|{ /*FIND ALL REFS*/[|{| isDefinition: true |}property1|] : p2 } of elems|>) { // } // === Definitions === @@ -358,7 +358,7 @@ // for (<|var { [|{| defId: 0 |}property1|]: p1 } of elems|>) { // } // var p2; -// for (<|{ [|{| defId: 0, isWriteAccess: true |}property1|] : p2 } of elems|>) { +// for (<|{ [|{| defId: 0 |}property1|] : p2 } of elems|>) { // } // === Definitions === diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index e3891da89bb34..025638b5bfc60 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -113,4 +113,18 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being } export default {}; + function f6() { + let key: string; // should not error + for (key in {}) { + console.log(key); + } + } + + function f7() { + let key: string; // should not error + for (key of []) { + console.log(key); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index d6a90331d694b..50dce9883f4f3 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -71,6 +71,20 @@ function f5() { } export default {}; +function f6() { + let key: string; // should not error + for (key in {}) { + console.log(key); + } +} + +function f7() { + let key: string; // should not error + for (key of []) { + console.log(key); + } +} + //// [unusedLocalsInMethod4.js] @@ -138,3 +152,16 @@ function f5() { x.toString(); } exports.default = {}; +function f6() { + var key; // should not error + for (key in {}) { + console.log(key); + } +} +function f7() { + var key; // should not error + for (var _i = 0, _a = []; _i < _a.length; _i++) { + key = _a[_i]; + console.log(key); + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index 575bc5fa397a0..981751ed063d4 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -234,4 +234,38 @@ function f5() { } export default {}; +function f6() { +>f6 : Symbol(f6, Decl(unusedLocalsInMethod4.ts, 68, 18)) + + let key: string; // should not error +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) + + for (key in {}) { +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) + + console.log(key); +>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, --, --)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) + } +} + +function f7() { +>f7 : Symbol(f7, Decl(unusedLocalsInMethod4.ts, 75, 1)) + + let key: string; // should not error +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) + + for (key of []) { +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) + + console.log(key); +>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, --, --)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) + } +} + diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index c1bf37a6a7774..deef38cf64d18 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -252,4 +252,42 @@ function f5() { export default {}; >{} : {} +function f6() { +>f6 : () => void + + let key: string; // should not error +>key : string + + for (key in {}) { +>key : string +>{} : {} + + console.log(key); +>console.log(key) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>key : string + } +} + +function f7() { +>f7 : () => void + + let key: string; // should not error +>key : string + + for (key of []) { +>key : string +>[] : never[] + + console.log(key); +>console.log(key) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>key : string + } +} + diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index 46c94d2d40cce..c2c529c42e0e0 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -70,3 +70,17 @@ function f5() { } export default {}; +function f6() { + let key: string; // should not error + for (key in {}) { + console.log(key); + } +} + +function f7() { + let key: string; // should not error + for (key of []) { + console.log(key); + } +} + From 8d814cd6e176ee8a0e61db30f254eb416049d90d Mon Sep 17 00:00:00 2001 From: Zzzen Date: Thu, 28 Sep 2023 16:50:43 +0800 Subject: [PATCH 05/22] reuse `isAssigned` flag --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 3 +-- src/compiler/utilities.ts | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 37db050ad00eb..a03fe09847e97 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3432,7 +3432,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isWritten && result) { - result.isInitialized = true; + result.isAssigned = true; } if (!result) { @@ -41490,7 +41490,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkUninitializedLocals(nodeWithLocals: HasLocals) { nodeWithLocals.locals!.forEach(local => { - if (!(local.flags & SymbolFlags.Variable) || local.isInitialized || !local.isReferenced) { + if (!(local.flags & SymbolFlags.Variable) || local.isAssigned || !local.isReferenced) { return; } const declaration = local.valueDeclaration; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6e79cbef335fb..2e144dc17f8c9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5765,9 +5765,8 @@ export interface Symbol { /** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @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. - /** @internal */ isInitialized?: boolean; // True if the symbol is ever initialized. /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? - /** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments + /** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments or a variable that is assigned elsewhere /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fd7ea9cc6e6d4..1105b7fd97ab1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8109,7 +8109,6 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.exportSymbol = undefined; this.constEnumOnlyModule = undefined; this.isReferenced = undefined; - this.isInitialized = undefined; this.isAssigned = undefined; (this as any).links = undefined; // used by TransientSymbol } From e1262a69c484acd66e97272cfe1ed851676ab5be Mon Sep 17 00:00:00 2001 From: Zzzen Date: Thu, 18 Jul 2024 22:42:29 +0800 Subject: [PATCH 06/22] add tests --- .../unusedLocalsInMethod4.errors.txt | 21 ++ .../reference/unusedLocalsInMethod4.js | 38 +++ .../reference/unusedLocalsInMethod4.symbols | 46 ++++ .../reference/unusedLocalsInMethod4.types | 243 +++++++++++++++++- tests/cases/compiler/unusedLocalsInMethod4.ts | 21 ++ 5 files changed, 365 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 025638b5bfc60..fb3465cb7901a 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -127,4 +127,25 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being } } + function f8() { + function ff() { + let _; + let rest: {}; // should not error + + [_, ...rest] = bar(); + } + } + declare function bar(): [number, ...string[]]; + + + function rw() { + let i: number; + function inside() { + i++; + console.log(i); // NaN + } + inside(); + } + rw(); + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index 50dce9883f4f3..308f8a0fe44d0 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -85,6 +85,27 @@ function f7() { } } +function f8() { + function ff() { + let _; + let rest: {}; // should not error + + [_, ...rest] = bar(); + } +} +declare function bar(): [number, ...string[]]; + + +function rw() { + let i: number; + function inside() { + i++; + console.log(i); // NaN + } + inside(); +} +rw(); + //// [unusedLocalsInMethod4.js] @@ -165,3 +186,20 @@ function f7() { console.log(key); } } +function f8() { + function ff() { + var _a; + var _; + var rest; // should not error + _a = bar(), _ = _a[0], rest = _a.slice(1); + } +} +function rw() { + var i; + function inside() { + i++; + console.log(i); // NaN + } + inside(); +} +rw(); diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index 981751ed063d4..e2f329234e06c 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -268,4 +268,50 @@ function f7() { } } +function f8() { +>f8 : Symbol(f8, Decl(unusedLocalsInMethod4.ts, 82, 1)) + + function ff() { +>ff : Symbol(ff, Decl(unusedLocalsInMethod4.ts, 84, 15)) + + let _; +>_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 86, 11)) + + let rest: {}; // should not error +>rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 87, 11)) + + [_, ...rest] = bar(); +>_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 86, 11)) +>rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 87, 11)) +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 91, 1)) + } +} +declare function bar(): [number, ...string[]]; +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 91, 1)) + + +function rw() { +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) + + let i: number; +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) + + function inside() { +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 96, 18)) + + i++; +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) + + console.log(i); // NaN +>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, --, --)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) + } + inside(); +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 96, 18)) +} +rw(); +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) + diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index deef38cf64d18..938b3c78fdf5e 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -3,291 +3,526 @@ === unusedLocalsInMethod4.ts === function f() { >f : () => void +> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ let x1: number[]; // should error >x1 : number[] +> : ^^^^^^^^ let x2: number[] | null; // should error >x2 : number[] | null +> : ^^^^^^^^^^^^^^^ let x3: number[] | undefined; // should not error >x3 : number[] | undefined +> : ^^^^^^^^^^^^^^^^^^^^ let x4: number[] | undefined | null; // should not error >x4 : number[] | null | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ let x5!: number[]; // should not error >x5 : number[] +> : ^^^^^^^^ let x6: any; // should not error >x6 : any +> : ^^^ let x7: unknown; // should not error >x7 : unknown +> : ^^^^^^^ let x8: T; // should error >x8 : T +> : ^ let x9: NonNull; // should error >x9 : NonNull +> : ^^^^^^^ function foo() { >foo : () => void +> : ^^^^^^^^^^ console.log(x1); >console.log(x1) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x1 : number[] +> : ^^^^^^^^ console.log(x2); >console.log(x2) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x2 : number[] | null +> : ^^^^^^^^^^^^^^^ console.log(x3); >console.log(x3) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x3 : number[] | undefined +> : ^^^^^^^^^^^^^^^^^^^^ console.log(x4); >console.log(x4) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x4 : number[] | null | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ console.log(x5); >console.log(x5) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x5 : number[] +> : ^^^^^^^^ console.log(x6); >console.log(x6) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x6 : any +> : ^^^ console.log(x7); >console.log(x7) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x7 : unknown +> : ^^^^^^^ console.log(x8); >console.log(x8) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x8 : T +> : ^ console.log(x9); >console.log(x9) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x9 : NonNull +> : ^^^^^^^ } foo(); >foo() : void +> : ^^^^ >foo : () => void +> : ^^^^^^^^^^ } function f2() { >f2 : () => void +> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ let x1: number[]; // should error >x1 : number[] +> : ^^^^^^^^ let x2: number[] | null; // should error >x2 : number[] | null +> : ^^^^^^^^^^^^^^^ let x3: number[] | undefined; // should not error >x3 : number[] | undefined +> : ^^^^^^^^^^^^^^^^^^^^ let x4: number[] | undefined | null; // should not error >x4 : number[] | null | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ let x5!: number[]; // should not error >x5 : number[] +> : ^^^^^^^^ let x6: any; // should not error >x6 : any +> : ^^^ let x7: unknown; // should not error >x7 : unknown +> : ^^^^^^^ let x8: T; // should error >x8 : T +> : ^ let x9: NonNull; // should error >x9 : NonNull +> : ^^^^^^^ console.log(x1); >console.log(x1) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x1 : number[] +> : ^^^^^^^^ console.log(x2); >console.log(x2) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x2 : number[] | null +> : ^^^^^^^^^^^^^^^ console.log(x3); >console.log(x3) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x3 : number[] | undefined +> : ^^^^^^^^^^^^^^^^^^^^ console.log(x4); >console.log(x4) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x4 : number[] | null | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ console.log(x5); >console.log(x5) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x5 : number[] +> : ^^^^^^^^ console.log(x6); >console.log(x6) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x6 : any +> : ^^^ console.log(x7); >console.log(x7) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x7 : unknown +> : ^^^^^^^ console.log(x8); >console.log(x8) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x8 : T +> : ^ console.log(x9); >console.log(x9) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >x9 : NonNull +> : ^^^^^^^ } function f3() { >f3 : () => void +> : ^^^^^^^^^^ let x: number[]; // should error >x : number[] +> : ^^^^^^^^ function foo() { >foo : () => void +> : ^^^^^^^^^^ x.toString(); >x.toString() : string +> : ^^^^^^ >x.toString : () => string +> : ^^^^^^ >x : number[] +> : ^^^^^^^^ >toString : () => string +> : ^^^^^^ } foo(); >foo() : void +> : ^^^^ >foo : () => void +> : ^^^^^^^^^^ } function f4() { >f4 : () => { foo(): string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ let x: number; // should error >x : number +> : ^^^^^^ return { >{ foo() { return x.toString(); } } : { foo(): string; } +> : ^^^^^^^^^^^^^^^^^^ foo() { >foo : () => string +> : ^^^^^^^^^^^^ return x.toString(); >x.toString() : string ->x.toString : (radix?: number | undefined) => string +> : ^^^^^^ +>x.toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ >x : number ->toString : (radix?: number | undefined) => string +> : ^^^^^^ +>toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ } }; } declare let x: number; // should error >x : number +> : ^^^^^^ function f5() { >f5 : () => void +> : ^^^^^^^^^^ x.toString(); >x.toString() : string ->x.toString : (radix?: number | undefined) => string +> : ^^^^^^ +>x.toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ >x : number ->toString : (radix?: number | undefined) => string +> : ^^^^^^ +>toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ } export default {}; >{} : {} +> : ^^ function f6() { >f6 : () => void +> : ^^^^^^^^^^ let key: string; // should not error >key : string +> : ^^^^^^ for (key in {}) { >key : string +> : ^^^^^^ >{} : {} +> : ^^ console.log(key); >console.log(key) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >key : string +> : ^^^^^^ } } function f7() { >f7 : () => void +> : ^^^^^^^^^^ let key: string; // should not error >key : string +> : ^^^^^^ for (key of []) { >key : string +> : ^^^^^^ >[] : never[] +> : ^^^^^^^ console.log(key); >console.log(key) : void +> : ^^^^ >console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >console : Console +> : ^^^^^^^ >log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ >key : string +> : ^^^^^^ } } +function f8() { +>f8 : () => void +> : ^^^^^^^^^^ + + function ff() { +>ff : () => void +> : ^^^^^^^^^^ + + let _; +>_ : any +> : ^^^ + + let rest: {}; // should not error +>rest : {} +> : ^^ + + [_, ...rest] = bar(); +>[_, ...rest] = bar() : [number, ...string[]] +> : ^^^^^^^^^^^^^^^^^^^^^ +>[_, ...rest] : [any, ...unknown[]] +> : ^^^^^^^^^^^^^^^^^^^ +>_ : any +> : ^^^ +>...rest : any +> : ^^^ +>rest : {} +> : ^^ +>bar() : [number, ...string[]] +> : ^^^^^^^^^^^^^^^^^^^^^ +>bar : () => [number, ...string[]] +> : ^^^^^^ + } +} +declare function bar(): [number, ...string[]]; +>bar : () => [number, ...string[]] +> : ^^^^^^ + + +function rw() { +>rw : () => void +> : ^^^^^^^^^^ + + let i: number; +>i : number +> : ^^^^^^ + + function inside() { +>inside : () => void +> : ^^^^^^^^^^ + + i++; +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + console.log(i); // NaN +>console.log(i) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>i : number +> : ^^^^^^ + } + inside(); +>inside() : void +> : ^^^^ +>inside : () => void +> : ^^^^^^^^^^ +} +rw(); +>rw() : void +> : ^^^^ +>rw : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index c2c529c42e0e0..54cc51713a921 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -84,3 +84,24 @@ function f7() { } } +function f8() { + function ff() { + let _; + let rest: {}; // should not error + + [_, ...rest] = bar(); + } +} +declare function bar(): [number, ...string[]]; + + +function rw() { + let i: number; + function inside() { + i++; + console.log(i); // NaN + } + inside(); +} +rw(); + From 2942adec245b3a872f6fdbd3876bc5f44887db90 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 20 Jul 2024 00:30:22 +0800 Subject: [PATCH 07/22] fix compoud assignment --- src/compiler/checker.ts | 13 +++++++++++-- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 1 + .../reference/unusedLocalsInMethod4.errors.txt | 5 ++++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 767e0ba2aa4fe..dd5f111ad2fc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29283,6 +29283,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !isPastLastAssignment(symbol, /*location*/ undefined); } + // Check if a parameter or catch variable is assigned definitely + function isSymbolAssignedDefinitely(symbol: Symbol) { + return symbol.isDefinitelyAssigned || (isSymbolAssigned(symbol) && symbol.isDefinitelyAssigned); + } + // Return true if there are no assignments to the given symbol or if the given location // is past the last assignment to the symbol. function isPastLastAssignment(symbol: Symbol, location: Node | undefined) { @@ -29331,8 +29336,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function markNodeAssignments(node: Node) { switch (node.kind) { case SyntaxKind.Identifier: - if (isAssignmentTarget(node)) { + const assignmentKind = getAssignmentTargetKind(node); + if (assignmentKind !== AssignmentKind.None) { const symbol = getResolvedSymbol(node as Identifier); + if (assignmentKind === AssignmentKind.Definite) { + symbol.isDefinitelyAssigned = true; + } if (isParameterOrMutableLocalVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) { const referencingFunction = findAncestor(node, isFunctionOrSourceFile); const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); @@ -43188,7 +43197,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkUninitializedLocals(nodeWithLocals: HasLocals) { nodeWithLocals.locals!.forEach(local => { - if (!(local.flags & SymbolFlags.Variable) || isSymbolAssigned(local) || !local.isReferenced) { + if (!(local.flags & SymbolFlags.Variable) || !local.isReferenced || isSymbolAssignedDefinitely(local)) { return; } const declaration = local.valueDeclaration; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4994322657070..deba5377e43e2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5921,6 +5921,7 @@ export interface Symbol { /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @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. /** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol + /** @internal */ isDefinitelyAssigned?: boolean; // True if the symbol is assigned definitely /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d6703461cc937..ae629853bc0de 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8255,6 +8255,7 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.constEnumOnlyModule = undefined; this.isReferenced = undefined; this.lastAssignmentPos = undefined; + this.isDefinitelyAssigned = undefined; (this as any).links = undefined; // used by TransientSymbol } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index fb3465cb7901a..8ae4cf61e0b28 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -12,9 +12,10 @@ unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before bein unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. unusedLocalsInMethod4.ts(49,9): error TS2454: Variable 'x' is used before being assigned. unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being assigned. +unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being assigned. -==== unusedLocalsInMethod4.ts (14 errors) ==== +==== unusedLocalsInMethod4.ts (15 errors) ==== function f() { let x1: number[]; // should error ~~ @@ -140,6 +141,8 @@ unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being function rw() { let i: number; + ~ +!!! error TS2454: Variable 'i' is used before being assigned. function inside() { i++; console.log(i); // NaN From 23be105e9c3761dc31eed1f7b1c317dff2e772a8 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 20 Jul 2024 00:49:33 +0800 Subject: [PATCH 08/22] update error message --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 4 ++ .../typeGuardsAsAssertions.errors.txt | 8 ++-- .../unusedLocalsInMethod4.errors.txt | 44 +++++++++---------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd5f111ad2fc6..3d3f5b2df7a5f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -43215,7 +43215,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const type = getTypeFromTypeNode(declaration.type); if (!(type.flags & TypeFlags.AnyOrUnknown) && !containsUndefinedType(type)) { - error(declaration, Diagnostics.Variable_0_is_used_before_being_assigned, idText(declaration.name as Identifier)); + error(declaration, Diagnostics.Variable_0_is_used_but_never_initialized, idText(declaration.name as Identifier)); } } }); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index e74033719b5b8..3382672e2c1ad 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3911,6 +3911,10 @@ "category": "Error", "code": 2868 }, + "Variable '{0}' is used but never initialized.": { + "category": "Error", + "code": 2869 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt index 2da9e1dede086..c855377414345 100644 --- a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt +++ b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt @@ -1,5 +1,5 @@ -typeGuardsAsAssertions.ts(3,5): error TS2454: Variable 'cond' is used before being assigned. -typeGuardsAsAssertions.ts(123,9): error TS2454: Variable 'x' is used before being assigned. +typeGuardsAsAssertions.ts(3,5): error TS2869: Variable 'cond' is used but never initialized. +typeGuardsAsAssertions.ts(123,9): error TS2869: Variable 'x' is used but never initialized. ==== typeGuardsAsAssertions.ts (2 errors) ==== @@ -7,7 +7,7 @@ typeGuardsAsAssertions.ts(123,9): error TS2454: Variable 'x' is used before bein let cond: boolean; ~~~~ -!!! error TS2454: Variable 'cond' is used before being assigned. +!!! error TS2869: Variable 'cond' is used but never initialized. export type Optional = Some | None; @@ -129,7 +129,7 @@ typeGuardsAsAssertions.ts(123,9): error TS2454: Variable 'x' is used before bein function f7() { let x: string; ~ -!!! error TS2454: Variable 'x' is used before being assigned. +!!! error TS2869: Variable 'x' is used but never initialized. x!.slice(); } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 8ae4cf61e0b28..20dd7501791db 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -1,28 +1,28 @@ -unusedLocalsInMethod4.ts(2,9): error TS2454: Variable 'x1' is used before being assigned. -unusedLocalsInMethod4.ts(3,9): error TS2454: Variable 'x2' is used before being assigned. -unusedLocalsInMethod4.ts(9,9): error TS2454: Variable 'x8' is used before being assigned. -unusedLocalsInMethod4.ts(10,9): error TS2454: Variable 'x9' is used before being assigned. -unusedLocalsInMethod4.ts(27,9): error TS2454: Variable 'x1' is used before being assigned. -unusedLocalsInMethod4.ts(28,9): error TS2454: Variable 'x2' is used before being assigned. -unusedLocalsInMethod4.ts(34,9): error TS2454: Variable 'x8' is used before being assigned. -unusedLocalsInMethod4.ts(35,9): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(2,9): error TS2869: Variable 'x1' is used but never initialized. +unusedLocalsInMethod4.ts(3,9): error TS2869: Variable 'x2' is used but never initialized. +unusedLocalsInMethod4.ts(9,9): error TS2869: Variable 'x8' is used but never initialized. +unusedLocalsInMethod4.ts(10,9): error TS2869: Variable 'x9' is used but never initialized. +unusedLocalsInMethod4.ts(27,9): error TS2869: Variable 'x1' is used but never initialized. +unusedLocalsInMethod4.ts(28,9): error TS2869: Variable 'x2' is used but never initialized. +unusedLocalsInMethod4.ts(34,9): error TS2869: Variable 'x8' is used but never initialized. +unusedLocalsInMethod4.ts(35,9): error TS2869: Variable 'x9' is used but never initialized. unusedLocalsInMethod4.ts(37,17): error TS2454: Variable 'x1' is used before being assigned. unusedLocalsInMethod4.ts(38,17): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before being assigned. unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. -unusedLocalsInMethod4.ts(49,9): error TS2454: Variable 'x' is used before being assigned. -unusedLocalsInMethod4.ts(57,9): error TS2454: Variable 'x' is used before being assigned. -unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being assigned. +unusedLocalsInMethod4.ts(49,9): error TS2869: Variable 'x' is used but never initialized. +unusedLocalsInMethod4.ts(57,9): error TS2869: Variable 'x' is used but never initialized. +unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never initialized. ==== unusedLocalsInMethod4.ts (15 errors) ==== function f() { let x1: number[]; // should error ~~ -!!! error TS2454: Variable 'x1' is used before being assigned. +!!! error TS2869: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error ~~ -!!! error TS2454: Variable 'x2' is used before being assigned. +!!! error TS2869: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error @@ -30,10 +30,10 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being let x7: unknown; // should not error let x8: T; // should error ~~ -!!! error TS2454: Variable 'x8' is used before being assigned. +!!! error TS2869: Variable 'x8' is used but never initialized. let x9: NonNull; // should error ~~ -!!! error TS2454: Variable 'x9' is used before being assigned. +!!! error TS2869: Variable 'x9' is used but never initialized. function foo() { console.log(x1); @@ -52,10 +52,10 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being function f2() { let x1: number[]; // should error ~~ -!!! error TS2454: Variable 'x1' is used before being assigned. +!!! error TS2869: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error ~~ -!!! error TS2454: Variable 'x2' is used before being assigned. +!!! error TS2869: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error @@ -63,10 +63,10 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being let x7: unknown; // should not error let x8: T; // should error ~~ -!!! error TS2454: Variable 'x8' is used before being assigned. +!!! error TS2869: Variable 'x8' is used but never initialized. let x9: NonNull; // should error ~~ -!!! error TS2454: Variable 'x9' is used before being assigned. +!!! error TS2869: Variable 'x9' is used but never initialized. console.log(x1); ~~ @@ -90,7 +90,7 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being function f3() { let x: number[]; // should error ~ -!!! error TS2454: Variable 'x' is used before being assigned. +!!! error TS2869: Variable 'x' is used but never initialized. function foo() { x.toString(); } @@ -100,7 +100,7 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being function f4() { let x: number; // should error ~ -!!! error TS2454: Variable 'x' is used before being assigned. +!!! error TS2869: Variable 'x' is used but never initialized. return { foo() { return x.toString(); @@ -142,7 +142,7 @@ unusedLocalsInMethod4.ts(97,9): error TS2454: Variable 'i' is used before being function rw() { let i: number; ~ -!!! error TS2454: Variable 'i' is used before being assigned. +!!! error TS2869: Variable 'i' is used but never initialized. function inside() { i++; console.log(i); // NaN From d32f69674070ca06ec027d9bf41664895996514c Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 20 Jul 2024 20:52:03 +0800 Subject: [PATCH 09/22] update baseline --- tests/baselines/reference/unusedLocalsInMethod4.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index 938b3c78fdf5e..f7aa120041a1f 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -3,7 +3,7 @@ === unusedLocalsInMethod4.ts === function f() { >f : () => void -> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ +> : ^ ^^ ^^^^^^^^^ ^^^^^^^^^^^ let x1: number[]; // should error >x1 : number[] @@ -162,7 +162,7 @@ function f() { function f2() { >f2 : () => void -> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ +> : ^ ^^ ^^^^^^^^^ ^^^^^^^^^^^ let x1: number[]; // should error >x1 : number[] From b3edbf01be2a9e9cdc7cdda02626fff71c0efad5 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 21 Jul 2024 14:32:07 +0800 Subject: [PATCH 10/22] ignore var declarations --- src/compiler/checker.ts | 41 +++++++++---------- .../unusedLocalsInMethod4.errors.txt | 10 ++++- .../reference/unusedLocalsInMethod4.js | 17 +++++++- .../reference/unusedLocalsInMethod4.symbols | 19 +++++++++ .../reference/unusedLocalsInMethod4.types | 32 +++++++++++++++ tests/cases/compiler/unusedLocalsInMethod4.ts | 9 ++++ 6 files changed, 105 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 001fd70cfe7e9..4d8c432c320de 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29557,13 +29557,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind !== AssignmentKind.None) { const symbol = getResolvedSymbol(node as Identifier); - if (assignmentKind === AssignmentKind.Definite) { - symbol.isDefinitelyAssigned = true; - } - if (isParameterOrMutableLocalVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) { - const referencingFunction = findAncestor(node, isFunctionOrSourceFile); - const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); - symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; + if (isParameterOrMutableLocalVariable(symbol)) { + if (assignmentKind === AssignmentKind.Definite) { + symbol.isDefinitelyAssigned = true; + } + + if (symbol.lastAssignmentPos !== Number.MAX_VALUE) { + const referencingFunction = findAncestor(node, isFunctionOrSourceFile); + const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); + symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; + } } } return; @@ -43494,22 +43497,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } const declaration = local.valueDeclaration; - if (!declaration) { + if ( + !declaration || !isVariableDeclaration(declaration) || !(getCombinedNodeFlagsCached(declaration) & NodeFlags.BlockScoped) || + getCombinedNodeFlagsCached(declaration) & NodeFlags.Ambient || + declaration.exclamationToken || + declaration.initializer || + !declaration.type + ) { return; } - if (isVariableDeclaration(declaration)) { - if ( - getCombinedNodeFlagsCached(declaration) & NodeFlags.Ambient || - declaration.exclamationToken || - declaration.initializer || - !declaration.type - ) { - return; - } - const type = getTypeFromTypeNode(declaration.type); - if (!(type.flags & TypeFlags.AnyOrUnknown) && !containsUndefinedType(type)) { - error(declaration, Diagnostics.Variable_0_is_used_but_never_initialized, idText(declaration.name as Identifier)); - } + const type = getTypeFromTypeNode(declaration.type); + if (!(type.flags & TypeFlags.AnyOrUnknown) && !containsUndefinedType(type)) { + error(declaration, Diagnostics.Variable_0_is_used_but_never_initialized, idText(declaration.name as Identifier)); } }); } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 20dd7501791db..1cd20cdc2524d 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -151,4 +151,12 @@ unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never ini } rw(); - \ No newline at end of file + function createBinder() { + var file: string; + + function bindSourceFile(f: string) { + file = f; + + file.toString(); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index 308f8a0fe44d0..922539c26bf87 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -106,7 +106,15 @@ function rw() { } rw(); - +function createBinder() { + var file: string; + + function bindSourceFile(f: string) { + file = f; + + file.toString(); + } +} //// [unusedLocalsInMethod4.js] "use strict"; @@ -203,3 +211,10 @@ function rw() { inside(); } rw(); +function createBinder() { + var file; + function bindSourceFile(f) { + file = f; + file.toString(); + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index e2f329234e06c..c852171c51fe1 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -314,4 +314,23 @@ function rw() { rw(); >rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) +function createBinder() { +>createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 103, 5)) + var file: string; +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) + + function bindSourceFile(f: string) { +>bindSourceFile : Symbol(bindSourceFile, Decl(unusedLocalsInMethod4.ts, 106, 21)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 108, 28)) + + file = f; +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 108, 28)) + + file.toString(); +>file.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) +>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index f7aa120041a1f..ec101994a41a5 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -525,4 +525,36 @@ rw(); >rw : () => void > : ^^^^^^^^^^ +function createBinder() { +>createBinder : () => void +> : ^^^^^^^^^^ + + var file: string; +>file : string +> : ^^^^^^ + + function bindSourceFile(f: string) { +>bindSourceFile : (f: string) => void +> : ^ ^^ ^^^^^^^^^ +>f : string +> : ^^^^^^ + + file = f; +>file = f : string +> : ^^^^^^ +>file : string +> : ^^^^^^ +>f : string +> : ^^^^^^ + file.toString(); +>file.toString() : string +> : ^^^^^^ +>file.toString : () => string +> : ^^^^^^ +>file : string +> : ^^^^^^ +>toString : () => string +> : ^^^^^^ + } +} diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index 54cc51713a921..0a77ef0577bb9 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -105,3 +105,12 @@ function rw() { } rw(); +function createBinder() { + var file: string; + + function bindSourceFile(f: string) { + file = f; + + file.toString(); + } +} \ No newline at end of file From 775670c990da9c1f9e55a5e2d7b61f2174c1c8a9 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 21 Jul 2024 15:16:37 +0800 Subject: [PATCH 11/22] fix self check --- src/compiler/transformers/classFields.ts | 2 +- src/compiler/transformers/es2015.ts | 2 +- src/compiler/transformers/es2017.ts | 2 +- src/compiler/transformers/es2018.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- .../unusedLocalsInMethod4.errors.txt | 24 ++++++- .../reference/unusedLocalsInMethod4.js | 35 ++++++++-- .../reference/unusedLocalsInMethod4.symbols | 37 ++++++++++- .../reference/unusedLocalsInMethod4.types | 65 ++++++++++++++++++- tests/cases/compiler/unusedLocalsInMethod4.ts | 19 +++++- 10 files changed, 172 insertions(+), 18 deletions(-) diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 7ddafb36f7ea6..1c8b9fa60b868 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -401,7 +401,7 @@ export function transformClassFields(context: TransformationContext): (x: Source context.onEmitNode = onEmitNode; let shouldTransformPrivateStaticElementsInFile = false; - let enabledSubstitutions: ClassPropertySubstitutionFlags; + let enabledSubstitutions!: ClassPropertySubstitutionFlags; let classAliases: Identifier[]; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 6973b1aef26e0..4986da6ee20c4 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -523,7 +523,7 @@ export function transformES2015(context: TransformationContext): (x: SourceFile * They are persisted between each SourceFile transformation and should not * be reset. */ - let enabledSubstitutions: ES2015SubstitutionFlags; + let enabledSubstitutions!: ES2015SubstitutionFlags; return chainBundle(context, transformSourceFile); diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index e1f0c9756b242..501feb0bc82c1 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -132,7 +132,7 @@ export function transformES2017(context: TransformationContext): (x: SourceFile * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. */ - let enabledSubstitutions: ES2017SubstitutionFlags; + let enabledSubstitutions!: ES2017SubstitutionFlags; /** * This keeps track of containers where `super` is valid, for use with diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 4eff61428d9fd..a4c079b400009 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -170,7 +170,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile context.onSubstituteNode = onSubstituteNode; let exportedVariableStatement = false; - let enabledSubstitutions: ESNextSubstitutionFlags; + let enabledSubstitutions!: ESNextSubstitutionFlags; let enclosingFunctionFlags: FunctionFlags; let parametersWithPrecedingObjectRestOrSpread: Set | undefined; let enclosingSuperContainerFlags: NodeCheckFlags = 0; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 668da90d8e696..b3844e154b540 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -272,7 +272,7 @@ export function transformTypeScript(context: TransformationContext) { * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. */ - let enabledSubstitutions: TypeScriptSubstitutionFlags; + let enabledSubstitutions!: TypeScriptSubstitutionFlags; /** * Keeps track of whether we are within any containing namespaces when performing diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 1cd20cdc2524d..4719b192fdd67 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -13,9 +13,10 @@ unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before bein unusedLocalsInMethod4.ts(49,9): error TS2869: Variable 'x' is used but never initialized. unusedLocalsInMethod4.ts(57,9): error TS2869: Variable 'x' is used but never initialized. unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never initialized. +unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' is used but never initialized. -==== unusedLocalsInMethod4.ts (15 errors) ==== +==== unusedLocalsInMethod4.ts (16 errors) ==== function f() { let x1: number[]; // should error ~~ @@ -140,7 +141,7 @@ unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never ini function rw() { - let i: number; + let i: number; // should error ~ !!! error TS2869: Variable 'i' is used but never initialized. function inside() { @@ -152,11 +153,28 @@ unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never ini rw(); function createBinder() { - var file: string; + var file: string; // should not error function bindSourceFile(f: string) { file = f; file.toString(); } + } + + function transformClassFields() { + enum ClassPropertySubstitutionFlags { + ClassAliases = 1 << 0, + ClassStaticThisOrSuperReference = 1 << 1, + } + + let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2869: Variable 'enabledSubstitutions' is used but never initialized. + + function enableSubstitutionForClassAliases() { + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; + + enabledSubstitutions.toString(); + } } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index 922539c26bf87..03bcb3d6aab0a 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -97,7 +97,7 @@ declare function bar(): [number, ...string[]]; function rw() { - let i: number; + let i: number; // should error function inside() { i++; console.log(i); // NaN @@ -107,13 +107,28 @@ function rw() { rw(); function createBinder() { - var file: string; + var file: string; // should not error function bindSourceFile(f: string) { file = f; file.toString(); } +} + +function transformClassFields() { + enum ClassPropertySubstitutionFlags { + ClassAliases = 1 << 0, + ClassStaticThisOrSuperReference = 1 << 1, + } + + let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error + + function enableSubstitutionForClassAliases() { + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; + + enabledSubstitutions.toString(); + } } //// [unusedLocalsInMethod4.js] @@ -203,7 +218,7 @@ function f8() { } } function rw() { - var i; + var i; // should error function inside() { i++; console.log(i); // NaN @@ -212,9 +227,21 @@ function rw() { } rw(); function createBinder() { - var file; + var file; // should not error function bindSourceFile(f) { file = f; file.toString(); } } +function transformClassFields() { + var ClassPropertySubstitutionFlags; + (function (ClassPropertySubstitutionFlags) { + ClassPropertySubstitutionFlags[ClassPropertySubstitutionFlags["ClassAliases"] = 1] = "ClassAliases"; + ClassPropertySubstitutionFlags[ClassPropertySubstitutionFlags["ClassStaticThisOrSuperReference"] = 2] = "ClassStaticThisOrSuperReference"; + })(ClassPropertySubstitutionFlags || (ClassPropertySubstitutionFlags = {})); + var enabledSubstitutions; // should error + function enableSubstitutionForClassAliases() { + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; + enabledSubstitutions.toString(); + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index c852171c51fe1..13633164d8f44 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -293,7 +293,7 @@ declare function bar(): [number, ...string[]]; function rw() { >rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) - let i: number; + let i: number; // should error >i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) function inside() { @@ -317,7 +317,7 @@ rw(); function createBinder() { >createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 103, 5)) - var file: string; + var file: string; // should not error >file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) function bindSourceFile(f: string) { @@ -334,3 +334,36 @@ function createBinder() { >toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) } } + +function transformClassFields() { +>transformClassFields : Symbol(transformClassFields, Decl(unusedLocalsInMethod4.ts, 113, 1)) + + enum ClassPropertySubstitutionFlags { +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) + + ClassAliases = 1 << 0, +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) + + ClassStaticThisOrSuperReference = 1 << 1, +>ClassStaticThisOrSuperReference : Symbol(ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference, Decl(unusedLocalsInMethod4.ts, 117, 30)) + } + + let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) + + function enableSubstitutionForClassAliases() { +>enableSubstitutionForClassAliases : Symbol(enableSubstitutionForClassAliases, Decl(unusedLocalsInMethod4.ts, 121, 61)) + + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) +>ClassPropertySubstitutionFlags.ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) + + enabledSubstitutions.toString(); +>enabledSubstitutions.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index ec101994a41a5..025bbbc4f2f59 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -487,7 +487,7 @@ function rw() { >rw : () => void > : ^^^^^^^^^^ - let i: number; + let i: number; // should error >i : number > : ^^^^^^ @@ -529,7 +529,7 @@ function createBinder() { >createBinder : () => void > : ^^^^^^^^^^ - var file: string; + var file: string; // should not error >file : string > : ^^^^^^ @@ -558,3 +558,64 @@ function createBinder() { > : ^^^^^^ } } + +function transformClassFields() { +>transformClassFields : () => void +> : ^^^^^^^^^^ + + enum ClassPropertySubstitutionFlags { +>ClassPropertySubstitutionFlags : ClassPropertySubstitutionFlags +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + ClassAliases = 1 << 0, +>ClassAliases : ClassPropertySubstitutionFlags.ClassAliases +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>1 << 0 : number +> : ^^^^^^ +>1 : 1 +> : ^ +>0 : 0 +> : ^ + + ClassStaticThisOrSuperReference = 1 << 1, +>ClassStaticThisOrSuperReference : ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>1 << 1 : number +> : ^^^^^^ +>1 : 1 +> : ^ +>1 : 1 +> : ^ + } + + let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error +>enabledSubstitutions : ClassPropertySubstitutionFlags +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + function enableSubstitutionForClassAliases() { +>enableSubstitutionForClassAliases : () => void +> : ^^^^^^^^^^ + + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; +>enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases : number +> : ^^^^^^ +>enabledSubstitutions : ClassPropertySubstitutionFlags +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ClassPropertySubstitutionFlags.ClassAliases : ClassPropertySubstitutionFlags.ClassAliases +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ClassPropertySubstitutionFlags : typeof ClassPropertySubstitutionFlags +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ClassAliases : ClassPropertySubstitutionFlags.ClassAliases +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + enabledSubstitutions.toString(); +>enabledSubstitutions.toString() : string +> : ^^^^^^ +>enabledSubstitutions.toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ +>enabledSubstitutions : ClassPropertySubstitutionFlags +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>toString : (radix?: number) => string +> : ^ ^^^ ^^^^^ + } +} diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index 0a77ef0577bb9..c5eb75a248e30 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -96,7 +96,7 @@ declare function bar(): [number, ...string[]]; function rw() { - let i: number; + let i: number; // should error function inside() { i++; console.log(i); // NaN @@ -106,11 +106,26 @@ function rw() { rw(); function createBinder() { - var file: string; + var file: string; // should not error function bindSourceFile(f: string) { file = f; file.toString(); } +} + +function transformClassFields() { + enum ClassPropertySubstitutionFlags { + ClassAliases = 1 << 0, + ClassStaticThisOrSuperReference = 1 << 1, + } + + let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error + + function enableSubstitutionForClassAliases() { + enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; + + enabledSubstitutions.toString(); + } } \ No newline at end of file From 158d0f3e3e2a84e11672f579b2f425a29ccaec22 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Tue, 23 Jul 2024 09:34:26 +0800 Subject: [PATCH 12/22] cache isDefinitelyAssigned --- src/compiler/checker.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4d8c432c320de..5e4abdf89f4b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29503,7 +29503,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Check if a parameter or catch variable is assigned definitely function isSymbolAssignedDefinitely(symbol: Symbol) { - return symbol.isDefinitelyAssigned || (isSymbolAssigned(symbol) && symbol.isDefinitelyAssigned); + return symbol.isDefinitelyAssigned ?? (isSymbolAssigned(symbol) && symbol.isDefinitelyAssigned); } // Return true if there are no assignments to the given symbol or if the given location @@ -29518,6 +29518,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.flags |= NodeCheckFlags.AssignmentsMarked; if (!hasParentWithAssignmentsMarked(parent)) { markNodeAssignments(parent); + symbol.isDefinitelyAssigned ??= false; } } return !symbol.lastAssignmentPos || location && symbol.lastAssignmentPos < location.pos; @@ -43221,7 +43222,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: if (checkUnused) { - if (node.body) { // Don't report unused parameters in overloads + // Only report unused parameters on the implementation, not overloads. + if (node.body) { checkUnusedLocalsAndParameters(node, addDiagnostic); } checkUnusedTypeParameters(node, addDiagnostic); From 66f2210baa88380f431df5108139a3e7453b1344 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Tue, 23 Jul 2024 09:40:33 +0800 Subject: [PATCH 13/22] update error code --- .../typeGuardsAsAssertions.errors.txt | 8 ++-- .../unusedLocalsInMethod4.errors.txt | 48 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt index c855377414345..08293652f464e 100644 --- a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt +++ b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt @@ -1,5 +1,5 @@ -typeGuardsAsAssertions.ts(3,5): error TS2869: Variable 'cond' is used but never initialized. -typeGuardsAsAssertions.ts(123,9): error TS2869: Variable 'x' is used but never initialized. +typeGuardsAsAssertions.ts(3,5): error TS2874: Variable 'cond' is used but never initialized. +typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never initialized. ==== typeGuardsAsAssertions.ts (2 errors) ==== @@ -7,7 +7,7 @@ typeGuardsAsAssertions.ts(123,9): error TS2869: Variable 'x' is used but never i let cond: boolean; ~~~~ -!!! error TS2869: Variable 'cond' is used but never initialized. +!!! error TS2874: Variable 'cond' is used but never initialized. export type Optional = Some | None; @@ -129,7 +129,7 @@ typeGuardsAsAssertions.ts(123,9): error TS2869: Variable 'x' is used but never i function f7() { let x: string; ~ -!!! error TS2869: Variable 'x' is used but never initialized. +!!! error TS2874: Variable 'x' is used but never initialized. x!.slice(); } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 4719b192fdd67..1e94c9f895e5e 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -1,29 +1,29 @@ -unusedLocalsInMethod4.ts(2,9): error TS2869: Variable 'x1' is used but never initialized. -unusedLocalsInMethod4.ts(3,9): error TS2869: Variable 'x2' is used but never initialized. -unusedLocalsInMethod4.ts(9,9): error TS2869: Variable 'x8' is used but never initialized. -unusedLocalsInMethod4.ts(10,9): error TS2869: Variable 'x9' is used but never initialized. -unusedLocalsInMethod4.ts(27,9): error TS2869: Variable 'x1' is used but never initialized. -unusedLocalsInMethod4.ts(28,9): error TS2869: Variable 'x2' is used but never initialized. -unusedLocalsInMethod4.ts(34,9): error TS2869: Variable 'x8' is used but never initialized. -unusedLocalsInMethod4.ts(35,9): error TS2869: Variable 'x9' is used but never initialized. +unusedLocalsInMethod4.ts(2,9): error TS2874: Variable 'x1' is used but never initialized. +unusedLocalsInMethod4.ts(3,9): error TS2874: Variable 'x2' is used but never initialized. +unusedLocalsInMethod4.ts(9,9): error TS2874: Variable 'x8' is used but never initialized. +unusedLocalsInMethod4.ts(10,9): error TS2874: Variable 'x9' is used but never initialized. +unusedLocalsInMethod4.ts(27,9): error TS2874: Variable 'x1' is used but never initialized. +unusedLocalsInMethod4.ts(28,9): error TS2874: Variable 'x2' is used but never initialized. +unusedLocalsInMethod4.ts(34,9): error TS2874: Variable 'x8' is used but never initialized. +unusedLocalsInMethod4.ts(35,9): error TS2874: Variable 'x9' is used but never initialized. unusedLocalsInMethod4.ts(37,17): error TS2454: Variable 'x1' is used before being assigned. unusedLocalsInMethod4.ts(38,17): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before being assigned. unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. -unusedLocalsInMethod4.ts(49,9): error TS2869: Variable 'x' is used but never initialized. -unusedLocalsInMethod4.ts(57,9): error TS2869: Variable 'x' is used but never initialized. -unusedLocalsInMethod4.ts(97,9): error TS2869: Variable 'i' is used but never initialized. -unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' is used but never initialized. +unusedLocalsInMethod4.ts(49,9): error TS2874: Variable 'x' is used but never initialized. +unusedLocalsInMethod4.ts(57,9): error TS2874: Variable 'x' is used but never initialized. +unusedLocalsInMethod4.ts(97,9): error TS2874: Variable 'i' is used but never initialized. +unusedLocalsInMethod4.ts(122,9): error TS2874: Variable 'enabledSubstitutions' is used but never initialized. ==== unusedLocalsInMethod4.ts (16 errors) ==== function f() { let x1: number[]; // should error ~~ -!!! error TS2869: Variable 'x1' is used but never initialized. +!!! error TS2874: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error ~~ -!!! error TS2869: Variable 'x2' is used but never initialized. +!!! error TS2874: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error @@ -31,10 +31,10 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i let x7: unknown; // should not error let x8: T; // should error ~~ -!!! error TS2869: Variable 'x8' is used but never initialized. +!!! error TS2874: Variable 'x8' is used but never initialized. let x9: NonNull; // should error ~~ -!!! error TS2869: Variable 'x9' is used but never initialized. +!!! error TS2874: Variable 'x9' is used but never initialized. function foo() { console.log(x1); @@ -53,10 +53,10 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i function f2() { let x1: number[]; // should error ~~ -!!! error TS2869: Variable 'x1' is used but never initialized. +!!! error TS2874: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error ~~ -!!! error TS2869: Variable 'x2' is used but never initialized. +!!! error TS2874: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error @@ -64,10 +64,10 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i let x7: unknown; // should not error let x8: T; // should error ~~ -!!! error TS2869: Variable 'x8' is used but never initialized. +!!! error TS2874: Variable 'x8' is used but never initialized. let x9: NonNull; // should error ~~ -!!! error TS2869: Variable 'x9' is used but never initialized. +!!! error TS2874: Variable 'x9' is used but never initialized. console.log(x1); ~~ @@ -91,7 +91,7 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i function f3() { let x: number[]; // should error ~ -!!! error TS2869: Variable 'x' is used but never initialized. +!!! error TS2874: Variable 'x' is used but never initialized. function foo() { x.toString(); } @@ -101,7 +101,7 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i function f4() { let x: number; // should error ~ -!!! error TS2869: Variable 'x' is used but never initialized. +!!! error TS2874: Variable 'x' is used but never initialized. return { foo() { return x.toString(); @@ -143,7 +143,7 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i function rw() { let i: number; // should error ~ -!!! error TS2869: Variable 'i' is used but never initialized. +!!! error TS2874: Variable 'i' is used but never initialized. function inside() { i++; console.log(i); // NaN @@ -170,7 +170,7 @@ unusedLocalsInMethod4.ts(122,9): error TS2869: Variable 'enabledSubstitutions' i let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error ~~~~~~~~~~~~~~~~~~~~ -!!! error TS2869: Variable 'enabledSubstitutions' is used but never initialized. +!!! error TS2874: Variable 'enabledSubstitutions' is used but never initialized. function enableSubstitutionForClassAliases() { enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; From 44ce8762fdbbd39226c365aa19630144dbc810de Mon Sep 17 00:00:00 2001 From: Zzzen Date: Tue, 23 Jul 2024 09:43:43 +0800 Subject: [PATCH 14/22] format --- src/compiler/diagnosticMessages.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 73ef488c57d7d..b133b47604f24 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3934,7 +3934,8 @@ "This kind of expression is always falsy.": { "category": "Error", "code": 2873 - }, "Variable '{0}' is used but never initialized.": { + }, + "Variable '{0}' is used but never initialized.": { "category": "Error", "code": 2874 }, From f9a820703fd3715c4161cee4da0233df50ec04a2 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Fri, 26 Jul 2024 21:32:46 +0800 Subject: [PATCH 15/22] update comments and ignore constant variables --- src/compiler/checker.ts | 6 +- .../unusedLocalsInMethod4.errors.txt | 16 ++++- .../reference/unusedLocalsInMethod4.js | 13 ++++ .../reference/unusedLocalsInMethod4.symbols | 67 ++++++++++++------- .../reference/unusedLocalsInMethod4.types | 20 ++++++ tests/cases/compiler/unusedLocalsInMethod4.ts | 7 ++ 6 files changed, 97 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 056c1fc0c770c..d3f1ca7373f69 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29499,12 +29499,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { node.kind === SyntaxKind.PropertyDeclaration)!; } - // Check if a parameter or catch variable is assigned anywhere + // Check if a parameter, catch variable, or mutable local variable is assigned anywhere function isSymbolAssigned(symbol: Symbol) { return !isPastLastAssignment(symbol, /*location*/ undefined); } - // Check if a parameter or catch variable is assigned definitely + // Check if a parameter, catch variable, or mutable local variable is assigned anywhere definitely function isSymbolAssignedDefinitely(symbol: Symbol) { return symbol.isDefinitelyAssigned ?? (isSymbolAssigned(symbol) && symbol.isDefinitelyAssigned); } @@ -43558,7 +43558,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const declaration = local.valueDeclaration; if ( - !declaration || !isVariableDeclaration(declaration) || !(getCombinedNodeFlagsCached(declaration) & NodeFlags.BlockScoped) || + !declaration || !isVariableDeclaration(declaration) || !(getCombinedNodeFlagsCached(declaration) & NodeFlags.Let) || getCombinedNodeFlagsCached(declaration) & NodeFlags.Ambient || declaration.exclamationToken || declaration.initializer || diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 1e94c9f895e5e..5acbc8b7d61ff 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -12,11 +12,12 @@ unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before bein unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. unusedLocalsInMethod4.ts(49,9): error TS2874: Variable 'x' is used but never initialized. unusedLocalsInMethod4.ts(57,9): error TS2874: Variable 'x' is used but never initialized. -unusedLocalsInMethod4.ts(97,9): error TS2874: Variable 'i' is used but never initialized. -unusedLocalsInMethod4.ts(122,9): error TS2874: Variable 'enabledSubstitutions' is used but never initialized. +unusedLocalsInMethod4.ts(96,11): error TS1155: 'const' declarations must be initialized. +unusedLocalsInMethod4.ts(104,9): error TS2874: Variable 'i' is used but never initialized. +unusedLocalsInMethod4.ts(129,9): error TS2874: Variable 'enabledSubstitutions' is used but never initialized. -==== unusedLocalsInMethod4.ts (16 errors) ==== +==== unusedLocalsInMethod4.ts (17 errors) ==== function f() { let x1: number[]; // should error ~~ @@ -139,6 +140,15 @@ unusedLocalsInMethod4.ts(122,9): error TS2874: Variable 'enabledSubstitutions' i } declare function bar(): [number, ...string[]]; + function f9() { + const x: number; // should have only one error + ~ +!!! error TS1155: 'const' declarations must be initialized. + function bar() { + let y = x; + } + } + function rw() { let i: number; // should error diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index 03bcb3d6aab0a..81278939e409e 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -95,6 +95,13 @@ function f8() { } declare function bar(): [number, ...string[]]; +function f9() { + const x: number; // should have only one error + function bar() { + let y = x; + } +} + function rw() { let i: number; // should error @@ -217,6 +224,12 @@ function f8() { _a = bar(), _ = _a[0], rest = _a.slice(1); } } +function f9() { + var x; // should have only one error + function bar() { + var y = x; + } +} function rw() { var i; // should error function inside() { diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index 13633164d8f44..88915041560a9 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -289,81 +289,96 @@ function f8() { declare function bar(): [number, ...string[]]; >bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 91, 1)) +function f9() { +>f9 : Symbol(f9, Decl(unusedLocalsInMethod4.ts, 92, 46)) + + const x: number; // should have only one error +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 95, 9)) + + function bar() { +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 95, 20)) + + let y = x; +>y : Symbol(y, Decl(unusedLocalsInMethod4.ts, 97, 11)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 95, 9)) + } +} + function rw() { ->rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 99, 1)) let i: number; // should error ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) function inside() { ->inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 96, 18)) +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 103, 18)) i++; ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) console.log(i); // NaN >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, --, --)) ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 96, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) } inside(); ->inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 96, 18)) +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 103, 18)) } rw(); ->rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 92, 46)) +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 99, 1)) function createBinder() { ->createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 103, 5)) +>createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 110, 5)) var file: string; // should not error ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) function bindSourceFile(f: string) { ->bindSourceFile : Symbol(bindSourceFile, Decl(unusedLocalsInMethod4.ts, 106, 21)) ->f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 108, 28)) +>bindSourceFile : Symbol(bindSourceFile, Decl(unusedLocalsInMethod4.ts, 113, 21)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 115, 28)) file = f; ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) ->f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 108, 28)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 115, 28)) file.toString(); >file.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 106, 7)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) >toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) } } function transformClassFields() { ->transformClassFields : Symbol(transformClassFields, Decl(unusedLocalsInMethod4.ts, 113, 1)) +>transformClassFields : Symbol(transformClassFields, Decl(unusedLocalsInMethod4.ts, 120, 1)) enum ClassPropertySubstitutionFlags { ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) ClassAliases = 1 << 0, ->ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) ClassStaticThisOrSuperReference = 1 << 1, ->ClassStaticThisOrSuperReference : Symbol(ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference, Decl(unusedLocalsInMethod4.ts, 117, 30)) +>ClassStaticThisOrSuperReference : Symbol(ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference, Decl(unusedLocalsInMethod4.ts, 124, 30)) } let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) function enableSubstitutionForClassAliases() { ->enableSubstitutionForClassAliases : Symbol(enableSubstitutionForClassAliases, Decl(unusedLocalsInMethod4.ts, 121, 61)) +>enableSubstitutionForClassAliases : Symbol(enableSubstitutionForClassAliases, Decl(unusedLocalsInMethod4.ts, 128, 61)) enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) ->ClassPropertySubstitutionFlags.ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 115, 33)) ->ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 116, 41)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) +>ClassPropertySubstitutionFlags.ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) enabledSubstitutions.toString(); >enabledSubstitutions.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 121, 7)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) >toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) } } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index 025bbbc4f2f59..90cf4642a2913 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -482,6 +482,26 @@ declare function bar(): [number, ...string[]]; >bar : () => [number, ...string[]] > : ^^^^^^ +function f9() { +>f9 : () => void +> : ^^^^^^^^^^ + + const x: number; // should have only one error +>x : number +> : ^^^^^^ + + function bar() { +>bar : () => void +> : ^^^^^^^^^^ + + let y = x; +>y : number +> : ^^^^^^ +>x : number +> : ^^^^^^ + } +} + function rw() { >rw : () => void diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index c5eb75a248e30..577a9ff240e36 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -94,6 +94,13 @@ function f8() { } declare function bar(): [number, ...string[]]; +function f9() { + const x: number; // should have only one error + function bar() { + let y = x; + } +} + function rw() { let i: number; // should error From 1aabd49a3345739e5236ed7c9195f747b8433553 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 27 Jul 2024 22:31:04 +0800 Subject: [PATCH 16/22] reset impl --- src/compiler/checker.ts | 152 +++++++++------------------ src/compiler/diagnosticMessages.json | 5 - src/compiler/types.ts | 1 - 3 files changed, 47 insertions(+), 111 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 23185c2826cb7..f18a0b2a32a23 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1893,11 +1893,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked)); diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName)); - checkUnusedOrUninitializedIdentifiers(getPotentiallyUnusedOrUninitializedIdentifiers(file), (containingNode, kind, diag) => { + checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => { if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion }); } - }, /*checkUnused*/ true); + }); return diagnostics || emptyArray; } @@ -2275,7 +2275,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var deferredGlobalClassAccessorDecoratorResultType: GenericType | undefined; var deferredGlobalClassFieldDecoratorContextType: GenericType | undefined; - var allPotentiallyUnusedOrUninitializedIdentifiers = new Map(); // key is file name + var allPotentiallyUnusedIdentifiers = new Map(); // key is file name var flowLoopStart = 0; var flowLoopCount = 0; @@ -29504,16 +29504,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { node.kind === SyntaxKind.PropertyDeclaration)!; } - // Check if a parameter, catch variable, or mutable local variable is assigned anywhere + // Check if a parameter or catch variable is assigned anywhere function isSymbolAssigned(symbol: Symbol) { return !isPastLastAssignment(symbol, /*location*/ undefined); } - // Check if a parameter, catch variable, or mutable local variable is assigned anywhere definitely - function isSymbolAssignedDefinitely(symbol: Symbol) { - return symbol.isDefinitelyAssigned ?? (isSymbolAssigned(symbol) && symbol.isDefinitelyAssigned); - } - // Return true if there are no assignments to the given symbol or if the given location // is past the last assignment to the symbol. function isPastLastAssignment(symbol: Symbol, location: Node | undefined) { @@ -29526,7 +29521,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.flags |= NodeCheckFlags.AssignmentsMarked; if (!hasParentWithAssignmentsMarked(parent)) { markNodeAssignments(parent); - symbol.isDefinitelyAssigned ??= false; } } return !symbol.lastAssignmentPos || location && symbol.lastAssignmentPos < location.pos; @@ -29563,19 +29557,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function markNodeAssignments(node: Node) { switch (node.kind) { case SyntaxKind.Identifier: - const assignmentKind = getAssignmentTargetKind(node); - if (assignmentKind !== AssignmentKind.None) { + if (isAssignmentTarget(node)) { const symbol = getResolvedSymbol(node as Identifier); - if (isParameterOrMutableLocalVariable(symbol)) { - if (assignmentKind === AssignmentKind.Definite) { - symbol.isDefinitelyAssigned = true; - } - - if (symbol.lastAssignmentPos !== Number.MAX_VALUE) { - const referencingFunction = findAncestor(node, isFunctionOrSourceFile); - const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); - symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; - } + if (isParameterOrMutableLocalVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) { + const referencingFunction = findAncestor(node, isFunctionOrSourceFile); + const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); + symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; } } return; @@ -41142,7 +41129,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.JSDocFunctionType) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } } @@ -41853,7 +41840,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } function checkTemplateLiteralType(node: TemplateLiteralTypeNode) { @@ -43234,34 +43221,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function registerForUnusedOrUninitializedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void { - addLazyDiagnostic(registerForUnusedOrUninitializedIdentifiersCheckDiagnostics); + function registerForUnusedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void { + addLazyDiagnostic(registerForUnusedIdentifiersCheckDiagnostics); - function registerForUnusedOrUninitializedIdentifiersCheckDiagnostics() { + function registerForUnusedIdentifiersCheckDiagnostics() { // May be in a call such as getTypeOfNode that happened to call this. But potentiallyUnusedIdentifiers is only defined in the scope of `checkSourceFile`. const sourceFile = getSourceFileOfNode(node); - let potentiallyUnusedOrUninitializedIdentifiers = allPotentiallyUnusedOrUninitializedIdentifiers.get(sourceFile.path); - if (!potentiallyUnusedOrUninitializedIdentifiers) { - potentiallyUnusedOrUninitializedIdentifiers = []; - allPotentiallyUnusedOrUninitializedIdentifiers.set(sourceFile.path, potentiallyUnusedOrUninitializedIdentifiers); + let potentiallyUnusedIdentifiers = allPotentiallyUnusedIdentifiers.get(sourceFile.path); + if (!potentiallyUnusedIdentifiers) { + potentiallyUnusedIdentifiers = []; + allPotentiallyUnusedIdentifiers.set(sourceFile.path, potentiallyUnusedIdentifiers); } // TODO: GH#22580 // Debug.assert(addToSeen(seenPotentiallyUnusedIdentifiers, getNodeId(node)), "Adding potentially-unused identifier twice"); - potentiallyUnusedOrUninitializedIdentifiers.push(node); + potentiallyUnusedIdentifiers.push(node); } } type PotentiallyUnusedIdentifier = SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration | Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement | Exclude | TypeAliasDeclaration | InferTypeNode; - function checkUnusedOrUninitializedIdentifiers(potentiallyUnusedIdentifiers: readonly PotentiallyUnusedIdentifier[], addDiagnostic: AddUnusedDiagnostic, checkUnused?: boolean, checkUninitialized?: boolean) { + function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: readonly PotentiallyUnusedIdentifier[], addDiagnostic: AddUnusedDiagnostic) { for (const node of potentiallyUnusedIdentifiers) { switch (node.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: - if (checkUnused) { - checkUnusedClassMembers(node, addDiagnostic); - checkUnusedTypeParameters(node, addDiagnostic); - } + checkUnusedClassMembers(node, addDiagnostic); + checkUnusedTypeParameters(node, addDiagnostic); break; case SyntaxKind.SourceFile: case SyntaxKind.ModuleDeclaration: @@ -43270,12 +43255,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: - if (checkUnused) { - checkUnusedLocalsAndParameters(node, addDiagnostic); - } - if (checkUninitialized) { - checkUninitializedLocals(node); - } + checkUnusedLocalsAndParameters(node, addDiagnostic); break; case SyntaxKind.Constructor: case SyntaxKind.FunctionExpression: @@ -43284,16 +43264,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - if (checkUnused) { - // Only report unused parameters on the implementation, not overloads. - if (node.body) { - checkUnusedLocalsAndParameters(node, addDiagnostic); - } - checkUnusedTypeParameters(node, addDiagnostic); - } - if (checkUninitialized) { - checkUninitializedLocals(node); + if (node.body) { // Don't report unused parameters in overloads + checkUnusedLocalsAndParameters(node, addDiagnostic); } + checkUnusedTypeParameters(node, addDiagnostic); break; case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: @@ -43302,14 +43276,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ConstructorType: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.InterfaceDeclaration: - if (checkUnused) { - checkUnusedTypeParameters(node, addDiagnostic); - } + checkUnusedTypeParameters(node, addDiagnostic); break; case SyntaxKind.InferType: - if (checkUnused) { - checkUnusedInferTypeParameter(node, addDiagnostic); - } + checkUnusedInferTypeParameter(node, addDiagnostic); break; default: Debug.assertNever(node, "Node should not have been registered for unused identifiers check"); @@ -43556,28 +43526,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); } - function checkUninitializedLocals(nodeWithLocals: HasLocals) { - nodeWithLocals.locals!.forEach(local => { - if (!(local.flags & SymbolFlags.Variable) || !local.isReferenced || isSymbolAssignedDefinitely(local)) { - return; - } - const declaration = local.valueDeclaration; - if ( - !declaration || !isVariableDeclaration(declaration) || !(getCombinedNodeFlagsCached(declaration) & NodeFlags.Let) || - getCombinedNodeFlagsCached(declaration) & NodeFlags.Ambient || - declaration.exclamationToken || - declaration.initializer || - !declaration.type - ) { - return; - } - const type = getTypeFromTypeNode(declaration.type); - if (!(type.flags & TypeFlags.AnyOrUnknown) && !containsUndefinedType(type)) { - error(declaration, Diagnostics.Variable_0_is_used_but_never_initialized, idText(declaration.name as Identifier)); - } - }); - } - function checkPotentialUncheckedRenamedBindingElementsInTypes() { for (const node of potentialUnusedRenamedBindingElementsInTypes) { if (!getSymbolOfDeclaration(node)?.isReferenced) { @@ -43630,7 +43578,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { forEach(node.statements, checkSourceElement); } if (node.locals) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -44408,7 +44356,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (node.incrementor) checkExpression(node.incrementor); checkSourceElement(node.statement); if (node.locals) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -44472,7 +44420,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkSourceElement(node.statement); if (node.locals) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -44524,7 +44472,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkSourceElement(node.statement); if (node.locals) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -45464,7 +45412,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } }); if (node.caseBlock.locals) { - registerForUnusedOrUninitializedIdentifiersCheck(node.caseBlock); + registerForUnusedIdentifiersCheck(node.caseBlock); } } @@ -45875,7 +45823,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkClassExpressionDeferred(node: ClassExpression) { forEach(node.members, checkSourceElement); - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } function checkClassDeclaration(node: ClassDeclaration) { @@ -45889,7 +45837,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkClassLikeDeclaration(node); forEach(node.members, checkSourceElement); - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } function checkClassLikeDeclaration(node: ClassLikeDeclaration) { @@ -46633,7 +46581,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addLazyDiagnostic(() => { checkTypeForDuplicateIndexSignatures(node); - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); }); } @@ -46653,7 +46601,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } else { checkSourceElement(node.type); - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -46912,7 +46860,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (node.body) { checkSourceElement(node.body); if (!isGlobalScopeAugmentation(node)) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } } @@ -48205,8 +48153,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getPotentiallyUnusedOrUninitializedIdentifiers(sourceFile: SourceFile): readonly PotentiallyUnusedIdentifier[] { - return allPotentiallyUnusedOrUninitializedIdentifiers.get(sourceFile.path) || emptyArray; + function getPotentiallyUnusedIdentifiers(sourceFile: SourceFile): readonly PotentiallyUnusedIdentifier[] { + return allPotentiallyUnusedIdentifiers.get(sourceFile.path) || emptyArray; } // Fully type check a source file and collect the relevant diagnostics. @@ -48240,23 +48188,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkDeferredNodes(node); if (isExternalOrCommonJsModule(node)) { - registerForUnusedOrUninitializedIdentifiersCheck(node); + registerForUnusedIdentifiersCheck(node); } addLazyDiagnostic(() => { // This relies on the results of other lazy diagnostics, so must be computed after them - const checkUnused = !node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters); - if (checkUnused || strictNullChecks) { - checkUnusedOrUninitializedIdentifiers( - getPotentiallyUnusedOrUninitializedIdentifiers(node), - (containingNode, kind, diag) => { - if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { - diagnostics.add(diag); - } - }, - checkUnused, - strictNullChecks, - ); + if (!node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters)) { + checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(node), (containingNode, kind, diag) => { + if (!containsParseError(containingNode) && unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { + diagnostics.add(diag); + } + }); } if (!node.isDeclarationFile) { checkPotentialUncheckedRenamedBindingElementsInTypes(); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 12f35e75ffb5a..850d5ca1022af 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3940,11 +3940,6 @@ "category": "Error", "code": 2873 }, - "Variable '{0}' is used but never initialized.": { - "category": "Error", - "code": 2874 - }, - "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6b028876fe6a9..564fa64e1294c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5945,7 +5945,6 @@ export interface Symbol { /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @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. /** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol - /** @internal */ isDefinitelyAssigned?: boolean; // True if the symbol is assigned definitely /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol } From 45c00119ea9723759765477af5d43baa8cf627b6 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 28 Jul 2024 22:48:18 +0800 Subject: [PATCH 17/22] refactor --- src/compiler/checker.ts | 39 +++- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 1 - .../typeGuardsAsAssertions.errors.txt | 17 +- .../unusedLocalsInMethod4.errors.txt | 87 ++++---- .../reference/unusedLocalsInMethod4.js | 16 +- .../reference/unusedLocalsInMethod4.symbols | 196 ++++++++++-------- .../reference/unusedLocalsInMethod4.types | 48 ++++- tests/cases/compiler/unusedLocalsInMethod4.ts | 9 +- 9 files changed, 265 insertions(+), 150 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f18a0b2a32a23..3df23fc9a900d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29504,7 +29504,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { node.kind === SyntaxKind.PropertyDeclaration)!; } - // Check if a parameter or catch variable is assigned anywhere + // Check if a parameter, catch variable, or mutable local variable is assigned anywhere definitely + function isSymbolAssignedDefinitely(symbol: Symbol) { + if (symbol.lastAssignmentPos !== undefined) { + return symbol.lastAssignmentPos < 0; + } + return isSymbolAssigned(symbol) && symbol.lastAssignmentPos !== undefined && symbol.lastAssignmentPos < 0; + } + + // Check if a parameter, catch variable, or mutable local variable is assigned anywhere function isSymbolAssigned(symbol: Symbol) { return !isPastLastAssignment(symbol, /*location*/ undefined); } @@ -29523,7 +29531,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { markNodeAssignments(parent); } } - return !symbol.lastAssignmentPos || location && symbol.lastAssignmentPos < location.pos; + return !symbol.lastAssignmentPos || location && Math.abs(symbol.lastAssignmentPos) < location.pos; } // Check if a parameter or catch variable (or their bindings elements) is assigned anywhere @@ -29557,12 +29565,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function markNodeAssignments(node: Node) { switch (node.kind) { case SyntaxKind.Identifier: - if (isAssignmentTarget(node)) { + const assigmentTarget = getAssignmentTargetKind(node); + if (assigmentTarget !== AssignmentKind.None) { const symbol = getResolvedSymbol(node as Identifier); - if (isParameterOrMutableLocalVariable(symbol) && symbol.lastAssignmentPos !== Number.MAX_VALUE) { - const referencingFunction = findAncestor(node, isFunctionOrSourceFile); - const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); - symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; + const isDefiniteAssignment = assigmentTarget === AssignmentKind.Definite || (symbol.lastAssignmentPos !== undefined && symbol.lastAssignmentPos < 0); + if (isParameterOrMutableLocalVariable(symbol)) { + if (symbol.lastAssignmentPos === undefined || Math.abs(symbol.lastAssignmentPos) !== Number.MAX_VALUE) { + const referencingFunction = findAncestor(node, isFunctionOrSourceFile); + const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); + symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; + } + if (isDefiniteAssignment && symbol.lastAssignmentPos > 0) { + symbol.lastAssignmentPos = -symbol.lastAssignmentPos; + } } } return; @@ -29572,7 +29587,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!(node as ExportSpecifier).isTypeOnly && !exportDeclaration.isTypeOnly && !exportDeclaration.moduleSpecifier && name.kind !== SyntaxKind.StringLiteral) { const symbol = resolveEntityName(name, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ true); if (symbol && isParameterOrMutableLocalVariable(symbol)) { - symbol.lastAssignmentPos = Number.MAX_VALUE; + const sign = symbol.lastAssignmentPos !== undefined && symbol.lastAssignmentPos < 0 ? -1 : 1; + symbol.lastAssignmentPos = sign * Number.MAX_VALUE; } } return; @@ -30316,6 +30332,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); let declaration = localOrExportSymbol.valueDeclaration; + const immediateDeclaration = declaration; // If the identifier is declared in a binding pattern for which we're currently computing the implied type and the // reference occurs with the same binding pattern, return the non-inferrable any type. This for example occurs in @@ -30402,10 +30419,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ) { flowContainer = getControlFlowContainer(flowContainer); } + const isMutableLocalVariableWithoutInitializer = immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(immediateDeclaration); // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). - const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || + const assumeInitialized = isParameter || isAlias || (isOuterVariable && !(isMutableLocalVariableWithoutInitializer && !isSymbolAssignedDefinitely(symbol))) || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 || isInTypeQuery(node) || isInAmbientOrTypeNode(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || @@ -43264,7 +43282,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - if (node.body) { // Don't report unused parameters in overloads + // Only report unused parameters on the implementation, not overloads. + if (node.body) { checkUnusedLocalsAndParameters(node, addDiagnostic); } checkUnusedTypeParameters(node, addDiagnostic); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 564fa64e1294c..fd7470ea1ca19 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5944,7 +5944,7 @@ export interface Symbol { /** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @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. - /** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol + /** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol. Negative if it is assigned anywhere definitely /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c6f0b4380bf6a..b7642d5c7c9b2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8271,7 +8271,6 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.constEnumOnlyModule = undefined; this.isReferenced = undefined; this.lastAssignmentPos = undefined; - this.isDefinitelyAssigned = undefined; (this as any).links = undefined; // used by TransientSymbol } diff --git a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt index 08293652f464e..dbe1d428c4bed 100644 --- a/tests/baselines/reference/typeGuardsAsAssertions.errors.txt +++ b/tests/baselines/reference/typeGuardsAsAssertions.errors.txt @@ -1,13 +1,12 @@ -typeGuardsAsAssertions.ts(3,5): error TS2874: Variable 'cond' is used but never initialized. -typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never initialized. +typeGuardsAsAssertions.ts(23,12): error TS2454: Variable 'cond' is used before being assigned. +typeGuardsAsAssertions.ts(33,12): error TS2454: Variable 'cond' is used before being assigned. +typeGuardsAsAssertions.ts(44,12): error TS2454: Variable 'cond' is used before being assigned. -==== typeGuardsAsAssertions.ts (2 errors) ==== +==== typeGuardsAsAssertions.ts (3 errors) ==== // Repro from #8513 let cond: boolean; - ~~~~ -!!! error TS2874: Variable 'cond' is used but never initialized. export type Optional = Some | None; @@ -28,6 +27,8 @@ typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never i let result: Optional = none; result; // None while (cond) { + ~~~~ +!!! error TS2454: Variable 'cond' is used before being assigned. result; // Some | None result = someFrom(isSome(result) ? result.some : makeSome()); result; // Some @@ -38,6 +39,8 @@ typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never i let x: string | number | boolean = 0; x; // number while (cond) { + ~~~~ +!!! error TS2454: Variable 'cond' is used before being assigned. x; // number, then string | number x = typeof x === "string" ? x.slice() : "abc"; x; // string @@ -49,6 +52,8 @@ typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never i let x: string | number | boolean = 0; x; // number while (cond) { + ~~~~ +!!! error TS2454: Variable 'cond' is used before being assigned. x; // number, then string | number if (typeof x === "string") { x = x.slice(); @@ -128,8 +133,6 @@ typeGuardsAsAssertions.ts(123,9): error TS2874: Variable 'x' is used but never i function f7() { let x: string; - ~ -!!! error TS2874: Variable 'x' is used but never initialized. x!.slice(); } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 5acbc8b7d61ff..9e3b9d814c8ba 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -1,74 +1,71 @@ -unusedLocalsInMethod4.ts(2,9): error TS2874: Variable 'x1' is used but never initialized. -unusedLocalsInMethod4.ts(3,9): error TS2874: Variable 'x2' is used but never initialized. -unusedLocalsInMethod4.ts(9,9): error TS2874: Variable 'x8' is used but never initialized. -unusedLocalsInMethod4.ts(10,9): error TS2874: Variable 'x9' is used but never initialized. -unusedLocalsInMethod4.ts(27,9): error TS2874: Variable 'x1' is used but never initialized. -unusedLocalsInMethod4.ts(28,9): error TS2874: Variable 'x2' is used but never initialized. -unusedLocalsInMethod4.ts(34,9): error TS2874: Variable 'x8' is used but never initialized. -unusedLocalsInMethod4.ts(35,9): error TS2874: Variable 'x9' is used but never initialized. -unusedLocalsInMethod4.ts(37,17): error TS2454: Variable 'x1' is used before being assigned. -unusedLocalsInMethod4.ts(38,17): error TS2454: Variable 'x2' is used before being assigned. -unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x8' is used before being assigned. -unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x9' is used before being assigned. -unusedLocalsInMethod4.ts(49,9): error TS2874: Variable 'x' is used but never initialized. -unusedLocalsInMethod4.ts(57,9): error TS2874: Variable 'x' is used but never initialized. -unusedLocalsInMethod4.ts(96,11): error TS1155: 'const' declarations must be initialized. -unusedLocalsInMethod4.ts(104,9): error TS2874: Variable 'i' is used but never initialized. -unusedLocalsInMethod4.ts(129,9): error TS2874: Variable 'enabledSubstitutions' is used but never initialized. +unusedLocalsInMethod4.ts(15,21): error TS2454: Variable 'x1' is used before being assigned. +unusedLocalsInMethod4.ts(16,21): error TS2454: Variable 'x2' is used before being assigned. +unusedLocalsInMethod4.ts(22,21): error TS2454: Variable 'x8' is used before being assigned. +unusedLocalsInMethod4.ts(23,21): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x1' is used before being assigned. +unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x2' is used before being assigned. +unusedLocalsInMethod4.ts(51,17): error TS2454: Variable 'x8' is used before being assigned. +unusedLocalsInMethod4.ts(52,17): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(58,9): error TS2454: Variable 'x' is used before being assigned. +unusedLocalsInMethod4.ts(67,20): error TS2454: Variable 'x' is used before being assigned. +unusedLocalsInMethod4.ts(103,11): error TS1155: 'const' declarations must be initialized. +unusedLocalsInMethod4.ts(113,9): error TS2454: Variable 'i' is used before being assigned. +unusedLocalsInMethod4.ts(114,21): error TS2454: Variable 'i' is used before being assigned. +unusedLocalsInMethod4.ts(139,9): error TS2454: Variable 'enabledSubstitutions' is used before being assigned. +unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' is used before being assigned. -==== unusedLocalsInMethod4.ts (17 errors) ==== +==== unusedLocalsInMethod4.ts (15 errors) ==== function f() { let x1: number[]; // should error - ~~ -!!! error TS2874: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error - ~~ -!!! error TS2874: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error let x6: any; // should not error let x7: unknown; // should not error let x8: T; // should error - ~~ -!!! error TS2874: Variable 'x8' is used but never initialized. let x9: NonNull; // should error - ~~ -!!! error TS2874: Variable 'x9' is used but never initialized. + var x10: NonNull; // should error + var x11: NonNull; // should not error function foo() { console.log(x1); + ~~ +!!! error TS2454: Variable 'x1' is used before being assigned. console.log(x2); + ~~ +!!! error TS2454: Variable 'x2' is used before being assigned. console.log(x3); console.log(x4); console.log(x5); console.log(x6); console.log(x7); console.log(x8); + ~~ +!!! error TS2454: Variable 'x8' is used before being assigned. console.log(x9); + ~~ +!!! error TS2454: Variable 'x9' is used before being assigned. + console.log(x10); + console.log(x11); + } + function bar() { + x11 = {} as any; } foo(); } function f2() { let x1: number[]; // should error - ~~ -!!! error TS2874: Variable 'x1' is used but never initialized. let x2: number[] | null; // should error - ~~ -!!! error TS2874: Variable 'x2' is used but never initialized. let x3: number[] | undefined; // should not error let x4: number[] | undefined | null; // should not error let x5!: number[]; // should not error let x6: any; // should not error let x7: unknown; // should not error let x8: T; // should error - ~~ -!!! error TS2874: Variable 'x8' is used but never initialized. let x9: NonNull; // should error - ~~ -!!! error TS2874: Variable 'x9' is used but never initialized. console.log(x1); ~~ @@ -91,26 +88,26 @@ unusedLocalsInMethod4.ts(129,9): error TS2874: Variable 'enabledSubstitutions' i function f3() { let x: number[]; // should error - ~ -!!! error TS2874: Variable 'x' is used but never initialized. function foo() { x.toString(); + ~ +!!! error TS2454: Variable 'x' is used before being assigned. } foo(); } function f4() { let x: number; // should error - ~ -!!! error TS2874: Variable 'x' is used but never initialized. return { foo() { return x.toString(); + ~ +!!! error TS2454: Variable 'x' is used before being assigned. } }; } - declare let x: number; // should error + declare let x: number; // should not error function f5() { x.toString(); } @@ -152,11 +149,13 @@ unusedLocalsInMethod4.ts(129,9): error TS2874: Variable 'enabledSubstitutions' i function rw() { let i: number; // should error - ~ -!!! error TS2874: Variable 'i' is used but never initialized. function inside() { i++; + ~ +!!! error TS2454: Variable 'i' is used before being assigned. console.log(i); // NaN + ~ +!!! error TS2454: Variable 'i' is used before being assigned. } inside(); } @@ -179,12 +178,14 @@ unusedLocalsInMethod4.ts(129,9): error TS2874: Variable 'enabledSubstitutions' i } let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error - ~~~~~~~~~~~~~~~~~~~~ -!!! error TS2874: Variable 'enabledSubstitutions' is used but never initialized. function enableSubstitutionForClassAliases() { enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2454: Variable 'enabledSubstitutions' is used before being assigned. enabledSubstitutions.toString(); + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2454: Variable 'enabledSubstitutions' is used before being assigned. } } \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index 81278939e409e..ea8ca0dd19334 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -11,6 +11,8 @@ function f() { let x7: unknown; // should not error let x8: T; // should error let x9: NonNull; // should error + var x10: NonNull; // should error + var x11: NonNull; // should not error function foo() { console.log(x1); @@ -22,6 +24,11 @@ function f() { console.log(x7); console.log(x8); console.log(x9); + console.log(x10); + console.log(x11); + } + function bar() { + x11 = {} as any; } foo(); } @@ -65,7 +72,7 @@ function f4() { }; } -declare let x: number; // should error +declare let x: number; // should not error function f5() { x.toString(); } @@ -151,6 +158,8 @@ function f() { var x7; // should not error var x8; // should error var x9; // should error + var x10; // should error + var x11; // should not error function foo() { console.log(x1); console.log(x2); @@ -161,6 +170,11 @@ function f() { console.log(x7); console.log(x8); console.log(x9); + console.log(x10); + console.log(x11); + } + function bar() { + x11 = {}; } foo(); } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index 88915041560a9..473a054fdb838 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -33,10 +33,18 @@ function f() { let x9: NonNull; // should error >x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 9, 7)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) + + var x10: NonNull; // should error +>x10 : Symbol(x10, Decl(unusedLocalsInMethod4.ts, 10, 7)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) + + var x11: NonNull; // should not error +>x11 : Symbol(x11, Decl(unusedLocalsInMethod4.ts, 11, 7)) >NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) function foo() { ->foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 9, 20)) +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 11, 21)) console.log(x1); >console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) @@ -91,294 +99,312 @@ function f() { >console : Symbol(console, Decl(lib.dom.d.ts, --, --)) >log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) >x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 9, 7)) + + console.log(x10); +>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, --, --)) +>x10 : Symbol(x10, Decl(unusedLocalsInMethod4.ts, 10, 7)) + + console.log(x11); +>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, --, --)) +>x11 : Symbol(x11, Decl(unusedLocalsInMethod4.ts, 11, 7)) + } + function bar() { +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 25, 5)) + + x11 = {} as any; +>x11 : Symbol(x11, Decl(unusedLocalsInMethod4.ts, 11, 7)) } foo(); ->foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 9, 20)) +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 11, 21)) } function f2() { ->f2 : Symbol(f2, Decl(unusedLocalsInMethod4.ts, 23, 1)) ->T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 25, 12)) ->NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 25, 14)) +>f2 : Symbol(f2, Decl(unusedLocalsInMethod4.ts, 30, 1)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 32, 12)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 32, 14)) let x1: number[]; // should error ->x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 26, 7)) +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 33, 7)) let x2: number[] | null; // should error ->x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 27, 7)) +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 34, 7)) let x3: number[] | undefined; // should not error ->x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 28, 7)) +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 35, 7)) let x4: number[] | undefined | null; // should not error ->x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 29, 7)) +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 36, 7)) let x5!: number[]; // should not error ->x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 30, 7)) +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 37, 7)) let x6: any; // should not error ->x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 31, 7)) +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 38, 7)) let x7: unknown; // should not error ->x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 32, 7)) +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 39, 7)) let x8: T; // should error ->x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 33, 7)) ->T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 25, 12)) +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 40, 7)) +>T : Symbol(T, Decl(unusedLocalsInMethod4.ts, 32, 12)) let x9: NonNull; // should error ->x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 34, 7)) ->NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 25, 14)) +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 41, 7)) +>NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 32, 14)) console.log(x1); >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, --, --)) ->x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 26, 7)) +>x1 : Symbol(x1, Decl(unusedLocalsInMethod4.ts, 33, 7)) console.log(x2); >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, --, --)) ->x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 27, 7)) +>x2 : Symbol(x2, Decl(unusedLocalsInMethod4.ts, 34, 7)) console.log(x3); >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, --, --)) ->x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 28, 7)) +>x3 : Symbol(x3, Decl(unusedLocalsInMethod4.ts, 35, 7)) console.log(x4); >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, --, --)) ->x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 29, 7)) +>x4 : Symbol(x4, Decl(unusedLocalsInMethod4.ts, 36, 7)) console.log(x5); >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, --, --)) ->x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 30, 7)) +>x5 : Symbol(x5, Decl(unusedLocalsInMethod4.ts, 37, 7)) console.log(x6); >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, --, --)) ->x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 31, 7)) +>x6 : Symbol(x6, Decl(unusedLocalsInMethod4.ts, 38, 7)) console.log(x7); >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, --, --)) ->x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 32, 7)) +>x7 : Symbol(x7, Decl(unusedLocalsInMethod4.ts, 39, 7)) console.log(x8); >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, --, --)) ->x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 33, 7)) +>x8 : Symbol(x8, Decl(unusedLocalsInMethod4.ts, 40, 7)) console.log(x9); >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, --, --)) ->x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 34, 7)) +>x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 41, 7)) } function f3() { ->f3 : Symbol(f3, Decl(unusedLocalsInMethod4.ts, 45, 1)) +>f3 : Symbol(f3, Decl(unusedLocalsInMethod4.ts, 52, 1)) let x: number[]; // should error ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 48, 7)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 55, 7)) function foo() { ->foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 48, 20)) +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 55, 20)) x.toString(); >x.toString : Symbol(Array.toString, Decl(lib.es5.d.ts, --, --)) ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 48, 7)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 55, 7)) >toString : Symbol(Array.toString, Decl(lib.es5.d.ts, --, --)) } foo(); ->foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 48, 20)) +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 55, 20)) } function f4() { ->f4 : Symbol(f4, Decl(unusedLocalsInMethod4.ts, 53, 1)) +>f4 : Symbol(f4, Decl(unusedLocalsInMethod4.ts, 60, 1)) let x: number; // should error ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 56, 7)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 63, 7)) return { foo() { ->foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 57, 12)) +>foo : Symbol(foo, Decl(unusedLocalsInMethod4.ts, 64, 12)) return x.toString(); >x.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 56, 7)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 63, 7)) >toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) } }; } -declare let x: number; // should error ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 64, 11)) +declare let x: number; // should not error +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 71, 11)) function f5() { ->f5 : Symbol(f5, Decl(unusedLocalsInMethod4.ts, 64, 22)) +>f5 : Symbol(f5, Decl(unusedLocalsInMethod4.ts, 71, 22)) x.toString(); >x.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 64, 11)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 71, 11)) >toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) } export default {}; function f6() { ->f6 : Symbol(f6, Decl(unusedLocalsInMethod4.ts, 68, 18)) +>f6 : Symbol(f6, Decl(unusedLocalsInMethod4.ts, 75, 18)) let key: string; // should not error ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) for (key in {}) { ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) console.log(key); >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, --, --)) ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 71, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) } } function f7() { ->f7 : Symbol(f7, Decl(unusedLocalsInMethod4.ts, 75, 1)) +>f7 : Symbol(f7, Decl(unusedLocalsInMethod4.ts, 82, 1)) let key: string; // should not error ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 85, 7)) for (key of []) { ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 85, 7)) console.log(key); >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, --, --)) ->key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 78, 7)) +>key : Symbol(key, Decl(unusedLocalsInMethod4.ts, 85, 7)) } } function f8() { ->f8 : Symbol(f8, Decl(unusedLocalsInMethod4.ts, 82, 1)) +>f8 : Symbol(f8, Decl(unusedLocalsInMethod4.ts, 89, 1)) function ff() { ->ff : Symbol(ff, Decl(unusedLocalsInMethod4.ts, 84, 15)) +>ff : Symbol(ff, Decl(unusedLocalsInMethod4.ts, 91, 15)) let _; ->_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 86, 11)) +>_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 93, 11)) let rest: {}; // should not error ->rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 87, 11)) +>rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 94, 11)) [_, ...rest] = bar(); ->_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 86, 11)) ->rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 87, 11)) ->bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 91, 1)) +>_ : Symbol(_, Decl(unusedLocalsInMethod4.ts, 93, 11)) +>rest : Symbol(rest, Decl(unusedLocalsInMethod4.ts, 94, 11)) +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 98, 1)) } } declare function bar(): [number, ...string[]]; ->bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 91, 1)) +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 98, 1)) function f9() { ->f9 : Symbol(f9, Decl(unusedLocalsInMethod4.ts, 92, 46)) +>f9 : Symbol(f9, Decl(unusedLocalsInMethod4.ts, 99, 46)) const x: number; // should have only one error ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 95, 9)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 102, 9)) function bar() { ->bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 95, 20)) +>bar : Symbol(bar, Decl(unusedLocalsInMethod4.ts, 102, 20)) let y = x; ->y : Symbol(y, Decl(unusedLocalsInMethod4.ts, 97, 11)) ->x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 95, 9)) +>y : Symbol(y, Decl(unusedLocalsInMethod4.ts, 104, 11)) +>x : Symbol(x, Decl(unusedLocalsInMethod4.ts, 102, 9)) } } function rw() { ->rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 99, 1)) +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 106, 1)) let i: number; // should error ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 110, 7)) function inside() { ->inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 103, 18)) +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 110, 18)) i++; ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 110, 7)) console.log(i); // NaN >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, --, --)) ->i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 103, 7)) +>i : Symbol(i, Decl(unusedLocalsInMethod4.ts, 110, 7)) } inside(); ->inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 103, 18)) +>inside : Symbol(inside, Decl(unusedLocalsInMethod4.ts, 110, 18)) } rw(); ->rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 99, 1)) +>rw : Symbol(rw, Decl(unusedLocalsInMethod4.ts, 106, 1)) function createBinder() { ->createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 110, 5)) +>createBinder : Symbol(createBinder, Decl(unusedLocalsInMethod4.ts, 117, 5)) var file: string; // should not error ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 120, 7)) function bindSourceFile(f: string) { ->bindSourceFile : Symbol(bindSourceFile, Decl(unusedLocalsInMethod4.ts, 113, 21)) ->f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 115, 28)) +>bindSourceFile : Symbol(bindSourceFile, Decl(unusedLocalsInMethod4.ts, 120, 21)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 122, 28)) file = f; ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) ->f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 115, 28)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 120, 7)) +>f : Symbol(f, Decl(unusedLocalsInMethod4.ts, 122, 28)) file.toString(); >file.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) ->file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 113, 7)) +>file : Symbol(file, Decl(unusedLocalsInMethod4.ts, 120, 7)) >toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) } } function transformClassFields() { ->transformClassFields : Symbol(transformClassFields, Decl(unusedLocalsInMethod4.ts, 120, 1)) +>transformClassFields : Symbol(transformClassFields, Decl(unusedLocalsInMethod4.ts, 127, 1)) enum ClassPropertySubstitutionFlags { ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 129, 33)) ClassAliases = 1 << 0, ->ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 130, 41)) ClassStaticThisOrSuperReference = 1 << 1, ->ClassStaticThisOrSuperReference : Symbol(ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference, Decl(unusedLocalsInMethod4.ts, 124, 30)) +>ClassStaticThisOrSuperReference : Symbol(ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference, Decl(unusedLocalsInMethod4.ts, 131, 30)) } let enabledSubstitutions: ClassPropertySubstitutionFlags; // should error ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 135, 7)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 129, 33)) function enableSubstitutionForClassAliases() { ->enableSubstitutionForClassAliases : Symbol(enableSubstitutionForClassAliases, Decl(unusedLocalsInMethod4.ts, 128, 61)) +>enableSubstitutionForClassAliases : Symbol(enableSubstitutionForClassAliases, Decl(unusedLocalsInMethod4.ts, 135, 61)) enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases; ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) ->ClassPropertySubstitutionFlags.ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) ->ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 122, 33)) ->ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 123, 41)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 135, 7)) +>ClassPropertySubstitutionFlags.ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 130, 41)) +>ClassPropertySubstitutionFlags : Symbol(ClassPropertySubstitutionFlags, Decl(unusedLocalsInMethod4.ts, 129, 33)) +>ClassAliases : Symbol(ClassPropertySubstitutionFlags.ClassAliases, Decl(unusedLocalsInMethod4.ts, 130, 41)) enabledSubstitutions.toString(); >enabledSubstitutions.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) ->enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 128, 7)) +>enabledSubstitutions : Symbol(enabledSubstitutions, Decl(unusedLocalsInMethod4.ts, 135, 7)) >toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) } } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index 90cf4642a2913..440f6049de33c 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -41,6 +41,14 @@ function f() { >x9 : NonNull > : ^^^^^^^ + var x10: NonNull; // should error +>x10 : NonNull +> : ^^^^^^^ + + var x11: NonNull; // should not error +>x11 : NonNull +> : ^^^^^^^ + function foo() { >foo : () => void > : ^^^^^^^^^^ @@ -152,6 +160,44 @@ function f() { > : ^^^^ ^^ ^^^^^ >x9 : NonNull > : ^^^^^^^ + + console.log(x10); +>console.log(x10) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>x10 : NonNull +> : ^^^^^^^ + + console.log(x11); +>console.log(x11) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>x11 : NonNull +> : ^^^^^^^ + } + function bar() { +>bar : () => void +> : ^^^^^^^^^^ + + x11 = {} as any; +>x11 = {} as any : any +> : ^^^ +>x11 : NonNull +> : ^^^^^^^ +>{} as any : any +> : ^^^ +>{} : {} +> : ^^ } foo(); >foo() : void @@ -367,7 +413,7 @@ function f4() { }; } -declare let x: number; // should error +declare let x: number; // should not error >x : number > : ^^^^^^ diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index 577a9ff240e36..78ae2578c232b 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -10,6 +10,8 @@ function f() { let x7: unknown; // should not error let x8: T; // should error let x9: NonNull; // should error + var x10: NonNull; // should error + var x11: NonNull; // should not error function foo() { console.log(x1); @@ -21,6 +23,11 @@ function f() { console.log(x7); console.log(x8); console.log(x9); + console.log(x10); + console.log(x11); + } + function bar() { + x11 = {} as any; } foo(); } @@ -64,7 +71,7 @@ function f4() { }; } -declare let x: number; // should error +declare let x: number; // should not error function f5() { x.toString(); } From f0b637feeba868497679b4b3aaec39c6f4021f79 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Mon, 29 Jul 2024 00:47:28 +0800 Subject: [PATCH 18/22] narrow function scoped variables --- src/compiler/checker.ts | 8 ++--- .../narrowingPastLastAssignment.errors.txt | 10 +++++- .../narrowingPastLastAssignment.symbols | 16 ++++++++++ .../narrowingPastLastAssignment.types | 31 +++++++++++++++++++ .../unusedLocalsInMethod4.errors.txt | 5 ++- .../compiler/narrowingPastLastAssignment.ts | 9 ++++++ 6 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3df23fc9a900d..25745c6e38a03 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29638,13 +29638,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration); return !!declaration && ( isParameter(declaration) || - isVariableDeclaration(declaration) && (isCatchClause(declaration.parent) || isMutableLocalVariableDeclaration(declaration)) + isVariableDeclaration(declaration) && (isCatchClause(declaration.parent) || isMutableLocalVariableDeclaration(symbol, declaration)) ); } - function isMutableLocalVariableDeclaration(declaration: VariableDeclaration) { + function isMutableLocalVariableDeclaration(symbol: Symbol, declaration: VariableDeclaration) { // Return true if symbol is a non-exported and non-global `let` variable - return !!(declaration.parent.flags & NodeFlags.Let) && !( + return !!(symbol.flags & SymbolFlags.FunctionScopedVariable || (declaration.parent.flags & NodeFlags.Let)) && !( getCombinedModifierFlags(declaration) & ModifierFlags.Export || declaration.parent.parent.kind === SyntaxKind.VariableStatement && isGlobalSourceFile(declaration.parent.parent.parent) ); @@ -30419,7 +30419,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ) { flowContainer = getControlFlowContainer(flowContainer); } - const isMutableLocalVariableWithoutInitializer = immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(immediateDeclaration); + const isMutableLocalVariableWithoutInitializer = immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(symbol, immediateDeclaration); // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). diff --git a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt index 0477f4c4877a1..1bc78b8eeb4c3 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt +++ b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt @@ -160,4 +160,12 @@ narrowingPastLastAssignment.ts(90,20): error TS7005: Variable 'x' implicitly has } values.forEach(v => foo.push(v)); } - \ No newline at end of file + + function f13() { + var foo: string | undefined; + foo = ''; + + return () => { + foo.toLocaleLowerCase(); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/narrowingPastLastAssignment.symbols b/tests/baselines/reference/narrowingPastLastAssignment.symbols index 4ace603d16418..2421f0135ebbc 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.symbols +++ b/tests/baselines/reference/narrowingPastLastAssignment.symbols @@ -363,3 +363,19 @@ function f12() { >v : Symbol(v, Decl(narrowingPastLastAssignment.ts, 151, 19)) } +function f13() { +>f13 : Symbol(f13, Decl(narrowingPastLastAssignment.ts, 152, 1)) + + var foo: string | undefined; +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) + + foo = ''; +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) + + return () => { + foo.toLocaleLowerCase(); +>foo.toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.string.d.ts, --, --)) +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) +>toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.string.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/narrowingPastLastAssignment.types b/tests/baselines/reference/narrowingPastLastAssignment.types index ba185b982df4f..1cc34a69108d9 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.types +++ b/tests/baselines/reference/narrowingPastLastAssignment.types @@ -731,3 +731,34 @@ function f12() { > : ^^^^^^ } +function f13() { +>f13 : () => () => void +> : ^^^^^^^^^^^^^^^^ + + var foo: string | undefined; +>foo : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + foo = ''; +>foo = '' : "" +> : ^^ +>foo : string | undefined +> : ^^^^^^^^^^^^^^^^^^ +>'' : "" +> : ^^ + + return () => { +>() => { foo.toLocaleLowerCase(); } : () => void +> : ^^^^^^^^^^ + + foo.toLocaleLowerCase(); +>foo.toLocaleLowerCase() : string +> : ^^^^^^ +>foo.toLocaleLowerCase : { (locales?: string | string[]): string; (locales?: Intl.LocalesArgument): string; } +> : ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ +>foo : string +> : ^^^^^^ +>toLocaleLowerCase : { (locales?: string | string[]): string; (locales?: Intl.LocalesArgument): string; } +> : ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ + } +} diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 9e3b9d814c8ba..c0c21bb8ffc1d 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -2,6 +2,7 @@ unusedLocalsInMethod4.ts(15,21): error TS2454: Variable 'x1' is used before bein unusedLocalsInMethod4.ts(16,21): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(22,21): error TS2454: Variable 'x8' is used before being assigned. unusedLocalsInMethod4.ts(23,21): error TS2454: Variable 'x9' is used before being assigned. +unusedLocalsInMethod4.ts(24,21): error TS2454: Variable 'x10' is used before being assigned. unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x1' is used before being assigned. unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(51,17): error TS2454: Variable 'x8' is used before being assigned. @@ -15,7 +16,7 @@ unusedLocalsInMethod4.ts(139,9): error TS2454: Variable 'enabledSubstitutions' i unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' is used before being assigned. -==== unusedLocalsInMethod4.ts (15 errors) ==== +==== unusedLocalsInMethod4.ts (16 errors) ==== function f() { let x1: number[]; // should error let x2: number[] | null; // should error @@ -48,6 +49,8 @@ unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' i ~~ !!! error TS2454: Variable 'x9' is used before being assigned. console.log(x10); + ~~~ +!!! error TS2454: Variable 'x10' is used before being assigned. console.log(x11); } function bar() { diff --git a/tests/cases/compiler/narrowingPastLastAssignment.ts b/tests/cases/compiler/narrowingPastLastAssignment.ts index ae66ab03db719..f1fe2d6d429d8 100644 --- a/tests/cases/compiler/narrowingPastLastAssignment.ts +++ b/tests/cases/compiler/narrowingPastLastAssignment.ts @@ -155,3 +155,12 @@ function f12() { } values.forEach(v => foo.push(v)); } + +function f13() { + var foo: string | undefined; + foo = ''; + + return () => { + foo.toLocaleLowerCase(); + } +} \ No newline at end of file From bf4bb51b23d29470930781bf682d52b30e35ea9a Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 4 Aug 2024 23:38:43 +0800 Subject: [PATCH 19/22] inline variables --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 25745c6e38a03..e1d7df3a70273 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30419,11 +30419,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ) { flowContainer = getControlFlowContainer(flowContainer); } - const isMutableLocalVariableWithoutInitializer = immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(symbol, immediateDeclaration); // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). - const assumeInitialized = isParameter || isAlias || (isOuterVariable && !(isMutableLocalVariableWithoutInitializer && !isSymbolAssignedDefinitely(symbol))) || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || + const assumeInitialized = isParameter || isAlias || + (isOuterVariable && !((immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(symbol, immediateDeclaration)) && !isSymbolAssignedDefinitely(symbol))) || + isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 || isInTypeQuery(node) || isInAmbientOrTypeNode(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || From 10e2cf8f52541bcea238c7c77705502ca95690a6 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Thu, 8 Aug 2024 23:35:38 +0800 Subject: [PATCH 20/22] ignore var variables --- src/compiler/checker.ts | 13 +++++++------ .../narrowingPastLastAssignment.errors.txt | 5 ++++- .../reference/narrowingPastLastAssignment.types | 4 ++-- .../reference/unusedLocalsInMethod4.errors.txt | 5 +---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1d7df3a70273..7cd1a54f9a0be 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29568,14 +29568,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const assigmentTarget = getAssignmentTargetKind(node); if (assigmentTarget !== AssignmentKind.None) { const symbol = getResolvedSymbol(node as Identifier); - const isDefiniteAssignment = assigmentTarget === AssignmentKind.Definite || (symbol.lastAssignmentPos !== undefined && symbol.lastAssignmentPos < 0); + const hasDefiniteAssignment = assigmentTarget === AssignmentKind.Definite || (symbol.lastAssignmentPos !== undefined && symbol.lastAssignmentPos < 0); if (isParameterOrMutableLocalVariable(symbol)) { if (symbol.lastAssignmentPos === undefined || Math.abs(symbol.lastAssignmentPos) !== Number.MAX_VALUE) { const referencingFunction = findAncestor(node, isFunctionOrSourceFile); const declaringFunction = findAncestor(symbol.valueDeclaration, isFunctionOrSourceFile); symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; } - if (isDefiniteAssignment && symbol.lastAssignmentPos > 0) { + if (hasDefiniteAssignment && symbol.lastAssignmentPos > 0) { symbol.lastAssignmentPos = -symbol.lastAssignmentPos; } } @@ -29638,13 +29638,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration); return !!declaration && ( isParameter(declaration) || - isVariableDeclaration(declaration) && (isCatchClause(declaration.parent) || isMutableLocalVariableDeclaration(symbol, declaration)) + isVariableDeclaration(declaration) && (isCatchClause(declaration.parent) || isMutableLocalVariableDeclaration(declaration)) ); } - function isMutableLocalVariableDeclaration(symbol: Symbol, declaration: VariableDeclaration) { + function isMutableLocalVariableDeclaration(declaration: VariableDeclaration) { // Return true if symbol is a non-exported and non-global `let` variable - return !!(symbol.flags & SymbolFlags.FunctionScopedVariable || (declaration.parent.flags & NodeFlags.Let)) && !( + return !!(declaration.parent.flags & NodeFlags.Let) && !( getCombinedModifierFlags(declaration) & ModifierFlags.Export || declaration.parent.parent.kind === SyntaxKind.VariableStatement && isGlobalSourceFile(declaration.parent.parent.parent) ); @@ -30422,8 +30422,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). + const isNeverInitialized = immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(immediateDeclaration) && !isSymbolAssignedDefinitely(symbol); const assumeInitialized = isParameter || isAlias || - (isOuterVariable && !((immediateDeclaration && isVariableDeclaration(immediateDeclaration) && !immediateDeclaration.initializer && !immediateDeclaration.exclamationToken && isMutableLocalVariableDeclaration(symbol, immediateDeclaration)) && !isSymbolAssignedDefinitely(symbol))) || + (isOuterVariable && !isNeverInitialized) || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 || isInTypeQuery(node) || isInAmbientOrTypeNode(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || diff --git a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt index 1bc78b8eeb4c3..a7460e63b7f11 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt +++ b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt @@ -1,8 +1,9 @@ narrowingPastLastAssignment.ts(88,9): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. narrowingPastLastAssignment.ts(90,20): error TS7005: Variable 'x' implicitly has an 'any' type. +narrowingPastLastAssignment.ts(160,9): error TS18048: 'foo' is possibly 'undefined'. -==== narrowingPastLastAssignment.ts (2 errors) ==== +==== narrowingPastLastAssignment.ts (3 errors) ==== function action(f: Function) {} // Narrowings are preserved in closures created past last assignment @@ -167,5 +168,7 @@ narrowingPastLastAssignment.ts(90,20): error TS7005: Variable 'x' implicitly has return () => { foo.toLocaleLowerCase(); + ~~~ +!!! error TS18048: 'foo' is possibly 'undefined'. } } \ No newline at end of file diff --git a/tests/baselines/reference/narrowingPastLastAssignment.types b/tests/baselines/reference/narrowingPastLastAssignment.types index 1cc34a69108d9..fdee033b7f8ec 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.types +++ b/tests/baselines/reference/narrowingPastLastAssignment.types @@ -756,8 +756,8 @@ function f13() { > : ^^^^^^ >foo.toLocaleLowerCase : { (locales?: string | string[]): string; (locales?: Intl.LocalesArgument): string; } > : ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ->foo : string -> : ^^^^^^ +>foo : string | undefined +> : ^^^^^^^^^^^^^^^^^^ >toLocaleLowerCase : { (locales?: string | string[]): string; (locales?: Intl.LocalesArgument): string; } > : ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ } diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index c0c21bb8ffc1d..9e3b9d814c8ba 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -2,7 +2,6 @@ unusedLocalsInMethod4.ts(15,21): error TS2454: Variable 'x1' is used before bein unusedLocalsInMethod4.ts(16,21): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(22,21): error TS2454: Variable 'x8' is used before being assigned. unusedLocalsInMethod4.ts(23,21): error TS2454: Variable 'x9' is used before being assigned. -unusedLocalsInMethod4.ts(24,21): error TS2454: Variable 'x10' is used before being assigned. unusedLocalsInMethod4.ts(44,17): error TS2454: Variable 'x1' is used before being assigned. unusedLocalsInMethod4.ts(45,17): error TS2454: Variable 'x2' is used before being assigned. unusedLocalsInMethod4.ts(51,17): error TS2454: Variable 'x8' is used before being assigned. @@ -16,7 +15,7 @@ unusedLocalsInMethod4.ts(139,9): error TS2454: Variable 'enabledSubstitutions' i unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' is used before being assigned. -==== unusedLocalsInMethod4.ts (16 errors) ==== +==== unusedLocalsInMethod4.ts (15 errors) ==== function f() { let x1: number[]; // should error let x2: number[] | null; // should error @@ -49,8 +48,6 @@ unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' i ~~ !!! error TS2454: Variable 'x9' is used before being assigned. console.log(x10); - ~~~ -!!! error TS2454: Variable 'x10' is used before being assigned. console.log(x11); } function bar() { From 6c8536231eb5b2ba32f490a5489c6c98b795fd03 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Fri, 9 Aug 2024 22:40:55 +0800 Subject: [PATCH 21/22] update test --- tests/baselines/reference/unusedLocalsInMethod4.errors.txt | 4 ++-- tests/baselines/reference/unusedLocalsInMethod4.js | 6 +++--- tests/baselines/reference/unusedLocalsInMethod4.symbols | 4 ++-- tests/baselines/reference/unusedLocalsInMethod4.types | 4 ++-- tests/cases/compiler/unusedLocalsInMethod4.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt index 9e3b9d814c8ba..631245f86b71f 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.errors.txt +++ b/tests/baselines/reference/unusedLocalsInMethod4.errors.txt @@ -26,8 +26,8 @@ unusedLocalsInMethod4.ts(141,9): error TS2454: Variable 'enabledSubstitutions' i let x7: unknown; // should not error let x8: T; // should error let x9: NonNull; // should error - var x10: NonNull; // should error - var x11: NonNull; // should not error + var x10: NonNull; // should not error + let x11: NonNull; // should not error function foo() { console.log(x1); diff --git a/tests/baselines/reference/unusedLocalsInMethod4.js b/tests/baselines/reference/unusedLocalsInMethod4.js index ea8ca0dd19334..d066e78908ec4 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.js +++ b/tests/baselines/reference/unusedLocalsInMethod4.js @@ -11,8 +11,8 @@ function f() { let x7: unknown; // should not error let x8: T; // should error let x9: NonNull; // should error - var x10: NonNull; // should error - var x11: NonNull; // should not error + var x10: NonNull; // should not error + let x11: NonNull; // should not error function foo() { console.log(x1); @@ -158,7 +158,7 @@ function f() { var x7; // should not error var x8; // should error var x9; // should error - var x10; // should error + var x10; // should not error var x11; // should not error function foo() { console.log(x1); diff --git a/tests/baselines/reference/unusedLocalsInMethod4.symbols b/tests/baselines/reference/unusedLocalsInMethod4.symbols index 473a054fdb838..0d9aae9987410 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.symbols +++ b/tests/baselines/reference/unusedLocalsInMethod4.symbols @@ -35,11 +35,11 @@ function f() { >x9 : Symbol(x9, Decl(unusedLocalsInMethod4.ts, 9, 7)) >NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) - var x10: NonNull; // should error + var x10: NonNull; // should not error >x10 : Symbol(x10, Decl(unusedLocalsInMethod4.ts, 10, 7)) >NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) - var x11: NonNull; // should not error + let x11: NonNull; // should not error >x11 : Symbol(x11, Decl(unusedLocalsInMethod4.ts, 11, 7)) >NonNull : Symbol(NonNull, Decl(unusedLocalsInMethod4.ts, 0, 13)) diff --git a/tests/baselines/reference/unusedLocalsInMethod4.types b/tests/baselines/reference/unusedLocalsInMethod4.types index 440f6049de33c..b684bd318d203 100644 --- a/tests/baselines/reference/unusedLocalsInMethod4.types +++ b/tests/baselines/reference/unusedLocalsInMethod4.types @@ -41,11 +41,11 @@ function f() { >x9 : NonNull > : ^^^^^^^ - var x10: NonNull; // should error + var x10: NonNull; // should not error >x10 : NonNull > : ^^^^^^^ - var x11: NonNull; // should not error + let x11: NonNull; // should not error >x11 : NonNull > : ^^^^^^^ diff --git a/tests/cases/compiler/unusedLocalsInMethod4.ts b/tests/cases/compiler/unusedLocalsInMethod4.ts index 78ae2578c232b..973f68994ae7b 100644 --- a/tests/cases/compiler/unusedLocalsInMethod4.ts +++ b/tests/cases/compiler/unusedLocalsInMethod4.ts @@ -10,8 +10,8 @@ function f() { let x7: unknown; // should not error let x8: T; // should error let x9: NonNull; // should error - var x10: NonNull; // should error - var x11: NonNull; // should not error + var x10: NonNull; // should not error + let x11: NonNull; // should not error function foo() { console.log(x1); From b930faa37e4692d3c3e7e087d9ace394f6400210 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Wed, 21 Aug 2024 00:31:38 +0800 Subject: [PATCH 22/22] update code --- src/compiler/checker.ts | 2 +- src/compiler/transformers/classFields.ts | 3 ++- src/compiler/transformers/es2015.ts | 3 ++- src/compiler/transformers/es2017.ts | 3 ++- src/compiler/transformers/es2018.ts | 3 ++- src/compiler/transformers/ts.ts | 3 ++- .../reference/narrowingPastLastAssignment.errors.txt | 3 ++- .../reference/narrowingPastLastAssignment.symbols | 7 ++++--- .../baselines/reference/narrowingPastLastAssignment.types | 1 + tests/cases/compiler/narrowingPastLastAssignment.ts | 1 + 10 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7cd1a54f9a0be..a95baef7d4ccd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29576,7 +29576,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { symbol.lastAssignmentPos = referencingFunction === declaringFunction ? extendAssignmentPosition(node, symbol.valueDeclaration!) : Number.MAX_VALUE; } if (hasDefiniteAssignment && symbol.lastAssignmentPos > 0) { - symbol.lastAssignmentPos = -symbol.lastAssignmentPos; + symbol.lastAssignmentPos *= -1; } } } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index e4510d50f9fed..7d8bc6f8eab22 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -228,6 +228,7 @@ import { } from "../_namespaces/ts.js"; const enum ClassPropertySubstitutionFlags { + None = 0, /** * Enables substitutions for class expressions with static fields * which have initializers that reference the class name. @@ -401,7 +402,7 @@ export function transformClassFields(context: TransformationContext): (x: Source context.onEmitNode = onEmitNode; let shouldTransformPrivateStaticElementsInFile = false; - let enabledSubstitutions!: ClassPropertySubstitutionFlags; + let enabledSubstitutions = ClassPropertySubstitutionFlags.None; let classAliases: Identifier[]; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 63f0cec7f8bb3..5b29d34da9387 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -218,6 +218,7 @@ import { } from "../_namespaces/ts.js"; const enum ES2015SubstitutionFlags { + None = 0, /** Enables substitutions for captured `this` */ CapturedThis = 1 << 0, /** Enables substitutions for block-scoped bindings. */ @@ -523,7 +524,7 @@ export function transformES2015(context: TransformationContext): (x: SourceFile * They are persisted between each SourceFile transformation and should not * be reset. */ - let enabledSubstitutions!: ES2015SubstitutionFlags; + let enabledSubstitutions = ES2015SubstitutionFlags.None; return chainBundle(context, transformSourceFile); diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 501feb0bc82c1..8947a94a079f4 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -104,6 +104,7 @@ import { type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; const enum ES2017SubstitutionFlags { + None = 0, /** Enables substitutions for async methods with `super` calls. */ AsyncMethodsWithSuper = 1 << 0, } @@ -132,7 +133,7 @@ export function transformES2017(context: TransformationContext): (x: SourceFile * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. */ - let enabledSubstitutions!: ES2017SubstitutionFlags; + let enabledSubstitutions = ES2017SubstitutionFlags.None; /** * This keeps track of containers where `super` is valid, for use with diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index a4c079b400009..c8894d336af1c 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -113,6 +113,7 @@ import { } from "../_namespaces/ts.js"; const enum ESNextSubstitutionFlags { + None = 0, /** Enables substitutions for async methods with `super` calls. */ AsyncMethodsWithSuper = 1 << 0, } @@ -170,7 +171,7 @@ export function transformES2018(context: TransformationContext): (x: SourceFile context.onSubstituteNode = onSubstituteNode; let exportedVariableStatement = false; - let enabledSubstitutions!: ESNextSubstitutionFlags; + let enabledSubstitutions = ESNextSubstitutionFlags.None; let enclosingFunctionFlags: FunctionFlags; let parametersWithPrecedingObjectRestOrSpread: Set | undefined; let enclosingSuperContainerFlags: NodeCheckFlags = 0; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index b3844e154b540..2d9981391186c 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -209,6 +209,7 @@ import { const USE_NEW_TYPE_METADATA_FORMAT = false; const enum TypeScriptSubstitutionFlags { + None = 0, /** Enables substitutions for namespace exports. */ NamespaceExports = 1 << 1, /* Enables substitutions for unqualified enum members */ @@ -272,7 +273,7 @@ export function transformTypeScript(context: TransformationContext) { * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. */ - let enabledSubstitutions!: TypeScriptSubstitutionFlags; + let enabledSubstitutions = TypeScriptSubstitutionFlags.None; /** * Keeps track of whether we are within any containing namespaces when performing diff --git a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt index a7460e63b7f11..d601615b04455 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.errors.txt +++ b/tests/baselines/reference/narrowingPastLastAssignment.errors.txt @@ -1,6 +1,6 @@ narrowingPastLastAssignment.ts(88,9): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. narrowingPastLastAssignment.ts(90,20): error TS7005: Variable 'x' implicitly has an 'any' type. -narrowingPastLastAssignment.ts(160,9): error TS18048: 'foo' is possibly 'undefined'. +narrowingPastLastAssignment.ts(161,9): error TS18048: 'foo' is possibly 'undefined'. ==== narrowingPastLastAssignment.ts (3 errors) ==== @@ -163,6 +163,7 @@ narrowingPastLastAssignment.ts(160,9): error TS18048: 'foo' is possibly 'undefin } function f13() { + // Test for captured 'var' declaration (as opposed to parameters, let, const). var foo: string | undefined; foo = ''; diff --git a/tests/baselines/reference/narrowingPastLastAssignment.symbols b/tests/baselines/reference/narrowingPastLastAssignment.symbols index 2421f0135ebbc..0aa5ff567438f 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.symbols +++ b/tests/baselines/reference/narrowingPastLastAssignment.symbols @@ -366,16 +366,17 @@ function f12() { function f13() { >f13 : Symbol(f13, Decl(narrowingPastLastAssignment.ts, 152, 1)) + // Test for captured 'var' declaration (as opposed to parameters, let, const). var foo: string | undefined; ->foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 156, 7)) foo = ''; ->foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 156, 7)) return () => { foo.toLocaleLowerCase(); >foo.toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.string.d.ts, --, --)) ->foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 155, 7)) +>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 156, 7)) >toLocaleLowerCase : Symbol(String.toLocaleLowerCase, Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.string.d.ts, --, --)) } } diff --git a/tests/baselines/reference/narrowingPastLastAssignment.types b/tests/baselines/reference/narrowingPastLastAssignment.types index fdee033b7f8ec..50e1d0d26665c 100644 --- a/tests/baselines/reference/narrowingPastLastAssignment.types +++ b/tests/baselines/reference/narrowingPastLastAssignment.types @@ -735,6 +735,7 @@ function f13() { >f13 : () => () => void > : ^^^^^^^^^^^^^^^^ + // Test for captured 'var' declaration (as opposed to parameters, let, const). var foo: string | undefined; >foo : string | undefined > : ^^^^^^^^^^^^^^^^^^ diff --git a/tests/cases/compiler/narrowingPastLastAssignment.ts b/tests/cases/compiler/narrowingPastLastAssignment.ts index f1fe2d6d429d8..26d898f168897 100644 --- a/tests/cases/compiler/narrowingPastLastAssignment.ts +++ b/tests/cases/compiler/narrowingPastLastAssignment.ts @@ -157,6 +157,7 @@ function f12() { } function f13() { + // Test for captured 'var' declaration (as opposed to parameters, let, const). var foo: string | undefined; foo = '';