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

Rule Change: no-unused-vars should consider this is used #17299

Closed
1 task
fisker opened this issue Jun 21, 2023 · 11 comments · Fixed by #17320
Closed
1 task

Rule Change: no-unused-vars should consider this is used #17299

fisker opened this issue Jun 21, 2023 · 11 comments · Fixed by #17320
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules

Comments

@fisker
Copy link
Contributor

fisker commented Jun 21, 2023

What rule do you want to change?

no-unused-vars

What change to do you want to make?

Generate fewer warnings

How do you think the change should be implemented?

Other

Example code

let promise;

function doSomeThingOnce() {
  promise ??= doSomeThing();
}

function doSomeThing() {}
doSomeThingOnce();
doSomeThingOnce();

What does the rule currently do for this code?

Error 1:5 'promise' is assigned a value but never used.  ([no-unused-vars](https://eslint.org/docs/rules/no-unused-vars))

playground

What will the rule do after it's changed?

Should not report this case.

Participation

  • I am willing to submit a pull request to implement this change.

Additional comments

No response

@fisker fisker added enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules labels Jun 21, 2023
@Rec0iL99
Copy link
Member

Rec0iL99 commented Jun 21, 2023

Hi @fisker, can you please provide more context as to why this change is needed?

The variable promise is still unused here in the example and ESLint is right in reporting this right? Am I missing something here?

@aladdin-add
Copy link
Member

aladdin-add commented Jun 21, 2023

technically it's a bug to me.

// same as
promise = promise ?? doSomeThing();
promise = promise ?? doSomeThing();

promise was assigned a value and its value was actually read and used.

@fisker
Copy link
Contributor Author

fisker commented Jun 21, 2023

@Rec0iL99 Let me change the question. How should I fix my case?

@Rec0iL99
Copy link
Member

Rec0iL99 commented Jun 21, 2023

// same as
promise = promise ?? doSomeThing();
promise = promise ?? doSomeThing();

Ah, ok I get your point now. My apologies.

no-unused-vars rule is reported here:

let promise;

function doSomeThingOnce() {
  promise ??= doSomething();
}

function doSomeThing() {}
doSomeThingOnce();
doSomeThingOnce();

But not here where it is doing the same thing:

let promise;

function doSomeThingOnce() {
  promise = promise ?? doSomething();
}

function doSomeThing() {}
doSomeThingOnce();
doSomeThingOnce();

Looks like a bug.

@gweesin
Copy link
Contributor

gweesin commented Jun 24, 2023

@Rec0iL99 It looks interesting. The promise variable is not actually used based on the result, but from the perspective of reading and writing the variable, it is indeed being used.

So, could I consider this to be a valid expression?

Now I intend to make these two codeblocks consistent, but I have encountered some difficulties. I have identified the problem in the isUnusedExpression method, but I am currently unsure how to handle this scenario and make it compatible with all shorthand assignment statements.

/**
 * Checks whether a given node is unused expression or not.
 * @param {ASTNode} node The node itself
 * @returns {boolean} The node is an unused expression.
 * @private
 */
function isUnusedExpression(node) {
    const parent = node.parent;

    if (parent.type === "ExpressionStatement") {
        // TODO we need to update this method or take some action here to resolve this question
        return true;
    }

    if (parent.type === "SequenceExpression") {
        const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;

        if (!isLastExpression) {
            return true;
        }
        return isUnusedExpression(parent);
    }

    return false;
}

@gweesin
Copy link
Contributor

gweesin commented Jun 24, 2023

😕 I apologize for my impatience. We should first discuss which segment's result should be considered normal before considering how to modify it.

@aladdin-add
Copy link
Member

IMHO, it should be seen as used for promise = promise ?? doSomething() and promise ??= doSomething(), so no linting errors should be reported. marking as accepted.

@aladdin-add aladdin-add added the accepted There is consensus among the team that this change meets the criteria for inclusion label Jun 26, 2023
@mdjermanovic
Copy link
Member

I think promise should be considered used if there's a promise ??=, promise ||=, or promise &&= reference.

var promise1, promise2;

promise1 ?? x; // this is considered using `promise1`
promise2 ??= x; // so this should be considered using `promise2`

The fix would check the assignment operator here:

(
parent.type === "AssignmentExpression" &&
parent.left === id &&
isUnusedExpression(parent)
) ||

@gweesin would you like to work on this?

@gweesin
Copy link
Contributor

gweesin commented Jun 26, 2023

@mdjermanovic Yes, I'm glad to have the opportunity to try and resolve this issue.

While attempting to fix this issue, I noticed a conflict with existing test cases. I am a bit confused and would appreciate some clarification on what behavior would be considered reasonable in this case.

This is the brief example mentioned above.

var promise1, promise2;

promise1 ?? x; // this is considered using `promise1`
promise2 ??= x; // so this should be considered using `promise2`

if the cases are considered used, then why should the following scenario be considered as not being used?

var promise1, promise2;

promise1 + 1; // this is considered using `promise1`
promise2 += 1; // however, the current test cases expects `promise2` is never used 

in my opinion, the +=, |= and ??= are all AssignmentExpressions, right?

If we expect promise2 ??= x; to be considered used, should promise2 += x; to be considered used?

I apologize for the lengthy description, but I believe it is necessary to ensure the specific expected behavior. Only then can I determine whether I need to accommodate just the current issue or make adjustments to the previous behavior and corresponding test cases as well.

I am eagerly looking forward to your response.

@Skalman
Copy link

Skalman commented Jun 28, 2023

If we expect promise2 ??= x; to be considered used, should promise2 += x; to be considered used?

No. In promise2 ??= x, x is conditionally evaluated, but in promise2 += x, x is always evaluated, making promise2 unnecessary.

// These are equivalent
promise2 ??= x;

if (promise2 == null) { // <-- it's used here
  promise2 = x;
}

Such an if statement is hidden in ??=, ||= and &&=, but not in any of the other assignment operators.

@gweesin
Copy link
Contributor

gweesin commented Jun 29, 2023

Thank you for the explanation. I think I understand why we need to do it this way. I will consider how to adjust the code accordingly.

gweesin added a commit to gweesin/eslint that referenced this issue Jun 29, 2023
gweesin added a commit to gweesin/eslint that referenced this issue Jun 30, 2023
mdjermanovic pushed a commit that referenced this issue Jun 30, 2023
…17320)

* fix: no-unused-vars should be corrected in logical assignment operator

Fixes #17299

* refactor: use astUtils.isLogicalAssignmentOperator check exception
@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Dec 28, 2023
@eslint-github-bot eslint-github-bot bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Dec 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

7 participants
@Skalman @fisker @aladdin-add @gweesin @mdjermanovic @Rec0iL99 and others