Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

avoid_shadowing #872

Closed
wants to merge 7 commits into from
Closed

avoid_shadowing #872

wants to merge 7 commits into from

Conversation

a14n
Copy link
Contributor

@a14n a14n commented Jan 23, 2018

Copy link
Member

@bwilkerson bwilkerson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concerns about this rule because I suspect that it will have a lot of false positives. I think we should work harder to reduce the number of lints being produced before adding it.

}

current = current.parent;
if (current == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The body of this if could be moved outside the while.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// test w/ `pub run test -N avoid_shadowing`

var top = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any tests for shadowing parameter names.

Also, I suspect that the rule as written will produce multiple lints for the same location if the name being shadowed is defined in multiple locations. Consider a test such as:

class C {
  int x;
  m(int x) {
    int x; // LINT
  }
}

(Can we verify that only one lint is produced in this case?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

It looks like several report on the same token for the same rule do not result in lint duplications.

@a14n
Copy link
Contributor Author

a14n commented Jan 24, 2018

With the exclusion pattern var x = this.x; Flutter has the 82 below lints (121 without the pattern).

@bwilkerson
Copy link
Member

That still seems like a lot, and it leaves me concerned that we haven't really defined correctly what we want to have linted and what we don't. It might be worth adding those results to the original issue to see what the reaction from the Flutter team is.

@srawlins
Copy link
Member

@a14n What happened to this? Did you check if allowing @natebosch 's example helps?

@a14n
Copy link
Contributor Author

a14n commented Oct 21, 2018

Did you check if allowing @natebosch 's example helps?

Yes there's a test with that sample.

What happened to this?

I really don't remember why this rule was put aside.

// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library linter.src.rules.avoid_shadowing;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the library directive.


bool skipInstanceMembers = false;
AstNode current = node;
while (current != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem very efficient, but perhaps in practice it doesn't matter. Have we done any timing measures for this rule?

One easy improvement would be to return before line 98 if variables is empty.

@bwilkerson
Copy link
Member

Ah, I didn't think to re-read the conversation, so I'd forgotten the issue about false positives. It would be interesting to see how many we still have and whether there are other patterns we should allow (before merging this).

@a14n
Copy link
Contributor Author

a14n commented Oct 24, 2018

Here's the list of lints on flutter:

@bwilkerson
Copy link
Member

Thanks! I'm still convinced that we don't have a reasonable definition of what does and does not need to be linted. Until we get that we won't have a rule that's worth adding.

On a meta-level, I have to wonder whether this rule would have enough value (assuming we can define it well) to warrant the amount of effort it will likely take to finish defining the rule. It might be better to put our time toward something with a higher value.

@srawlins
Copy link
Member

srawlins commented Dec 7, 2018

There are two cases which this lint rule does not catch (or at least they're not tested). I've hit both recently in real code ☹️

import prefixes

import 'dart:math' as m;

void main() {
  var rand = m.Random();
}

void fn(String m) { // lint here
  m.Random x =  null; // dart2js breaks here.
}

This even passes the analyzer, and breaks on, .e.g, dart2js compile.

A type parameter shadowing a thingy (e.g. class)

class Foo {}

class Bar<Foo> {} // lint here

I could catch this in avoid_shadowing_type_parameters, but I think these two lint rules should be more separated by the thingies they track (the shadowees), rather than the offenders, the shadowers. Seems like the code and performance would be cleaner in each.

@mit-mit
Copy link
Member

mit-mit commented Aug 20, 2019

Hi @a14n this appears stale; can we close it?

@a14n a14n closed this Aug 28, 2019
@filiph
Copy link
Contributor

filiph commented Jul 2, 2020

This would be a great lint, for a very real problem. Is there a way to resurrect this PR? Or is it already implemented by some other lint? Looking at the available lints today, it looks like we have type parameter shadowing lint, but no regular identifier shadowing lint (at least not by that name).

@pq
Copy link
Member

pq commented Jul 2, 2020

I believe this fell due to concerns about an overwhelm of false-positives. I'm not sure that was ever validated though. Or maybe the logic could be dialed back to be less aggressive?

/cc @a14n

@bwilkerson
Copy link
Member

@filiph The reason this PR was closed is because we did not get to a definition of the rule that provided enough value to outweigh the cost of the false positives. While we do allow a small number of false positives from lint rules, we have a very low tolerance for them. This rule, as written in this PR, did not meet that standard.

I believe that the core of the issue is that, while shadowing causes lots of problems, there are far too many places where shadowing is standard practice in the developer community. Perhaps a better path forward would be to follow the pattern of avoid_shadowing_type_parameters and identify a small number of uses cases where shadowing is not standard practice and hence could be linted.

@filiph
Copy link
Contributor

filiph commented Jul 2, 2020

Thanks for the explanation!

FWIW, I think a more conservative (== less false positives) lint rule would be very useful, even if it doesn't get into pedantic by default. After all, this is a common bug, and a nasty one at that. I had it in my code and caught it just yesterday, Sam says here in the thread he ("recently", at the time) hit two instances in his, Kris has a very real example in dart-lang/language#737, and, looking outside Dart, someone fixed a pretty major variable shadowing bug in OpenBSD (the UNIX distribution with emphasis on code quality) a week ago.

@bwilkerson
Copy link
Member

Absolutely! Shadowing causes all lots of bugs and anything we can do to help users find them would be great.

But studies show that developers will stop using any lint that produces too many false positives [1], so there's no value doing this unless we can identify the patterns that won't cause false positives (which is doable, it just takes effort). I'd encourage every reading this thread to open an issue with the specific use cases you've found that you'd like to have a lint for.

[1] Actually, it's worse than that. The study actually shows that a significant portion of users will ignore all lints if any one lint produces too many false positives. So it's not really "no value" it's "negative value".

@jpenna
Copy link

jpenna commented Feb 6, 2024

Arrived here because I have something like this that I'd like to avoid:

  definitions.forEach((id, definition) {
    // ...other code

    for (var id in definition.downRefIds) { // <-- I'd like to know I'm shadowing `id` here
      if (definitions[id] != null) {
        definitions[id]!.addUpRefId(id); // <-- causes bug here
      }
    }

    for (var id in definition.upRefIds) { // <-- and here
      definitions[id]!.addDownRefId(id); // <-- and bug here
    }
  });

Coming from JS/TS, there is a rule to complain about it and I like the way they did it there: https://palantir.github.io/tslint/rules/no-shadowed-variable/

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

A lint for shadowing members?
8 participants