Skip to content

feat(lint/useIterableCallbackReturn): add allowImplicit option#9489

Open
mvanhorn wants to merge 7 commits intobiomejs:nextfrom
mvanhorn:osc/9445-allow-implicit-callback-return
Open

feat(lint/useIterableCallbackReturn): add allowImplicit option#9489
mvanhorn wants to merge 7 commits intobiomejs:nextfrom
mvanhorn:osc/9445-allow-implicit-callback-return

Conversation

@mvanhorn
Copy link

Summary

Adds an allowImplicit option to the useIterableCallbackReturn rule, matching ESLint's allowImplicit option for array-callback-return.

When allowImplicit is true, callbacks for methods that require a return value (like map, filter, find, etc.) can use return; to implicitly return undefined. This supports patterns like:

[1, 2, 3].map((x) => {
    if (x > 2) return x;
    return; // intentionally returns undefined
});

The default is false, preserving the existing behavior.

As discussed in the issue, this was identified as an enhancement rather than a bug fix. The maintainer confirmed this should match ESLint's behavior.

Fixes #9445

Test Plan

  • cargo check -p biome_js_analyze passes
  • Rule documentation updated with option description and examples

Docs

Rule documentation is updated inline with the new allowImplicit option, including a JSON config example and a JavaScript code example.

AI Assistance

This PR was written with AI assistance (Claude Code).

Add `allowImplicit` option that permits callbacks of methods requiring
return values (map, filter, etc.) to use `return;` for implicit
undefined returns. This matches ESLint's `allowImplicit` option for the
`array-callback-return` rule.

When enabled, `return;` in map/filter/etc. callbacks is accepted as
intentional. When disabled (the default), the existing behavior is
preserved.

Fixes biomejs#9445

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 15, 2026

🦋 Changeset detected

Latest commit: fa7c864

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Mar 15, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces an allowImplicit option to the useIterableCallbackReturn lint rule. When enabled, the rule permits explicit bare return statements (returning undefined) within iterable callbacks. The changes span the rule implementation, options configuration, and test cases. When allowImplicit is true, the diagnostic logic branches differently, allowing implicit undefined returns whilst still detecting genuinely suspicious return patterns. Default behaviour remains unchanged.

Possibly related PRs

Suggested labels

A-Diagnostic

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: adding an allowImplicit option to the useIterableCallbackReturn rule.
Description check ✅ Passed The description clearly explains the allowImplicit option, its purpose, examples, and how it aligns with ESLint's behaviour.
Linked Issues check ✅ Passed The PR fully addresses issue #9445 by implementing the allowImplicit option to permit bare returns in iterable callbacks whilst preserving existing detection of truly suspicious returns.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the allowImplicit option and its test coverage; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs`:
- Around line 217-233: The allow_implicit branch currently only reports
MissingReturnWithValue when there are exclusively fall-through paths and no
returns at all, missing cases where some explicit returns exist but other paths
fall through; update the block guarded by allow_implicit to mirror the logic
used in the non-allow branch for fall-throughs: if
returns_info.has_paths_without_returns then if
returns_info.returns_with_value.is_empty() push
RuleProblemKind::MissingReturnWithValue else push
RuleProblemKind::NotAllPathsReturnValue, leaving the treatment of
returns_without_value unchanged; adjust the code around allow_implicit,
returns_info, and problems to implement this behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0cce0f02-da88-4157-870f-901303f491ab

📥 Commits

Reviewing files that changed from the base of the PR and between fe9ff6b and dde21d6.

📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs
  • crates/biome_rule_options/src/use_iterable_callback_return.rs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/warm-clouds-float.md:
- Around line 1-3: The changeset currently declares a patch release for the
package "@biomejs/biome" but adds a new option (a feature) so update the
changeset header in .changeset/warm-clouds-float.md: change the value from
"patch" to "minor" for the "@biomejs/biome" entry so the release type correctly
reflects a minor (feature) bump.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f503e0c3-b720-4a86-9f89-d91c479ee38e

📥 Commits

Reviewing files that changed from the base of the PR and between dde21d6 and 78b550c.

📒 Files selected for processing (1)
  • .changeset/warm-clouds-float.md

mvanhorn and others added 2 commits March 15, 2026 10:13
Adding a new option is a feature addition under semver.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cit mode

When allowImplicit is true and some paths return values while others
fall through without any return, report NotAllPathsReturnValue. Previously
only the case where NO returns existed at all was caught.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.js (1)

1-31: Good test coverage for the core allowImplicit behaviour.

The test cases comprehensively cover the main scenarios. One small suggestion: the PR objectives mention the .filter(Boolean) chaining pattern as a use case. You might consider adding a test demonstrating this idiom:

// Valid: mapping with intentional undefined, then filtering
[1, 2, 3].map((x) => {
    if (x > 2) return x;
    return;
}).filter(Boolean);

