Skip to content

feat(lint): add ternary expression detection to useNullishCoalescing#9248

Open
pkallos wants to merge 1 commit intobiomejs:mainfrom
pkallos:feat/9229-nullish-ternary
Open

feat(lint): add ternary expression detection to useNullishCoalescing#9248
pkallos wants to merge 1 commit intobiomejs:mainfrom
pkallos:feat/9229-nullish-ternary

Conversation

@pkallos
Copy link
Contributor

@pkallos pkallos commented Feb 26, 2026

AI Disclosure: This PR was developed with AI assistance (Claude).

Summary

Closes #9229

Extends useNullishCoalescing to detect ternary expressions that perform explicit nullish checks and suggest rewriting them with ??.

// Invalid
declare const x: string | null;
const value = x !== null ? x : 'default'; // should use ??
const value = x == null ? 'default' : x;  // should use ??

// Valid
const value = x ?? 'default';

Detected patterns include strict equality (x !== null ? x : y), loose equality (x != null ? x : y), compound checks (x !== null && x !== undefined ? x : y), and member access (obj.prop !== null ? obj.prop : 'default').

Fix safety:

  • Loose equality and compound strict checks: always fixable (they already cover both null and undefined)
  • Single strict checks (e.g. x !== null): fix only offered when type analysis confirms the variable can't be both null and undefined
  • Expressions containing calls or new: no fix offered, since ?? changes the evaluation count

A new ignoreTernaryTests option (default: false) allows disabling ternary detection.

Comments attached to the ? and : tokens are transferred to the corresponding branch in the ?? output.

Continues the incremental approach from #8952. Addresses #8043.

Test Plan

  • ternaryInvalid.ts: strict/loose/compound patterns, member access, precedence edge cases, side-effect safety, various nullish type combinations
  • ternaryValid.ts: non-nullish comparisons, mixed operands, non-matching branches
  • ternaryCommentTrivia.ts: comment preservation through the fix
  • ignoreTernaryTestsEnabled.ts: option suppresses all ternary diagnostics
  • All tests pass: cargo test -p biome_js_analyze -- nursery::use_nullish_coalescing (10 tests)

Docs

Rule doc comments updated with ternary examples. Option documented inline.

@changeset-bot
Copy link

changeset-bot bot commented Feb 26, 2026

🦋 Changeset detected

