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

feat: add suggestions to no-unused-vars #18352

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

Tanujkanti4441
Copy link
Contributor

Prerequisites checklist

What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[x] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

What changes did you make? (Give an overview)

added suggestion to no-unused-vars rule.

Is there anything you'd like reviewers to focus on?

Fixes: #17545

@Tanujkanti4441 Tanujkanti4441 requested a review from a team as a code owner April 16, 2024 17:08
@eslint-github-bot eslint-github-bot bot added the feature This change adds a new feature to ESLint label Apr 16, 2024
@github-actions github-actions bot added the rule Relates to ESLint's core rules label Apr 16, 2024
Copy link

netlify bot commented Apr 16, 2024

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit 34be242
🔍 Latest deploy log https://app.netlify.com/sites/docs-eslint/deploys/6659f8d1e1046f0008edcab8

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

It looks like the whole file has been reformatted, making it difficult to view the diff. Can you fix the formatting?

@@ -5,6 +5,8 @@

"use strict";

// const { parents } = require("cheerio/lib/api/traversing");
// const { remove } = require("../linter/rule-fixer");
Copy link
Member

Choose a reason for hiding this comment

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

Looks like your editor might have added some unintentional imports?

@aladdin-add aladdin-add marked this pull request as draft April 17, 2024 03:40
@Tanujkanti4441
Copy link
Contributor Author

@eslint/eslint-team, code in this PR seems to work fine but having linting error that Method 'fix' expected no return value can i get some help in figuring out what is wrong or is there something i am not aware of?

@aladdin-add
Copy link
Member

aladdin-add commented Apr 22, 2024

It's required to return a value in fixers (to catch possible errors). if you don't want to fix in some cases, please use return null;

@Tanujkanti4441
Copy link
Contributor Author

Tanujkanti4441 commented Apr 23, 2024

It's required to return a value in fixers (to catch possible errors). if you don't want to fix in some cases, please use return null;

Thanks for reply!

but is this suggestion also true for the following code?

return fixer.removeRange(parent.parent.range);

because it actually returns a value and having error Method 'fix' expected no return value

lib/rules/no-unused-vars.js Outdated Show resolved Hide resolved
lib/rules/no-unused-vars.js Outdated Show resolved Hide resolved
@Tanujkanti4441 Tanujkanti4441 marked this pull request as ready for review April 26, 2024 15:16
@Tanujkanti4441
Copy link
Contributor Author

I think it's ready for review now.

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

Left some comments related to storing variables for later use.

Also, there are a lot of if statements that aren't obvious what they're doing. Can we add some comments describing what each is accomplishing?

