diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 00956e9daf9..fb16cf5786e 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -422,10 +422,15 @@ export default util.createRule({ for (const unusedVar of unusedVars) { // Report the first declaration. if (unusedVar.defs.length > 0) { + const writeReferences = unusedVar.references.filter( + ref => + ref.isWrite() && + ref.from.variableScope === unusedVar.scope.variableScope, + ); + context.report({ - node: unusedVar.references.length - ? unusedVar.references[unusedVar.references.length - 1] - .identifier + node: writeReferences.length + ? writeReferences[writeReferences.length - 1].identifier : unusedVar.identifiers[0], messageId: 'unusedVar', data: unusedVar.references.some(ref => ref.isWrite()) diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts index 14cfff7e0b9..44ffd089377 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts @@ -1753,8 +1753,8 @@ foo.forEach(item => { }, { ...assignedError('b'), - line: 4, - column: 7, + line: 2, + column: 9, }, ], }, @@ -2601,7 +2601,7 @@ myArray = myArray.filter(x => x == 1); { ...assignedError('myArray'), line: 3, - column: 11, + column: 1, }, ], }, @@ -2628,8 +2628,8 @@ var a = function () { errors: [ { ...assignedError('a'), - line: 3, - column: 3, + line: 2, + column: 5, }, ], }, @@ -2644,7 +2644,7 @@ var a = function () { errors: [ { ...assignedError('a'), - line: 4, + line: 2, column: 5, }, ], @@ -2659,8 +2659,8 @@ const a = () => { errors: [ { ...assignedError('a'), - line: 3, - column: 3, + line: 2, + column: 7, }, ], }, @@ -2674,11 +2674,18 @@ const a = () => () => { errors: [ { ...assignedError('a'), - line: 3, - column: 3, + line: 2, + column: 7, }, ], }, + + // https://github.com/eslint/eslint/issues/14324 + { + code: 'let x = [];\nx = x.concat(x);', + parserOptions: { ecmaVersion: 2015 }, + errors: [{ ...assignedError('x'), line: 2, column: 1 }], + }, { code: ` let a = 'a'; @@ -2692,20 +2699,42 @@ function foo() { `, parserOptions: { ecmaVersion: 2020 }, errors: [ + { + ...assignedError('a'), + line: 3, + column: 1, + }, { ...definedError('foo'), line: 4, column: 10, }, - { - ...assignedError('a'), - line: 7, - column: 5, - }, ], }, { code: ` +let foo; +init(); +foo = foo + 2; +function init() { + foo = 1; +} + `, + parserOptions: { ecmaVersion: 2020 }, + errors: [{ ...assignedError('foo'), line: 4, column: 1 }], + }, + { + code: ` +function foo(n) { + if (n < 2) return 1; + return n * foo(n - 1); +} + `, + parserOptions: { ecmaVersion: 2020 }, + errors: [{ ...definedError('foo'), line: 2, column: 10 }], + }, + { + code: ` let c = 'c'; c = 10; function foo1() { diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index cca98d2a952..362466ee1a0 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -1458,8 +1458,8 @@ namespace Foo { action: 'defined', additional: '', }, - line: 4, - column: 15, + line: 2, + column: 11, }, ], }, @@ -1490,8 +1490,8 @@ namespace Foo { action: 'defined', additional: '', }, - line: 5, - column: 17, + line: 3, + column: 13, }, ], }, @@ -1506,7 +1506,8 @@ interface Foo { errors: [ { messageId: 'unusedVar', - line: 4, + line: 2, + column: 11, data: { varName: 'Foo', action: 'defined', @@ -1523,6 +1524,7 @@ type Foo = Array; { messageId: 'unusedVar', line: 2, + column: 6, data: { varName: 'Foo', action: 'defined', @@ -1550,6 +1552,7 @@ export const ComponentFoo = () => { { messageId: 'unusedVar', line: 3, + column: 10, data: { varName: 'Fragment', action: 'defined', @@ -1577,6 +1580,7 @@ export const ComponentFoo = () => { { messageId: 'unusedVar', line: 2, + column: 8, data: { varName: 'React', action: 'defined', @@ -1604,6 +1608,7 @@ export const ComponentFoo = () => { { messageId: 'unusedVar', line: 2, + column: 8, data: { varName: 'React', action: 'defined', @@ -1624,6 +1629,7 @@ declare module 'foo' { { messageId: 'unusedVar', line: 3, + column: 8, data: { varName: 'Test', action: 'defined', @@ -1649,6 +1655,7 @@ export namespace Foo { { messageId: 'unusedVar', line: 4, + column: 13, data: { varName: 'Bar', action: 'defined', @@ -1658,6 +1665,7 @@ export namespace Foo { { messageId: 'unusedVar', line: 5, + column: 15, data: { varName: 'Baz', action: 'defined', @@ -1667,6 +1675,7 @@ export namespace Foo { { messageId: 'unusedVar', line: 6, + column: 17, data: { varName: 'Bam', action: 'defined', @@ -1676,6 +1685,7 @@ export namespace Foo { { messageId: 'unusedVar', line: 7, + column: 15, data: { varName: 'x', action: 'assigned a value', @@ -1696,7 +1706,8 @@ interface Foo { errors: [ { messageId: 'unusedVar', - line: 6, + line: 2, + column: 11, data: { varName: 'Foo', action: 'defined', @@ -1705,5 +1716,23 @@ interface Foo { }, ], }, + { + code: ` +let x = null; +x = foo(x); + `, + errors: [ + { + messageId: 'unusedVar', + line: 3, + column: 1, + data: { + varName: 'x', + action: 'assigned a value', + additional: '', + }, + }, + ], + }, ], });