Latest commit: fc346e5

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 Feb 26, 2026
"level": "error",
"options": {
"ignoreTernaryTests": true
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

as with the prior PR for this, the inclusion of the option here is meant to move towards feature parity with the corresponding eslint rule, logically this one is relatively straightforward because it should just "turn off" the ternary check behavior that's added in this changeset.

if any issue lmk and I can remove!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 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

Adds ternary (conditional) expression support to the useNullishCoalescing lint: the rule now queries both logical-or and conditional expressions, recognises loose/strict/compound nullish checks, and computes diagnostics and safe fixes for converting patterns like a !== null ? a : b to a ?? b. Introduces an ignoreTernaryTests option (default false) to skip ternary analysis, expands public query/state types, implements safety checks (side effects, type null/undefined presence, parentheses and trivia preservation), and adds tests and options fixtures covering valid, invalid and trivia-preservation cases.

Possibly related PRs

Suggested labels

A-Project, A-Diagnostic

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: extending useNullishCoalescing to detect and flag ternary expressions that could use the nullish coalescing operator.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the feature, patterns detected, fix safety considerations, new option, test coverage, and documentation updates.
Linked Issues check ✅ Passed All coding requirements from issue #9229 are met: ternary detection patterns implemented, JsConditionalExpression query added, ignoreTernaryTests option introduced, fix safety logic respects evaluation count, and trivia preservation is handled.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the objectives: rule extension, new option, test files, and documentation—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
  • 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.

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/src/lint/nursery/use_nullish_coalescing.rs (1)

225-232: Minor concern: potential early return on missing trivia.

Lines 228 and 231 use ? on first_leading_trivia() and last_trailing_trivia(). If the original ternary has no leading/trailing trivia, this returns None and the action silently fails. This is likely rare but could suppress fixes unexpectedly.

Consider whether an empty trivia fallback would be more robust, or if this is intentional defensive behaviour.

💡 Optional: handle missing trivia gracefully
-                let new_expr = AnyJsExpression::from(new_expr)
-                    .prepend_trivia_pieces(
-                        ternary.syntax().first_leading_trivia()?.pieces(),
-                    )?
-                    .append_trivia_pieces(
-                        ternary.syntax().last_trailing_trivia()?.pieces(),
-                    )?;
+                let mut new_expr = AnyJsExpression::from(new_expr);
+                if let Some(leading) = ternary.syntax().first_leading_trivia() {
+                    new_expr = new_expr.prepend_trivia_pieces(leading.pieces())?;
+                }
+                if let Some(trailing) = ternary.syntax().last_trailing_trivia() {
+                    new_expr = new_expr.append_trivia_pieces(trailing.pieces())?;
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs` around
lines 225 - 232, The current transform uses `?` on
`ternary.syntax().first_leading_trivia()` and `last_trailing_trivia()` in the
`AnyJsExpression::from(new_expr).prepend_trivia_pieces(...)?` /
`append_trivia_pieces(...)?` chain which causes an early return when trivia is
absent; change those calls to provide an empty-fallback instead of propagating
`None` (e.g., replace `first_leading_trivia()?` with
`first_leading_trivia().map(|t| t.pieces()).unwrap_or_default()` and similarly
for `last_trailing_trivia()`) so `prepend_trivia_pieces`/`append_trivia_pieces`
receive an empty iterator when there is no trivia and the fix is applied
reliably.
🤖 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/src/lint/nursery/use_nullish_coalescing.rs`:
- Around line 225-232: The current transform uses `?` on
`ternary.syntax().first_leading_trivia()` and `last_trailing_trivia()` in the
`AnyJsExpression::from(new_expr).prepend_trivia_pieces(...)?` /
`append_trivia_pieces(...)?` chain which causes an early return when trivia is
absent; change those calls to provide an empty-fallback instead of propagating
`None` (e.g., replace `first_leading_trivia()?` with
`first_leading_trivia().map(|t| t.pieces()).unwrap_or_default()` and similarly
for `last_trailing_trivia()`) so `prepend_trivia_pieces`/`append_trivia_pieces`
receive an empty iterator when there is no trivia and the fix is applied
reliably.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2368aa2 and d8f575d.

⛔ Files ignored due to path filters (4)
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (8)
  • .changeset/use-nullish-coalescing-ternary.md
  • crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.options.json
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts
  • crates/biome_rule_options/src/use_nullish_coalescing.rs

@pkallos
Copy link
Contributor Author

pkallos commented Feb 26, 2026

@ematipico @dyc3 hopefully this is a more maintainer-friendly implementation vs #9240 🫡

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 26, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing pkallos:feat/9229-nullish-ternary (fc346e5) with main (9bbdf4d)2

Open in CodSpeed

Footnotes

  1. 156 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 (412a08d) during the generation of this report, so 9bbdf4d was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

JsSyntaxKind::JS_CALL_EXPRESSION | JsSyntaxKind::JS_NEW_EXPRESSION
)
})
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried looking for a reusable construct here but wasn’t able to locate one, the gist of this check is looking for statements that are side effect producing.

if you have any thoughts about side effectfulness detection or other cases than just function call or object construction please let me know

@pkallos pkallos force-pushed the feat/9229-nullish-ternary branch from e64550a to 7daecf1 Compare February 26, 2026 07:36
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 (1)
crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs (1)

100-112: Move UseNullishCoalescingState below the impl Rule block for consistency.

Small layout nit, but it keeps nursery rule files aligned with the house style and easier to scan.

Based on learnings: In crates/biome_analyze/**/*.rs rule files, all helper functions, structs, and enums must be placed below the impl Rule block; only node unions are exempt.

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