* @returns {Object} fixer object
*/
function fixVariables(node) {
if (node.parent.type === "VariableDeclarator") {
Copy link
Member

Choose a reason for hiding this comment

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

It looks like you're using node.parent a lot, which requires looking up that property each time. I'd suggest saving a reference to both the parent and parent type so you don't have to keep evaluating it each time:

const parent  = node.parent;
const parentType = parent.type;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here node.parent is just used in fixVariable function and parent is already declared as

const parent = id.parent

is it ok if i don't replace node.parent for now because it will help me if i use the function later?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand what you're saying. I see node.parent multiple times in the following lines, and there's just no need to keep doing the lookup.

return fixer.removeRange(node.range);
}

if (sourceCode.getTokenBefore(node).value === "(" && sourceCode.getTokenAfter(node).value === ",") {
Copy link
Member

Choose a reason for hiding this comment

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

You're also doing getTokenBefore(node) and getTokenAfter(node) a few times here. It's best to store those values in a variable to save the function call.

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!

return node.elements.filter(e => e !== null).length === 1;
}

if (parent.type === "VariableDeclarator") {
Copy link
Member

Choose a reason for hiding this comment

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

Same comment about references throughout. We want to avoid a.b.c.d as much as possible.

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!

@@ -98,7 +100,8 @@ module.exports = {

messages: {
unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.",
usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}."
usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}.",
removeVar: "remove unused variable '{{varName}}'."
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
removeVar: "remove unused variable '{{varName}}'."
removeVar: "Remove unused variable '{{varName}}'."

Comment on lines 994 to 1001
if (parent.parent.type === "ObjectPattern") {
if (parent.parent.properties.length === 1) {

// var {a} = foo;
if (parent.parent.parent.type === "VariableDeclarator") {
if (parent.parent.parent.parent.declarations.length === 1) {
return fixer.removeRange(parent.parent.parent.parent.range);
}
Copy link
Member

Choose a reason for hiding this comment

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

Patterns can also appear in for-in/for-of loops:

for (const { foo } of bar);

After the fix:

for ( of bar);

Comment on lines 863 to +865
? getAssignedMessageData(unusedVar)
: getDefinedMessageData(unusedVar)
: getDefinedMessageData(unusedVar),
suggest: [
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if we should remove the declaration if there are references to that variable.

var x;

x = 1;

After the fix:

x = 1;

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 till here!

Comment on lines 1031 to 1035
if (getTokenBeforeValue(parent.parent) === ":") {
if (parent.parent.parent.parent.type === "ObjectPattern") {
if (parent.parent.parent.parent.properties.length === 1) {
return fixVariables(parent.parent.parent.parent);
}
Copy link
Member

Choose a reason for hiding this comment

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

There seems to be a lot of repeated code for different combinations of patterns in patterns. Can we maybe unify the code? For example, when we determine that something can be removed, recursively check the parent and so on until we find the uppermost node that can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is this statement suggesting to use loops? may be i don't get it properly.
how about if we use function to reduce the repetition.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, loops or recursion. That would avoid code repetition and cover more cases.

For example, the current implementation provides a suggestion to fix const [[foo]] = bar; because it has a branch that specifically covers the array pattern in array pattern case. But it doesn't provide a suggestion to fix const [[[foo]]] = bar; although it's basically the same case, just one more level deep. The same for any combination of patterns, for example the current implementation provides a suggestion to fix const { a: [b] } = c;, but doesn't provide a suggestion to fix const [{ a: [b] }] = c;.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix for some nested patterns has implemented, working on some others, so marked it as draft.

@mdjermanovic mdjermanovic added the accepted There is consensus among the team that this change meets the criteria for inclusion label May 2, 2024
@Tanujkanti4441 Tanujkanti4441 marked this pull request as draft May 16, 2024 13:14
@Tanujkanti4441 Tanujkanti4441 marked this pull request as ready for review May 18, 2024 11:06
Copy link

Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update.

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

I've added some comments, but my previous comments weren't addressed: There are a lot of if statements that aren't obvious as to what they do. Please add comments for all of them.

Also, we need to cut down on the number of times we have node.parent and node.parent.parent throughout. These references should be stored in variables to avoid the cost of property lookup over and over again.

* @param {number} skips number of token to skip
* @returns {number} start range of token before the identifier
*/
function getBeforeToken(node, skips) {
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 to describe what the function is actually doing. Maybe getPreviousTokenStart would be a better name?

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!

* @param {number} skips number of token to skip
* @returns {number} end range of token after the identifier
*/
function getAfterToken(node, skips) {
Copy link
Member

Choose a reason for hiding this comment

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

Same here. Maybe getNextTokenEnd would make more sense?

* @param {ASTNode} node parent of identifier
* @returns {boolean} true if node is function
*/
function isTypeFunction(node) {
Copy link
Member

Choose a reason for hiding this comment

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

We have a utility function for this:

function isFunction(node) {

* @param {ASTNode} node node to check
* @returns {boolean} true if node is forOf or forIn loop
*/
function isLoop(node) {
Copy link
Member

Choose a reason for hiding this comment

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

We have a utility function for this:

function isLoop(node) {

* @returns {Object} fixer object
*/
function fixVariables(node) {
if (node.parent.type === "VariableDeclarator") {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand what you're saying. I see node.parent multiple times in the following lines, and there's just no need to keep doing the lookup.

if (node.parent.type === "VariableDeclarator") {

// skip variable in for (const [ foo ] of bar);
if (isLoop(node.parent.parent.parent)) {
Copy link
Member

Choose a reason for hiding this comment

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

Again, a lot of node.parent and node.parent.parent in the following lines. These should be pulled up into variables to avoid constant lookup.

return null;
}

if (node.parent.parent.declarations.length === 1) {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment describing what this does.

return fixer.removeRange(node.parent.parent.range);
}

if (getTokenBeforeValue(node.parent) === ",") {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment describing what this does.

return fixer.removeRange([node.parent.range[0], getAfterToken(node.parent)]);
}

if (isTypeFunction(node.parent)) {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment describing what this does.

Also note, this is almost exactly the same as earlier in the file. Maybe there's a way to extract into a function?

return fixer.removeRange([getBeforeToken(node), node.range[1]]);
}

if (getTokenBeforeValue(node) === ":") {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment describing what this does.

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion feature This change adds a new feature to ESLint rule Relates to ESLint's core rules
Projects
Status: Implementing
Development

Successfully merging this pull request may close these issues.

Rule Change: Add support for suggestions for unused var
6 participants