Skip to content

fix(useAwaitThenable): treat unresolved call expressions as uninferred#8997

Merged
ematipico merged 2 commits intobiomejs:mainfrom
mldangelo:fix/use-await-thenable-false-positives
Feb 9, 2026
Merged

fix(useAwaitThenable): treat unresolved call expressions as uninferred#8997
ematipico merged 2 commits intobiomejs:mainfrom
mldangelo:fix/use-await-thenable-false-positives

Conversation

@mldangelo
Copy link
Contributor

@mldangelo mldangelo commented Feb 8, 2026

Summary

Fixes #8476.

useAwaitThenable reports false positives when await is used on call expressions whose return type cannot be resolved — e.g., await fsPromises.readFile(...), await response.json(), or any cross-module call where the callee type comes from an external module (Node.js builtins, npm packages).

The root cause is in TypeData::is_inferred(). Call expressions are stored as TypeofExpression(Call { ... }) during local inference. When the flattening phase cannot resolve the callee (because Biome doesn't parse .d.ts from node_modules), the TypeofExpression survives unflattened. is_inferred() returns true for these via the _ => true catch-all, so the rule fires: ty.is_inferred() && !ty.is_promise_instance()true && true → false positive.

This PR adds Self::TypeofExpression(_) => false to is_inferred(). Unflattened TypeofExpression types represent failed type computations — their actual type is unknown, so they should not be considered "inferred."

Changes

  • type_data.rs: Added Self::TypeofExpression(_) => false to TypeData::is_inferred()
  • valid.js: Added test cases for cross-module calls (import fs from "node:fs/promises", external packages)
  • Updated snapshots

Test Plan

  • cargo test -p biome_js_analyze -- use_await_thenable — both valid_js and invalid_js pass
  • Verified against a real-world codebase: 26 false positives eliminated, 2 real violations (function returning ChildProcess, not Promise) still correctly caught

Docs

No documentation changes needed — this is a bug fix to an existing nursery rule.

This PR was written with the assistance of Claude Code.

TypeofExpression types that survive the flattening phase represent
type computations that could not be resolved (e.g., cross-module
function calls to Node.js builtins or npm packages). These should
not be treated as "inferred" since their actual type is unknown.

Added Self::TypeofExpression(_) => false to TypeData::is_inferred()
to prevent false positives on unresolvable call expressions.

Fixes biomejs#8476
@changeset-bot
Copy link

changeset-bot bot commented Feb 8, 2026

🦋 Changeset detected

Latest commit: e9a1657

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 A-Type-Inference Area: type inference labels Feb 8, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

Walkthrough

This patch fixes false positives in the useAwaitThenable linter rule when handling cross-module calls whose return types cannot be resolved. The changes include: adding a changeset entry, expanding test coverage with unresolved cross-module call scenarios (such as Node.js built-in imports and external package calls), and modifying the type inference logic to explicitly treat TypeofExpression as unresolved rather than inferred.

Possibly related PRs

Suggested labels

A-Linter, A-Type-Inference, L-JavaScript, A-Diagnostic

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR addresses issue #8476 but solves it differently than specified: fixes false positives for unresolved cross-module calls generally rather than specifically handling Drizzle thenables. Verify whether the broader fix (treating all unresolved TypeofExpression as uninferred) adequately resolves Drizzle-specific false positives or if additional Drizzle-specific type handling is needed.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: treating unresolved call expressions as uninferred in the useAwaitThenable rule.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the useAwaitThenable rule's handling of unresolved types; no unrelated modifications detected.
Description check ✅ Passed The PR description clearly explains the motivation (false positives in useAwaitThenable), identifies the root cause in TypeData::is_inferred(), and documents the fix with test plan and verification.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 8, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 95 skipped benchmarks1


Comparing mldangelo:fix/use-await-thenable-false-positives (e9a1657) with main (9d1ae81)2

Open in CodSpeed

Footnotes

  1. 95 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.

  2. No successful run was found on main (6cde25c) during the generation of this report, so 9d1ae81 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

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.

Great fix, thank you !

@ematipico ematipico merged commit a5f3212 into biomejs:main Feb 9, 2026
17 checks passed
@github-actions github-actions bot mentioned this pull request Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter A-Type-Inference Area: type inference L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 useAwaitThenable many false positives with drizzle

2 participants