@@ -22620,14 +22620,6 @@ namespace ts {
2262022620
2262122621 function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
2262222622 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-
2263122623 const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read;
2263222624 addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name));
2263322625 }
@@ -22712,6 +22704,7 @@ namespace ts {
2271222704 // 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.
2271322705 const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
2271422706 const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>();
22707+ const unusedVariables = createMap<[VariableDeclarationList, VariableDeclaration[]]>();
2271522708 nodeWithLocals.locals.forEach(local => {
2271622709 // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
2271722710 // If it's a type parameter merged with a parameter, check if the parameter-side is used.
@@ -22731,6 +22724,11 @@ namespace ts {
2273122724 addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId);
2273222725 }
2273322726 }
22727+ else if (isVariableDeclaration(declaration)) {
22728+ if (!isIdentifierThatStartsWithUnderScore(declaration.name) || !isForInOrOfStatement(declaration.parent.parent)) {
22729+ addToGroup(unusedVariables, declaration.parent, declaration, getNodeId);
22730+ }
22731+ }
2273422732 else {
2273522733 const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
2273622734 if (parameter) {
@@ -22747,32 +22745,63 @@ namespace ts {
2274722745 });
2274822746 unusedImports.forEach(([importClause, unuseds]) => {
2274922747 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));
2275522756 }
2275622757 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 );
2275822759 }
2275922760 });
2276022761 unusedDestructures.forEach(([bindingPattern, bindingElements]) => {
2276122762 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 {
2276322774 for (const e of bindingElements) {
2276422775 addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(cast(e.name, isIdentifier))));
2276522776 }
2276622777 }
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));
2276922784 }
2277022785 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+ }
2277222789 }
2277322790 });
2277422791 }
2277522792
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+
2277622805 type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
2277722806 function isImportedDeclaration(node: Node): node is ImportedDeclaration {
2277822807 return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.NamespaceImport;
@@ -22781,12 +22810,6 @@ namespace ts {
2278122810 return decl.kind === SyntaxKind.ImportClause ? decl : decl.kind === SyntaxKind.NamespaceImport ? decl.parent : decl.parent.parent;
2278222811 }
2278322812
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-
2279022813 function checkBlock(node: Block) {
2279122814 // Grammar checking for SyntaxKind.Block
2279222815 if (node.kind === SyntaxKind.Block) {
0 commit comments