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: Add no-loop-func option to ignore known immediate execution calls #17375

Closed
1 task done
matwilko opened this issue Jul 17, 2023 · 1 comment
Closed
1 task done
Labels
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

@matwilko
Copy link
Contributor

What rule do you want to change?

no-loop-func

What change to do you want to make?

Generate fewer warnings

How do you think the change should be implemented?

A new option

Example code

const filterKeys = {
    'a': [],
    'b': []
    'c': []
};
const values = [ ['a', 5], ['b', 6], ['c', 3], ['a', 3]];

for (var key in filterKeys) {
    filterKeys[key] = values.filter(([k,v]) => k === key).map(([k,v]) => v);
}

What does the rule currently do for this code?

no-loop-func will highlight the use of key in filter as unsafe. Naively, this is true, because it's capturing the unsafe loop variable. But the closure doesn't escape the loop, because filter is known to evaluate eagerly and immediately, and so the closure is already dropped by the time filter returns.

What will the rule do after it's changed?

I'd propose adding an extra option to no-loop-func to allow ignoring the use of otherwise unsafe variables in functions when the function is being used in methods well-known to be eager and immediately executing, so we know the unsafe closure won't escape beyond that function invocation.

This analysis obviously doesn't apply to variable use inside nested functions within these function calls, because those inner closures can escape the loop (e.g. by being embedded in an array by map)

Why as a new option? Unlike in #16902, where we can analyse the immediate use and discard of the function syntactically, we can't check that a call to filter or map is guaranteed to be the call we think it is (i.e. Array.prototype.xxx). I think this fundamentally weakens the otherwise quite strict checking of this rule, and enables unsafe captures in inappropriate situations (e.g. a random object with a map method). Therefore it should be up to users to intentionally weaken the analysis here if they wish to.

(This kind of analysis would be possible in typescript-eslint when type info is available however.)

I'd propose to begin with, all the methods on Array that do eager + immediate work on an array based on a passed function be allow-listed, so: every, filter, find, findIndex, findLast, findLastIndex, flatMap, forEach, Array.from, Array.fromAsync, group, groupToMap, map, reduce, reduceRight, some, sort, toSorted.

Participation

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

Additional comments

I'm happy to take a stab at implementation, though writing more complex rule scenarios like this is new to me, so it might be quicker/easier for someone more knowledgeable to implement.

@matwilko matwilko added enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules labels Jul 17, 2023
@matwilko matwilko changed the title Rule Change: Add option to not warn on known immediate execution calls Rule Change: [no-loop-func] Add option to not warn on known immediate execution calls Jul 17, 2023
@matwilko matwilko changed the title Rule Change: [no-loop-func] Add option to not warn on known immediate execution calls Rule Change: Add no-loop-func option to ignore known immediate execution calls Jul 18, 2023
@nzakas
Copy link
Member

nzakas commented Jul 24, 2023

Thanks for the suggestion. We try not to create rule or rule options that rely on the name of the method being called to determine what to do. Concretely, we can't know that because a method is called filter() that it's being called on an array, so it's not safe to make assumptions about that.

This rule is intentionally a bit dumb -- when you come across special cases, you can always use a disable comment for that.

@nzakas nzakas closed this as not planned Won't fix, can't repro, duplicate, stale Jul 24, 2023
@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Jan 21, 2024
@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 Jan 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
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

No branches or pull requests

2 participants