@@ -22352,10 +22352,6 @@ namespace ts {
22352
22352
function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
22353
22353
for (const node of potentiallyUnusedIdentifiers) {
22354
22354
switch (node.kind) {
22355
- case SyntaxKind.SourceFile:
22356
- case SyntaxKind.ModuleDeclaration:
22357
- checkUnusedModuleMembers(node, addDiagnostic);
22358
- break;
22359
22355
case SyntaxKind.ClassDeclaration:
22360
22356
case SyntaxKind.ClassExpression:
22361
22357
checkUnusedClassMembers(node, addDiagnostic);
@@ -22364,6 +22360,8 @@ namespace ts {
22364
22360
case SyntaxKind.InterfaceDeclaration:
22365
22361
checkUnusedTypeParameters(node, addDiagnostic);
22366
22362
break;
22363
+ case SyntaxKind.SourceFile:
22364
+ case SyntaxKind.ModuleDeclaration:
22367
22365
case SyntaxKind.Block:
22368
22366
case SyntaxKind.CaseBlock:
22369
22367
case SyntaxKind.ForStatement:
@@ -22397,35 +22395,6 @@ namespace ts {
22397
22395
}
22398
22396
}
22399
22397
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
-
22429
22398
function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
22430
22399
const node = getNameOfDeclaration(declaration) || declaration;
22431
22400
if (isIdentifierThatStartsWithUnderScore(node)) {
@@ -22436,10 +22405,8 @@ namespace ts {
22436
22405
}
22437
22406
}
22438
22407
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));
22443
22410
}
22444
22411
22445
22412
function parameterNameStartsWithUnderscore(parameterName: DeclarationName) {
@@ -22501,44 +22468,86 @@ namespace ts {
22501
22468
}
22502
22469
}
22503
22470
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)));
22521
22517
}
22522
22518
}
22523
22519
else {
22524
22520
errorUnusedLocal(declaration, symbolName(local), addDiagnostic);
22525
22521
}
22526
22522
}
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)));
22539
22542
}
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
+ });
22542
22551
}
22543
22552
22544
22553
type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
0 commit comments