@@ -22352,10 +22352,6 @@ namespace ts {
2235222352 function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
2235322353 for (const node of potentiallyUnusedIdentifiers) {
2235422354 switch (node.kind) {
22355- case SyntaxKind.SourceFile:
22356- case SyntaxKind.ModuleDeclaration:
22357- checkUnusedModuleMembers(node, addDiagnostic);
22358- break;
2235922355 case SyntaxKind.ClassDeclaration:
2236022356 case SyntaxKind.ClassExpression:
2236122357 checkUnusedClassMembers(node, addDiagnostic);
@@ -22364,6 +22360,8 @@ namespace ts {
2236422360 case SyntaxKind.InterfaceDeclaration:
2236522361 checkUnusedTypeParameters(node, addDiagnostic);
2236622362 break;
22363+ case SyntaxKind.SourceFile:
22364+ case SyntaxKind.ModuleDeclaration:
2236722365 case SyntaxKind.Block:
2236822366 case SyntaxKind.CaseBlock:
2236922367 case SyntaxKind.ForStatement:
@@ -22397,35 +22395,6 @@ namespace ts {
2239722395 }
2239822396 }
2239922397
22400- function checkUnusedLocalsAndParameters(node: Node, addDiagnostic: AddUnusedDiagnostic): void {
22401- if (!(node.flags & NodeFlags.Ambient)) {
22402- node.locals.forEach(local => {
22403- // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
22404- // If it's a type parameter merged with a parameter, check if the parameter-side is used.
22405- if (local.flags & SymbolFlags.TypeParameter ? (local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : !local.isReferenced) {
22406- if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) {
22407- const parameter = <ParameterDeclaration>getRootDeclaration(local.valueDeclaration);
22408- const name = getNameOfDeclaration(local.valueDeclaration);
22409- if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) {
22410- addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
22411- }
22412- }
22413- else {
22414- forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local), addDiagnostic));
22415- }
22416- }
22417- });
22418- }
22419- }
22420-
22421- function isRemovedPropertyFromObjectSpread(node: Node) {
22422- if (isBindingElement(node) && isObjectBindingPattern(node.parent)) {
22423- const lastElement = lastOrUndefined(node.parent.elements);
22424- return lastElement !== node && !!lastElement.dotDotDotToken;
22425- }
22426- return false;
22427- }
22428-
2242922398 function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
2243022399 const node = getNameOfDeclaration(declaration) || declaration;
2243122400 if (isIdentifierThatStartsWithUnderScore(node)) {
@@ -22436,10 +22405,8 @@ namespace ts {
2243622405 }
2243722406 }
2243822407
22439- if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
22440- const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
22441- addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
22442- }
22408+ const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
22409+ addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
2244322410 }
2244422411
2244522412 function parameterNameStartsWithUnderscore(parameterName: DeclarationName) {
@@ -22501,44 +22468,86 @@ namespace ts {
2250122468 }
2250222469 }
2250322470
22504- function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile, addDiagnostic: AddUnusedDiagnostic): void {
22505- if (!(node.flags & NodeFlags.Ambient)) {
22506- // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
22507- const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
22508- node.locals.forEach(local => {
22509- if (local.isReferenced || local.exportSymbol) return;
22510- for (const declaration of local.declarations) {
22511- if (isAmbientModule(declaration)) continue;
22512- if (isImportedDeclaration(declaration)) {
22513- const importClause = importClauseFromImported(declaration);
22514- const key = String(getNodeId(importClause));
22515- const group = unusedImports.get(key);
22516- if (group) {
22517- group[1].push(declaration);
22518- }
22519- else {
22520- unusedImports.set(key, [importClause, [declaration]]);
22471+ function addToGroup<K, V>(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void {
22472+ const keyString = String(getKey(key));
22473+ const group = map.get(keyString);
22474+ if (group) {
22475+ group[1].push(value);
22476+ }
22477+ else {
22478+ map.set(keyString, [key, [value]]);
22479+ }
22480+ }
22481+
22482+ function tryGetRootParameterDeclaration(node: Node): ParameterDeclaration | undefined {
22483+ return tryCast(getRootDeclaration(node), isParameter);
22484+ }
22485+
22486+ function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void {
22487+ if (nodeWithLocals.flags & NodeFlags.Ambient) return;
22488+
22489+ // Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
22490+ const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
22491+ const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>();
22492+ nodeWithLocals.locals.forEach(local => {
22493+ // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
22494+ // If it's a type parameter merged with a parameter, check if the parameter-side is used.
22495+ if (local.flags & SymbolFlags.TypeParameter ? !(local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : local.isReferenced || local.exportSymbol) {
22496+ return;
22497+ }
22498+
22499+ for (const declaration of local.declarations) {
22500+ if (isAmbientModule(declaration)) continue;
22501+ if (isImportedDeclaration(declaration)) {
22502+ addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId);
22503+ }
22504+ else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) {
22505+ // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though.
22506+ const lastElement = last(declaration.parent.elements);
22507+ if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) {
22508+ addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId);
22509+ }
22510+ }
22511+ else {
22512+ const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
22513+ if (parameter) {
22514+ const name = getNameOfDeclaration(local.valueDeclaration);
22515+ if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) {
22516+ addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
2252122517 }
2252222518 }
2252322519 else {
2252422520 errorUnusedLocal(declaration, symbolName(local), addDiagnostic);
2252522521 }
2252622522 }
22527- });
22528-
22529- unusedImports.forEach(([importClause, unuseds]) => {
22530- const importDecl = importClause.parent;
22531- if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
22532- for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
22533- }
22534- else if (unuseds.length === 1) {
22535- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
22536- }
22537- else {
22538- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl)));
22523+ }
22524+ });
22525+ unusedImports.forEach(([importClause, unuseds]) => {
22526+ const importDecl = importClause.parent;
22527+ if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
22528+ for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
22529+ }
22530+ else if (unuseds.length === 1) {
22531+ addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
22532+ }
22533+ else {
22534+ addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused));
22535+ }
22536+ });
22537+ unusedDestructures.forEach(([bindingPattern, bindingElements]) => {
22538+ const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local;
22539+ if (!bindingPattern.elements.every(e => contains(bindingElements, e))) {
22540+ for (const e of bindingElements) {
22541+ addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(e)));
2253922542 }
22540- });
22541- }
22543+ }
22544+ else if (bindingElements.length === 1) {
22545+ addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(first(bindingElements))));
22546+ }
22547+ else {
22548+ addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused));
22549+ }
22550+ });
2254222551 }
2254322552
2254422553 type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
0 commit comments