In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs` around
lines 100 - 112, The enum UseNullishCoalescingState is declared above the impl
Rule block; move its declaration so it appears below the impl Rule block to
match project layout conventions. Locate the UseNullishCoalescingState enum and
cut/paste it to a position after the impl Rule { ... } implementation for the
rule (the impl for Rule in this file), ensuring any uses inside the impl still
compile and adjusting use/import ordering if necessary.
🤖 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/nursery/use_nullish_coalescing.rs`:
- Around line 409-417: The current fix logic must be tightened: change
extract_nullish_comparison_operand to return which literal was tested (null or
undefined), extend the NullishCheckKind enum so StrictSingle carries that
literal and update the Compound variant validation to ensure the two sides test
different literals; then in the validation branch that uses
ctx.type_of_expression and type_has_null_and_undefined, for StrictSingle(lit)
only verify that the opposite literal is impossible in the expression type (not
both), and for Compound require one side is null and the other is undefined;
update the matching code paths that construct/consume NullishCheckKind
(including the branches around where StrictSingle and Compound are checked) and
add unit tests (e.g., x !== null ? x : y with type string | undefined) to cover
these cases.

---

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs`:
- Around line 100-112: The enum UseNullishCoalescingState is declared above the
impl Rule block; move its declaration so it appears below the impl Rule block to
match project layout conventions. Locate the UseNullishCoalescingState enum and
cut/paste it to a position after the impl Rule { ... } implementation for the
rule (the impl for Rule in this file), ensuring any uses inside the impl still
compile and adjusting use/import ordering if necessary.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8f575d and 7daecf1.

⛔ Files ignored due to path filters (2)
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs

@pkallos pkallos force-pushed the feat/9229-nullish-ternary branch from 7daecf1 to 0c22263 Compare February 26, 2026 18:14
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/src/lint/nursery/use_nullish_coalescing.rs (1)

17-94: Consider adding a compound-check example to the documentation.

The rule docs cover simple strict/loose patterns but don't show compound checks like x !== null && x !== undefined ? x : y. A brief example would help users understand the full scope of detected patterns.

📚 Suggested documentation addition

Add after line 59 in the Invalid examples:

 /// const value = x == null ? 'default' : x; // should use ??
 /// ```
+///
+/// ```ts,expect_diagnostic
+/// declare const x: string | null | undefined;
+/// const value = x !== null && x !== undefined ? x : 'default'; // should use ??
+/// ```
 ///
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs` around
lines 17 - 94, The documentation for the UseNullishCoalescing rule is missing an
example of a compound null/undefined check; update the doc comment inside the
declare_lint_rule for UseNullishCoalescing by adding an Invalid example that
shows a compound ternary pattern such as checking both null and undefined (e.g.,
"x !== null && x !== undefined ? x : 'default'") so users can see that patterns
like x !== null && x !== undefined ? x : y are flagged and should be rewritten
to use ??.
🤖 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/src/lint/nursery/use_nullish_coalescing.rs`:
- Around line 17-94: The documentation for the UseNullishCoalescing rule is
missing an example of a compound null/undefined check; update the doc comment
inside the declare_lint_rule for UseNullishCoalescing by adding an Invalid
example that shows a compound ternary pattern such as checking both null and
undefined (e.g., "x !== null && x !== undefined ? x : 'default'") so users can
see that patterns like x !== null && x !== undefined ? x : y are flagged and
should be rewritten to use ??.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7daecf1 and 0c22263.

⛔ Files ignored due to path filters (6)
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (8)
  • .changeset/use-nullish-coalescing-ternary.md
  • crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.options.json
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts
  • crates/biome_rule_options/src/use_nullish_coalescing.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/biome_rule_options/src/use_nullish_coalescing.rs
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.options.json
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts

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.

Tests that use the new option are missing. We must add them, and the documentation must be updated to reflect the new option

Copy link
Member

Choose a reason for hiding this comment

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

