-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
Introduce a no-java-constructor rule. #8212
Conversation
@bolinfest, thanks for your PR! By analyzing the history of the files in this pull request, we identified @nzakas, @platinumazure and @kaicataldo to be potential reviewers. |
Thanks for the pull request, @bolinfest! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
This introduces a rule to catch the case where the developer writes this: ``` class Point { Point(x, y) { this._x = x; this._y = y; } } ``` but meant to write this: ``` class Point { constructor(x, y) { this._x = x; this._y = y; } } ```
bbf6180
to
8884bd8
Compare
LGTM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the pull request.
Personally, I'm not sure this rule is a good fit for adding to core. It seems like the problem that it's trying to prevent is Java-specific, and is also probably quite uncommon (the syntax for JS classes is very different from the syntax for Java classes anyway).
That said, let's wait and see what the rest of the team thinks.
I left a few comments on the implementation. Feel free to address them now if you'd like. Alternatively you might want to wait and see if the team accepts this proposal, so that you don't end up wasting your time if the team decides to reject the proposal. (Of course, if that happens you can always distribute your rule as a plugin and/or use it for your own projects.)
conf/eslint-recommended.js
Outdated
@@ -127,6 +127,7 @@ module.exports = { | |||
"no-invalid-this": "off", | |||
"no-irregular-whitespace": "error", | |||
"no-iterator": "off", | |||
"no-java-constructor": "error", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't enable rules in eslint:recommended
until the next major release, so please set this to "off"
for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/rules/no-java-constructor.js
Outdated
docs: { | ||
description: "disallow method names that match the class name", | ||
category: "Possible Errors", | ||
recommended: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't enable rules in eslint:recommended
until the next major release, so please set this to false
for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/rules/no-java-constructor.js
Outdated
}, | ||
|
||
// TODO(mbolin): Find out what is supposed to go here. | ||
schema: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
schema
is used to configure options that a rule can accept.
Since this rule doesn't accept any options, you would probably want to set schema
to an empty array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks, done.
lib/rules/no-java-constructor.js
Outdated
return { | ||
MethodDefinition(node) { | ||
const methodName = node.key.name; | ||
const className = node.parent.parent.id.name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will throw an error for class expressions that don't have names:
(class {
foo() {}
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I fixed the code and added a test case.
lib/rules/no-java-constructor.js
Outdated
// eslint-disable-next-line eslint-plugin/report-message-format | ||
message: "Method name should not match class name. Did you mean to use `constructor`?", | ||
fix(fixer) { | ||
return fixer.replaceText(node.key, "constructor"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this rule should be autofixable. Autofixers should generally avoid changing the runtime behavior of code (e.g. causing a working program to break). If the user actually intended to use a method name which is the same as a class name, this would cause their code to break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible/acceptable to have an option to offer an autofix that defaults to off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I removed the code for the autofix and updated the test and docs.
LGTM |
LGTM |
I've done this twice. Both times, it was maddening to debug. As I put in the documentation, I urge you to do an informal survey with some friends and put the following code in front of them and quickly ask them what it does:
I suspect they don't see it, which is why I think this makes a good candidate for a core rule. |
Maybe the issue is just with the name. For example, maybe |
Thanks for contributing to ESLint! Though I understand the motivation for the rule, I'm in agreement with @not-an-aardvark on this one. This seems like a better fit for a plugin rather than in core. |
@platinumazure That seems sensible. I'm completely flexible on the name, so if someone has a strong opinion, I'm happy to go through to change it throughout. |
Thank you for contributing. Sounds reasonable to me. Constructors which are the same name as the class name is a general practice (C++, Java, C#, ...). I'm sure this is "good to have", but I'm not sure if this is enough important to core. My 2 cents, I can accept this rule since the maintenance cost is mostly zero and implementation is here. |
Alright, I'm going to champion this. So many other languages name a constructor with the class name, and on top of that, |
@platinumazure Awesome: thanks so much! As you point out, this pattern is not limited to Java, but also C# and C++, so giving it a more Java-agnostic name as you suggested seems apt. So far, you've thrown out:
Some other ideas:
I think that |
Awesome, thanks for throwing out suggestions. That said, I don't think ESLint is supposed to care about users' feelings 😀 |
@platinumazure Do you have a preference on the name? I'd just like to decide on something and turn this PR around. I guess we can wait until midday tomorrow to see if anyone else wants to chime in to bikeshed the name. Also, do you prefer to see the commits for a PR squashed, or do you like merging in entire stacks? From http://eslint.org/docs/developer-guide/contributing/pull-requests, it seems like squashing is preferred, which is totally fine with me. It doesn't always seem to play well with GitHub's code review flow, but whatever. |
@bolinfest I apologize, I was remiss in failing to explain our intake process. We require both a champion and team consensus on any new rule or rule change proposal. I've volunteered to champion the rule and that means I will be the team member driving this forward; however, we also need at least three other team members to support this rule (indicated via thumbs-up on the original post) and no one on the team to vote against including the rule (indicated by thumbs-down on the original post). See here for more information. So my next step is to try to persuade @not-an-aardvark and @kaicataldo that this is at least a good enough idea that they don't need to thumbs-down. Thus, I apologize but I think this will take a while to turn around (and if we can't reach consensus before too long, we might have to close the issue). ESLint is already a massive project with a mind-boggling number of core rules, so we've had to tighten our process on including new rules. That said, I'm on your side here and I will do what I can to persuade the team that this is worth including. Given all of the above, I'm not in a huge rush to get a name picked. I will do my best to ensure that the rule concept gets a fair evaluation, without getting bogged down in a debate on the name. |
@platinumazure All that sounds totally fair: thanks for the detailed explanation! |
@bolinfest Sorry, forgot to answer your other question. We prefer to merge contributions in squashed form to keep the changelog simple. However, we started using Github's "Squash and Merge" function, so we are happy to review PRs with multiple commits (and that works really well with the PR Review feature as well). So please don't feel the need to squash commits unless you feel it just makes the history more readable for later reviews. Hope this makes sense 😄 |
@not-an-aardvark @kaicataldo @hzoo If we can agree on a different name, is this use case something you could find palatable for core rule inclusion? As I noted here, there are legitimate use cases for this for people coming in from different languages, and the OP has noted that it is very hard to debug this case. I don't think there would be much maintainability cost to this rule and it would be a shoe-in for |
Thanks for contributing. My vote is also 👎 for this as a core rule. But feel free to create a plugin for this rule. |
I think that the word |
Thanks for your interest in improving ESLint. Unfortunately, it looks like this issue didn't get enough support from the team and so I'm closing it. While we wish we'd be able to accommodate everyone's requests, we do need to prioritize. We've found that issues failing to reach consensus after a long time tend to never do it, and as such, we close those issues. This doesn't mean the idea isn't interesting, just that it's not something the team can commit to. |
What is the purpose of this pull request? (put an "X" next to item)
[ ] Documentation update
[ ] Bug fix (template)
[X] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:
Please describe what the rule should do:
This catches an error where the developer tries to declare an ES6 constructor by creating a method with the name of the class instead of the
constructor
keyword.What category of rule is this? (place an "X" next to just one item)
[ ] Enforces code style
[X] Warns about a potential error
[ ] Suggests an alternate way of doing something
[ ] Other (please specify:)
Provide 2-3 code examples that this rule will warn about:
This introduces a rule to catch the case where the developer writes this:
but meant to write this:
Why should this rule be included in ESLint (instead of a plugin)?
I believe this satisfies the criteria enumerated at http://eslint.org/docs/developer-guide/contributing/new-rules. It addresses a generic, non-library specific issue and provides a convenient autofix. If you are new to JavaScript and make this mistake, it can be very frustrating to debug.
What changes did you make? (Give an overview)
Introduced a new rule with documentation.
Is there anything you'd like reviewers to focus on?
Assuming the core team is open to this rule, I could use some help to ensure I have specified the correct data for the
"meta"
section.Also, I am making use of
eslint-disable-next-line eslint-plugin/report-message-format
, which I am not sure is kosher.