+
Skip to content

Conversation

turbocrime
Copy link
Contributor

@turbocrime turbocrime commented Sep 22, 2025

Summary

the rule useIterableCallbackReturn now correctly handles arrow functions with a single-expression void body

Test Plan

tests and snapshots updated

Docs

example added to macro comment

Copy link

changeset-bot bot commented Sep 22, 2025

🦋 Changeset detected

Latest commit: efc0442

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

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

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 Sep 22, 2025
@turbocrime turbocrime changed the title fix: void iterable callback return fix(lint/suspicious/useIterableCallbackReturn): single-expression void arrow function Sep 22, 2025
@turbocrime
Copy link
Contributor Author

i believe an ideal fix should identify the actual return type of the callback, but it would be more complex and #6617 (comment) seemed to discourage that approach.

Copy link

codspeed-hq bot commented Sep 22, 2025

CodSpeed Performance Report

Merging #7564 will not alter performance

Comparing turbocrime:fix/void-iterable-callback-return (2903cc4) with main (551bc32)

Summary

✅ 133 untouched

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.

That's great, thank you. The linting is failing, I also added a couple of nits

@turbocrime
Copy link
Contributor Author

@ematipico updated with requested changes

@turbocrime
Copy link
Contributor Author

turbocrime commented Sep 24, 2025

strangely, just ready doesn't work for me, so i'm uncertain if i'm meeting CI requirements before i push

this error repeats many times:

error: let chains are only allowed in Rust 2024 or later
 --> <stdin>:1:284002
  |
