Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow variable declarations from shadowing variables declared in the outer scope (no-shadow) #711

Closed
dcousens opened this issue Dec 6, 2016 · 15 comments

Comments

@dcousens
Copy link
Member

commented Dec 6, 2016

Couldn't find an issue for this, so I just thought I'd bring it up.

http://eslint.org/docs/rules/no-shadow

"no-shadow": ["error", { "builtinGlobals": true, "hoist": "functions", "allow": [] }]

Bad

if (true) {
    let b = 6;
}

function b () {}

OK

if (true) {
    let a = 3;
}

let a = 5;

Where can I see this being an issue?

let parallel = require('run-parallel')

function (array, callback) {
  let tasks = array.map((x) => {
    return (callback) => foo(x, callback)
  })

  parallel(tasks, callback)
}

Personally, the above has bitten me when callback was lost inside the inner scope, so, I run this rule locally now.

@dcousens dcousens added the enhancement label Dec 6, 2016

@dcousens

This comment has been minimized.

Copy link
Member Author

commented Dec 6, 2016

Another annoying, commonly used pattern:

foo((err) => {
  if (err) return
  bar((err) => {
    if (err) return
  })
})

err is shadowed... would probably want to define it and others like it in "allow".

@feross

This comment has been minimized.

Copy link
Member

commented Dec 8, 2016

Generally feel fine about this. Need to see how many repos in the test suite would be affected by the change, though.

@dcousens

This comment has been minimized.

Copy link
Member Author

commented Dec 15, 2016

Having turned this on local for a few days now... it has caught quite a lot of potential issues I was not previously aware of, about a 5-10% hit rate for actual bugs though.

@cesarandreu

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2016

Would the following case be an error?

const result = ['a', 'b', 'b'].reduce((result, letter) => {
  result[letter] = (result[letter] || 0) + 1
  return result
}, {})
@dcousens

This comment has been minimized.

Copy link
Member Author

commented Dec 18, 2016

@cesarandreu yes, I'm not sure if that would be configurable

@feross feross changed the title No-shadow Disallow variable declarations from shadowing variables declared in the outer scope (no-shadow) Apr 4, 2017

@feross feross added this to the standard v11 milestone Apr 4, 2017

@dcousens dcousens closed this Feb 1, 2018

@dcousens dcousens reopened this Feb 1, 2018

@patrickvenetz patrickvenetz referenced this issue May 5, 2018
41 of 58 tasks complete

@feross feross modified the milestones: standard v12, standard v13 Aug 28, 2018

@feross feross modified the milestones: standard v13, standard v14 Jul 5, 2019

@ManuelFte

This comment has been minimized.

Copy link

commented Aug 5, 2019

I went a bit more strict and used "hoist": "all" in my local settings, it really did catch a lot of issues I probably wouldn't have noticed otherwise. o.O

Wouldn't it be preferable to turn that setting to "all" instead of "functions" in the core rules?

@feross

This comment has been minimized.

Copy link
Member

commented Aug 14, 2019

While this rule seems appealing, it breaks way too many packages (33%!) even with me specifying as many exceptions as I could think of. And it's not automatically fixable. Most of these are harmless instances of shadowing.

Config:

"no-shadow": ["error", { "builtinGlobals": true, "hoist": "functions", "allow": ["err", "cb", "callback", "done", "resolve", "reject"] }]

Results:

1..188
# tests 188
# pass  121
# fail  67

I think it's too noisy to ship.

Note: we already have https://eslint.org/docs/rules/no-shadow-restricted-names enabled, which is related, but much more conservative. It only prevents shadowing important globals.

@feross feross closed this Aug 14, 2019

@feross feross removed this from the standard 14 milestone Aug 14, 2019

@mightyiam

This comment has been minimized.

Copy link
Contributor

commented Aug 14, 2019

I don't have a problem with shadowing. I wouldn't go as far as to rule it out.

@LinusU

This comment has been minimized.

Copy link
Member

commented Aug 14, 2019

I personally don't have a problem with shadowing either, I think it's appropriate in some situations ☺️

@dcousens

This comment has been minimized.

Copy link
Member Author

commented Aug 15, 2019

For me, the problem with shadowing is the temporal nature of bugs that appear.

If eslint could compare code at point A and point B and warn me that by removing a variable, I am causing a previously shadowed variable to inherit another's state... shadowing ahoy!

Until that point, I think it is a risk kept alive only by our lack of creativity with our variable names.

@ManuelFte

This comment has been minimized.

Copy link

commented Aug 18, 2019

I think shadowing, if it wasn't deliberate, is in general a bad practice and a symptom of lazy programming, whether it causes problems or not. Yes, it's harmless in most cases, but that's also the case for most rules in this standard; which is not the point. The point is forcing some good habits of ideal programming, and a habit like shadowing that has the potential to cause bugs and that can be easily avoided by merely picking different names for variables doesn't fit in that goal.

However, I acknowledge that it's a very extended practice, and if so many packages would break by implementing it in the core standard then I guess it can't be helped. For my part I'll continue disallowing it for my projects.

@mightyiam

This comment has been minimized.

Copy link
Contributor

commented Aug 18, 2019

I'd like to change my mind, please. I might have come across a shadowing bug in my short career. I wouldn't mind having this rule. I hardly ever shadow, anyway.

@feross

This comment has been minimized.

Copy link
Member

commented Aug 18, 2019

@ManuelFte @mightyiam I don't want to prevent patterns like this:

foo(err => {
  if (err) return console.error(err)
  bar(err => {
    if (err) return console.error(err)
    console.log('success!')
  })
})

If you try changing the rule and running the test suite, you'll find tons of harmless cases like this one. It's also not automatically fixable. While it's regrettable to miss out on catching some possible bugs, we can't enable rules that are too noisy or people will find standard is too annoying and stop using it. It's always a balance between pushing to add more strictness and being pragmatic. We have to take baby steps. And a rule that has mostly false positives and fails in 33% of existing repos seems too big to me.

@dcousens

This comment has been minimized.

Copy link
Member Author

commented Aug 18, 2019

@feross I think that is a reasonable position. Out of interest, were there any actual bugs found in your tests?

@feross

This comment has been minimized.

Copy link
Member

commented Aug 19, 2019

@dcousens I only skimmed it, but I didn't see any.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
6 participants
You can’t perform that action at this time.