-
-
Notifications
You must be signed in to change notification settings - Fork 716
fix(useOptionalChain): support Yoda expressions #7387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: f2296b3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
WalkthroughIntroduces a new JsBinaryExpression API Assessment against linked issues
Suggested reviewers
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
✨ Finishing Touches
🧪 Generate 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (10)
.changeset/big-keys-play.md (1)
5-5
: Polish the changeset (tense split, final full stop, canonical docs URL, add example).Bring it in line with our changeset conventions: past tense for the action, present tense for current behaviour, sentences ending with a full stop, and include a representative example. Also swap the Japanese docs URL for the canonical one.
-Fixed [#7381](https://github.com/biomejs/biome/issues/7381), now the [`useOptionalChain`](https://biomejs.dev/ja/linter/rules/use-optional-chain/) rule recognizes optional chaining using Yoda expressions (e.g., `undefined !== foo && foo.bar`) +Fixed [#7381](https://github.com/biomejs/biome/issues/7381). +The [`useOptionalChain`](https://biomejs.dev/linter/rules/use-optional-chain/) rule now recognises optional chaining when written as Yoda expressions. + +#### Example +```js +// Before +undefined !== foo && foo.bar +// After +foo?.bar +```crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js (1)
56-66
: Add ‘void 0’ Yoda variants to exercise undefined aliasing.Many codebases still use
void 0
forundefined
. Adding a couple of lines ensures the extractor treats it equivalently.// chained calls undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// 'void 0' variants (alias of undefined) +void 0 != foo && foo.bar +void 0 != foo.bar && foo.bar.baz +void 0 != foo && foo()crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js (1)
48-55
: Consider a couple of parenthesised Yoda forms and ‘void 0’ strict variants.Parentheses and
void 0
are cheap extra edges that have bitten normalisers before.undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + +// Parenthesised expressions +(null !== (foo)) && (foo).bar +(undefined !== (foo?.bar)) && (foo?.bar).baz + +// 'void 0' strict variants +void 0 !== foo && foo.bar +void 0 !== foo.bar && foo.bar.bazcrates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx (1)
21-27
: Private fields outside a declaring class may trigger early errors—please confirm parser mode.Access like
foo.#bar
is an early error unless the private names are declared in the containing class. If our test harness disables early errors, we're fine; otherwise, consider wrapping these in a minimal class to keep fixtures parseable.Happy to propose a wrapped variant if needed.
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts (3)
36-39
: Remove duplicated valid case.
foo["some long"] && foo["some long string"].baz;
appears twice (Lines 36 and 38). Drop one to keep the fixture lean.Apply:
-foo["some long"] && foo["some long string"].baz; foo[`some long`] && foo[`some long string`].baz; -foo["some long"] && foo["some long string"].baz;
46-47
: Track or enable the commented-out case.The FIXME for
(new foo() || {}).bar
suggests an expected no-diagnostic. Either enable it (if now supported) or link a tracking issue in a comment for future work.Happy to raise a follow-up issue and add a focused test once confirmed.
1-1
: Test filename convention.Our snapshots typically use files prefixed with
valid*
/invalid*
. Consider renaming this to start withvalid
(e.g.,valid.yoda_expressions.ts
) for consistency with the harness.crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js (1)
26-62
: Add non‑strict andundefined
Yoda coverage.Great breadth. To fully exercise the extractor, add a couple of
!=
cases andundefined !== …
variants, e.g.:
undefined != foo && foo.bar
undefined !== foo.bar && foo.bar.baz
crates/biome_js_syntax/src/expr_ext.rs (1)
345-369
: Recognisevoid …
as undefined (and considerglobalThis.undefined
).
is_nullish
only checks literalnull
and identifierundefined
. Many codebases usevoid 0
(orvoid any
) asundefined
. Optionally recognise it; likewiseglobalThis.undefined
/window.undefined
if you wish to be generous.Apply minimal support for
void
:fn is_nullish(expression: &AnyJsExpression) -> bool { - expression - .as_static_value() - .is_some_and(|x| x.is_null_or_undefined()) + expression + .as_static_value() + .is_some_and(|x| x.is_null_or_undefined()) + || matches!( + expression, + AnyJsExpression::JsUnaryExpression(u) + if u.operator().is_ok_and(|op| matches!(op, JsUnaryOperator::Void)) + ) }If you also want
globalThis.undefined
:- fn is_nullish(expression: &AnyJsExpression) -> bool { + fn is_nullish(expression: &AnyJsExpression) -> bool { // existing checks … + || crate::global_identifier(expression) + .is_some_and(|(_, name)| matches!(name, StaticValue::String(tok) if tok.text_trimmed() == "undefined")) }crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs (1)
564-567
: Minor readability tweak for nested??
.
ok()??
is concise but slightly opaque. Consider expanding for clarity.-AnyJsExpression::JsBinaryExpression(expression) => { - expression.extract_optional_chain_like().ok()?? -} +AnyJsExpression::JsBinaryExpression(expression) => { + match expression.extract_optional_chain_like() { + Ok(Some(expr)) => expr, + _ => return None, + } +}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (5)
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts.snap
is excluded by!**/*.snap
and included by**
📒 Files selected for processing (8)
.changeset/big-keys-play.md
(1 hunks)crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs
(2 hunks)crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js
(1 hunks)crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js
(1 hunks)crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js
(1 hunks)crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx
(1 hunks)crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts
(1 hunks)crates/biome_js_syntax/src/expr_ext.rs
(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/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx
crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js
crates/biome_js_syntax/src/expr_ext.rs
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js
crates/biome_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx
crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js
crates/biome_js_syntax/src/expr_ext.rs
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js
**/tests/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place test files under a tests/ directory in each crate
Files:
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js
.changeset/*.md
📄 CodeRabbit inference engine (CONTRIBUTING.md)
.changeset/*.md
: Create changesets withjust new-changeset
; store them in.changeset/
with correct frontmatter (package keys and change type).
In changeset descriptions, follow content conventions: user-facing changes only; past tense for what you did; present tense for current behavior; link issues for fixes; link rules/assists; include representative code blocks; end every sentence with a period.
When adding headers in a changeset, only use #### or ##### levels.
Files:
.changeset/big-keys-play.md
**/*.{rs,toml}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Format Rust and TOML files before committing (use
just f
/just format
).
Files:
crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs
crates/biome_js_syntax/src/expr_ext.rs
🧠 Learnings (2)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Avoid `unwrap`/`expect`; use `?`, `ok()?`, and combinators (`map`, `and_then`, `filter`) to handle `Result`/`Option` when navigating the CST
Applied to files:
crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/tests/specs/**/{invalid*,valid*}.{js,jsx,ts,tsx,css,graphql,jsonc} : Place snapshot test cases under `tests/specs/<group>/<ruleName>/` using files prefixed with `invalid` and `valid`
Applied to files:
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: autofix
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Test Node.js API
- GitHub Check: Documentation
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Check Dependencies
- GitHub Check: Parser conformance
🔇 Additional comments (6)
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js (1)
1-66
: Solid coverage of loose/nullish Yoda cases.Nice spread of chained members, element access, and calls. This will catch regressions quickly.
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js (1)
1-57
: Good breadth on strict inequality forms.Covers the important shapes (dot/bracket, calls, partial optional chains). Looks great.
crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx (1)
36-63
: Nice touch on preserving whitespace, JSX, and comments.These tend to regress; lovely to see them covered.
crates/biome_js_syntax/src/expr_ext.rs (2)
323-343
: Nice normalisation: Yoda expressions now handled.Returning the non-nullish side via
Option<AnyJsExpression>
is a clean API for callers.
323-343
: All internal references tois_optional_chain_like
have been removed; onlyextract_optional_chain_like
remains—no semver break within this workspace.crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs (1)
266-270
: Good switch to the extractor.Normalising with
extract_optional_chain_like()?
simplifies the flow and covers Yoda cases.
CodSpeed Performance ReportMerging #7387 will not alter performanceComparing Summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Snapshots look good. Nice! Please fix the lint :)
Whoops — I'm ready now. |
benchmark tests failed... 😞 |
Summary
Closes #7381
Now the
useOptionalChain
rule recognizes optional chaining using Yoda expressions (e.g.,undefined !== foo && foo.bar
).Test Plan
Added snap tests.
Using the shell script below, Yoda expressions versions of the existing tests were generated, and the snapshots of the Yoda versions were compared with the originals to confirm identical results.
Docs
changeset only