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: ignore IIFE's in the no-loop-func rule #17528

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

Conversation

snitin315
Copy link
Contributor

Prerequisites checklist

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

[x] 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)

Fixes #16902

var arr = [];

for (var i = 0; i < 5; i++) {
    arr.push((f => f)((() => i)())); // OK
}

for (var i = 0; i < 5; i++) {
    arr.push((() => {
        return (() => i)(); // OK
    })());
}

for (var i = 0; i < 5; i++) {
    arr.push((f => f)(() => i)); // Error
}

for (var i = 0; i < 5; i++) {
    arr.push((() => {
        return () => i; // Error
    })());
}

for (var i = 0; i < 5; i++) {
    (function fun () {
        if (arr.includes(fun)) return i; // Error
        else arr.push(fun);
    })();
}

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

@snitin315 snitin315 requested a review from a team as a code owner September 2, 2023 12:25
@eslint-github-bot eslint-github-bot bot added the feature This change adds a new feature to ESLint label Sep 2, 2023
@netlify
Copy link

netlify bot commented Sep 2, 2023

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit 18cddef
🔍 Latest deploy log https://app.netlify.com/sites/docs-eslint/deploys/6594d4c95fae9b0008630cbb

@snitin315 snitin315 added rule Relates to ESLint's core rules accepted There is consensus among the team that this change meets the criteria for inclusion labels Sep 2, 2023
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.

Code LGTM. Can you also add a note in the rule details that explains that IIFEs are ignored while generators/async are not?

docs/src/rules/no-loop-func.md Show resolved Hide resolved
nzakas
nzakas previously approved these changes Sep 12, 2023
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.

LGTM. Just a suggestion clean up the docs.

docs/src/rules/no-loop-func.md Show resolved Hide resolved
lib/rules/no-loop-func.js Show resolved Hide resolved
lib/rules/no-loop-func.js Outdated Show resolved Hide resolved
lib/rules/no-loop-func.js Show resolved Hide resolved
lib/rules/no-loop-func.js Show resolved Hide resolved
lib/rules/no-loop-func.js Show resolved Hide resolved
@mdjermanovic
Copy link
Member

I think we could simplify this a lot by taking a different approach, that is to skip IIFEs (that are not async, generators, or self-referencing) here and thus let the rule check and report their inner functions directly.

case "ArrowFunctionExpression":
case "FunctionExpression":
case "FunctionDeclaration":
// We don't need to check nested functions.
return null;

For example, the current version of this rule reports error for unsafe reference to i on the IIFE:

/* eslint no-loop-func: "error" */

var arr = [];

for (var i = 0; i < 5; i++) {
    arr.push((
        () => { // <-- error here
            return () => i;
        }
    )());
}

After this change, it would report the same error directly on the inner function:

/* eslint no-loop-func: "error" */

var arr = [];

for (var i = 0; i < 5; i++) {
    arr.push((
        () => {
            return () => i; // <-- error here
        }
    )());
}

function checkForLoops could just skip IIFEs without checking anything about their inner functions since they'll be checked directly later in the traversal.

The only downside is that this can change error locations (and thus existing eslint-disable-next?-line comments would have to be reallocated) and the total number of errors if there are multiple inner functions, but I think it's okay for a semver-minor release.

@github-actions
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.

@github-actions github-actions bot added the Stale label Sep 27, 2023
@Rec0iL99 Rec0iL99 removed the Stale label Sep 28, 2023
@Rec0iL99
Copy link
Member

Not Stale. @snitin315 is less active at the moment. Will be back soon.

@github-actions
Copy link

github-actions bot commented Oct 8, 2023

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.

@github-actions github-actions bot added the Stale label Oct 8, 2023
@Rec0iL99 Rec0iL99 removed the Stale label Oct 10, 2023
@github-actions
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.

@github-actions github-actions bot added the Stale label Oct 20, 2023
@snitin315
Copy link
Contributor Author

The only downside is that this can change error locations (and thus existing eslint-disable-next?-line comments would have to be reallocated) and the total number of errors if there are multiple inner functions, but I think it's okay for a semver-minor release.

@mdjermanovic Once we changed error locations for max-lines-per-function, we had to revert it later in #15397. So, I'm not sure if we should do it in a minor release here.

@snitin315 snitin315 removed the Stale label Oct 21, 2023
@mdjermanovic
Copy link
Member

The only downside is that this can change error locations (and thus existing eslint-disable-next?-line comments would have to be reallocated) and the total number of errors if there are multiple inner functions, but I think it's okay for a semver-minor release.

@mdjermanovic Once we changed error locations for max-lines-per-function, we had to revert it later in #15397. So, I'm not sure if we should do it in a minor release here.

I think it's okay in this case. For max-lines-per-function, the change wasn't a bug fix and it was affecting every lint problem reported by this rule, while this change is considered a bug fix and affects only problems on IIFEs, and most eslint-disable comments on IIFEs should be just removed so I wouldn't expect many cases where eslint-disable comments need to be reallocated (Loop > IIFE with inner functions seems like a rare case).

Copy link

github-actions bot commented Nov 2, 2023

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.

@github-actions github-actions bot added the Stale label Nov 2, 2023
@Rec0iL99 Rec0iL99 removed the Stale label Nov 3, 2023
@snitin315
Copy link
Contributor Author

Makes sense, I'll update the PR 👍🏻

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.

@github-actions github-actions bot added the Stale label Nov 17, 2023
@Rec0iL99
Copy link
Member

Ping @snitin315

@Rec0iL99 Rec0iL99 removed the Stale label Nov 18, 2023
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.

@github-actions github-actions bot added the Stale label Nov 28, 2023
@snitin315 snitin315 self-assigned this Nov 29, 2023
@snitin315 snitin315 removed the Stale label Nov 29, 2023
@snitin315
Copy link
Contributor Author

@mdjermanovic Thanks for the detailed review, I've resolved and added corresponding test cases to your comments.

// We don't need to check nested functions.
return null;
// We need to check nested functions only in case of IIFE.
if (isIIFE(node)) {
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
if (isIIFE(node)) {
if (isIIFE(parent)) {

Comment on lines +206 to +247
if (!(node.async || node.generator)) {
const isFunctionExpression = node.type === "FunctionExpression";

if (isIIFE(node)) {

// Check if the function is referenced elsewhere in the code
const isFunctionReferenced = isFunctionExpression && node.id ? references.some(r => r.identifier.name === node.id.name) : false;

// Check if there are unsafe references used within non-immediately-invoked nested functions
const unsafeRefsInNonIIFE = unsafeRefs.filter(r => {
let refScope = r.from.variableScope;

if (node === refScope.block) {
return !isIIFE(refScope.block);
}

while (node !== refScope.block) {
if (!isIIFE(refScope.block)) {
return true;
}
refScope = refScope.upper.variableScope;
}

return false;
});

const unsafeRefsInNonIIFEName = unsafeRefsInNonIIFE.map(r => r.identifier.name);

if (!isFunctionReferenced && unsafeRefsInNonIIFEName.length === 0) {
return;
}

if (unsafeRefsInNonIIFEName.length > 0) {
context.report({
node,
messageId: "unsafeRefs",
data: { varNames: `'${unsafeRefsInNonIIFEName.join("', '")}'` }
});
return;
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

There is no need to check inner functions because they'll be checked separately.

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: [no-loop-func] add an option to ignore IIFEs
4 participants