That said, functionally it's the same as case 1, so this is entirely optional.

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.js`
around lines 1 - 31, Add a test case to the existing allowImplicit spec
demonstrating the common `.map(...).filter(Boolean)` idiom: append a snippet
that maps with an explicit empty `return;` on some paths (same pattern as the
first valid case) and then chains `.filter(Boolean)` to show that intentional
undefineds are filtered out; reference the arrow callback used in the existing
tests (the `.map((x) => { if (x > 2) return x; return; })` pattern) so the new
test mirrors that behavior but adds the `.filter(Boolean)` call to the
expression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.js`:
- Around line 1-31: Add a test case to the existing allowImplicit spec
demonstrating the common `.map(...).filter(Boolean)` idiom: append a snippet
that maps with an explicit empty `return;` on some paths (same pattern as the
first valid case) and then chains `.filter(Boolean)` to show that intentional
undefineds are filtered out; reference the arrow callback used in the existing
tests (the `.map((x) => { if (x > 2) return x; return; })` pattern) so the new
test mirrors that behavior but adds the `.filter(Boolean)` call to the
expression.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26c43933-27c6-4b97-b354-3546e80348a5

📥 Commits

Reviewing files that changed from the base of the PR and between e89b399 and 455711e.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.js
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/allowImplicit.options.json

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Thank you! Still some work required

  • tests needs to be separated
  • the PR most go next, as per contribution guidelines

/// This is useful for patterns like mapping with early returns where some paths
/// intentionally return `undefined`.
///
/// This matches ESLint's [`allowImplicit`](https://eslint.org/docs/latest/rules/array-callback-return#allowimplicit) option.
Copy link
Member

Choose a reason for hiding this comment

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

This information isn't needed

Copy link
Author

Choose a reason for hiding this comment

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

Removed in a742f34.

Comment on lines +134 to +137
/// [1, 2, 3].map((x) => {
/// if (x > 2) return x;
/// return;
/// });
Copy link
Member

Choose a reason for hiding this comment

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

Can we have a snippet that is more real?

For example we should a .filter(Boolean)

Copy link
Author

Choose a reason for hiding this comment

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

Updated in a742f34: doc example now shows the .filter(Boolean) chaining pattern.

@@ -0,0 +1,31 @@
// Valid: empty return is accepted with allowImplicit
Copy link
Member

Choose a reason for hiding this comment

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

The file should have // should not generate diagnostics comment on top

Copy link
Author

Choose a reason for hiding this comment

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

Done in a742f34: split into allowImplicitValid.js with the header.

Comment on lines +2 to +31
[1, 2, 3].map((x) => {
if (x > 2) return x;
return;
});

// Valid: all paths return values
[1, 2, 3].map((x) => {
if (x > 2) return x;
return 0;
});

// Valid: implicit return (arrow expression)
[1, 2, 3].map((x) => x * 2);

// Invalid: some paths return values, others fall through without any return
[1, 2, 3].map((x) => {
if (x > 2) return x;
// falls through - likely a bug
});

// Invalid: no returns at all
[1, 2, 3].map((x) => {
console.log(x);
});

// Valid: only empty returns (all paths covered)
[1, 2, 3].forEach((x) => {
if (x > 2) return;
console.log(x);
});
Copy link
Member

Choose a reason for hiding this comment

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

We don't mix valid and invalid cases. Valid in a file, invalid in another file

Copy link
Author

Choose a reason for hiding this comment

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

Split into allowImplicitValid.js and allowImplicitInvalid.js in a742f34. Also retargeted the PR to next per contribution guidelines.

- Split allowImplicit tests into valid/invalid files per convention
- Add "// should not generate diagnostics" header to valid file
- Replace basic map example with .filter(Boolean) pattern in docs
- Remove ESLint reference and inline comments from rule logic
@mvanhorn mvanhorn changed the base branch from main to next March 16, 2026 14:12
/// }).filter(Boolean);
/// ```
///
/// When `allowImplicit` is `true`, the above code will not trigger any diagnostic.
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
/// When `allowImplicit` is `true`, the above code will not trigger any diagnostic.

That's implied by the infrastructure

Copy link
Author

Choose a reason for hiding this comment

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

Removed in 629f724.

@@ -0,0 +1,10 @@
// some paths return values, others fall through without any return
Copy link
Member

Choose a reason for hiding this comment

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

Invalid files must have // should not generate diagnostics. You're using an AI agent, load the skills we provide.

Copy link
Author

Choose a reason for hiding this comment

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

I read your CLAUDE.md and the analyzer CONTRIBUTING.md - thank you for maintaining those. On this comment: adding // should not generate diagnostics to the invalid file causes the test infrastructure to expect zero diagnostics, which fails since the file is meant to produce them. The existing invalid.js for this rule also has no such header. I kept the invalid file without the header so tests pass. Let me know if I'm misunderstanding the convention.

Re: AI disclosure - this PR uses Claude Code for implementation. I should have stated that upfront per your contribution guidelines. All tests verified locally.

Copy link
Member

@ematipico ematipico Mar 16, 2026

Choose a reason for hiding this comment

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

Yeah sorry, between the two files I got confused, but it's easily understandable:

  • valid -> should not
  • invalid -> should

Every file should have the top-level comments

Copy link
Author

Choose a reason for hiding this comment

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

Added // should generate diagnostics to the invalid file in fa7c864. Thanks for clarifying the convention.

@@ -0,0 +1,29 @@
// should not generate diagnostics
Copy link
Member

Choose a reason for hiding this comment

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

That's wrong

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in 629f724 - switched to /* should not generate diagnostics */ block comment to match the existing valid.js convention.

mvanhorn and others added 2 commits March 16, 2026 08:39
- Remove "When allowImplicit is true..." line (implied by infrastructure)
- Fix valid test file header: use /* */ block comment to match convention
- Verified all 10 useIterableCallbackReturn tests pass locally
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 lint/suspicious/useIterableCallbackReturn makes impossible to explicitly return undefined

3 participants