@@ -22307,10 +22307,6 @@ namespace ts {
22307
22307
function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
22308
22308
for (const node of potentiallyUnusedIdentifiers) {
22309
22309
switch (node.kind) {
22310
- case SyntaxKind.SourceFile:
22311
- case SyntaxKind.ModuleDeclaration:
22312
- checkUnusedModuleMembers(node, addDiagnostic);
22313
- break;
22314
22310
case SyntaxKind.ClassDeclaration:
22315
22311
case SyntaxKind.ClassExpression:
22316
22312
checkUnusedClassMembers(node, addDiagnostic);
@@ -22319,6 +22315,8 @@ namespace ts {
22319
22315
case SyntaxKind.InterfaceDeclaration:
22320
22316
checkUnusedTypeParameters(node, addDiagnostic);
22321
22317
break;
22318
+ case SyntaxKind.SourceFile:
22319
+ case SyntaxKind.ModuleDeclaration:
22322
22320
case SyntaxKind.Block:
22323
22321
case SyntaxKind.CaseBlock:
22324
22322
case SyntaxKind.ForStatement:
@@ -22352,35 +22350,6 @@ namespace ts {
22352
22350
}
22353
22351
}
22354
22352
22355
- function checkUnusedLocalsAndParameters(node: Node, addDiagnostic: AddUnusedDiagnostic): void {
22356
- if (!(node.flags & NodeFlags.Ambient)) {
22357
- node.locals.forEach(local => {
22358
- // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
22359
- // If it's a type parameter merged with a parameter, check if the parameter-side is used.
22360
- if (local.flags & SymbolFlags.TypeParameter ? (local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : !local.isReferenced) {
22361
- if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) {
22362
- const parameter = <ParameterDeclaration>getRootDeclaration(local.valueDeclaration);
22363
- const name = getNameOfDeclaration(local.valueDeclaration);
22364
- if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) {
22365
- addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
22366
- }
22367
- }
22368
- else {
22369
- forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local), addDiagnostic));
22370
- }
22371
- }
22372
- });
22373
- }
22374
- }
22375
-
22376
- function isRemovedPropertyFromObjectSpread(node: Node) {
22377
- if (isBindingElement(node) && isObjectBindingPattern(node.parent)) {
22378
- const lastElement = lastOrUndefined(node.parent.elements);
22379
- return lastElement !== node && !!lastElement.dotDotDotToken;
22380
- }
22381
- return false;
22382
- }
22383
-
22384
22353
function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
22385
22354
const node = getNameOfDeclaration(declaration) || declaration;
22386
22355
if (isIdentifierThatStartsWithUnderScore(node)) {
@@ -22391,10 +22360,8 @@ namespace ts {
22391
22360
}
22392
22361
}
22393
22362
22394
- if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
22395
- const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
22396
- addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
22397
- }
22363
+ const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
22364
+ addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
22398
22365
}
22399
22366
22400
22367
function parameterNameStartsWithUnderscore(parameterName: DeclarationName) {
@@ -22456,44 +22423,86 @@ namespace ts {
22456
22423
}
22457
22424
}
22458
22425
22459
- function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile, addDiagnostic: AddUnusedDiagnostic): void {
22460
- if (!(node.flags & NodeFlags.Ambient)) {
22461
- // 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.
22462
- const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
22463
- node.locals.forEach(local => {
22464
- if (local.isReferenced || local.exportSymbol) return;
22465
- for (const declaration of local.declarations) {
22466
- if (isAmbientModule(declaration)) continue;
22467
- if (isImportedDeclaration(declaration)) {
22468
- const importClause = importClauseFromImported(declaration);
22469
- const key = String(getNodeId(importClause));
22470
- const group = unusedImports.get(key);
22471
- if (group) {
22472
- group[1].push(declaration);
22473
- }
22474
- else {
22475
- unusedImports.set(key, [importClause, [declaration]]);
22426
+ function addToGroup<K, V>(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void {
22427
+ const keyString = String(getKey(key));
22428
+ const group = map.get(keyString);
22429
+ if (group) {
22430
+ group[1].push(value);
22431
+ }
22432
+ else {
22433
+ map.set(keyString, [key, [value]]);
22434
+ }
22435
+ }
22436
+
22437
+ function tryGetRootParameterDeclaration(node: Node): ParameterDeclaration | undefined {
22438
+ return tryCast(getRootDeclaration(node), isParameter);
22439
+ }
22440
+
22441
+ function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void {
22442
+ if (nodeWithLocals.flags & NodeFlags.Ambient) return;
22443
+
22444
+ // 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.
22445
+ const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
22446
+ const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>();
22447
+ nodeWithLocals.locals.forEach(local => {
22448
+ // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
22449
+ // If it's a type parameter merged with a parameter, check if the parameter-side is used.
22450
+ if (local.flags & SymbolFlags.TypeParameter ? !(local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : local.isReferenced || local.exportSymbol) {
22451
+ return;
22452
+ }
22453
+
22454
+ for (const declaration of local.declarations) {
22455
+ if (isAmbientModule(declaration)) continue;
22456
+ if (isImportedDeclaration(declaration)) {
22457
+ addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId);
22458
+ }
22459
+ else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) {
22460
+ // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though.
22461
+ const lastElement = last(declaration.parent.elements);
22462
+ if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) {
22463
+ addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId);
22464
+ }
22465
+ }
22466
+ else {
22467
+ const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
22468
+ if (parameter) {
22469
+ const name = getNameOfDeclaration(local.valueDeclaration);
22470
+ if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) {
22471
+ addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
22476
22472
}
22477
22473
}
22478
22474
else {
22479
22475
errorUnusedLocal(declaration, symbolName(local), addDiagnostic);
22480
22476
}
22481
22477
}
22482
- });
22483
-
22484
- unusedImports.forEach(([importClause, unuseds]) => {
22485
- const importDecl = importClause.parent;
22486
- if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
22487
- for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
22488
- }
22489
- else if (unuseds.length === 1) {
22490
- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
22491
- }
22492
- else {
22493
- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl)));
22478
+ }
22479
+ });
22480
+ unusedImports.forEach(([importClause, unuseds]) => {
22481
+ const importDecl = importClause.parent;
22482
+ if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
22483
+ for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
22484
+ }
22485
+ else if (unuseds.length === 1) {
22486
+ addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
22487
+ }
22488
+ else {
22489
+ addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused));
22490
+ }
22491
+ });
22492
+ unusedDestructures.forEach(([bindingPattern, bindingElements]) => {
22493
+ const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local;
22494
+ if (!bindingPattern.elements.every(e => contains(bindingElements, e))) {
22495
+ for (const e of bindingElements) {
22496
+ addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(e)));
22494
22497
}
22495
- });
22496
- }
22498
+ }
22499
+ else if (bindingElements.length === 1) {
22500
+ addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(first(bindingElements))));
22501
+ }
22502
+ else {
22503
+ addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused));
22504
+ }
22505
+ });
22497
22506
}
22498
22507
22499
22508
type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
0 commit comments