eslint: ESLint 6.2.0 + babel-eslint + no-unused-vars false positive with for-in loop

There’s a new issue in ESLint 6.2.0 just caught by the standard test suite. Issue did not exist in ESLint 6.1.0.

Tell us about your environment

  • ESLint Version: 6.2.0
  • Node Version: 10.16.3
  • npm Version: 6.9.0

What parser (default, Babel-ESLint, etc.) are you using? babel-eslint

Please show your full configuration:

Configuration

{
  "parserOptions": {
    "ecmaVersion": 2019,
    "ecmaFeatures": {
      "jsx": true
    },
    "sourceType": "module"
  },
  "parser": "babel-eslint",
  "rules": {
    "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }]
  }
}

What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.

function scoreAudioCoverFile (imgFile) {
  const fileName = path.basename(imgFile.name, path.extname(imgFile.name)).toLowerCase()
  const relevanceScore = {
    cover: 80,
    folder: 80,
    album: 80,
    front: 80,
    back: 20,
    spectrogram: -80
  }

  for (const keyword in relevanceScore) {
    if (fileName === keyword) {
      return relevanceScore[keyword]
    }
    if (fileName.indexOf(keyword) !== -1) {
      return relevanceScore[keyword]
    }
  }
  return 0
}
npx eslint --config eslintrc.json t.js

What did you expect to happen?

Just one error:

  1:10  error  'scoreAudioCoverFile' is defined but never used  no-unused-vars

What actually happened? Please include the actual, raw output from ESLint.

Two errors:

   1:10  error  'scoreAudioCoverFile' is defined but never used  no-unused-vars
  12:14  error  'keyword' is defined but never used              no-unused-vars

The variable keyword from the for-in loop is seen as not used.

This may be a bug in babel-eslint since the issue does not happen when the default parser is used. However, this issue did not exist with babel-eslint + 6.1.0 and does exist with babel-eslint + 6.2.0.

Issue opened on babel-eslint: https://github.com/babel/babel-eslint/issues/791

Are you willing to submit a pull request to fix this bug? Yes

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 204
  • Comments: 55 (16 by maintainers)

Commits related to this issue

Most upvoted comments

To whom it affects:

As of yesterday (I think), create-react-app finally released its fix for this babel-eslint bug in react-scripts version 3.1.2.

If you were doing any dirty hacks to temporarily get around this, now’s the time you can finally remove them.

I just noticed this as well, maybe a second example might help in finding the problem.

test.js

export const foo = bar => {
    const baz = {};
    for (const key of bar) {
        baz[key] = [];
    }
    return baz;
};
eslint --parser babel-eslint --rule '{"no-unused-vars": ["error"]}' --no-eslintrc test.js

I am using ESLint v6.7.2 and this error is happening when I use a for..of loop.

image

If anyone is still running into this issue with React Native due to @react-native-community/eslint-config having a hard reference to version 10.0.1 of babel-eslint and you’re using yarn, add this to your package.json, remove node_modules and re-run yarn:

  "resolutions": {
    "@react-native-community/eslint-config/babel-eslint": "^10.0.3"
  }

@devinrhode2 upgrade babel-eslint to v10.0.3.

Looks like webpack uses eslint-scope@4.0.3. Could that be causing issues?

I think so. Please see if a new version of webpack is available which uses a more recent eslint-scope, otherwise you might wish to install eslint-scope directly as a devDependency.

To be clear, this is completely an npm bug, not an ESLint bug. In your dependency structure, ESLint is dependent on latest eslint-scope, but for some reason, npm laid out the physical folder structure in such a way that ESLint gets an old instance of eslint-scope when the linter is run.

same happens with yarn

solved by upgrading babel-eslint to 10.0.3

@platinumazure @jharris4 both answers are correct guys, thanks! I upgraded eslint-scope from 3.7.1 to 5.0.0 and babel aslant from 10.0.1 to 10.0.3 and it now works correctly.

Kudos!

We are getting man false positives with 6.2.0 as well:

6:14 error ‘arg’ is defined but never used no-unused-vars

  for (const arg of args) {
    if (arg !== undefined) {
      return arg
    }
  }

