diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f865d2fa58f..2f7332c181e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31013,6 +31013,18 @@ namespace ts { return tryCast(getRootDeclaration(node), isParameter); } + function isValidUnusedLocalDeclaration(declaration: Declaration): boolean { + if (isBindingElement(declaration) && isIdentifierThatStartsWithUnderscore(declaration.name)) { + return !!findAncestor(declaration.parent, ancestor => + isArrayBindingPattern(ancestor) || isVariableDeclaration(ancestor) || isVariableDeclarationList(ancestor) ? false : + isForOfStatement(ancestor) ? true : "quit" + ); + } + + return isAmbientModule(declaration) || + (isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderscore(declaration.name!); + } + function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void { // 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[]]>(); @@ -31026,8 +31038,7 @@ namespace ts { } for (const declaration of local.declarations) { - if (isAmbientModule(declaration) || - (isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderscore(declaration.name!)) { + if (isValidUnusedLocalDeclaration(declaration)) { continue; } diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.js b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.js new file mode 100644 index 00000000000..0c08ba84193 --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.js @@ -0,0 +1,28 @@ +//// [unusedVariablesWithUnderscoreInForOfLoop.ts] +function f() { + for (const [_a, b] of [['key', 1]]) { + console.log(b); + } + + for (const [a, _b] of [['key', 1]]) { + console.log(a); + } + + for (const [_a, _b] of [['key', 1]]) {} +} + + +//// [unusedVariablesWithUnderscoreInForOfLoop.js] +function f() { + for (var _i = 0, _c = [['key', 1]]; _i < _c.length; _i++) { + var _d = _c[_i], _a = _d[0], b = _d[1]; + console.log(b); + } + for (var _e = 0, _f = [['key', 1]]; _e < _f.length; _e++) { + var _g = _f[_e], a = _g[0], _b = _g[1]; + console.log(a); + } + for (var _h = 0, _j = [['key', 1]]; _h < _j.length; _h++) { + var _k = _j[_h], _a = _k[0], _b = _k[1]; + } +} diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.symbols b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.symbols new file mode 100644 index 00000000000..d6c97536d3b --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.symbols @@ -0,0 +1,31 @@ +=== tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop.ts === +function f() { +>f : Symbol(f, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 0, 0)) + + for (const [_a, b] of [['key', 1]]) { +>_a : Symbol(_a, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 1, 16)) +>b : Symbol(b, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 1, 19)) + + console.log(b); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>b : Symbol(b, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 1, 19)) + } + + for (const [a, _b] of [['key', 1]]) { +>a : Symbol(a, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 5, 16)) +>_b : Symbol(_b, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 5, 18)) + + console.log(a); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>a : Symbol(a, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 5, 16)) + } + + for (const [_a, _b] of [['key', 1]]) {} +>_a : Symbol(_a, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 9, 16)) +>_b : Symbol(_b, Decl(unusedVariablesWithUnderscoreInForOfLoop.ts, 9, 19)) +} + diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types new file mode 100644 index 00000000000..3ba565b7914 --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop.ts === +function f() { +>f : () => void + + for (const [_a, b] of [['key', 1]]) { +>_a : string | number +>b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 + + console.log(b); +>console.log(b) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>b : string | number + } + + for (const [a, _b] of [['key', 1]]) { +>a : string | number +>_b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 + + console.log(a); +>console.log(a) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>a : string | number + } + + for (const [_a, _b] of [['key', 1]]) {} +>_a : string | number +>_b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 +} + diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.errors.txt b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.errors.txt new file mode 100644 index 00000000000..fa8b7d9d55c --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts(2,21): error TS6133: 'b' is declared but its value is never read. +tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts(4,17): error TS6133: 'a' is declared but its value is never read. +tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts(6,17): error TS6133: 'a' is declared but its value is never read. +tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts(6,20): error TS6133: 'b' is declared but its value is never read. + + +==== tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts (4 errors) ==== + function f() { + for (const [_a, b] of [['key', 1]]) {} + ~ +!!! error TS6133: 'b' is declared but its value is never read. + + for (const [a, _b] of [['key', 1]]) {} + ~ +!!! error TS6133: 'a' is declared but its value is never read. + + for (const [a, b] of [['key', 1]]) {} + ~ +!!! error TS6133: 'a' is declared but its value is never read. + ~ +!!! error TS6133: 'b' is declared but its value is never read. + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.js b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.js new file mode 100644 index 00000000000..bb5ecbfe974 --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.js @@ -0,0 +1,22 @@ +//// [unusedVariablesWithUnderscoreInForOfLoop1.ts] +function f() { + for (const [_a, b] of [['key', 1]]) {} + + for (const [a, _b] of [['key', 1]]) {} + + for (const [a, b] of [['key', 1]]) {} +} + + +//// [unusedVariablesWithUnderscoreInForOfLoop1.js] +function f() { + for (var _i = 0, _c = [['key', 1]]; _i < _c.length; _i++) { + var _d = _c[_i], _a = _d[0], b = _d[1]; + } + for (var _e = 0, _f = [['key', 1]]; _e < _f.length; _e++) { + var _g = _f[_e], a = _g[0], _b = _g[1]; + } + for (var _h = 0, _j = [['key', 1]]; _h < _j.length; _h++) { + var _k = _j[_h], a = _k[0], b = _k[1]; + } +} diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.symbols b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.symbols new file mode 100644 index 00000000000..b98293944af --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts === +function f() { +>f : Symbol(f, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 0, 0)) + + for (const [_a, b] of [['key', 1]]) {} +>_a : Symbol(_a, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 1, 16)) +>b : Symbol(b, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 1, 19)) + + for (const [a, _b] of [['key', 1]]) {} +>a : Symbol(a, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 3, 16)) +>_b : Symbol(_b, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 3, 18)) + + for (const [a, b] of [['key', 1]]) {} +>a : Symbol(a, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 5, 16)) +>b : Symbol(b, Decl(unusedVariablesWithUnderscoreInForOfLoop1.ts, 5, 18)) +} + diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.types b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.types new file mode 100644 index 00000000000..100e8719148 --- /dev/null +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop1.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts === +function f() { +>f : () => void + + for (const [_a, b] of [['key', 1]]) {} +>_a : string | number +>b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 + + for (const [a, _b] of [['key', 1]]) {} +>a : string | number +>_b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 + + for (const [a, b] of [['key', 1]]) {} +>a : string | number +>b : string | number +>[['key', 1]] : (string | number)[][] +>['key', 1] : (string | number)[] +>'key' : "key" +>1 : 1 +} + diff --git a/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop.ts b/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop.ts new file mode 100644 index 00000000000..064e1971ead --- /dev/null +++ b/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop.ts @@ -0,0 +1,13 @@ +//@noUnusedLocals:true + +function f() { + for (const [_a, b] of [['key', 1]]) { + console.log(b); + } + + for (const [a, _b] of [['key', 1]]) { + console.log(a); + } + + for (const [_a, _b] of [['key', 1]]) {} +} diff --git a/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts b/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts new file mode 100644 index 00000000000..0848f20d34a --- /dev/null +++ b/tests/cases/compiler/unusedVariablesWithUnderscoreInForOfLoop1.ts @@ -0,0 +1,9 @@ +//@noUnusedLocals:true + +function f() { + for (const [_a, b] of [['key', 1]]) {} + + for (const [a, _b] of [['key', 1]]) {} + + for (const [a, b] of [['key', 1]]) {} +}