I think the changeset can be a little more succinct, the documentation will take care of explaining the rest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm a bit unsure about when a changeset is required/useful.

From the contributing docs:

If the PR you're about to open is a bugfix/feature visible to users of the Biome toolchain or of the published Biome crates, you are encouraged to provide a changeset.

In this comment for #9257 there was some feedback that maybe I shouldn't include a changeset because the rule isn't new or released yet, so I removed the changeset from there.

This PR is making a similar type of change (adding an enhancement to an existing the nursery rule), should it be accompanied with a changeset or should I remove it from the PR?

Maybe they both need changesets, or neither need changesets?

Happy to do it either way! Just curious because I have another PR coming up for implementing #9232 and I'll want to apply the same practice.

Please let me know and I'll adjust.

/// offered when type analysis confirms the left operand can only be truthy or
/// nullish (not other falsy values like `0` or `''`).
///
/// For ternary expressions, this rule detects patterns like `x !== null ? x : y`
Copy link
Member

Choose a reason for hiding this comment

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

The documentation should now document the option you added.

Please refer to the contribution guide to understand the format

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK I believe I've managed to adjust the documentation to describe the behavior and have documented the option in a new ## Options

I also added that same documentation for ignoreConditionalTests , which was an option I had introduced alongside the initial implementation in #8952 without following the pattern, so hopefully this cleans that up in the right way as well!

@pkallos pkallos force-pushed the feat/9229-nullish-ternary branch from 0c22263 to fbb8738 Compare February 26, 2026 22:05
@pkallos
Copy link
Contributor Author

pkallos commented Feb 26, 2026

@ematipico thanks for spending time on this!

I've squashed/pushed some fixes to address the feedback. Shortened the changeset statement.

Added ## Options documentation for both ignoreConditionalTests and ignoreTernaryTests in the rule's doc comments, following the json,options + ts,use_options format from the contribution guide. Also shortened the changeset.

Tests that use the new option are missing. We must add them, and the documentation must be updated to reflect the new option

I may have mustunderstood but I think spec tests for the option already existed (ignoreTernaryTestsEnabled.ts + .options.json), but the doc-comment test blocks were missing; those are now added too.

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.

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs (1)

455-465: ⚠️ Potential issue | 🔴 Critical

Unsafe auto-fix is still possible for strict-single and malformed compound checks

On Line 461, StrictSingle is treated as fixable unless the type has both null and undefined. That still permits behaviour changes (e.g. null-only check with string | undefined).
Also, Lines 538-545 allow compound checks that can test the same literal twice (null/null or undefined/undefined) and still be fixable.

Please carry the tested literal kind through extract_nullish_comparison_operand, then:

  • for strict-single: ensure the other nullish variant is impossible,
  • for compound: require one side checks null and the other checks undefined.
Suggested direction (minimal diff sketch)
-enum NullishCheckKind { Loose, StrictSingle, Compound }
+enum NullishCheckKind { Loose, StrictSingle(NullishLiteralKind), Compound }

-fn extract_nullish_comparison_operand(...) -> Option<AnyJsExpression>
+fn extract_nullish_comparison_operand(...) -> Option<(AnyJsExpression, NullishLiteralKind)>

-NullishCheckKind::StrictSingle => !type_has_null_and_undefined(&ty)
+NullishCheckKind::StrictSingle(kind) => type_excludes_opposite_nullish(&ty, kind)

-if let (Some(checked_left), Some(checked_right)) = (...)
+if let (Some((checked_left, left_kind)), Some((checked_right, right_kind))) = (...)
+   && left_kind != right_kind

Also applies to: 510-545, 551-582

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

