@@ -22620,14 +22620,6 @@ namespace ts {
22620
22620
22621
22621
function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
22622
22622
const node = getNameOfDeclaration(declaration) || declaration;
22623
- if (isIdentifierThatStartsWithUnderScore(node)) {
22624
- const declaration = getRootDeclaration(node.parent);
22625
- if ((declaration.kind === SyntaxKind.VariableDeclaration && isForInOrOfStatement(declaration.parent.parent)) ||
22626
- declaration.kind === SyntaxKind.TypeParameter) {
22627
- return;
22628
- }
22629
- }
22630
-
22631
22623
const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
22632
22624
addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
22633
22625
}
@@ -22712,6 +22704,7 @@ namespace ts {
22712
22704
// 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.
22713
22705
const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
22714
22706
const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>();
22707
+ const unusedVariables = createMap<[VariableDeclarationList, VariableDeclaration[]]>();
22715
22708
nodeWithLocals.locals.forEach(local => {
22716
22709
// If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
22717
22710
// If it's a type parameter merged with a parameter, check if the parameter-side is used.
@@ -22731,6 +22724,11 @@ namespace ts {
22731
22724
addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId);
22732
22725
}
22733
22726
}
22727
+ else if (isVariableDeclaration(declaration)) {
22728
+ if (!isIdentifierThatStartsWithUnderScore(declaration.name) || !isForInOrOfStatement(declaration.parent.parent)) {
22729
+ addToGroup(unusedVariables, declaration.parent, declaration, getNodeId);
22730
+ }
22731
+ }
22734
22732
else {
22735
22733
const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
22736
22734
if (parameter) {
@@ -22747,32 +22745,63 @@ namespace ts {
22747
22745
});
22748
22746
unusedImports.forEach(([importClause, unuseds]) => {
22749
22747
const importDecl = importClause.parent;
22750
- if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
22751
- for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
22752
- }
22753
- else if (unuseds.length === 1) {
22754
- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
22748
+ const nDeclarations = (importClause.name ? 1 : 0) +
22749
+ (importClause.namedBindings ?
22750
+ (importClause.namedBindings.kind === SyntaxKind.NamespaceImport ? 1 : importClause.namedBindings.elements.length)
22751
+ : 0);
22752
+ if (nDeclarations === unuseds.length) {
22753
+ addDiagnostic(UnusedKind.Local, unuseds.length === 1
22754
+ ? createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name))
22755
+ : createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused));
22755
22756
}
22756
22757
else {
22757
- addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused) );
22758
+ for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic );
22758
22759
}
22759
22760
});
22760
22761
unusedDestructures.forEach(([bindingPattern, bindingElements]) => {
22761
22762
const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local;
22762
- if (!bindingPattern.elements.every(e => contains(bindingElements, e))) {
22763
+ if (bindingPattern.elements.length === bindingElements.length) {
22764
+ if (bindingElements.length === 1 && bindingPattern.parent.kind === SyntaxKind.VariableDeclaration && bindingPattern.parent.parent.kind === SyntaxKind.VariableDeclarationList) {
22765
+ addToGroup(unusedVariables, bindingPattern.parent.parent, bindingPattern.parent, getNodeId);
22766
+ }
22767
+ else {
22768
+ addDiagnostic(kind, bindingElements.length === 1
22769
+ ? createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(cast(first(bindingElements).name, isIdentifier)))
22770
+ : createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused));
22771
+ }
22772
+ }
22773
+ else {
22763
22774
for (const e of bindingElements) {
22764
22775
addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(cast(e.name, isIdentifier))));
22765
22776
}
22766
22777
}
22767
- else if (bindingElements.length === 1) {
22768
- addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(cast(first(bindingElements).name, isIdentifier))));
22778
+ });
22779
+ unusedVariables.forEach(([declarationList, declarations]) => {
22780
+ if (declarationList.declarations.length === declarations.length) {
22781
+ addDiagnostic(UnusedKind.Local, declarations.length === 1
22782
+ ? createDiagnosticForNode(first(declarations).name, Diagnostics._0_is_declared_but_its_value_is_never_read, bindingNameText(first(declarations).name))
22783
+ : createDiagnosticForNode(declarationList.parent.kind === SyntaxKind.VariableStatement ? declarationList.parent : declarationList, Diagnostics.All_variables_are_unused));
22769
22784
}
22770
22785
else {
22771
- addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused));
22786
+ for (const decl of declarations) {
22787
+ addDiagnostic(UnusedKind.Local, createDiagnosticForNode(decl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(cast(decl.name, isIdentifier))));
22788
+ }
22772
22789
}
22773
22790
});
22774
22791
}
22775
22792
22793
+ function bindingNameText(name: BindingName): string {
22794
+ switch (name.kind) {
22795
+ case SyntaxKind.Identifier:
22796
+ return idText(name);
22797
+ case SyntaxKind.ArrayBindingPattern:
22798
+ case SyntaxKind.ObjectBindingPattern:
22799
+ return bindingNameText(cast(first(name.elements), isBindingElement).name);
22800
+ default:
22801
+ return Debug.assertNever(name);
22802
+ }
22803
+ }
22804
+
22776
22805
type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
22777
22806
function isImportedDeclaration(node: Node): node is ImportedDeclaration {
22778
22807
return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.NamespaceImport;
@@ -22781,12 +22810,6 @@ namespace ts {
22781
22810
return decl.kind === SyntaxKind.ImportClause ? decl : decl.kind === SyntaxKind.NamespaceImport ? decl.parent : decl.parent.parent;
22782
22811
}
22783
22812
22784
- function forEachImportedDeclaration<T>(importClause: ImportClause, cb: (im: ImportedDeclaration) => T | undefined): T | undefined {
22785
- const { name: defaultName, namedBindings } = importClause;
22786
- return (defaultName && cb(importClause)) ||
22787
- namedBindings && (namedBindings.kind === SyntaxKind.NamespaceImport ? cb(namedBindings) : forEach(namedBindings.elements, cb));
22788
- }
22789
-
22790
22813
function checkBlock(node: Block) {
22791
22814
// Grammar checking for SyntaxKind.Block
22792
22815
if (node.kind === SyntaxKind.Block) {
0 commit comments