111:16 error ‘type’ is defined but never used no-unused-vars

    for (const type of ['a', 'b']) {
      const x = Array.isArray(options[type]) ? options[type] : []

@devinrhode2 Please open a new issue with the issue template filled out and we can try to help. Thanks!

I have proposed another PR as a PATCH change for the babel-eslint repository that should be easier to integrate and I hope it will be merged soon. In the meanwhile, I have released a forked package for the ^10 version with the fixes included:

https://www.npmjs.com/package/@przemyslawzalewski/babel-eslint/v/10.0.3

Usage is simple as changing some entires in a package.json: "babel-eslint": "^10.0.2" -> "@przemyslawzalewski/babel-eslint": "^10.0.3" and "parser": "babel-eslint" -> "parser": "@przemyslawzalewski/babel-eslint" within the eslint config file.

There is also a fork of the not yet released ^11 branch, however, it may include some breaking changes: https://www.npmjs.com/package/@przemyslawzalewski/babel-eslint/v/11.0.0

babel-eslint is using an outdated version of eslint-scope and doesn’t really support ESLint 6 as the ESlint requires eslint-scope@^5.0.0 but babel-eslint requires a pinned version of 3.7.1. I have managed to fix the issue by updating the dependencies and including proposed changes to match Espree 6 AST. This fork is based on a not yet released major version of babel-eslint and may come with breaking changes. Anyway, the fork (which can be installed by just replacing the regular version inside package.json with "babel-eslint": "github:przemyslawzalewski/babel-eslint#b6965ab8dedd5a9374475e692912a0df2f239262") works for me and yields no false positives so I will wait for the babel-eslint to be released with the fixes.

I just hit the same issue with a bunch of false positives after upgrading to 6.2.0. I’m also using babel-eslint.

Each of the false positives for the no-unused-vars that I’m seeing are related to variable declared in a for…of loop, like:

for (let theVariable of theCollection)

This triggers an error for theVariable.

This may have been caused by the change to the no-unused-vars rule introduced in this PR: https://github.com/eslint/eslint/pull/12055 cc @mdjermanovic

Idk if it’s related, but note that create-react-app still hasn’t been updated with the fixed version of eslint/babel-eslint/eslint-scope/whatever. The patch has been merged but it hasn’t been released and pushed to npm yet, it looks like:

https://github.com/facebook/create-react-app/pull/7662 https://github.com/facebook/create-react-app/issues/7566

For me it has been fixed 🤷‍♀️ we’re on the latest version with no problems

@feross Thanks for confirming!

I see there’s already an issue open for babel-eslint, and it looks like we have a lead on what might be causing it. I think we should leave this issue open until it’s fixed in babel-eslint so that people who encounter this error find the temporary workaround, but can discussion on how to fix it move to that issue?

+1

It’s a bug in eslint, just upgrade eslint to v6.7.1.

@allan2coder This is not accurate, or at least not fully accurate. babel-eslint did have to write a fix on their side as well. Please ensure you have latest babel-eslint.

I upgraded to eslint v6.7.2 and still seeing this.

@MarkPare See above: You do need to upgrade babel-eslint to latest as well. If you upgrade babel-eslint and still run into this issue, please open a new issue and fill out our issue template so we can take a look at your configuration and help you more effectively. Thanks!

@marcinlesek It’s also working for me since upgrading babel-eslint to latest. What version of babel-eslint are you running?

Looks like it is still failing for this snippet of code here…

        if (this.props.data && this.props.tileData) {
            for (const key in this.props.tileData) {
                tiles.push(
                    Object.assign({}, this.props.tileData[key], {
                        id: parseInt(key.replace(/\D/g, ''), 10),
                        size: (this.props.tileData[key].size || '').toLowerCase()
                    })
                );
            }
        }

and

            if (task.errors) {
                for (const name in task.errors) {
                    if (errors[name]) {
                        errors[name] = [...errors[name], ...task.errors[name]];
                    } else {
                        errors[name] = task.errors[name];
                    }
                }
            }

It’s a bug in eslint, just upgrade eslint to v6.7.1.

@yoyo837 that worked! I’m using eslint 6.2.2. and babel-eslint 10.0.3 and it all works! 😃 thank you

I just experienced this issue, here’s another example:

  for (const [fileType, fileExt] of Object.entries(extensions)) {
    try {
      componentFiles[fileType] = dir + "/" + name + fileExt;
    } catch (e) {}
  }

@kaicataldo The issue doesn’t exist with the default parser.