Skip to content

perf(nursery/noFloatingPromises): skip type inference for assignment statements - #10991

Closed
BezSaharaD wants to merge 3 commits into
biomejs:mainfrom
BezSaharaD:fix/no-floating-promises-perf
Closed

BezSaharaD wants to merge 3 commits into
biomejs:mainfrom
BezSaharaD:fix/no-floating-promises-perf

Conversation

@BezSaharaD

Copy link
Copy Markdown
Contributor

Summary

is_handled_promise() unconditionally treats a bare assignment expression-statement (e.g. x = getPromise();) as handled, regardless of its type. That check runs after ctx.type_of_expression(&expression) -- full type inference -- meaning the inference result is computed and then simply discarded for this case. Checking for an assignment expression first (a cheap, purely syntactic check) and bailing out before the type computation skips that wasted work.

Measurement

Profiled with --profile-rules on the same large real-world project as #10990:

cumulative rule time worst single call
before ~52s 92ms
after ~37s (-29%) 30ms

Smaller improvement than #10990's ~4.7x, since this rule's Query (Typed<JsExpressionStatement>, statement-level only) is already much narrower than noMisusedPromises's (Typed<AnyJsExpression>, every expression) -- there was less redundant work to remove to begin with. Diagnostics are byte-identical before/after (11331 errors both times).

Testing

  • Added assignmentIsHandled.ts as a new regression fixture (generated its snapshot via INSTA_UPDATE=always).
  • All 61 noFloatingPromises spec tests (60 existing + 1 new) pass.
  • All 478 tests in the nursery rule group pass unchanged.

Disclosure

Found and fixed with Claude's (Anthropic) assistance while continuing the profiling from #10990 against the same real-world project - reviewed and verified locally (unit tests, byte-identical diagnostic diff, release build) before submitting.

…statements

is_handled_promise() unconditionally treats a bare assignment
expression-statement as handled, regardless of its type -- so there's
no need to run full type inference (ctx.type_of_expression) just to
be told that afterwards. Check for an assignment expression first,
before the type computation, and bail out early in that case.

Measured via --profile-rules on a large real-world TypeScript/React
project: this rule's cumulative cost dropped from ~52s to ~37s (~29%),
and its worst-case single-call time dropped from 92ms to 30ms. Smaller
than the noMisusedPromises fix in a prior PR since fewer of this
rule's (already query-narrowed) invocations hit the pattern, but a
real, measured improvement with byte-identical diagnostics before and
after (11331 errors both times).

Added a regression test fixture (assignmentIsHandled.ts) alongside
the existing spec suite. All 61 rule tests (60 existing + 1 new) and
all 478 nursery-group tests pass.
@changeset-bot

changeset-bot Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 990334a

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

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@github-actions github-actions Bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Jul 19, 2026
@codspeed

codspeed Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 62 untouched benchmarks
⏩ 213 skipped benchmarks1


Comparing BezSaharaD:fix/no-floating-promises-perf (990334a) with main (89c27c6)

Open in CodSpeed

Footnotes

  1. 213 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@BezSaharaD
BezSaharaD marked this pull request as ready for review July 19, 2026 11:05
@coderabbitai

coderabbitai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 061501ed-a97e-4c98-9a99-1f2fa7452d77

📥 Commits

Reviewing files that changed from the base of the PR and between c51f7b7 and 0c63650.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/assignmentOfArrayIsHandled.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs
  • crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/assignmentOfArrayIsHandled.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs

Walkthrough

The noFloatingPromises rule now treats assignment expressions as handled and exits before type inference or promise analysis. Documentation includes assignment examples, tests cover direct and array-of-promise assignments, and a patch changeset records the performance update.

Possibly related PRs

  • biomejs/biome#10934: Also changes promise handling and inferred type checks in NoFloatingPromises::run.

Suggested labels: A-Type-Inference

Suggested reviewers: dyc3

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Clearly summarises the main change: skipping type inference for assignment statements in noFloatingPromises.
Description check ✅ Passed The description matches the optimisation, the added fixture, and the testing/profile details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/perf-no-floating-promises.md:
- Line 5: Update the changeset description for noFloatingPromises to include the
required issue, rule, and assist links. Preserve the existing rule link, add the
applicable issue and assist links using the repository’s standard changeset link
format, or explicitly confirm when either link does not exist.

In `@crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs`:
- Around line 180-182: Add a rustdoc note to the no_floating_promises rule
declaration documenting that bare assignment statements are treated as handled
regardless of type. Place the documentation on the rule declaration associated
with is_handled_promise, preserving the existing internal comment and rule
behavior.
- Around line 183-188: Remove or relocate the early return for
JsAssignmentExpression in the promise analysis flow so assignments continue
through the ArrayOfPromises handling, including cases such as target =
[returnsPromise()]. Preserve the existing branch ordering and add a regression
test covering an assignment containing an array of promises.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 174b7130-fcfe-431a-9eee-fb627d0be360

📥 Commits

Reviewing files that changed from the base of the PR and between bd1038b and c51f7b7.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/assignmentIsHandled.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • .changeset/perf-no-floating-promises.md
  • crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs
  • crates/biome_js_analyze/tests/specs/nursery/noFloatingPromises/assignmentIsHandled.ts

Comment thread .changeset/perf-no-floating-promises.md
Comment thread crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs
Comment thread crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs
…gression test

- Add a rustdoc bullet point and a "Valid" example showing that
  assigning a floating promise to a variable is considered handled
  (previously only explained by an internal comment).
- Add assignmentOfArrayIsHandled.ts: verified that assigning the
  result of an array-producing call with an async callback (e.g.
  `target = arr.map(async (x) => ...)`) is also not flagged, and that
  this is unchanged by the assignment-expression bail-out (checked
  both with and without that bail-out; same result either way, since
  the assignment expression's type isn't resolved to an array type by
  the underlying type inference regardless).

Verified: all 61 noFloatingPromises spec tests and all 480 nursery-group
tests pass.

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good. Do we have a case where we assign to a target and then call the target? Is that handled correctly?

Comment on lines +185 to +187
// `is_handled_promise` (below) always treats a bare assignment
// statement as handled, regardless of its type -- so there's no
// point paying for type inference just to be told that afterwards.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment is weird. Can't we just update is_handled_promise to take in consideration this new logic.

If not, just remove it. This will become the new logic

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please remove all those comments from the new cases. Only the ones at the top are the ones that matter

@BezSaharaD BezSaharaD closed this by deleting the head repository Jul 31, 2026
@BezSaharaD

Copy link
Copy Markdown
Contributor Author

Hello, I apologize for deleting the repositories. I forgot that I have open PRs. I deleted them for personal reasons and being busy with the main project. I do not claim any code or credit, but you can take the code if needed.

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.

2 participants