From 6bda0cd6d4b4dd3fea63f560a4e32aec75bbaaf8 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 1 May 2018 08:53:19 -0700 Subject: [PATCH 1/3] fixUnusedIdentifier: Handle destructure with all bindings unused --- src/compiler/checker.ts | 145 ++++++++++-------- src/compiler/diagnosticMessages.json | 9 ++ src/services/codefixes/fixUnusedIdentifier.ts | 29 +++- .../reference/unusedDestructuring.errors.txt | 35 +++++ .../reference/unusedDestructuring.js | 26 ++++ .../reference/unusedDestructuring.symbols | 34 ++++ .../reference/unusedDestructuring.types | 34 ++++ .../unusedDestructuringParameters.errors.txt | 4 +- .../unusedLocalsAndObjectSpread.errors.txt | 8 +- .../unusedLocalsAndObjectSpread2.errors.txt | 8 +- .../unusedParametersWithUnderscore.errors.txt | 5 +- tests/cases/compiler/unusedDestructuring.ts | 13 ++ ...xUnusedIdentifier_destructure_allUnused.ts | 14 ++ ...sedIdentifier_destructure_allUnused_all.ts | 18 +++ ...Identifier_destructure_allUnused_nested.ts | 14 ++ 15 files changed, 316 insertions(+), 80 deletions(-) create mode 100644 tests/baselines/reference/unusedDestructuring.errors.txt create mode 100644 tests/baselines/reference/unusedDestructuring.js create mode 100644 tests/baselines/reference/unusedDestructuring.symbols create mode 100644 tests/baselines/reference/unusedDestructuring.types create mode 100644 tests/cases/compiler/unusedDestructuring.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d825b0d971758..d075e4863efcc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22307,10 +22307,6 @@ namespace ts { function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray, addDiagnostic: AddUnusedDiagnostic) { for (const node of potentiallyUnusedIdentifiers) { switch (node.kind) { - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - checkUnusedModuleMembers(node, addDiagnostic); - break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: checkUnusedClassMembers(node, addDiagnostic); @@ -22319,6 +22315,8 @@ namespace ts { case SyntaxKind.InterfaceDeclaration: checkUnusedTypeParameters(node, addDiagnostic); break; + case SyntaxKind.SourceFile: + case SyntaxKind.ModuleDeclaration: case SyntaxKind.Block: case SyntaxKind.CaseBlock: case SyntaxKind.ForStatement: @@ -22352,35 +22350,6 @@ namespace ts { } } - function checkUnusedLocalsAndParameters(node: Node, addDiagnostic: AddUnusedDiagnostic): void { - if (!(node.flags & NodeFlags.Ambient)) { - node.locals.forEach(local => { - // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. - // If it's a type parameter merged with a parameter, check if the parameter-side is used. - if (local.flags & SymbolFlags.TypeParameter ? (local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : !local.isReferenced) { - if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { - const parameter = getRootDeclaration(local.valueDeclaration); - const name = getNameOfDeclaration(local.valueDeclaration); - if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) { - addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); - } - } - else { - forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local), addDiagnostic)); - } - } - }); - } - } - - function isRemovedPropertyFromObjectSpread(node: Node) { - if (isBindingElement(node) && isObjectBindingPattern(node.parent)) { - const lastElement = lastOrUndefined(node.parent.elements); - return lastElement !== node && !!lastElement.dotDotDotToken; - } - return false; - } - function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) { const node = getNameOfDeclaration(declaration) || declaration; if (isIdentifierThatStartsWithUnderScore(node)) { @@ -22391,10 +22360,8 @@ namespace ts { } } - if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) { - const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read; - addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name)); - } + const message = isTypeDeclaration(declaration) ? Diagnostics._0_is_declared_but_never_used : Diagnostics._0_is_declared_but_its_value_is_never_read; + addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, message, name)); } function parameterNameStartsWithUnderscore(parameterName: DeclarationName) { @@ -22456,44 +22423,86 @@ namespace ts { } } - function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile, addDiagnostic: AddUnusedDiagnostic): void { - if (!(node.flags & NodeFlags.Ambient)) { - // 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. - const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>(); - node.locals.forEach(local => { - if (local.isReferenced || local.exportSymbol) return; - for (const declaration of local.declarations) { - if (isAmbientModule(declaration)) continue; - if (isImportedDeclaration(declaration)) { - const importClause = importClauseFromImported(declaration); - const key = String(getNodeId(importClause)); - const group = unusedImports.get(key); - if (group) { - group[1].push(declaration); - } - else { - unusedImports.set(key, [importClause, [declaration]]); + function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { + const keyString = String(getKey(key)); + const group = map.get(keyString); + if (group) { + group[1].push(value); + } + else { + map.set(keyString, [key, [value]]); + } + } + + function tryGetRootParameterDeclaration(node: Node): ParameterDeclaration | undefined { + return tryCast(getRootDeclaration(node), isParameter); + } + + function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void { + if (nodeWithLocals.flags & NodeFlags.Ambient) return; + + // 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. + const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>(); + const unusedDestructures = createMap<[ObjectBindingPattern, BindingElement[]]>(); + nodeWithLocals.locals.forEach(local => { + // If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`. + // If it's a type parameter merged with a parameter, check if the parameter-side is used. + if (local.flags & SymbolFlags.TypeParameter ? !(local.flags & SymbolFlags.Variable && !(local.isReferenced & SymbolFlags.Variable)) : local.isReferenced || local.exportSymbol) { + return; + } + + for (const declaration of local.declarations) { + if (isAmbientModule(declaration)) continue; + if (isImportedDeclaration(declaration)) { + addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); + } + else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { + // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. + const lastElement = last(declaration.parent.elements); + if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) { + addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); + } + } + else { + const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration); + if (parameter) { + const name = getNameOfDeclaration(local.valueDeclaration); + if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) { + addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); } } else { errorUnusedLocal(declaration, symbolName(local), addDiagnostic); } } - }); - - unusedImports.forEach(([importClause, unuseds]) => { - const importDecl = importClause.parent; - if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) { - for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic); - } - else if (unuseds.length === 1) { - addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name))); - } - else { - addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl))); + } + }); + unusedImports.forEach(([importClause, unuseds]) => { + const importDecl = importClause.parent; + if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) { + for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic); + } + else if (unuseds.length === 1) { + addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name))); + } + else { + addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused)); + } + }); + unusedDestructures.forEach(([bindingPattern, bindingElements]) => { + const kind = tryGetRootParameterDeclaration(bindingPattern.parent) ? UnusedKind.Parameter : UnusedKind.Local; + if (!bindingPattern.elements.every(e => contains(bindingElements, e))) { + for (const e of bindingElements) { + addDiagnostic(kind, createDiagnosticForNode(e, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(e))); } - }); - } + } + else if (bindingElements.length === 1) { + addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics._0_is_declared_but_its_value_is_never_read, getBindingElementNameText(first(bindingElements)))); + } + else { + addDiagnostic(kind, createDiagnosticForNode(bindingPattern, Diagnostics.All_destructured_elements_are_unused)); + } + }); } type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 46695ab2d75bf..8b293384c43d2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3543,6 +3543,11 @@ "code": 6196, "reportsUnnecessary": true }, + "All destructured elements are unused.": { + "category": "Error", + "code": 6197, + "reportsUnnecessary": true + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 @@ -3935,6 +3940,10 @@ "category": "Message", "code": 90008 }, + "Remove destructuring": { + "category": "Message", + "code": 90009 + }, "Import '{0}' from module \"{1}\"": { "category": "Message", "code": 90013 diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index fd360c73ea1ad..b52a77dfc5147 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -8,6 +8,7 @@ namespace ts.codefix { Diagnostics._0_is_declared_but_never_used.code, Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code, Diagnostics.All_imports_in_import_declaration_are_unused.code, + Diagnostics.All_destructured_elements_are_unused.code, ]; registerCodeFix({ errorCodes, @@ -18,6 +19,10 @@ namespace ts.codefix { const changes = textChanges.ChangeTracker.with(context, t => t.deleteNode(sourceFile, importDecl)); return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; } + const delDestructure = textChanges.ChangeTracker.with(context, t => tryDeleteFullDestructure(t, sourceFile, context.span.start)); + if (delDestructure.length) { + return [createCodeFixAction(fixName, delDestructure, Diagnostics.Remove_destructuring, fixIdDelete, Diagnostics.Delete_all_unused_declarations)]; + } const token = getToken(sourceFile, textSpanEnd(context.span)); const result: CodeFixAction[] = []; @@ -50,7 +55,9 @@ namespace ts.codefix { changes.deleteNode(sourceFile, importDecl); } else { - tryDeleteDeclaration(changes, sourceFile, token); + if (!tryDeleteFullDestructure(changes, sourceFile, diag.start!)) { + tryDeleteDeclaration(changes, sourceFile, token); + } } break; default: @@ -65,6 +72,26 @@ namespace ts.codefix { return startToken.kind === SyntaxKind.ImportKeyword ? tryCast(startToken.parent, isImportDeclaration) : undefined; } + function tryDeleteFullDestructure(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number): boolean { + const startToken = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + if (startToken.kind !== SyntaxKind.OpenBraceToken || !isObjectBindingPattern(startToken.parent)) return false; + const decl = startToken.parent.parent; + switch (decl.kind) { + case SyntaxKind.VariableDeclaration: + tryDeleteVariableDeclaration(changes, sourceFile, decl); + break; + case SyntaxKind.Parameter: + changes.deleteNodeInList(sourceFile, decl); + break; + case SyntaxKind.BindingElement: + changes.deleteNode(sourceFile, decl); + break; + default: + return Debug.assertNever(decl); + } + return true; + } + function getToken(sourceFile: SourceFile, pos: number): Node { const token = findPrecedingToken(pos, sourceFile); // this handles var ["computed"] = 12; diff --git a/tests/baselines/reference/unusedDestructuring.errors.txt b/tests/baselines/reference/unusedDestructuring.errors.txt new file mode 100644 index 0000000000000..9be359ff778a7 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.errors.txt @@ -0,0 +1,35 @@ +tests/cases/compiler/unusedDestructuring.ts(3,7): error TS6197: All destructured elements are unused. +tests/cases/compiler/unusedDestructuring.ts(4,9): error TS6133: 'c' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(6,7): error TS6133: 'e' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,1): error TS6133: 'f' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,12): error TS6197: All destructured elements are unused. +tests/cases/compiler/unusedDestructuring.ts(8,24): error TS6133: 'c' is declared but its value is never read. +tests/cases/compiler/unusedDestructuring.ts(8,32): error TS6133: 'e' is declared but its value is never read. + + +==== tests/cases/compiler/unusedDestructuring.ts (7 errors) ==== + export {}; + declare const o: any; + const { a, b } = o; + ~~~~~~~~ +!!! error TS6197: All destructured elements are unused. + const { c, d } = o; + ~ +!!! error TS6133: 'c' is declared but its value is never read. + d; + const { e } = o; + ~~~~~ +!!! error TS6133: 'e' is declared but its value is never read. + + function f({ a, b }, { c, d }, { e }) { + ~~~~~~~~~~ +!!! error TS6133: 'f' is declared but its value is never read. + ~~~~~~~~ +!!! error TS6197: All destructured elements are unused. + ~ +!!! error TS6133: 'c' is declared but its value is never read. + ~~~~~ +!!! error TS6133: 'e' is declared but its value is never read. + d; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedDestructuring.js b/tests/baselines/reference/unusedDestructuring.js new file mode 100644 index 0000000000000..d025b22654280 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.js @@ -0,0 +1,26 @@ +//// [unusedDestructuring.ts] +export {}; +declare const o: any; +const { a, b } = o; +const { c, d } = o; +d; +const { e } = o; + +function f({ a, b }, { c, d }, { e }) { + d; +} + + +//// [unusedDestructuring.js] +"use strict"; +exports.__esModule = true; +var a = o.a, b = o.b; +var c = o.c, d = o.d; +d; +var e = o.e; +function f(_a, _b, _c) { + var a = _a.a, b = _a.b; + var c = _b.c, d = _b.d; + var e = _c.e; + d; +} diff --git a/tests/baselines/reference/unusedDestructuring.symbols b/tests/baselines/reference/unusedDestructuring.symbols new file mode 100644 index 0000000000000..0490774919b1d --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unusedDestructuring.ts === +export {}; +declare const o: any; +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +const { a, b } = o; +>a : Symbol(a, Decl(unusedDestructuring.ts, 2, 7)) +>b : Symbol(b, Decl(unusedDestructuring.ts, 2, 10)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +const { c, d } = o; +>c : Symbol(c, Decl(unusedDestructuring.ts, 3, 7)) +>d : Symbol(d, Decl(unusedDestructuring.ts, 3, 10)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +d; +>d : Symbol(d, Decl(unusedDestructuring.ts, 3, 10)) + +const { e } = o; +>e : Symbol(e, Decl(unusedDestructuring.ts, 5, 7)) +>o : Symbol(o, Decl(unusedDestructuring.ts, 1, 13)) + +function f({ a, b }, { c, d }, { e }) { +>f : Symbol(f, Decl(unusedDestructuring.ts, 5, 16)) +>a : Symbol(a, Decl(unusedDestructuring.ts, 7, 12)) +>b : Symbol(b, Decl(unusedDestructuring.ts, 7, 15)) +>c : Symbol(c, Decl(unusedDestructuring.ts, 7, 22)) +>d : Symbol(d, Decl(unusedDestructuring.ts, 7, 25)) +>e : Symbol(e, Decl(unusedDestructuring.ts, 7, 32)) + + d; +>d : Symbol(d, Decl(unusedDestructuring.ts, 7, 25)) +} + diff --git a/tests/baselines/reference/unusedDestructuring.types b/tests/baselines/reference/unusedDestructuring.types new file mode 100644 index 0000000000000..605f08c3bbd19 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuring.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unusedDestructuring.ts === +export {}; +declare const o: any; +>o : any + +const { a, b } = o; +>a : any +>b : any +>o : any + +const { c, d } = o; +>c : any +>d : any +>o : any + +d; +>d : any + +const { e } = o; +>e : any +>o : any + +function f({ a, b }, { c, d }, { e }) { +>f : ({ a, b }: { a: any; b: any; }, { c, d }: { c: any; d: any; }, { e }: { e: any; }) => void +>a : any +>b : any +>c : any +>d : any +>e : any + + d; +>d : any +} + diff --git a/tests/baselines/reference/unusedDestructuringParameters.errors.txt b/tests/baselines/reference/unusedDestructuringParameters.errors.txt index 24a7b3838cd1e..a9423145cbeea 100644 --- a/tests/baselines/reference/unusedDestructuringParameters.errors.txt +++ b/tests/baselines/reference/unusedDestructuringParameters.errors.txt @@ -1,5 +1,5 @@ tests/cases/compiler/unusedDestructuringParameters.ts(1,13): error TS6133: 'a' is declared but its value is never read. -tests/cases/compiler/unusedDestructuringParameters.ts(3,14): error TS6133: 'a' is declared but its value is never read. +tests/cases/compiler/unusedDestructuringParameters.ts(3,13): error TS6133: 'a' is declared but its value is never read. ==== tests/cases/compiler/unusedDestructuringParameters.ts (2 errors) ==== @@ -8,7 +8,7 @@ tests/cases/compiler/unusedDestructuringParameters.ts(3,14): error TS6133: 'a' i !!! error TS6133: 'a' is declared but its value is never read. f([1]); const f2 = ({a}) => { }; - ~ + ~~~ !!! error TS6133: 'a' is declared but its value is never read. f2({ a: 10 }); const f3 = ([_]) => { }; diff --git a/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt b/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt index e590de2989c8f..c3f89f32488f4 100644 --- a/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndObjectSpread.errors.txt @@ -1,5 +1,5 @@ -tests/cases/compiler/unusedLocalsAndObjectSpread.ts(20,15): error TS6133: 'bar' is declared but its value is never read. -tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread.ts(20,18): error TS6133: 'bar' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,21): error TS6133: 'bar' is declared but its value is never read. ==== tests/cases/compiler/unusedLocalsAndObjectSpread.ts (2 errors) ==== @@ -23,7 +23,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' const foo = { a: 1, b: 2 }; // 'a' is declared but never used const {a, ...bar} = foo; // bar should be unused - ~~~~~~ + ~~~ !!! error TS6133: 'bar' is declared but its value is never read. //console.log(bar); } @@ -32,7 +32,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread.ts(27,18): error TS6133: 'bar' const foo = { a: 1, b: 2 }; // '_' is declared but never used const {a: _, ...bar} = foo; // bar should be unused - ~~~~~~ + ~~~ !!! error TS6133: 'bar' is declared but its value is never read. //console.log(bar); } diff --git a/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt b/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt index b980b86a61da4..100f86468adcf 100644 --- a/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndObjectSpread2.errors.txt @@ -1,6 +1,6 @@ -tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(5,3): error TS6133: 'rest' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(5,6): error TS6133: 'rest' is declared but its value is never read. tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(8,1): error TS6133: 'foo' is declared but its value is never read. -tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' is declared but its value is never read. +tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,12): error TS6133: 'rest' is declared but its value is never read. ==== tests/cases/compiler/unusedLocalsAndObjectSpread2.ts (3 errors) ==== @@ -9,7 +9,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' children, // here! active: _a, // here! ...rest - ~~~~~~~ + ~~~~ !!! error TS6133: 'rest' is declared but its value is never read. } = props; @@ -20,7 +20,7 @@ tests/cases/compiler/unusedLocalsAndObjectSpread2.ts(12,9): error TS6133: 'rest' children, active: _a, ...rest - ~~~~~~~ + ~~~~ !!! error TS6133: 'rest' is declared but its value is never read. } = props; } diff --git a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt index 841e8ec95fd25..df43f082cd945 100644 --- a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt +++ b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt @@ -2,11 +2,12 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(1,12): error TS6133: 'a' tests/cases/compiler/unusedParametersWithUnderscore.ts(1,19): error TS6133: 'c' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(1,27): error TS6133: 'd' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(1,29): error TS6133: 'e___' is declared but its value is never read. +tests/cases/compiler/unusedParametersWithUnderscore.ts(5,13): error TS6197: All destructured elements are unused. tests/cases/compiler/unusedParametersWithUnderscore.ts(11,16): error TS6133: 'arg' is declared but its value is never read. tests/cases/compiler/unusedParametersWithUnderscore.ts(17,13): error TS6133: 'arg' is declared but its value is never read. -==== tests/cases/compiler/unusedParametersWithUnderscore.ts (6 errors) ==== +==== tests/cases/compiler/unusedParametersWithUnderscore.ts (7 errors) ==== function f(a, _b, c, ___, d,e___, _f) { ~ !!! error TS6133: 'a' is declared but its value is never read. @@ -20,6 +21,8 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(17,13): error TS6133: 'ar function f2({_a, __b}) { + ~~~~~~~~~ +!!! error TS6197: All destructured elements are unused. } function f3([_a, ,__b]) { diff --git a/tests/cases/compiler/unusedDestructuring.ts b/tests/cases/compiler/unusedDestructuring.ts new file mode 100644 index 0000000000000..9e2bb443ade8a --- /dev/null +++ b/tests/cases/compiler/unusedDestructuring.ts @@ -0,0 +1,13 @@ +// @noUnusedLocals: true +// @noUnusedParameters: true + +export {}; +declare const o: any; +const { a, b } = o; +const { c, d } = o; +d; +const { e } = o; + +function f({ a, b }, { c, d }, { e }) { + d; +} diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts new file mode 100644 index 0000000000000..4f3f9a3e61ebb --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////export {}; +////const { x, y } = o; + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`export {}; +`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts new file mode 100644 index 0000000000000..03c3b38878472 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts @@ -0,0 +1,18 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////export {}; +////const { x, y } = o; +////const { a, b } = o; +////a; + +verify.codeFixAll({ + fixId: "unusedIdentifier_delete", + fixAllDescription: "Delete all unused declarations", + newFileContent: +`export {}; +const { a, } = o; +a;`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts new file mode 100644 index 0000000000000..73fca1139289f --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_nested.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////export {}; +////const { x: { a, b } } = o; + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`export {}; +const { } = o;`, +}); From 540998f6b969033c7835f1fcb348780b1e24387b Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 1 May 2018 14:55:13 -0700 Subject: [PATCH 2/3] Add parameters test --- ...eFixUnusedIdentifier_destructure_allUnused_all.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts index 03c3b38878472..1c58e893a5b98 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_all.ts @@ -3,16 +3,20 @@ // @noUnusedLocals: true // @noUnusedParameters: true -////export {}; ////const { x, y } = o; ////const { a, b } = o; ////a; +////export function f({ x, y }, { a, b }) { +//// a; +////} verify.codeFixAll({ fixId: "unusedIdentifier_delete", fixAllDescription: "Delete all unused declarations", newFileContent: -`export {}; -const { a, } = o; -a;`, +`const { a, } = o; +a; +export function f({ a, }) { + a; +}`, }); From 10df13e3711ca2771bc4ccb249753ff6fbe4e196 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 8 May 2018 11:44:09 -0700 Subject: [PATCH 3/3] Add test for 'for' loop --- ...eFixUnusedIdentifier_destructure_allUnused_for.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts new file mode 100644 index 0000000000000..27103357d3dd9 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructure_allUnused_for.ts @@ -0,0 +1,12 @@ +/// + +// @noUnusedLocals: true +// @noUnusedParameters: true + +////for (const { x } of o) {} + +verify.codeFix({ + description: "Remove destructuring", + newFileContent: +`for (const {} of o) {}`, +});