In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs` around
lines 455 - 465, The current fixability logic incorrectly allows unsafe
auto-fixes for StrictSingle and malformed Compound checks; update
extract_nullish_comparison_operand to return which literal kind (Null or
Undefined) each side tests, then use that in the can_fix computation: for
NullishCheckKind::StrictSingle, confirm the extracted literal kind rules out the
other nullish variant (i.e., the expression's type cannot include the opposite
literal) before marking fixable; for NullishCheckKind::Compound, require that
the two extracted literal kinds are complementary (one Null and one Undefined)
and reject cases where both sides test the same literal; apply this change
wherever the StrictSingle/Compound fixability is evaluated (functions handling
NullishCheckKind and uses of extract_nullish_comparison_operand).
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs (1)

147-159: Move UseNullishCoalescingState below the impl Rule block

Small layout tidy-up: this enum is a helper type for the rule and is better placed below impl Rule to match repository conventions (node unions are the explicit exception).

Based on learnings: In crates/biome_analyze/**/*.rs rule files, all helper functions, structs, and enums must be placed below the impl Rule block; only node unions may stay above for readability.

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

In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs` around
lines 147 - 159, Move the helper enum UseNullishCoalescingState so it appears
below the impl Rule block (i.e., after the Rule implementation) to follow the
repository convention that helper functions/structs/enums live below impl Rule;
update any local references inside the file (e.g., places that pattern-match on
UseNullishCoalescingState) to the same visibility/scope after moving it so
compilation and name resolution remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs`:
- Around line 455-465: The current fixability logic incorrectly allows unsafe
auto-fixes for StrictSingle and malformed Compound checks; update
extract_nullish_comparison_operand to return which literal kind (Null or
Undefined) each side tests, then use that in the can_fix computation: for
NullishCheckKind::StrictSingle, confirm the extracted literal kind rules out the
other nullish variant (i.e., the expression's type cannot include the opposite
literal) before marking fixable; for NullishCheckKind::Compound, require that
the two extracted literal kinds are complementary (one Null and one Undefined)
and reject cases where both sides test the same literal; apply this change
wherever the StrictSingle/Compound fixability is evaluated (functions handling
NullishCheckKind and uses of extract_nullish_comparison_operand).

---

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs`:
- Around line 147-159: Move the helper enum UseNullishCoalescingState so it
appears below the impl Rule block (i.e., after the Rule implementation) to
follow the repository convention that helper functions/structs/enums live below
impl Rule; update any local references inside the file (e.g., places that
pattern-match on UseNullishCoalescingState) to the same visibility/scope after
moving it so compilation and name resolution remain unchanged.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c22263 and fbb8738.

⛔ Files ignored due to path filters (4)
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (8)
  • .changeset/use-nullish-coalescing-ternary.md
  • crates/biome_js_analyze/src/lint/nursery/use_nullish_coalescing.rs
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.options.json
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts
  • crates/biome_rule_options/src/use_nullish_coalescing.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.options.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryCommentTrivia.ts
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryInvalid.ts
  • .changeset/use-nullish-coalescing-ternary.md
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ignoreTernaryTestsEnabled.ts
  • crates/biome_rule_options/src/use_nullish_coalescing.rs
  • crates/biome_js_analyze/tests/specs/nursery/useNullishCoalescing/ternaryValid.ts

@pkallos pkallos force-pushed the feat/9229-nullish-ternary branch from 900a784 to 430c515 Compare February 26, 2026 22:41
@pkallos pkallos requested a review from ematipico February 26, 2026 23:15
@ematipico
Copy link
Member

ematipico commented Feb 26, 2026

may have mustunderstood but I think spec tests for the option already existed (ignoreTernaryTestsEnabled.ts + .options.json), but the doc-comment test blocks were missing; those are now added too.

Ah that's on me. The GitHub UI cut off the name of the file, and I couldn't see the suffix. Sorry about that

@pkallos
Copy link
Contributor Author

pkallos commented Feb 27, 2026

Ah that's on me. The GitHub UI cut off the name of the file, and I couldn't see the suffix. Sorry about that

All good! Odds are on your side that you'd be right haha

@pkallos pkallos force-pushed the feat/9229-nullish-ternary branch from 430c515 to fc346e5 Compare February 28, 2026 02:10
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.

📎 useNullishCoalescing: suggest ?? for ternary nullish checks

2 participants