-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): [no-unused-vars] clear error report range #8640
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
fix(eslint-plugin): [no-unused-vars] clear error report range #8640
Conversation
Thanks for the PR, @developer-bandi! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #8640 +/- ##
==========================================
- Coverage 87.40% 87.21% -0.19%
==========================================
Files 260 251 -9
Lines 12605 12308 -297
Branches 3937 3880 -57
==========================================
- Hits 11017 10735 -282
+ Misses 1313 1303 -10
+ Partials 275 270 -5
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Hi @developer-bandi , I think it would be nice to explicitly specify
{
messageId: 'unusedVar',
line: 3,
column: 1,
endColumn: 3, <<<<<<< add `endColumn` on some test cases
data: {
varName: 'foo',
action: 'assigned a value',
additional: '',
},
}, |
@yeonjuan thank you, i add test case with
|
const node = writeReferences.length | ||
? writeReferences[writeReferences.length - 1].identifier | ||
: unusedVar.identifiers[0]; | ||
|
||
const { start } = node.loc; | ||
const nodeVariableLength = node.name.length; |
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.
👍 What do you think about changing the variable names a bit?
const node = writeReferences.length | |
? writeReferences[writeReferences.length - 1].identifier | |
: unusedVar.identifiers[0]; | |
const { start } = node.loc; | |
const nodeVariableLength = node.name.length; | |
const id = writeReferences.length | |
? writeReferences[writeReferences.length - 1].identifier | |
: unusedVar.identifiers[0]; | |
const { start } = node.loc; | |
const idLength = node.name.length; |
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.
it make sense, so i change variable name. thank you!
node: writeReferences.length | ||
? writeReferences[writeReferences.length - 1].identifier | ||
: unusedVar.identifiers[0], | ||
node, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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 delete node. thank you!
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, looking closer at this... I may have led you astray, at least partially. Sorry about that! The test failures in CI are due to having removed the node
from the report, since some of the tests specifically check for the type of the node
(the following function generates error assertions that demand an Identifier
be reported on via the type
field)
typescript-eslint/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts
Lines 56 to 77 in 584db29
/** | |
* Returns an expected error for assigned-but-not-used variables. | |
* @param varName The name of the variable | |
* @param [additional] The additional text for the message data | |
* @param [type] The node type (defaults to "Identifier") | |
* @returns An expected error object | |
*/ | |
function assignedError( | |
varName: string, | |
additional = '', | |
type = AST_NODE_TYPES.Identifier, | |
): TSESLint.TestCaseError<MessageIds> { | |
return { | |
messageId: 'unusedVar', | |
data: { | |
varName, | |
action: 'assigned a value', | |
additional, | |
}, | |
type, | |
}; | |
} |
Now, it's still not clear to me that the value of the node
is actually used in any way other than being tested, though, when the loc
is also provided. I ran into the same problem in #8874 and just removed the type
from the test cases, but I'm not totally sure if that is sound.
My thought is
if it has no effect => we should remove the type
from the test, since it's not testing anything at all, but it looks like it is
if it does have an effect => just put node
back.
Maybe @JoshuaKGoldberg or @bradzacher will know for sure? Would either of you know whether there's any reason to provide a report node
if loc
is being provided?
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.
Yeah you only need to provide one or the other.
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.
@@ -419,10 +419,23 @@ export default createRule<Options, MessageIds>({ | |||
ref.from.variableScope === unusedVar.scope.variableScope, | |||
); | |||
|
|||
const id = writeReferences.length |
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.
Even though these branches collapse to do the same thing, I think it could be good to test both branches, in case one day they get separated (for example, to report different error messages)
What do you think about adding test cases with all 4 report loc fields for some cases like the following?
let foo: number;
let foo: number = 1;
foo = 2;
foo = 3;
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.
(testing)
Could you also add a test case that uses the declare
modifier with the full loc
data?
FYI - here and in other comments, there's probably no need to add new test cases; modifying existing ones to have the full loc
data would work too 🙂
This comment was marked as duplicate.
This comment was marked as duplicate.
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.
In great shape - only thing remaining blocking merge is resolution of #8640 (comment) (by removing type from test cases). Thanks!
8819bd3
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.
8127873
into
typescript-eslint:main
I believe this change broke It now crashes here because I don't see a good reason why |
It does seem a very brittle that that plugin is depending on our private, internal implementation details like that. For example if we were to change the rule to report on a different node then the rule would similarly break. From the looks of it that's a horridly inefficient plugin too.
(1) is especially bad because it means you have multiple copies of the rule. i.e. if you use both This is a really unstable setup and I'm really not sure if we should be supporting usage of our private internals like that. I'm not sure I understand the point of the plugin - what is the usecase for separating out the imports from the regular variables? What's the benefit from working this way? |
Yup, I get your argument. In this case you could argue it keeps API shape compatibility with the corresponding rule in eslint, if you need a reason. The plugin comes with a fixer, which I run when hitting Note that the ESLint plugin has almost 2m downloads per week, and I'm sure many are using it while also using typescript-eslint, so you'll likely hear from more people soon. I'm just the one who lives on the bleeding edge :D cc @sweepline – would you be open to porting this rule into |
@bradzacher we're using the plugin for auto-removing unused imports on save / build as well |
This comment has been minimized.
This comment has been minimized.
I ran into the same issue, and now it's just messed up the whole plugin. I don't think we should be doing these breaking updates. |
Let me be very clear: The other plugin monkey patches eslint's API so it can hook into our internals. There is no breaking change here. If something builds on top of private implementation details and breaks when we change those details - that is not a breaking change. That fact is not up for debate. How to best remedy this is an open question that we shall discuss amongst the maintenance team and make a decision. |
To be clear: the plugin in question is used by almost 10% of Is there any good reason such functionality shouldn't be a part of |
mabe typescript-eslint can set a new rule named |
No, it's very unlikely that 1 in 10 of our users are using A more reliable metric would be something like Sourcegraph searches of open source repositories' At least in open source land, it seems more likely that the |
Two reasons. The first is that @typescript-eslint/no-unused-vars acts as an "extension rule" on top of the base eslint/no-unused-vars rule. We don't add any major new functionality on top of base rules here. If you want that added, it'd be an issue for ESLint core. The reason why ESLint core isn't enthusiastically adding in the automatic removal of values is that it's actually a tricky thing to get right. Seemingly unused imports and variables can actually have side effects that change your code's behavior - which is very dangerous to have in an automatic rule fixer! Example of an import with a side effect: // a.js
let count = 0;
console.log("Gotcha!");
export const getCount = () => count; // b.js
// Unused. Should we auto fix to...
// import {} from "./a.js" ?
// import "./a.js" ?
// nothing at all?
import { getCount } from "./a.js";
console.log("Hi!"); Example of a variable with a side effect: let count = 0;
export function getNextCount() {
return (count += 1);
}
// Unused. Should we auto-fix to...
// getNextCount()
// nothing at all?
let unusedIsh = getNextCount(); Example of a variable without: function hasNoSideEffect() {
for (let i = 0; i < 9001 ** 7; i += 1) {}
}
// Unused. Should we auto-fix to...
// hasNoSideEffect()
// nothing at all?
let alsoUnusedIsh = hasNoSideEffect() By using a plugin like eslint-plugin-unused-imports, you explicitly are taking on the risk of changing code behavior due to side effects like that. Which, hey, might be a great sign about how you've structured your code 😄 nice job avoiding side effects! But lint rules can't make that assumption for the general case. One thing lint rules can do is add suggestions: which are like fixes but won't be applied automatically. Rule Change: Add support for suggestions for unused var tracks adding that on the ESLint side. That issue was accepted, assigned, and has an open pull request now! 🙌 For more info on the difference between a fix and a suggestion, see: |
Given that:
...the "right" solution would be for Edit: that PR was merged the same minute as I posted this comment. Nice! |
Anyway, per our PR contributing guide, a previously-merged PR isn't the right place to discuss changes. We'd generally ask that bug reports and feature requests be reported as a standalone new issue so they're more discoverable. We get that this was a blocker for everyone using Locking this issue as this specific typescript-eslint PR doesn't need any more discussion. If more issues on the topic get filed I'll make sure they show up here. Cheers! |
The issue with an autofixer is that a lot of people run them on save. So imagine this:
There's many variations of this situation - the development loop is an inherently messy thing and there's no way for a static analysis tool to delineate between "unused and it should be deleted" and "unused but it should not be deleted". There's also the issue of side-effects that Josh mentioned. If you autofix delete an import or a variable - you can change runtime behaviour because side-effects are lost. There actually used to be a fixer for There's always scope to introduce an optional fixer for people to opt-in to the danger / annoyance. But as Josh mentioned the rule is intended to be a TS-compatible replacement for the base rule. Whilst it is a complete rewrite - the options, features and reports of the rule are the same. |
PR Checklist
Overview
clear error range in
no-unused-vars
rule