1 | ...eSlots :: default () ; let mut current_element = elements . next () ; if let Some (element) = & current_element && element . kind () == T ! [void] { slots . mark_present () ; current_e...
  |                                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Error: process `rustfmt --config newline_style=Unix --config style_edition=2024` failed

Caused by:
    exit status: 1
error: Recipe `gen-all` failed on line 22 with exit code 1
error: Recipe `ready` failed on line 167 with exit code 1

edit: see #7590 #7591

Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Updates the nursery lint rule use_iterable_callback_return and its tests. Adds a patch changeset for @biomejs/biome documenting a bug fix. get_function_returns_info no longer treats single-expression arrow bodies as always valued; it now classifies them as value or void by detecting a surrounding unary void expression and returns early for that case. Adds many invalid tests for iterable callbacks returning void and two valid forEach void-return cases. No exported/public API changes.

Suggested reviewers

  • arendjr
  • siketyan

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title uses conventional commit syntax and succinctly identifies the exact lint rule and bug being fixed, specifically the handling of single-expression void arrow functions in useIterableCallbackReturn, which aligns directly with the primary change in the PR.
Linked Issues Check ✅ Passed The implementation meets the objective of issue #6617 by detecting void unary expressions in single-expression arrow callbacks and classifying them correctly, and the added tests verify that void-returning callbacks no longer trigger the lint.
Out of Scope Changes Check ✅ Passed All modifications—including the rule logic adjustments, new tests, and the changeset entry—are directly focused on improving void arrow function handling in the useIterableCallbackReturn lint and do not introduce any unrelated functionality.
Description Check ✅ Passed The PR description clearly states the motivation, links the relevant issues, and outlines updates to tests and snapshots to demonstrate that the useIterableCallbackReturn rule now properly handles single-expression void arrow functions, making it directly relevant to the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1fa092 and efc0442.

📒 Files selected for processing (1)
  • .changeset/vast-waves-write.md (1 hunks)

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

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

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs (1)

279-298: Also handle parenthesised void bodies (e.g. () => (void 0)).

Currently only direct unary void is detected; wrapping it in parentheses won’t be recognised. Unwrapping parentheses before the check avoids false negatives.

Apply this minimal tweak:

 if let Some(arrow_expression) = JsArrowFunctionExpression::cast_ref(&cfg.node)
     && let Ok(AnyJsFunctionBody::AnyJsExpression(expression)) = arrow_expression.body()
 {
-        let is_void_expression = expression
-            .as_js_unary_expression()
-            .and_then(|unary| unary.is_void().ok())
-            .unwrap_or(false);
+        // Unwrap surrounding parentheses to recognise patterns like `() => (void 0)`
+        let mut expr = expression;
+        loop {
+            if let Some(paren) = expr.as_js_parenthesized_expression() {
+                match paren.expression() {
+                    Ok(inner) => expr = inner,
+                    Err(_) => break,
+                }
+            } else {
+                break;
+            }
+        }
+
+        let is_void_expression = expr
+            .as_js_unary_expression()
+            .and_then(|unary| unary.is_void().ok())
+            .unwrap_or(false);
 
-        if is_void_expression {
-            function_returns_info
-                .returns_without_value
-                .push(expression.range())
-        } else {
-            function_returns_info
-                .returns_with_value
-                .push(expression.range())
-        }
+        if is_void_expression {
+            function_returns_info
+                .returns_without_value
+                .push(expr.range())
+        } else {
+            function_returns_info
+                .returns_with_value
+                .push(expr.range())
+        }
 
         return function_returns_info;
 }
crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js (1)

25-26: LGTM; maybe add a parens variant too.

To guard regressions, consider adding: [].forEach(() => (void null));

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 485c2e5 and 5948c40.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/vast-waves-write.md (1 hunks)
  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs (2 hunks)
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js (14 hunks)
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/valid.js
  • crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changeset files, only use #### or ##### headers; avoid other header levels
Changeset descriptions should use past tense for what you did (e.g., "Added...")
Describe current Biome behavior in present tense within changesets (e.g., "Biome now supports...")
For bug fixes in changesets, start with a link to the issue (e.g., "Fixed #1234: ...")
When referencing rules or assists in changesets, include links to their documentation pages
Include a minimal code block in the changeset when applicable to demonstrate the change
End every sentence in the changeset description with a period

Files:

  • .changeset/vast-waves-write.md
🧠 Learnings (1)
📚 Learning: 2025-09-07T17:35:00.517Z
Learnt from: CR
PR: biomejs/biome#0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-09-07T17:35:00.517Z
Learning: Applies to .changeset/*.md : Describe current Biome behavior in present tense within changesets (e.g., "Biome now supports...")

Applied to files:

  • .changeset/vast-waves-write.md
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs (1)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs (1)

79-82: Nice doc touch; earlier feedback addressed.

The added example for a single‑expression void arrow callback clarifies intent and matches the rule’s behaviour.

crates/biome_js_analyze/tests/specs/suspicious/useIterableCallbackReturn/invalid.js (1)

38-39: Great coverage; add parens/void 0 variants for completeness.

Nice job adding void call forms—addresses earlier feedback. To harden the suite, sprinkle a couple of cases like () => (void 0) and () => (void a.fn()) across one or two methods (e.g. map, every).

Also applies to: 87-88, 97-98, 107-108, 117-118, 127-128, 137-138, 147-148, 157-158, 167-168, 177-178, 187-188, 197-198, 228-229

@turbocrime
Copy link
Contributor Author

turbocrime commented Sep 24, 2025

crates/biome_js_analyze/src/lint/suspicious/use_iterable_callback_return.rs (1)> 279-298: Also handle parenthesised void bodies (e.g. () => (void 0)).

Currently only direct unary void is detected; wrapping it in parentheses won’t be recognised. Unwrapping parentheses before the check avoids false negatives.

i think this is not a good idea

  • using parentheses this way is disallowed by the default configuration (they would be removed)
  • if we're unwrapping an outer set of parentheses, should we unwrap another nested set of parentheses?
  • there will always be another edge case that could unwrap until the entire execution tree of the callback is exhausted

as i said above, an ideal fix should use real type information to identify the return type of the callback. i consider this to be a minimal solution, which satisfies the most common use-case, and which provides an easy technique for developers to satisfy the demands of the rule without applying braces to every arrow callback.

@coderabbitai resolve

Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

@turbocrime Your reasoning is solid. You're absolutely right that handling parenthesized void expressions would create unnecessary complexity:

  1. The default formatter configuration would remove those parentheses anyway
  2. The question of how many levels to unwrap creates an implementation rabbit hole
  3. A minimal fix targeting the common case is more maintainable than trying to handle every edge case

Your approach of detecting direct void expressions in single-expression arrow functions is pragmatic and addresses the core issue reported in #6617. The ideal type-aware solution can be considered for future improvements.

✅ Actions performed

Comments resolved.

@ematipico ematipico merged commit 40e515f into biomejs:main Sep 25, 2025
11 of 29 checks passed
@github-actions github-actions bot mentioned this pull request Sep 25, 2025
@turbocrime turbocrime deleted the fix/void-iterable-callback-return branch September 25, 2025 17:41
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/nursery/useIterableCallbackReturn incorrectly flags functions that return void as returning a value

2 participants

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载