From a2bcc33c096946ccb2c8c09eb1bf08e30f6f05cd Mon Sep 17 00:00:00 2001 From: qraqras Date: Wed, 3 Sep 2025 22:28:24 +0900 Subject: [PATCH 1/2] fix(useOptionalChain): support Yoda expressions --- .changeset/big-keys-play.md | 5 + .../src/lint/complexity/use_optional_chain.rs | 15 +- .../yoda_expressions_logicalAndCases3.js | 62 ++ .../yoda_expressions_logicalAndCases3.js.snap | 808 ++++++++++++++++ .../yoda_expressions_logicalAndCases4.js | 64 ++ .../yoda_expressions_logicalAndCases4.js.snap | 915 ++++++++++++++++++ .../yoda_expressions_logicalAndCases5.js | 65 ++ .../yoda_expressions_logicalAndCases5.js.snap | 857 ++++++++++++++++ .../yoda_expressions_logicalAndCases6.jsx | 67 ++ ...yoda_expressions_logicalAndCases6.jsx.snap | 895 +++++++++++++++++ .../yoda_expressions_validCases.ts | 47 + .../yoda_expressions_validCases.ts.snap | 56 ++ crates/biome_js_syntax/src/expr_ext.rs | 50 +- 13 files changed, 3887 insertions(+), 19 deletions(-) create mode 100644 .changeset/big-keys-play.md create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx.snap create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts create mode 100644 crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts.snap diff --git a/.changeset/big-keys-play.md b/.changeset/big-keys-play.md new file mode 100644 index 000000000000..86b1cbadaec1 --- /dev/null +++ b/.changeset/big-keys-play.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +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`). diff --git a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs index abb730d1d70c..394bf3e11a7a 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs @@ -263,10 +263,10 @@ impl Rule for UseOptionalChain { /// Normalize optional chain like. /// E.g. `foo != null` is normalized to `foo` fn normalized_optional_chain_like(expression: AnyJsExpression) -> SyntaxResult { - if let AnyJsExpression::JsBinaryExpression(expression) = &expression - && expression.is_optional_chain_like()? - { - return expression.left(); + if let AnyJsExpression::JsBinaryExpression(binary_expression) = &expression { + if let Some(expr) = binary_expression.extract_optional_chain_like()? { + return Ok(expr); + } } Ok(expression) } @@ -561,10 +561,9 @@ impl LogicalAndChain { // ```js // foo && foo.bar; // ``` - AnyJsExpression::JsBinaryExpression(expression) => expression - .is_optional_chain_like() - .ok()? - .then_some(expression.left().ok()?)?, + AnyJsExpression::JsBinaryExpression(expression) => { + expression.extract_optional_chain_like().ok()?? + } expression => expression, }; let head = match expression { diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js new file mode 100644 index 000000000000..81beefecee95 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js @@ -0,0 +1,62 @@ +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() && bing.bong +foo.bar && foo.bar.baz.buzz() && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + +// case with a call expr inside the chain for some inefficient reason +foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong +foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong +foo && foo?.() && foo?.().bar && bing.bong +foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + +// strict nullish equality checks null !== x && null !== x.y +// chained members +null !== foo && foo.bar +null !== foo.bar && foo.bar.baz +null !== foo && foo() +null !== foo.bar && foo.bar() +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz +null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && foo.bar.baz.buzz +null !== foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz +null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +null !== foo && null !== foo[bar] && null !== foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo[bar].baz && foo[bar].baz.buzz + +// chained calls +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() +null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && foo.bar.baz.buzz() +null !== foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +null !== foo && null !== foo.bar() && null !== foo.bar().baz && null !== foo.bar().baz.buzz && foo.bar().baz.buzz() diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js.snap b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js.snap new file mode 100644 index 000000000000..3ea82f224250 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases3.js.snap @@ -0,0 +1,808 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 152 +expression: yoda_expressions_logicalAndCases3.js +--- +# Input +```js +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() && bing.bong +foo.bar && foo.bar.baz.buzz() && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + +// case with a call expr inside the chain for some inefficient reason +foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong +foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong +foo && foo?.() && foo?.().bar && bing.bong +foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + +// strict nullish equality checks null !== x && null !== x.y +// chained members +null !== foo && foo.bar +null !== foo.bar && foo.bar.baz +null !== foo && foo() +null !== foo.bar && foo.bar() +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz +null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && foo.bar.baz.buzz +null !== foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz +null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +null !== foo && null !== foo[bar] && null !== foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo[bar].baz && foo[bar].baz.buzz + +// chained calls +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() +null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && foo.bar.baz.buzz() +null !== foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null !== foo && null !== foo.bar && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +null !== foo && null !== foo.bar() && null !== foo.bar().baz && null !== foo.bar().baz.buzz && foo.bar().baz.buzz() + +``` + +# Diagnostics +``` +yoda_expressions_logicalAndCases3.js:2:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // chained calls + > 2 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 3 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 4 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // chained calls + 2 │ - foo·&&·foo.bar·&&·foo.bar.baz·&&·foo.bar.baz.buzz()·&&·bing.bong + 2 │ + foo?.bar?.baz?.buzz()·&&·bing.bong + 3 3 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 4 4 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + + +``` + +``` +yoda_expressions_logicalAndCases3.js:3:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // chained calls + 2 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + > 3 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 5 │ + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // chained calls + 2 2 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + 3 │ - foo·&&·foo.bar·&&·foo.bar.baz·&&·foo.bar.baz.buzz·&&·foo.bar.baz.buzz()·&&·bing.bong + 3 │ + foo?.bar?.baz?.buzz?.()·&&·bing.bong + 4 4 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 5 5 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:4:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 2 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + 3 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + > 4 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 5 │ + 6 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 2 2 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + 3 3 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 4 │ - foo.bar·&&·foo.bar.baz·&&·foo.bar.baz.buzz·&&·foo.bar.baz.buzz()·&&·bing.bong + 4 │ + foo.bar?.baz?.buzz?.()·&&·bing.bong + 5 5 │ + 6 6 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:7:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 6 │ // case with a jump (i.e. a non-nullish prop) + > 7 │ foo && foo.bar && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 8 │ foo.bar && foo.bar.baz.buzz() && bing.bong + 9 │ + + i Unsafe fix: Change to an optional chain. + + 5 5 │ + 6 6 │ // case with a jump (i.e. a non-nullish prop) + 7 │ - foo·&&·foo.bar·&&·foo.bar.baz.buzz()·&&·bing.bong + 7 │ + foo?.bar?.baz.buzz()·&&·bing.bong + 8 8 │ foo.bar && foo.bar.baz.buzz() && bing.bong + 9 9 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:8:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 6 │ // case with a jump (i.e. a non-nullish prop) + 7 │ foo && foo.bar && foo.bar.baz.buzz() && bing.bong + > 8 │ foo.bar && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ + 10 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 6 6 │ // case with a jump (i.e. a non-nullish prop) + 7 7 │ foo && foo.bar && foo.bar.baz.buzz() && bing.bong + 8 │ - foo.bar·&&·foo.bar.baz.buzz()·&&·bing.bong + 8 │ + foo.bar?.baz.buzz()·&&·bing.bong + 9 9 │ + 10 10 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:11:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 10 │ // case with a jump (i.e. a non-nullish prop) + > 11 │ foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 12 │ + 13 │ // case with a call expr inside the chain for some inefficient reason + + i Unsafe fix: Change to an optional chain. + + 9 9 │ + 10 10 │ // case with a jump (i.e. a non-nullish prop) + 11 │ - foo·&&·foo.bar·&&·foo.bar.baz.buzz·&&·foo.bar.baz.buzz()·&&·bing.bong + 11 │ + foo?.bar?.baz.buzz?.()·&&·bing.bong + 12 12 │ + 13 13 │ // case with a call expr inside the chain for some inefficient reason + + +``` + +``` +yoda_expressions_logicalAndCases3.js:14:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 13 │ // case with a call expr inside the chain for some inefficient reason + > 14 │ foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 15 │ + 16 │ // chained calls with element access + + i Unsafe fix: Change to an optional chain. + + 12 12 │ + 13 13 │ // case with a call expr inside the chain for some inefficient reason + 14 │ - foo·&&·foo.bar()·&&·foo.bar().baz·&&·foo.bar().baz.buzz·&&·foo.bar().baz.buzz()·&&·bing.bong + 14 │ + foo?.bar()?.baz?.buzz?.()·&&·bing.bong + 15 15 │ + 16 16 │ // chained calls with element access + + +``` + +``` +yoda_expressions_logicalAndCases3.js:17:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 16 │ // chained calls with element access + > 17 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + 19 │ + + i Unsafe fix: Change to an optional chain. + + 15 15 │ + 16 16 │ // chained calls with element access + 17 │ - foo·&&·foo.bar·&&·foo.bar.baz·&&·foo.bar.baz[buzz]()·&&·bing.bong + 17 │ + foo?.bar?.baz?.[buzz]()·&&·bing.bong + 18 18 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + 19 19 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:18:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 16 │ // chained calls with element access + 17 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + > 18 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ + 20 │ // (partially) pre-optional chained + + i Unsafe fix: Change to an optional chain. + + 16 16 │ // chained calls with element access + 17 17 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + 18 │ - foo·&&·foo.bar·&&·foo.bar.baz·&&·foo.bar.baz[buzz]·&&·foo.bar.baz[buzz]()·&&·bing.bong + 18 │ + foo?.bar?.baz?.[buzz]?.()·&&·bing.bong + 19 19 │ + 20 20 │ // (partially) pre-optional chained + + +``` + +``` +yoda_expressions_logicalAndCases3.js:21:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 20 │ // (partially) pre-optional chained + > 21 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 22 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + 23 │ foo && foo?.() && foo?.().bar && bing.bong + + i Unsafe fix: Change to an optional chain. + + 19 19 │ + 20 20 │ // (partially) pre-optional chained + 21 │ - foo·&&·foo?.bar·&&·foo?.bar.baz·&&·foo?.bar.baz[buzz]·&&·foo?.bar.baz[buzz]()·&&·bing.bong + 21 │ + foo?.bar?.baz?.[buzz]?.()·&&·bing.bong + 22 22 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + 23 23 │ foo && foo?.() && foo?.().bar && bing.bong + + +``` + +``` +yoda_expressions_logicalAndCases3.js:22:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 20 │ // (partially) pre-optional chained + 21 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + > 22 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 23 │ foo && foo?.() && foo?.().bar && bing.bong + 24 │ foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + + i Unsafe fix: Change to an optional chain. + + 22 │ foo·&&·foo?.bar.baz·&&·foo?.bar.baz[buzz]·&&·bing.bong + │ ------- ------- ------- + +``` + +``` +yoda_expressions_logicalAndCases3.js:23:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 21 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + 22 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + > 23 │ foo && foo?.() && foo?.().bar && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 24 │ foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + 25 │ + + i Unsafe fix: Change to an optional chain. + + 23 │ foo·&&·foo?.()·&&·foo?.().bar·&&·bing.bong + │ ------- ------- --- + +``` + +``` +yoda_expressions_logicalAndCases3.js:24:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 22 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + 23 │ foo && foo?.() && foo?.().bar && bing.bong + > 24 │ foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 25 │ + 26 │ // strict nullish equality checks null !== x && null !== x.y + + i Unsafe fix: Change to an optional chain. + + 24 │ foo.bar·&&·foo.bar?.()·&&·foo.bar?.().baz·&&·bing.bong + │ ----------- ----------- --- + +``` + +``` +yoda_expressions_logicalAndCases3.js:28:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 26 │ // strict nullish equality checks null !== x && null !== x.y + 27 │ // chained members + > 28 │ null !== foo && foo.bar + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 29 │ null !== foo.bar && foo.bar.baz + 30 │ null !== foo && foo() + + i Unsafe fix: Change to an optional chain. + + 26 26 │ // strict nullish equality checks null !== x && null !== x.y + 27 27 │ // chained members + 28 │ - null·!==·foo·&&·foo.bar + 28 │ + foo?.bar + 29 29 │ null !== foo.bar && foo.bar.baz + 30 30 │ null !== foo && foo() + + +``` + +``` +yoda_expressions_logicalAndCases3.js:29:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 27 │ // chained members + 28 │ null !== foo && foo.bar + > 29 │ null !== foo.bar && foo.bar.baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 30 │ null !== foo && foo() + 31 │ null !== foo.bar && foo.bar() + + i Unsafe fix: Change to an optional chain. + + 27 27 │ // chained members + 28 28 │ null !== foo && foo.bar + 29 │ - null·!==·foo.bar·&&·foo.bar.baz + 29 │ + foo.bar?.baz + 30 30 │ null !== foo && foo() + 31 31 │ null !== foo.bar && foo.bar() + + +``` + +``` +yoda_expressions_logicalAndCases3.js:30:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 28 │ null !== foo && foo.bar + 29 │ null !== foo.bar && foo.bar.baz + > 30 │ null !== foo && foo() + │ ^^^^^^^^^^^^^^^^^^^^^ + 31 │ null !== foo.bar && foo.bar() + 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 28 28 │ null !== foo && foo.bar + 29 29 │ null !== foo.bar && foo.bar.baz + 30 │ - null·!==·foo·&&·foo() + 30 │ + foo?.() + 31 31 │ null !== foo.bar && foo.bar() + 32 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases3.js:31:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 29 │ null !== foo.bar && foo.bar.baz + 30 │ null !== foo && foo() + > 31 │ null !== foo.bar && foo.bar() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + 33 │ null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 29 29 │ null !== foo.bar && foo.bar.baz + 30 30 │ null !== foo && foo() + 31 │ - null·!==·foo.bar·&&·foo.bar() + 31 │ + foo.bar?.() + 32 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + 33 33 │ null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases3.js:32:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 30 │ null !== foo && foo() + 31 │ null !== foo.bar && foo.bar() + > 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 │ null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + 34 │ + + i Unsafe fix: Change to an optional chain. + + 30 30 │ null !== foo && foo() + 31 31 │ null !== foo.bar && foo.bar() + 32 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 32 │ + foo?.bar?.baz?.buzz + 33 33 │ null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + 34 34 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:33:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 31 │ null !== foo.bar && foo.bar() + 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + > 33 │ null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 34 │ + 35 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 31 31 │ null !== foo.bar && foo.bar() + 32 32 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz + 33 │ - null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 33 │ + foo.bar?.baz?.buzz + 34 34 │ + 35 35 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:36:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 35 │ // case with a jump (i.e. a non-nullish prop) + > 36 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 37 │ null !== foo.bar && foo.bar.baz.buzz + 38 │ + + i Unsafe fix: Change to an optional chain. + + 34 34 │ + 35 35 │ // case with a jump (i.e. a non-nullish prop) + 36 │ - null·!==·foo·&&·null·!==·foo.bar·&&·foo.bar.baz.buzz + 36 │ + foo?.bar?.baz.buzz + 37 37 │ null !== foo.bar && foo.bar.baz.buzz + 38 38 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:37:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 35 │ // case with a jump (i.e. a non-nullish prop) + 36 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz + > 37 │ null !== foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 38 │ + 39 │ // case where for some reason there is a doubled up expression + + i Unsafe fix: Change to an optional chain. + + 35 35 │ // case with a jump (i.e. a non-nullish prop) + 36 36 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz + 37 │ - null·!==·foo.bar·&&·foo.bar.baz.buzz + 37 │ + foo.bar?.baz.buzz + 38 38 │ + 39 39 │ // case where for some reason there is a doubled up expression + + +``` + +``` +yoda_expressions_logicalAndCases3.js:40:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 39 │ // case where for some reason there is a doubled up expression + > 40 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 41 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + 42 │ + + i Unsafe fix: Change to an optional chain. + + 38 38 │ + 39 39 │ // case where for some reason there is a doubled up expression + 40 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·null·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 40 │ + foo?.bar?.baz?.buzz + 41 41 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + 42 42 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:41:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 39 │ // case where for some reason there is a doubled up expression + 40 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + > 41 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 42 │ + 43 │ // chained members with element access + + i Unsafe fix: Change to an optional chain. + + 39 39 │ // case where for some reason there is a doubled up expression + 40 40 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz + 41 │ - null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·null·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 41 │ + foo.bar?.baz?.buzz + 42 42 │ + 43 43 │ // chained members with element access + + +``` + +``` +yoda_expressions_logicalAndCases3.js:44:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 43 │ // chained members with element access + > 44 │ null !== foo && null !== foo[bar] && null !== foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 45 │ + 46 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 42 42 │ + 43 43 │ // chained members with element access + 44 │ - null·!==·foo·&&·null·!==·foo[bar]·&&·null·!==·foo[bar].baz·&&·foo[bar].baz.buzz + 44 │ + foo?.[bar]?.baz?.buzz + 45 45 │ + 46 46 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:47:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 46 │ // case with a jump (i.e. a non-nullish prop) + > 47 │ null !== foo && null !== foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 48 │ + 49 │ // chained calls + + i Unsafe fix: Change to an optional chain. + + 45 45 │ + 46 46 │ // case with a jump (i.e. a non-nullish prop) + 47 │ - null·!==·foo·&&·null·!==·foo[bar].baz·&&·foo[bar].baz.buzz + 47 │ + foo?.[bar].baz?.buzz + 48 48 │ + 49 49 │ // chained calls + + +``` + +``` +yoda_expressions_logicalAndCases3.js:50:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 49 │ // chained calls + > 50 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 51 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 52 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + + i Unsafe fix: Change to an optional chain. + + 48 48 │ + 49 49 │ // chained calls + 50 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·foo.bar.baz.buzz() + 50 │ + foo?.bar?.baz?.buzz() + 51 51 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 52 52 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + + +``` + +``` +yoda_expressions_logicalAndCases3.js:51:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 49 │ // chained calls + 50 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() + > 51 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 52 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 53 │ + + i Unsafe fix: Change to an optional chain. + + 49 49 │ // chained calls + 50 50 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() + 51 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·null·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 51 │ + foo?.bar?.baz?.buzz?.() + 52 52 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 53 53 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:52:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 50 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() + 51 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + > 52 │ null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 53 │ + 54 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 50 50 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz() + 51 51 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 52 │ - null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·null·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 52 │ + foo.bar?.baz?.buzz?.() + 53 53 │ + 54 54 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:55:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 54 │ // case with a jump (i.e. a non-nullish prop) + > 55 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 56 │ null !== foo.bar && foo.bar.baz.buzz() + 57 │ + + i Unsafe fix: Change to an optional chain. + + 53 53 │ + 54 54 │ // case with a jump (i.e. a non-nullish prop) + 55 │ - null·!==·foo·&&·null·!==·foo.bar·&&·foo.bar.baz.buzz() + 55 │ + foo?.bar?.baz.buzz() + 56 56 │ null !== foo.bar && foo.bar.baz.buzz() + 57 57 │ + + +``` + +``` +yoda_expressions_logicalAndCases3.js:56:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 54 │ // case with a jump (i.e. a non-nullish prop) + 55 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz() + > 56 │ null !== foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 57 │ + 58 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 54 54 │ // case with a jump (i.e. a non-nullish prop) + 55 55 │ null !== foo && null !== foo.bar && foo.bar.baz.buzz() + 56 │ - null·!==·foo.bar·&&·foo.bar.baz.buzz() + 56 │ + foo.bar?.baz.buzz() + 57 57 │ + 58 58 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases3.js:59:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 58 │ // case with a jump (i.e. a non-nullish prop) + > 59 │ null !== foo && null !== foo.bar && null !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 60 │ + 61 │ // case with a call expr inside the chain for some inefficient reason + + i Unsafe fix: Change to an optional chain. + + 57 57 │ + 58 58 │ // case with a jump (i.e. a non-nullish prop) + 59 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 59 │ + foo?.bar?.baz.buzz?.() + 60 60 │ + 61 61 │ // case with a call expr inside the chain for some inefficient reason + + +``` + +``` +yoda_expressions_logicalAndCases3.js:62:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 61 │ // case with a call expr inside the chain for some inefficient reason + > 62 │ null !== foo && null !== foo.bar() && null !== foo.bar().baz && null !== foo.bar().baz.buzz && foo.bar().baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 63 │ + + i Unsafe fix: Change to an optional chain. + + 60 60 │ + 61 61 │ // case with a call expr inside the chain for some inefficient reason + 62 │ - null·!==·foo·&&·null·!==·foo.bar()·&&·null·!==·foo.bar().baz·&&·null·!==·foo.bar().baz.buzz·&&·foo.bar().baz.buzz() + 62 │ + foo?.bar()?.baz?.buzz?.() + 63 63 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js new file mode 100644 index 000000000000..262970361bbe --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js @@ -0,0 +1,64 @@ +// chained calls with element access +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz[buzz]() +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +null !== foo && null !== foo?.bar && null !== foo?.bar.baz && null !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] +null !== foo && null !== foo?.() && foo?.().bar +null !== foo.bar && null !== foo.bar?.() && foo.bar?.().baz + +// chained members +undefined !== foo && foo.bar +undefined !== foo.bar && foo.bar.baz +undefined !== foo && foo() +undefined !== foo.bar && foo.bar() +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz +undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz +undefined !== foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz +undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +undefined !== foo && undefined !== foo[bar] && undefined !== foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo[bar].baz && foo[bar].baz.buzz + +// 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() + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz() +undefined !== foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +undefined !== foo && undefined !== foo.bar() && undefined !== foo.bar().baz && undefined !== foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +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]() + +// (partially) pre-optional chained +undefined !== foo && undefined !== foo?.bar && undefined !== foo?.bar.baz && undefined !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] +undefined !== foo && undefined !== foo?.() && foo?.().bar +undefined !== foo.bar && undefined !== foo.bar?.() && foo.bar?.().baz + +// chained members +null != foo && foo.bar +null != foo.bar && foo.bar.baz +null != foo && foo() +null != foo.bar && foo.bar() +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz +null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js.snap b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js.snap new file mode 100644 index 000000000000..cc0504776746 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases4.js.snap @@ -0,0 +1,915 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 152 +expression: yoda_expressions_logicalAndCases4.js +--- +# Input +```js +// chained calls with element access +null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz[buzz]() +null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +null !== foo && null !== foo?.bar && null !== foo?.bar.baz && null !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] +null !== foo && null !== foo?.() && foo?.().bar +null !== foo.bar && null !== foo.bar?.() && foo.bar?.().baz + +// chained members +undefined !== foo && foo.bar +undefined !== foo.bar && foo.bar.baz +undefined !== foo && foo() +undefined !== foo.bar && foo.bar() +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz +undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz +undefined !== foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz +undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +undefined !== foo && undefined !== foo[bar] && undefined !== foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo[bar].baz && foo[bar].baz.buzz + +// 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() + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz() +undefined !== foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +undefined !== foo && undefined !== foo.bar() && undefined !== foo.bar().baz && undefined !== foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +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]() + +// (partially) pre-optional chained +undefined !== foo && undefined !== foo?.bar && undefined !== foo?.bar.baz && undefined !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] +undefined !== foo && undefined !== foo?.() && foo?.().bar +undefined !== foo.bar && undefined !== foo.bar?.() && foo.bar?.().baz + +// chained members +null != foo && foo.bar +null != foo.bar && foo.bar.baz +null != foo && foo() +null != foo.bar && foo.bar() +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz +null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + +``` + +# Diagnostics +``` +yoda_expressions_logicalAndCases4.js:2:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // chained calls with element access + > 2 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 3 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 4 │ + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // chained calls with element access + 2 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·foo.bar.baz[buzz]() + 2 │ + foo?.bar?.baz?.[buzz]() + 3 3 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 4 4 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:3:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // chained calls with element access + 2 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz[buzz]() + > 3 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ + 5 │ // (partially) pre-optional chained + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // chained calls with element access + 2 2 │ null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz[buzz]() + 3 │ - null·!==·foo·&&·null·!==·foo.bar·&&·null·!==·foo.bar.baz·&&·null·!==·foo.bar.baz[buzz]·&&·foo.bar.baz[buzz]() + 3 │ + foo?.bar?.baz?.[buzz]?.() + 4 4 │ + 5 5 │ // (partially) pre-optional chained + + +``` + +``` +yoda_expressions_logicalAndCases4.js:6:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 5 │ // (partially) pre-optional chained + > 6 │ null !== foo && null !== foo?.bar && null !== foo?.bar.baz && null !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 7 │ null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] + 8 │ null !== foo && null !== foo?.() && foo?.().bar + + i Unsafe fix: Change to an optional chain. + + 4 4 │ + 5 5 │ // (partially) pre-optional chained + 6 │ - null·!==·foo·&&·null·!==·foo?.bar·&&·null·!==·foo?.bar.baz·&&·null·!==·foo?.bar.baz[buzz]·&&·foo?.bar.baz[buzz]() + 6 │ + foo?.bar?.baz?.[buzz]?.() + 7 7 │ null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] + 8 8 │ null !== foo && null !== foo?.() && foo?.().bar + + +``` + +``` +yoda_expressions_logicalAndCases4.js:7:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 5 │ // (partially) pre-optional chained + 6 │ null !== foo && null !== foo?.bar && null !== foo?.bar.baz && null !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + > 7 │ null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 8 │ null !== foo && null !== foo?.() && foo?.().bar + 9 │ null !== foo.bar && null !== foo.bar?.() && foo.bar?.().baz + + i Unsafe fix: Change to an optional chain. + + 7 │ null·!==·foo·&&·null·!==·foo?.bar.baz·&&·foo?.bar.baz[buzz] + │ --------- ---------------- ------- ------- + +``` + +``` +yoda_expressions_logicalAndCases4.js:8:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 6 │ null !== foo && null !== foo?.bar && null !== foo?.bar.baz && null !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + 7 │ null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] + > 8 │ null !== foo && null !== foo?.() && foo?.().bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ null !== foo.bar && null !== foo.bar?.() && foo.bar?.().baz + 10 │ + + i Unsafe fix: Change to an optional chain. + + 8 │ null·!==·foo·&&·null·!==·foo?.()·&&·foo?.().bar + │ --------- ---------------- ------- --- + +``` + +``` +yoda_expressions_logicalAndCases4.js:9:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 7 │ null !== foo && null !== foo?.bar.baz && foo?.bar.baz[buzz] + 8 │ null !== foo && null !== foo?.() && foo?.().bar + > 9 │ null !== foo.bar && null !== foo.bar?.() && foo.bar?.().baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 │ + 11 │ // chained members + + i Unsafe fix: Change to an optional chain. + + 9 │ null·!==·foo.bar·&&·null·!==·foo.bar?.()·&&·foo.bar?.().baz + │ --------- -------------------- ----------- --- + +``` + +``` +yoda_expressions_logicalAndCases4.js:12:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 11 │ // chained members + > 12 │ undefined !== foo && foo.bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 13 │ undefined !== foo.bar && foo.bar.baz + 14 │ undefined !== foo && foo() + + i Unsafe fix: Change to an optional chain. + + 10 10 │ + 11 11 │ // chained members + 12 │ - undefined·!==·foo·&&·foo.bar + 12 │ + foo?.bar + 13 13 │ undefined !== foo.bar && foo.bar.baz + 14 14 │ undefined !== foo && foo() + + +``` + +``` +yoda_expressions_logicalAndCases4.js:13:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 11 │ // chained members + 12 │ undefined !== foo && foo.bar + > 13 │ undefined !== foo.bar && foo.bar.baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 14 │ undefined !== foo && foo() + 15 │ undefined !== foo.bar && foo.bar() + + i Unsafe fix: Change to an optional chain. + + 11 11 │ // chained members + 12 12 │ undefined !== foo && foo.bar + 13 │ - undefined·!==·foo.bar·&&·foo.bar.baz + 13 │ + foo.bar?.baz + 14 14 │ undefined !== foo && foo() + 15 15 │ undefined !== foo.bar && foo.bar() + + +``` + +``` +yoda_expressions_logicalAndCases4.js:14:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 12 │ undefined !== foo && foo.bar + 13 │ undefined !== foo.bar && foo.bar.baz + > 14 │ undefined !== foo && foo() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + 15 │ undefined !== foo.bar && foo.bar() + 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 12 12 │ undefined !== foo && foo.bar + 13 13 │ undefined !== foo.bar && foo.bar.baz + 14 │ - undefined·!==·foo·&&·foo() + 14 │ + foo?.() + 15 15 │ undefined !== foo.bar && foo.bar() + 16 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases4.js:15:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 13 │ undefined !== foo.bar && foo.bar.baz + 14 │ undefined !== foo && foo() + > 15 │ undefined !== foo.bar && foo.bar() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + 17 │ undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 13 13 │ undefined !== foo.bar && foo.bar.baz + 14 14 │ undefined !== foo && foo() + 15 │ - undefined·!==·foo.bar·&&·foo.bar() + 15 │ + foo.bar?.() + 16 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + 17 17 │ undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases4.js:16:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 14 │ undefined !== foo && foo() + 15 │ undefined !== foo.bar && foo.bar() + > 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 17 │ undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + 18 │ + + i Unsafe fix: Change to an optional chain. + + 14 14 │ undefined !== foo && foo() + 15 15 │ undefined !== foo.bar && foo.bar() + 16 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 16 │ + foo?.bar?.baz?.buzz + 17 17 │ undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + 18 18 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:17:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 15 │ undefined !== foo.bar && foo.bar() + 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + > 17 │ undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ + 19 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 15 15 │ undefined !== foo.bar && foo.bar() + 16 16 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz + 17 │ - undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 17 │ + foo.bar?.baz?.buzz + 18 18 │ + 19 19 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases4.js:20:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 19 │ // case with a jump (i.e. a non-nullish prop) + > 20 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 21 │ undefined !== foo.bar && foo.bar.baz.buzz + 22 │ + + i Unsafe fix: Change to an optional chain. + + 18 18 │ + 19 19 │ // case with a jump (i.e. a non-nullish prop) + 20 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·foo.bar.baz.buzz + 20 │ + foo?.bar?.baz.buzz + 21 21 │ undefined !== foo.bar && foo.bar.baz.buzz + 22 22 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:21:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 19 │ // case with a jump (i.e. a non-nullish prop) + 20 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz + > 21 │ undefined !== foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 22 │ + 23 │ // case where for some reason there is a doubled up expression + + i Unsafe fix: Change to an optional chain. + + 19 19 │ // case with a jump (i.e. a non-nullish prop) + 20 20 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz + 21 │ - undefined·!==·foo.bar·&&·foo.bar.baz.buzz + 21 │ + foo.bar?.baz.buzz + 22 22 │ + 23 23 │ // case where for some reason there is a doubled up expression + + +``` + +``` +yoda_expressions_logicalAndCases4.js:24:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 23 │ // case where for some reason there is a doubled up expression + > 24 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 25 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + 26 │ + + i Unsafe fix: Change to an optional chain. + + 22 22 │ + 23 23 │ // case where for some reason there is a doubled up expression + 24 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 24 │ + foo?.bar?.baz?.buzz + 25 25 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + 26 26 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:25:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 23 │ // case where for some reason there is a doubled up expression + 24 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + > 25 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 26 │ + 27 │ // chained members with element access + + i Unsafe fix: Change to an optional chain. + + 23 23 │ // case where for some reason there is a doubled up expression + 24 24 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz && foo.bar.baz.buzz + 25 │ - undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz + 25 │ + foo.bar?.baz?.buzz + 26 26 │ + 27 27 │ // chained members with element access + + +``` + +``` +yoda_expressions_logicalAndCases4.js:28:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 27 │ // chained members with element access + > 28 │ undefined !== foo && undefined !== foo[bar] && undefined !== foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 29 │ + 30 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 26 26 │ + 27 27 │ // chained members with element access + 28 │ - undefined·!==·foo·&&·undefined·!==·foo[bar]·&&·undefined·!==·foo[bar].baz·&&·foo[bar].baz.buzz + 28 │ + foo?.[bar]?.baz?.buzz + 29 29 │ + 30 30 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases4.js:31:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 30 │ // case with a jump (i.e. a non-nullish prop) + > 31 │ undefined !== foo && undefined !== foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 32 │ + 33 │ // chained calls + + i Unsafe fix: Change to an optional chain. + + 29 29 │ + 30 30 │ // case with a jump (i.e. a non-nullish prop) + 31 │ - undefined·!==·foo·&&·undefined·!==·foo[bar].baz·&&·foo[bar].baz.buzz + 31 │ + foo?.[bar].baz?.buzz + 32 32 │ + 33 33 │ // chained calls + + +``` + +``` +yoda_expressions_logicalAndCases4.js:34:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 33 │ // chained calls + > 34 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 35 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 36 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + + i Unsafe fix: Change to an optional chain. + + 32 32 │ + 33 33 │ // chained calls + 34 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz() + 34 │ + foo?.bar?.baz?.buzz() + 35 35 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 36 36 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + + +``` + +``` +yoda_expressions_logicalAndCases4.js:35:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 33 │ // chained calls + 34 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz() + > 35 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 36 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 37 │ + + i Unsafe fix: Change to an optional chain. + + 33 33 │ // chained calls + 34 34 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz() + 35 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·undefined·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 35 │ + foo?.bar?.baz?.buzz?.() + 36 36 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 37 37 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:36:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 34 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz() + 35 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + > 36 │ undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 37 │ + 38 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 34 34 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz() + 35 35 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + 36 │ - undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·undefined·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 36 │ + foo.bar?.baz?.buzz?.() + 37 37 │ + 38 38 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases4.js:39:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 38 │ // case with a jump (i.e. a non-nullish prop) + > 39 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 40 │ undefined !== foo.bar && foo.bar.baz.buzz() + 41 │ + + i Unsafe fix: Change to an optional chain. + + 37 37 │ + 38 38 │ // case with a jump (i.e. a non-nullish prop) + 39 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·foo.bar.baz.buzz() + 39 │ + foo?.bar?.baz.buzz() + 40 40 │ undefined !== foo.bar && foo.bar.baz.buzz() + 41 41 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:40:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 38 │ // case with a jump (i.e. a non-nullish prop) + 39 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz() + > 40 │ undefined !== foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 41 │ + 42 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 38 38 │ // case with a jump (i.e. a non-nullish prop) + 39 39 │ undefined !== foo && undefined !== foo.bar && foo.bar.baz.buzz() + 40 │ - undefined·!==·foo.bar·&&·foo.bar.baz.buzz() + 40 │ + foo.bar?.baz.buzz() + 41 41 │ + 42 42 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases4.js:43:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 42 │ // case with a jump (i.e. a non-nullish prop) + > 43 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 44 │ + 45 │ // case with a call expr inside the chain for some inefficient reason + + i Unsafe fix: Change to an optional chain. + + 41 41 │ + 42 42 │ // case with a jump (i.e. a non-nullish prop) + 43 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 43 │ + foo?.bar?.baz.buzz?.() + 44 44 │ + 45 45 │ // case with a call expr inside the chain for some inefficient reason + + +``` + +``` +yoda_expressions_logicalAndCases4.js:46:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 45 │ // case with a call expr inside the chain for some inefficient reason + > 46 │ undefined !== foo && undefined !== foo.bar() && undefined !== foo.bar().baz && undefined !== foo.bar().baz.buzz && foo.bar().baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 47 │ + 48 │ // chained calls with element access + + i Unsafe fix: Change to an optional chain. + + 44 44 │ + 45 45 │ // case with a call expr inside the chain for some inefficient reason + 46 │ - undefined·!==·foo·&&·undefined·!==·foo.bar()·&&·undefined·!==·foo.bar().baz·&&·undefined·!==·foo.bar().baz.buzz·&&·foo.bar().baz.buzz() + 46 │ + foo?.bar()?.baz?.buzz?.() + 47 47 │ + 48 48 │ // chained calls with element access + + +``` + +``` +yoda_expressions_logicalAndCases4.js:49:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 48 │ // chained calls with element access + > 49 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 50 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 51 │ + + i Unsafe fix: Change to an optional chain. + + 47 47 │ + 48 48 │ // chained calls with element access + 49 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz[buzz]() + 49 │ + foo?.bar?.baz?.[buzz]() + 50 50 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 51 51 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:50:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 48 │ // chained calls with element access + 49 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz[buzz]() + > 50 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && undefined !== foo.bar.baz[buzz] && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 51 │ + 52 │ // (partially) pre-optional chained + + i Unsafe fix: Change to an optional chain. + + 48 48 │ // chained calls with element access + 49 49 │ undefined !== foo && undefined !== foo.bar && undefined !== foo.bar.baz && foo.bar.baz[buzz]() + 50 │ - undefined·!==·foo·&&·undefined·!==·foo.bar·&&·undefined·!==·foo.bar.baz·&&·undefined·!==·foo.bar.baz[buzz]·&&·foo.bar.baz[buzz]() + 50 │ + foo?.bar?.baz?.[buzz]?.() + 51 51 │ + 52 52 │ // (partially) pre-optional chained + + +``` + +``` +yoda_expressions_logicalAndCases4.js:53:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 52 │ // (partially) pre-optional chained + > 53 │ undefined !== foo && undefined !== foo?.bar && undefined !== foo?.bar.baz && undefined !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 54 │ undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] + 55 │ undefined !== foo && undefined !== foo?.() && foo?.().bar + + i Unsafe fix: Change to an optional chain. + + 51 51 │ + 52 52 │ // (partially) pre-optional chained + 53 │ - undefined·!==·foo·&&·undefined·!==·foo?.bar·&&·undefined·!==·foo?.bar.baz·&&·undefined·!==·foo?.bar.baz[buzz]·&&·foo?.bar.baz[buzz]() + 53 │ + foo?.bar?.baz?.[buzz]?.() + 54 54 │ undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] + 55 55 │ undefined !== foo && undefined !== foo?.() && foo?.().bar + + +``` + +``` +yoda_expressions_logicalAndCases4.js:54:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 52 │ // (partially) pre-optional chained + 53 │ undefined !== foo && undefined !== foo?.bar && undefined !== foo?.bar.baz && undefined !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + > 54 │ undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 55 │ undefined !== foo && undefined !== foo?.() && foo?.().bar + 56 │ undefined !== foo.bar && undefined !== foo.bar?.() && foo.bar?.().baz + + i Unsafe fix: Change to an optional chain. + + 54 │ undefined·!==·foo·&&·undefined·!==·foo?.bar.baz·&&·foo?.bar.baz[buzz] + │ -------------- --------------------- ------- ------- + +``` + +``` +yoda_expressions_logicalAndCases4.js:55:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 53 │ undefined !== foo && undefined !== foo?.bar && undefined !== foo?.bar.baz && undefined !== foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + 54 │ undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] + > 55 │ undefined !== foo && undefined !== foo?.() && foo?.().bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 56 │ undefined !== foo.bar && undefined !== foo.bar?.() && foo.bar?.().baz + 57 │ + + i Unsafe fix: Change to an optional chain. + + 55 │ undefined·!==·foo·&&·undefined·!==·foo?.()·&&·foo?.().bar + │ -------------- --------------------- ------- --- + +``` + +``` +yoda_expressions_logicalAndCases4.js:56:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 54 │ undefined !== foo && undefined !== foo?.bar.baz && foo?.bar.baz[buzz] + 55 │ undefined !== foo && undefined !== foo?.() && foo?.().bar + > 56 │ undefined !== foo.bar && undefined !== foo.bar?.() && foo.bar?.().baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 57 │ + 58 │ // chained members + + i Unsafe fix: Change to an optional chain. + + 56 │ undefined·!==·foo.bar·&&·undefined·!==·foo.bar?.()·&&·foo.bar?.().baz + │ -------------- ------------------------- ----------- --- + +``` + +``` +yoda_expressions_logicalAndCases4.js:59:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 58 │ // chained members + > 59 │ null != foo && foo.bar + │ ^^^^^^^^^^^^^^^^^^^^^^ + 60 │ null != foo.bar && foo.bar.baz + 61 │ null != foo && foo() + + i Unsafe fix: Change to an optional chain. + + 57 57 │ + 58 58 │ // chained members + 59 │ - null·!=·foo·&&·foo.bar + 59 │ + foo?.bar + 60 60 │ null != foo.bar && foo.bar.baz + 61 61 │ null != foo && foo() + + +``` + +``` +yoda_expressions_logicalAndCases4.js:60:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 58 │ // chained members + 59 │ null != foo && foo.bar + > 60 │ null != foo.bar && foo.bar.baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 61 │ null != foo && foo() + 62 │ null != foo.bar && foo.bar() + + i Unsafe fix: Change to an optional chain. + + 58 58 │ // chained members + 59 59 │ null != foo && foo.bar + 60 │ - null·!=·foo.bar·&&·foo.bar.baz + 60 │ + foo.bar?.baz + 61 61 │ null != foo && foo() + 62 62 │ null != foo.bar && foo.bar() + + +``` + +``` +yoda_expressions_logicalAndCases4.js:61:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 59 │ null != foo && foo.bar + 60 │ null != foo.bar && foo.bar.baz + > 61 │ null != foo && foo() + │ ^^^^^^^^^^^^^^^^^^^^ + 62 │ null != foo.bar && foo.bar() + 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 59 59 │ null != foo && foo.bar + 60 60 │ null != foo.bar && foo.bar.baz + 61 │ - null·!=·foo·&&·foo() + 61 │ + foo?.() + 62 62 │ null != foo.bar && foo.bar() + 63 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases4.js:62:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 60 │ null != foo.bar && foo.bar.baz + 61 │ null != foo && foo() + > 62 │ null != foo.bar && foo.bar() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + 64 │ null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 60 60 │ null != foo.bar && foo.bar.baz + 61 61 │ null != foo && foo() + 62 │ - null·!=·foo.bar·&&·foo.bar() + 62 │ + foo.bar?.() + 63 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + 64 64 │ null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases4.js:63:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 61 │ null != foo && foo() + 62 │ null != foo.bar && foo.bar() + > 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 64 │ null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + 65 │ + + i Unsafe fix: Change to an optional chain. + + 61 61 │ null != foo && foo() + 62 62 │ null != foo.bar && foo.bar() + 63 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 63 │ + foo?.bar?.baz?.buzz + 64 64 │ null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + 65 65 │ + + +``` + +``` +yoda_expressions_logicalAndCases4.js:64:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 62 │ null != foo.bar && foo.bar() + 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + > 64 │ null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 65 │ + + i Unsafe fix: Change to an optional chain. + + 62 62 │ null != foo.bar && foo.bar() + 63 63 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz + 64 │ - null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 64 │ + foo.bar?.baz?.buzz + 65 65 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js new file mode 100644 index 000000000000..aacf9dc934f9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js @@ -0,0 +1,65 @@ +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && foo.bar.baz.buzz +null != foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz +null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +null != foo && null != foo[bar] && null != foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo[bar].baz && foo[bar].baz.buzz + +// chained calls +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() +null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && foo.bar.baz.buzz() +null != foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +null != foo && null != foo.bar() && null != foo.bar().baz && null != foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz[buzz]() +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +null != foo && null != foo?.bar && null != foo?.bar.baz && null != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] +null != foo && null != foo?.() && foo?.().bar +null != foo.bar && null != foo.bar?.() && foo.bar?.().baz + +// chained members +undefined != foo && foo.bar +undefined != foo.bar && foo.bar.baz +undefined != foo && foo() +undefined != foo.bar && foo.bar() +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz +undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && foo.bar.baz.buzz +undefined != foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz +undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +undefined != foo && undefined != foo[bar] && undefined != foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo[bar].baz && foo[bar].baz.buzz + +// 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() diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js.snap b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js.snap new file mode 100644 index 000000000000..8e6ca6a535c2 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases5.js.snap @@ -0,0 +1,857 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 152 +expression: yoda_expressions_logicalAndCases5.js +--- +# Input +```js +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && foo.bar.baz.buzz +null != foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz +null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +null != foo && null != foo[bar] && null != foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo[bar].baz && foo[bar].baz.buzz + +// chained calls +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() +null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && foo.bar.baz.buzz() +null != foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +null != foo && null != foo.bar && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +null != foo && null != foo.bar() && null != foo.bar().baz && null != foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz[buzz]() +null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +null != foo && null != foo?.bar && null != foo?.bar.baz && null != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] +null != foo && null != foo?.() && foo?.().bar +null != foo.bar && null != foo.bar?.() && foo.bar?.().baz + +// chained members +undefined != foo && foo.bar +undefined != foo.bar && foo.bar.baz +undefined != foo && foo() +undefined != foo.bar && foo.bar() +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz +undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && foo.bar.baz.buzz +undefined != foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz +undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +undefined != foo && undefined != foo[bar] && undefined != foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo[bar].baz && foo[bar].baz.buzz + +// 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() + +``` + +# Diagnostics +``` +yoda_expressions_logicalAndCases5.js:2:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // case with a jump (i.e. a non-nullish prop) + > 2 │ null != foo && null != foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 3 │ null != foo.bar && foo.bar.baz.buzz + 4 │ + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // case with a jump (i.e. a non-nullish prop) + 2 │ - null·!=·foo·&&·null·!=·foo.bar·&&·foo.bar.baz.buzz + 2 │ + foo?.bar?.baz.buzz + 3 3 │ null != foo.bar && foo.bar.baz.buzz + 4 4 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:3:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // case with a jump (i.e. a non-nullish prop) + 2 │ null != foo && null != foo.bar && foo.bar.baz.buzz + > 3 │ null != foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ + 5 │ // case where for some reason there is a doubled up expression + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // case with a jump (i.e. a non-nullish prop) + 2 2 │ null != foo && null != foo.bar && foo.bar.baz.buzz + 3 │ - null·!=·foo.bar·&&·foo.bar.baz.buzz + 3 │ + foo.bar?.baz.buzz + 4 4 │ + 5 5 │ // case where for some reason there is a doubled up expression + + +``` + +``` +yoda_expressions_logicalAndCases5.js:6:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 5 │ // case where for some reason there is a doubled up expression + > 6 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 7 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + 8 │ + + i Unsafe fix: Change to an optional chain. + + 4 4 │ + 5 5 │ // case where for some reason there is a doubled up expression + 6 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·null·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 6 │ + foo?.bar?.baz?.buzz + 7 7 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + 8 8 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:7:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 5 │ // case where for some reason there is a doubled up expression + 6 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + > 7 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 8 │ + 9 │ // chained members with element access + + i Unsafe fix: Change to an optional chain. + + 5 5 │ // case where for some reason there is a doubled up expression + 6 6 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz && foo.bar.baz.buzz + 7 │ - null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·null·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 7 │ + foo.bar?.baz?.buzz + 8 8 │ + 9 9 │ // chained members with element access + + +``` + +``` +yoda_expressions_logicalAndCases5.js:10:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 9 │ // chained members with element access + > 10 │ null != foo && null != foo[bar] && null != foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 11 │ + 12 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 8 8 │ + 9 9 │ // chained members with element access + 10 │ - null·!=·foo·&&·null·!=·foo[bar]·&&·null·!=·foo[bar].baz·&&·foo[bar].baz.buzz + 10 │ + foo?.[bar]?.baz?.buzz + 11 11 │ + 12 12 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases5.js:13:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 12 │ // case with a jump (i.e. a non-nullish prop) + > 13 │ null != foo && null != foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 14 │ + 15 │ // chained calls + + i Unsafe fix: Change to an optional chain. + + 11 11 │ + 12 12 │ // case with a jump (i.e. a non-nullish prop) + 13 │ - null·!=·foo·&&·null·!=·foo[bar].baz·&&·foo[bar].baz.buzz + 13 │ + foo?.[bar].baz?.buzz + 14 14 │ + 15 15 │ // chained calls + + +``` + +``` +yoda_expressions_logicalAndCases5.js:16:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 15 │ // chained calls + > 16 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 17 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + 18 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + + i Unsafe fix: Change to an optional chain. + + 14 14 │ + 15 15 │ // chained calls + 16 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·foo.bar.baz.buzz() + 16 │ + foo?.bar?.baz?.buzz() + 17 17 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + 18 18 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + + +``` + +``` +yoda_expressions_logicalAndCases5.js:17:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 15 │ // chained calls + 16 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() + > 17 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + 19 │ + + i Unsafe fix: Change to an optional chain. + + 15 15 │ // chained calls + 16 16 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() + 17 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·null·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 17 │ + foo?.bar?.baz?.buzz?.() + 18 18 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + 19 19 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:18:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 16 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() + 17 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + > 18 │ null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ + 20 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 16 16 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz.buzz() + 17 17 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + 18 │ - null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·null·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 18 │ + foo.bar?.baz?.buzz?.() + 19 19 │ + 20 20 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases5.js:21:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 20 │ // case with a jump (i.e. a non-nullish prop) + > 21 │ null != foo && null != foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 22 │ null != foo.bar && foo.bar.baz.buzz() + 23 │ + + i Unsafe fix: Change to an optional chain. + + 19 19 │ + 20 20 │ // case with a jump (i.e. a non-nullish prop) + 21 │ - null·!=·foo·&&·null·!=·foo.bar·&&·foo.bar.baz.buzz() + 21 │ + foo?.bar?.baz.buzz() + 22 22 │ null != foo.bar && foo.bar.baz.buzz() + 23 23 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:22:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 20 │ // case with a jump (i.e. a non-nullish prop) + 21 │ null != foo && null != foo.bar && foo.bar.baz.buzz() + > 22 │ null != foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 23 │ + 24 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 20 20 │ // case with a jump (i.e. a non-nullish prop) + 21 21 │ null != foo && null != foo.bar && foo.bar.baz.buzz() + 22 │ - null·!=·foo.bar·&&·foo.bar.baz.buzz() + 22 │ + foo.bar?.baz.buzz() + 23 23 │ + 24 24 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases5.js:25:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 24 │ // case with a jump (i.e. a non-nullish prop) + > 25 │ null != foo && null != foo.bar && null != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 26 │ + 27 │ // case with a call expr inside the chain for some inefficient reason + + i Unsafe fix: Change to an optional chain. + + 23 23 │ + 24 24 │ // case with a jump (i.e. a non-nullish prop) + 25 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 25 │ + foo?.bar?.baz.buzz?.() + 26 26 │ + 27 27 │ // case with a call expr inside the chain for some inefficient reason + + +``` + +``` +yoda_expressions_logicalAndCases5.js:28:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 27 │ // case with a call expr inside the chain for some inefficient reason + > 28 │ null != foo && null != foo.bar() && null != foo.bar().baz && null != foo.bar().baz.buzz && foo.bar().baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 29 │ + 30 │ // chained calls with element access + + i Unsafe fix: Change to an optional chain. + + 26 26 │ + 27 27 │ // case with a call expr inside the chain for some inefficient reason + 28 │ - null·!=·foo·&&·null·!=·foo.bar()·&&·null·!=·foo.bar().baz·&&·null·!=·foo.bar().baz.buzz·&&·foo.bar().baz.buzz() + 28 │ + foo?.bar()?.baz?.buzz?.() + 29 29 │ + 30 30 │ // chained calls with element access + + +``` + +``` +yoda_expressions_logicalAndCases5.js:31:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 30 │ // chained calls with element access + > 31 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 32 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 33 │ + + i Unsafe fix: Change to an optional chain. + + 29 29 │ + 30 30 │ // chained calls with element access + 31 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·foo.bar.baz[buzz]() + 31 │ + foo?.bar?.baz?.[buzz]() + 32 32 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 33 33 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:32:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 30 │ // chained calls with element access + 31 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz[buzz]() + > 32 │ null != foo && null != foo.bar && null != foo.bar.baz && null != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 │ + 34 │ // (partially) pre-optional chained + + i Unsafe fix: Change to an optional chain. + + 30 30 │ // chained calls with element access + 31 31 │ null != foo && null != foo.bar && null != foo.bar.baz && foo.bar.baz[buzz]() + 32 │ - null·!=·foo·&&·null·!=·foo.bar·&&·null·!=·foo.bar.baz·&&·null·!=·foo.bar.baz[buzz]·&&·foo.bar.baz[buzz]() + 32 │ + foo?.bar?.baz?.[buzz]?.() + 33 33 │ + 34 34 │ // (partially) pre-optional chained + + +``` + +``` +yoda_expressions_logicalAndCases5.js:35:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 34 │ // (partially) pre-optional chained + > 35 │ null != foo && null != foo?.bar && null != foo?.bar.baz && null != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 36 │ null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] + 37 │ null != foo && null != foo?.() && foo?.().bar + + i Unsafe fix: Change to an optional chain. + + 33 33 │ + 34 34 │ // (partially) pre-optional chained + 35 │ - null·!=·foo·&&·null·!=·foo?.bar·&&·null·!=·foo?.bar.baz·&&·null·!=·foo?.bar.baz[buzz]·&&·foo?.bar.baz[buzz]() + 35 │ + foo?.bar?.baz?.[buzz]?.() + 36 36 │ null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] + 37 37 │ null != foo && null != foo?.() && foo?.().bar + + +``` + +``` +yoda_expressions_logicalAndCases5.js:36:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 34 │ // (partially) pre-optional chained + 35 │ null != foo && null != foo?.bar && null != foo?.bar.baz && null != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + > 36 │ null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 37 │ null != foo && null != foo?.() && foo?.().bar + 38 │ null != foo.bar && null != foo.bar?.() && foo.bar?.().baz + + i Unsafe fix: Change to an optional chain. + + 36 │ null·!=·foo·&&·null·!=·foo?.bar.baz·&&·foo?.bar.baz[buzz] + │ -------- --------------- ------- ------- + +``` + +``` +yoda_expressions_logicalAndCases5.js:37:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 35 │ null != foo && null != foo?.bar && null != foo?.bar.baz && null != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + 36 │ null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] + > 37 │ null != foo && null != foo?.() && foo?.().bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 38 │ null != foo.bar && null != foo.bar?.() && foo.bar?.().baz + 39 │ + + i Unsafe fix: Change to an optional chain. + + 37 │ null·!=·foo·&&·null·!=·foo?.()·&&·foo?.().bar + │ -------- --------------- ------- --- + +``` + +``` +yoda_expressions_logicalAndCases5.js:38:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 36 │ null != foo && null != foo?.bar.baz && foo?.bar.baz[buzz] + 37 │ null != foo && null != foo?.() && foo?.().bar + > 38 │ null != foo.bar && null != foo.bar?.() && foo.bar?.().baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 39 │ + 40 │ // chained members + + i Unsafe fix: Change to an optional chain. + + 38 │ null·!=·foo.bar·&&·null·!=·foo.bar?.()·&&·foo.bar?.().baz + │ -------- ------------------- ----------- --- + +``` + +``` +yoda_expressions_logicalAndCases5.js:41:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 40 │ // chained members + > 41 │ undefined != foo && foo.bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 42 │ undefined != foo.bar && foo.bar.baz + 43 │ undefined != foo && foo() + + i Unsafe fix: Change to an optional chain. + + 39 39 │ + 40 40 │ // chained members + 41 │ - undefined·!=·foo·&&·foo.bar + 41 │ + foo?.bar + 42 42 │ undefined != foo.bar && foo.bar.baz + 43 43 │ undefined != foo && foo() + + +``` + +``` +yoda_expressions_logicalAndCases5.js:42:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 40 │ // chained members + 41 │ undefined != foo && foo.bar + > 42 │ undefined != foo.bar && foo.bar.baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 43 │ undefined != foo && foo() + 44 │ undefined != foo.bar && foo.bar() + + i Unsafe fix: Change to an optional chain. + + 40 40 │ // chained members + 41 41 │ undefined != foo && foo.bar + 42 │ - undefined·!=·foo.bar·&&·foo.bar.baz + 42 │ + foo.bar?.baz + 43 43 │ undefined != foo && foo() + 44 44 │ undefined != foo.bar && foo.bar() + + +``` + +``` +yoda_expressions_logicalAndCases5.js:43:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 41 │ undefined != foo && foo.bar + 42 │ undefined != foo.bar && foo.bar.baz + > 43 │ undefined != foo && foo() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + 44 │ undefined != foo.bar && foo.bar() + 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 41 41 │ undefined != foo && foo.bar + 42 42 │ undefined != foo.bar && foo.bar.baz + 43 │ - undefined·!=·foo·&&·foo() + 43 │ + foo?.() + 44 44 │ undefined != foo.bar && foo.bar() + 45 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases5.js:44:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 42 │ undefined != foo.bar && foo.bar.baz + 43 │ undefined != foo && foo() + > 44 │ undefined != foo.bar && foo.bar() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + 46 │ undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + + i Unsafe fix: Change to an optional chain. + + 42 42 │ undefined != foo.bar && foo.bar.baz + 43 43 │ undefined != foo && foo() + 44 │ - undefined·!=·foo.bar·&&·foo.bar() + 44 │ + foo.bar?.() + 45 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + 46 46 │ undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +yoda_expressions_logicalAndCases5.js:45:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 43 │ undefined != foo && foo() + 44 │ undefined != foo.bar && foo.bar() + > 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 46 │ undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + 47 │ + + i Unsafe fix: Change to an optional chain. + + 43 43 │ undefined != foo && foo() + 44 44 │ undefined != foo.bar && foo.bar() + 45 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 45 │ + foo?.bar?.baz?.buzz + 46 46 │ undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + 47 47 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:46:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 44 │ undefined != foo.bar && foo.bar() + 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + > 46 │ undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 47 │ + 48 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 44 44 │ undefined != foo.bar && foo.bar() + 45 45 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz + 46 │ - undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 46 │ + foo.bar?.baz?.buzz + 47 47 │ + 48 48 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases5.js:49:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 48 │ // case with a jump (i.e. a non-nullish prop) + > 49 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 50 │ undefined != foo.bar && foo.bar.baz.buzz + 51 │ + + i Unsafe fix: Change to an optional chain. + + 47 47 │ + 48 48 │ // case with a jump (i.e. a non-nullish prop) + 49 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·foo.bar.baz.buzz + 49 │ + foo?.bar?.baz.buzz + 50 50 │ undefined != foo.bar && foo.bar.baz.buzz + 51 51 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:50:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 48 │ // case with a jump (i.e. a non-nullish prop) + 49 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz + > 50 │ undefined != foo.bar && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 51 │ + 52 │ // case where for some reason there is a doubled up expression + + i Unsafe fix: Change to an optional chain. + + 48 48 │ // case with a jump (i.e. a non-nullish prop) + 49 49 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz + 50 │ - undefined·!=·foo.bar·&&·foo.bar.baz.buzz + 50 │ + foo.bar?.baz.buzz + 51 51 │ + 52 52 │ // case where for some reason there is a doubled up expression + + +``` + +``` +yoda_expressions_logicalAndCases5.js:53:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 52 │ // case where for some reason there is a doubled up expression + > 53 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 54 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + 55 │ + + i Unsafe fix: Change to an optional chain. + + 51 51 │ + 52 52 │ // case where for some reason there is a doubled up expression + 53 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 53 │ + foo?.bar?.baz?.buzz + 54 54 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + 55 55 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:54:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 52 │ // case where for some reason there is a doubled up expression + 53 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + > 54 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 55 │ + 56 │ // chained members with element access + + i Unsafe fix: Change to an optional chain. + + 52 52 │ // case where for some reason there is a doubled up expression + 53 53 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz && foo.bar.baz.buzz + 54 │ - undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz.buzz + 54 │ + foo.bar?.baz?.buzz + 55 55 │ + 56 56 │ // chained members with element access + + +``` + +``` +yoda_expressions_logicalAndCases5.js:57:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 56 │ // chained members with element access + > 57 │ undefined != foo && undefined != foo[bar] && undefined != foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 58 │ + 59 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 55 55 │ + 56 56 │ // chained members with element access + 57 │ - undefined·!=·foo·&&·undefined·!=·foo[bar]·&&·undefined·!=·foo[bar].baz·&&·foo[bar].baz.buzz + 57 │ + foo?.[bar]?.baz?.buzz + 58 58 │ + 59 59 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases5.js:60:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 59 │ // case with a jump (i.e. a non-nullish prop) + > 60 │ undefined != foo && undefined != foo[bar].baz && foo[bar].baz.buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 61 │ + 62 │ // chained calls + + i Unsafe fix: Change to an optional chain. + + 58 58 │ + 59 59 │ // case with a jump (i.e. a non-nullish prop) + 60 │ - undefined·!=·foo·&&·undefined·!=·foo[bar].baz·&&·foo[bar].baz.buzz + 60 │ + foo?.[bar].baz?.buzz + 61 61 │ + 62 62 │ // chained calls + + +``` + +``` +yoda_expressions_logicalAndCases5.js:63:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 62 │ // chained calls + > 63 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 64 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + 65 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + + i Unsafe fix: Change to an optional chain. + + 61 61 │ + 62 62 │ // chained calls + 63 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz.buzz() + 63 │ + foo?.bar?.baz?.buzz() + 64 64 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + 65 65 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + + +``` + +``` +yoda_expressions_logicalAndCases5.js:64:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 62 │ // chained calls + 63 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() + > 64 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 65 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + 66 │ + + i Unsafe fix: Change to an optional chain. + + 62 62 │ // chained calls + 63 63 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() + 64 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·undefined·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 64 │ + foo?.bar?.baz?.buzz?.() + 65 65 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + 66 66 │ + + +``` + +``` +yoda_expressions_logicalAndCases5.js:65:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 63 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() + 64 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + > 65 │ undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 66 │ + + i Unsafe fix: Change to an optional chain. + + 63 63 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz.buzz() + 64 64 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + 65 │ - undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·undefined·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 65 │ + foo.bar?.baz?.buzz?.() + 66 66 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx new file mode 100644 index 000000000000..028ef0cec2d0 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx @@ -0,0 +1,67 @@ +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && foo.bar.baz.buzz() +undefined != foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +undefined != foo && undefined != foo.bar() && undefined != foo.bar().baz && undefined != foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +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]() + +// (partially) pre-optional chained +undefined != foo && undefined != foo?.bar && undefined != foo?.bar.baz && undefined != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] +undefined != foo && undefined != foo?.() && foo?.().bar +undefined != foo.bar && undefined != foo.bar?.() && foo.bar?.().baz + +//private static member name +foo && foo.#bar +foo.#bar && foo.#bar.#baz +foo.#bar && foo.#bar() +foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + +// two errors +foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + +// case with inconsistent checks +foo && null != foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz; + +foo.bar && null != foo.bar.baz && undefined !== foo.bar.baz.qux && foo.bar.baz.qux.buzz; + +// ensure essential whitespace isn't removed +foo && foo.bar(baz => ); +foo && foo.bar(baz => typeof baz); +foo && foo["some long string"] && foo["some long string"].baz +foo && foo[`some long string`] && foo[`some long string`].baz +foo && foo['some long string'] && foo['some long string'].baz; + +// other literal expressions +foo && foo[123] && foo[123].baz; +foo && foo[true] && foo[true].baz; +foo && foo[null] && foo[null].baz; +foo && foo[12n] && foo[12n].baz; +foo && foo[/\w+/] && foo[/\w+/].baz; + + +// should preserve comments in a call expression +foo && foo.bar(/* comment */a, +// comment2 +b, ); + +// other weird cases +foo && foo?.(); +foo.bar && foo.bar?.(); + +// comments +foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + +foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + +// call expressions with the same member name but different arguments +foo && foo.bar('a') && foo.bar('b') diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx.snap b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx.snap new file mode 100644 index 000000000000..a99464907095 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_logicalAndCases6.jsx.snap @@ -0,0 +1,895 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 152 +expression: yoda_expressions_logicalAndCases6.jsx +--- +# Input +```jsx +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && foo.bar.baz.buzz() +undefined != foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +undefined != foo && undefined != foo.bar && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +undefined != foo && undefined != foo.bar() && undefined != foo.bar().baz && undefined != foo.bar().baz.buzz && foo.bar().baz.buzz() + +// chained calls with element access +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]() + +// (partially) pre-optional chained +undefined != foo && undefined != foo?.bar && undefined != foo?.bar.baz && undefined != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] +undefined != foo && undefined != foo?.() && foo?.().bar +undefined != foo.bar && undefined != foo.bar?.() && foo.bar?.().baz + +//private static member name +foo && foo.#bar +foo.#bar && foo.#bar.#baz +foo.#bar && foo.#bar() +foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + +// two errors +foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + +// case with inconsistent checks +foo && null != foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz; + +foo.bar && null != foo.bar.baz && undefined !== foo.bar.baz.qux && foo.bar.baz.qux.buzz; + +// ensure essential whitespace isn't removed +foo && foo.bar(baz => ); +foo && foo.bar(baz => typeof baz); +foo && foo["some long string"] && foo["some long string"].baz +foo && foo[`some long string`] && foo[`some long string`].baz +foo && foo['some long string'] && foo['some long string'].baz; + +// other literal expressions +foo && foo[123] && foo[123].baz; +foo && foo[true] && foo[true].baz; +foo && foo[null] && foo[null].baz; +foo && foo[12n] && foo[12n].baz; +foo && foo[/\w+/] && foo[/\w+/].baz; + + +// should preserve comments in a call expression +foo && foo.bar(/* comment */a, +// comment2 +b, ); + +// other weird cases +foo && foo?.(); +foo.bar && foo.bar?.(); + +// comments +foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + +foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + +// call expressions with the same member name but different arguments +foo && foo.bar('a') && foo.bar('b') + +``` + +# Diagnostics +``` +yoda_expressions_logicalAndCases6.jsx:2:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // case with a jump (i.e. a non-nullish prop) + > 2 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 3 │ undefined != foo.bar && foo.bar.baz.buzz() + 4 │ + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // case with a jump (i.e. a non-nullish prop) + 2 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·foo.bar.baz.buzz() + 2 │ + foo?.bar?.baz.buzz() + 3 3 │ undefined != foo.bar && foo.bar.baz.buzz() + 4 4 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:3:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 1 │ // case with a jump (i.e. a non-nullish prop) + 2 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz() + > 3 │ undefined != foo.bar && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ + 5 │ // case with a jump (i.e. a non-nullish prop) + + i Unsafe fix: Change to an optional chain. + + 1 1 │ // case with a jump (i.e. a non-nullish prop) + 2 2 │ undefined != foo && undefined != foo.bar && foo.bar.baz.buzz() + 3 │ - undefined·!=·foo.bar·&&·foo.bar.baz.buzz() + 3 │ + foo.bar?.baz.buzz() + 4 4 │ + 5 5 │ // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:6:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 5 │ // case with a jump (i.e. a non-nullish prop) + > 6 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 7 │ + 8 │ // case with a call expr inside the chain for some inefficient reason + + i Unsafe fix: Change to an optional chain. + + 4 4 │ + 5 5 │ // case with a jump (i.e. a non-nullish prop) + 6 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz.buzz·&&·foo.bar.baz.buzz() + 6 │ + foo?.bar?.baz.buzz?.() + 7 7 │ + 8 8 │ // case with a call expr inside the chain for some inefficient reason + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:9:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 8 │ // case with a call expr inside the chain for some inefficient reason + > 9 │ undefined != foo && undefined != foo.bar() && undefined != foo.bar().baz && undefined != foo.bar().baz.buzz && foo.bar().baz.buzz() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 │ + 11 │ // chained calls with element access + + i Unsafe fix: Change to an optional chain. + + 7 7 │ + 8 8 │ // case with a call expr inside the chain for some inefficient reason + 9 │ - undefined·!=·foo·&&·undefined·!=·foo.bar()·&&·undefined·!=·foo.bar().baz·&&·undefined·!=·foo.bar().baz.buzz·&&·foo.bar().baz.buzz() + 9 │ + foo?.bar()?.baz?.buzz?.() + 10 10 │ + 11 11 │ // chained calls with element access + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:12:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 11 │ // chained calls with element access + > 12 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 13 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 14 │ + + i Unsafe fix: Change to an optional chain. + + 10 10 │ + 11 11 │ // chained calls with element access + 12 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·foo.bar.baz[buzz]() + 12 │ + foo?.bar?.baz?.[buzz]() + 13 13 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 14 14 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:13:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 11 │ // chained calls with element access + 12 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz[buzz]() + > 13 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && undefined != foo.bar.baz[buzz] && foo.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 14 │ + 15 │ // (partially) pre-optional chained + + i Unsafe fix: Change to an optional chain. + + 11 11 │ // chained calls with element access + 12 12 │ undefined != foo && undefined != foo.bar && undefined != foo.bar.baz && foo.bar.baz[buzz]() + 13 │ - undefined·!=·foo·&&·undefined·!=·foo.bar·&&·undefined·!=·foo.bar.baz·&&·undefined·!=·foo.bar.baz[buzz]·&&·foo.bar.baz[buzz]() + 13 │ + foo?.bar?.baz?.[buzz]?.() + 14 14 │ + 15 15 │ // (partially) pre-optional chained + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:16:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 15 │ // (partially) pre-optional chained + > 16 │ undefined != foo && undefined != foo?.bar && undefined != foo?.bar.baz && undefined != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 17 │ undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] + 18 │ undefined != foo && undefined != foo?.() && foo?.().bar + + i Unsafe fix: Change to an optional chain. + + 14 14 │ + 15 15 │ // (partially) pre-optional chained + 16 │ - undefined·!=·foo·&&·undefined·!=·foo?.bar·&&·undefined·!=·foo?.bar.baz·&&·undefined·!=·foo?.bar.baz[buzz]·&&·foo?.bar.baz[buzz]() + 16 │ + foo?.bar?.baz?.[buzz]?.() + 17 17 │ undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] + 18 18 │ undefined != foo && undefined != foo?.() && foo?.().bar + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:17:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 15 │ // (partially) pre-optional chained + 16 │ undefined != foo && undefined != foo?.bar && undefined != foo?.bar.baz && undefined != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + > 17 │ undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ undefined != foo && undefined != foo?.() && foo?.().bar + 19 │ undefined != foo.bar && undefined != foo.bar?.() && foo.bar?.().baz + + i Unsafe fix: Change to an optional chain. + + 17 │ undefined·!=·foo·&&·undefined·!=·foo?.bar.baz·&&·foo?.bar.baz[buzz] + │ ------------- -------------------- ------- ------- + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:18:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 16 │ undefined != foo && undefined != foo?.bar && undefined != foo?.bar.baz && undefined != foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + 17 │ undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] + > 18 │ undefined != foo && undefined != foo?.() && foo?.().bar + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ undefined != foo.bar && undefined != foo.bar?.() && foo.bar?.().baz + 20 │ + + i Unsafe fix: Change to an optional chain. + + 18 │ undefined·!=·foo·&&·undefined·!=·foo?.()·&&·foo?.().bar + │ ------------- -------------------- ------- --- + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:19:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 17 │ undefined != foo && undefined != foo?.bar.baz && foo?.bar.baz[buzz] + 18 │ undefined != foo && undefined != foo?.() && foo?.().bar + > 19 │ undefined != foo.bar && undefined != foo.bar?.() && foo.bar?.().baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 20 │ + 21 │ //private static member name + + i Unsafe fix: Change to an optional chain. + + 19 │ undefined·!=·foo.bar·&&·undefined·!=·foo.bar?.()·&&·foo.bar?.().baz + │ ------------- ------------------------ ----------- --- + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:22:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 21 │ //private static member name + > 22 │ foo && foo.#bar + │ ^^^^^^^^^^^^^^^ + 23 │ foo.#bar && foo.#bar.#baz + 24 │ foo.#bar && foo.#bar() + + i Unsafe fix: Change to an optional chain. + + 20 20 │ + 21 21 │ //private static member name + 22 │ - foo·&&·foo.#bar + 22 │ + foo?.#bar + 23 23 │ foo.#bar && foo.#bar.#baz + 24 24 │ foo.#bar && foo.#bar() + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:23:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 21 │ //private static member name + 22 │ foo && foo.#bar + > 23 │ foo.#bar && foo.#bar.#baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + 24 │ foo.#bar && foo.#bar() + 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + i Unsafe fix: Change to an optional chain. + + 21 21 │ //private static member name + 22 22 │ foo && foo.#bar + 23 │ - foo.#bar·&&·foo.#bar.#baz + 23 │ + foo.#bar?.#baz + 24 24 │ foo.#bar && foo.#bar() + 25 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:24:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 22 │ foo && foo.#bar + 23 │ foo.#bar && foo.#bar.#baz + > 24 │ foo.#bar && foo.#bar() + │ ^^^^^^^^^^^^^^^^^^^^^^ + 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 26 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + i Unsafe fix: Change to an optional chain. + + 22 22 │ foo && foo.#bar + 23 23 │ foo.#bar && foo.#bar.#baz + 24 │ - foo.#bar·&&·foo.#bar() + 24 │ + foo.#bar?.() + 25 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 26 26 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:25:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 23 │ foo.#bar && foo.#bar.#baz + 24 │ foo.#bar && foo.#bar() + > 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 26 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 27 │ + + i Unsafe fix: Change to an optional chain. + + 23 23 │ foo.#bar && foo.#bar.#baz + 24 24 │ foo.#bar && foo.#bar() + 25 │ - foo·&&·foo.#bar·&&·foo.#bar.#baz·&&·foo.#bar.#baz.#buzz + 25 │ + foo?.#bar?.#baz?.#buzz + 26 26 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 27 27 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:26:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 24 │ foo.#bar && foo.#bar() + 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + > 26 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 27 │ + 28 │ // two errors + + i Unsafe fix: Change to an optional chain. + + 24 24 │ foo.#bar && foo.#bar() + 25 25 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 26 │ - foo.#bar·&&·foo.#bar.#baz·&&·foo.#bar.#baz.#buzz + 26 │ + foo.#bar?.#baz?.#buzz + 27 27 │ + 28 28 │ // two errors + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:29:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 28 │ // two errors + > 29 │ foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 30 │ + 31 │ // case with inconsistent checks + + i Unsafe fix: Change to an optional chain. + + 27 27 │ + 28 28 │ // two errors + 29 │ - foo·&&·foo.bar·&&·foo.bar.baz·||·baz·&&·baz.bar·&&·baz.bar.foo + 29 │ + foo?.bar?.baz·||·baz·&&·baz.bar·&&·baz.bar.foo + 30 30 │ + 31 31 │ // case with inconsistent checks + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:29:34 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 28 │ // two errors + > 29 │ foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 30 │ + 31 │ // case with inconsistent checks + + i Unsafe fix: Change to an optional chain. + + 27 27 │ + 28 28 │ // two errors + 29 │ - foo·&&·foo.bar·&&·foo.bar.baz·||·baz·&&·baz.bar·&&·baz.bar.foo + 29 │ + foo·&&·foo.bar·&&·foo.bar.baz·||·baz?.bar?.foo + 30 30 │ + 31 31 │ // case with inconsistent checks + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:32:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 31 │ // case with inconsistent checks + > 32 │ foo && null != foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 │ + 34 │ foo.bar && null != foo.bar.baz && undefined !== foo.bar.baz.qux && foo.bar.baz.qux.buzz; + + i Unsafe fix: Change to an optional chain. + + 30 30 │ + 31 31 │ // case with inconsistent checks + 32 │ - foo·&&·null·!=·foo.bar·&&·undefined·!==·foo.bar.baz·&&·foo.bar.baz.buzz; + 32 │ + foo?.bar?.baz?.buzz; + 33 33 │ + 34 34 │ foo.bar && null != foo.bar.baz && undefined !== foo.bar.baz.qux && foo.bar.baz.qux.buzz; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:34:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 32 │ foo && null != foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz; + 33 │ + > 34 │ foo.bar && null != foo.bar.baz && undefined !== foo.bar.baz.qux && foo.bar.baz.qux.buzz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 35 │ + 36 │ // ensure essential whitespace isn't removed + + i Unsafe fix: Change to an optional chain. + + 32 32 │ foo && null != foo.bar && undefined !== foo.bar.baz && foo.bar.baz.buzz; + 33 33 │ + 34 │ - foo.bar·&&·null·!=·foo.bar.baz·&&·undefined·!==·foo.bar.baz.qux·&&·foo.bar.baz.qux.buzz; + 34 │ + foo.bar?.baz?.qux?.buzz; + 35 35 │ + 36 36 │ // ensure essential whitespace isn't removed + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:37:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 36 │ // ensure essential whitespace isn't removed + > 37 │ foo && foo.bar(baz => ); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 38 │ foo && foo.bar(baz => typeof baz); + 39 │ foo && foo["some long string"] && foo["some long string"].baz + + i Unsafe fix: Change to an optional chain. + + 35 35 │ + 36 36 │ // ensure essential whitespace isn't removed + 37 │ - foo·&&·foo.bar(baz·=>·); + 37 │ + foo?.bar(baz·=>·); + 38 38 │ foo && foo.bar(baz => typeof baz); + 39 39 │ foo && foo["some long string"] && foo["some long string"].baz + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:38:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 36 │ // ensure essential whitespace isn't removed + 37 │ foo && foo.bar(baz => ); + > 38 │ foo && foo.bar(baz => typeof baz); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 39 │ foo && foo["some long string"] && foo["some long string"].baz + 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + + i Unsafe fix: Change to an optional chain. + + 36 36 │ // ensure essential whitespace isn't removed + 37 37 │ foo && foo.bar(baz => ); + 38 │ - foo·&&·foo.bar(baz·=>·typeof·baz); + 38 │ + foo?.bar(baz·=>·typeof·baz); + 39 39 │ foo && foo["some long string"] && foo["some long string"].baz + 40 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:39:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 37 │ foo && foo.bar(baz => ); + 38 │ foo && foo.bar(baz => typeof baz); + > 39 │ foo && foo["some long string"] && foo["some long string"].baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + 41 │ foo && foo['some long string'] && foo['some long string'].baz; + + i Unsafe fix: Change to an optional chain. + + 37 37 │ foo && foo.bar(baz => ); + 38 38 │ foo && foo.bar(baz => typeof baz); + 39 │ - foo·&&·foo["some·long·string"]·&&·foo["some·long·string"].baz + 39 │ + foo?.["some·long·string"]?.baz + 40 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + 41 41 │ foo && foo['some long string'] && foo['some long string'].baz; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:40:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 38 │ foo && foo.bar(baz => typeof baz); + 39 │ foo && foo["some long string"] && foo["some long string"].baz + > 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 41 │ foo && foo['some long string'] && foo['some long string'].baz; + 42 │ + + i Unsafe fix: Change to an optional chain. + + 38 38 │ foo && foo.bar(baz => typeof baz); + 39 39 │ foo && foo["some long string"] && foo["some long string"].baz + 40 │ - foo·&&·foo[`some·long·string`]·&&·foo[`some·long·string`].baz + 40 │ + foo?.[`some·long·string`]·&&·foo[`some·long·string`].baz + 41 41 │ foo && foo['some long string'] && foo['some long string'].baz; + 42 42 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:41:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 39 │ foo && foo["some long string"] && foo["some long string"].baz + 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + > 41 │ foo && foo['some long string'] && foo['some long string'].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 42 │ + 43 │ // other literal expressions + + i Unsafe fix: Change to an optional chain. + + 39 39 │ foo && foo["some long string"] && foo["some long string"].baz + 40 40 │ foo && foo[`some long string`] && foo[`some long string`].baz + 41 │ - foo·&&·foo['some·long·string']·&&·foo['some·long·string'].baz; + 41 │ + foo?.['some·long·string']?.baz; + 42 42 │ + 43 43 │ // other literal expressions + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:44:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 43 │ // other literal expressions + > 44 │ foo && foo[123] && foo[123].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 45 │ foo && foo[true] && foo[true].baz; + 46 │ foo && foo[null] && foo[null].baz; + + i Unsafe fix: Change to an optional chain. + + 42 42 │ + 43 43 │ // other literal expressions + 44 │ - foo·&&·foo[123]·&&·foo[123].baz; + 44 │ + foo?.[123]?.baz; + 45 45 │ foo && foo[true] && foo[true].baz; + 46 46 │ foo && foo[null] && foo[null].baz; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:45:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 43 │ // other literal expressions + 44 │ foo && foo[123] && foo[123].baz; + > 45 │ foo && foo[true] && foo[true].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 46 │ foo && foo[null] && foo[null].baz; + 47 │ foo && foo[12n] && foo[12n].baz; + + i Unsafe fix: Change to an optional chain. + + 43 43 │ // other literal expressions + 44 44 │ foo && foo[123] && foo[123].baz; + 45 │ - foo·&&·foo[true]·&&·foo[true].baz; + 45 │ + foo?.[true]?.baz; + 46 46 │ foo && foo[null] && foo[null].baz; + 47 47 │ foo && foo[12n] && foo[12n].baz; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:46:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 44 │ foo && foo[123] && foo[123].baz; + 45 │ foo && foo[true] && foo[true].baz; + > 46 │ foo && foo[null] && foo[null].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 47 │ foo && foo[12n] && foo[12n].baz; + 48 │ foo && foo[/\w+/] && foo[/\w+/].baz; + + i Unsafe fix: Change to an optional chain. + + 44 44 │ foo && foo[123] && foo[123].baz; + 45 45 │ foo && foo[true] && foo[true].baz; + 46 │ - foo·&&·foo[null]·&&·foo[null].baz; + 46 │ + foo?.[null]?.baz; + 47 47 │ foo && foo[12n] && foo[12n].baz; + 48 48 │ foo && foo[/\w+/] && foo[/\w+/].baz; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:47:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 45 │ foo && foo[true] && foo[true].baz; + 46 │ foo && foo[null] && foo[null].baz; + > 47 │ foo && foo[12n] && foo[12n].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 48 │ foo && foo[/\w+/] && foo[/\w+/].baz; + 49 │ + + i Unsafe fix: Change to an optional chain. + + 45 45 │ foo && foo[true] && foo[true].baz; + 46 46 │ foo && foo[null] && foo[null].baz; + 47 │ - foo·&&·foo[12n]·&&·foo[12n].baz; + 47 │ + foo?.[12n]?.baz; + 48 48 │ foo && foo[/\w+/] && foo[/\w+/].baz; + 49 49 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:48:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 46 │ foo && foo[null] && foo[null].baz; + 47 │ foo && foo[12n] && foo[12n].baz; + > 48 │ foo && foo[/\w+/] && foo[/\w+/].baz; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 49 │ + + i Unsafe fix: Change to an optional chain. + + 46 46 │ foo && foo[null] && foo[null].baz; + 47 47 │ foo && foo[12n] && foo[12n].baz; + 48 │ - foo·&&·foo[/\w+/]·&&·foo[/\w+/].baz; + 48 │ + foo?.[/\w+/]?.baz; + 49 49 │ + 50 50 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:52:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 51 │ // should preserve comments in a call expression + > 52 │ foo && foo.bar(/* comment */a, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + > 53 │ // comment2 + > 54 │ b, ); + │ ^^^^ + 55 │ + 56 │ // other weird cases + + i Unsafe fix: Change to an optional chain. + + 50 50 │ + 51 51 │ // should preserve comments in a call expression + 52 │ - foo·&&·foo.bar(/*·comment·*/a, + 52 │ + foo?.bar(/*·comment·*/a, + 53 53 │ // comment2 + 54 54 │ b, ); + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:57:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 56 │ // other weird cases + > 57 │ foo && foo?.(); + │ ^^^^^^^^^^^^^^ + 58 │ foo.bar && foo.bar?.(); + 59 │ + + i Unsafe fix: Change to an optional chain. + + 57 │ foo·&&·foo?.(); + │ ------- + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:58:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 56 │ // other weird cases + 57 │ foo && foo?.(); + > 58 │ foo.bar && foo.bar?.(); + │ ^^^^^^^^^^^^^^^^^^^^^^ + 59 │ + 60 │ // comments + + i Unsafe fix: Change to an optional chain. + + 58 │ foo.bar·&&·foo.bar?.(); + │ ----------- + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:61:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 60 │ // comments + > 61 │ foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 62 │ foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + 63 │ + + i Unsafe fix: Change to an optional chain. + + 59 59 │ + 60 60 │ // comments + 61 │ - foo·&&·foo.bar·&&·/*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + 61 │ + foo/*1*/?./*2*/bar/*3*/?./*4*/baz/*5*/; + 62 62 │ foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + 63 63 │ + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:62:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 60 │ // comments + 61 │ foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + > 62 │ foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 63 │ + 64 │ foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + + i Unsafe fix: Change to an optional chain. + + 60 60 │ // comments + 61 61 │ foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + 62 │ - foo·&&·foo[bar]·&&·/*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + 62 │ + foo/*1*/?.[/*2*/bar/*3*/]/*4*/?.[/*5*/baz/*6*/]/*7*/; + 63 63 │ + 64 64 │ foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:64:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 62 │ foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + 63 │ + > 64 │ foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 65 │ + 66 │ // call expressions with the same member name but different arguments + + i Unsafe fix: Change to an optional chain. + + 64 │ foo·&&·foo[bar]·&&·/*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + │ ------------------------ + +``` + +``` +yoda_expressions_logicalAndCases6.jsx:67:1 lint/complexity/useOptionalChain FIXABLE ━━━━━━━━━━━━━━ + + ! Change to an optional chain. + + 66 │ // call expressions with the same member name but different arguments + > 67 │ foo && foo.bar('a') && foo.bar('b') + │ ^^^^^^^^^^^^^^^^^^^ + 68 │ + + i Unsafe fix: Change to an optional chain. + + 65 65 │ + 66 66 │ // call expressions with the same member name but different arguments + 67 │ - foo·&&·foo.bar('a')·&&·foo.bar('b') + 67 │ + foo?.bar('a')·&&·foo.bar('b') + 68 68 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts new file mode 100644 index 000000000000..5ff9013a5d39 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts @@ -0,0 +1,47 @@ +/* should not generate diagnostics */ +// Do not simplify when a binary expression is used in the last expression +foo && null != foo.bar; +foo && undefined != foo.bar; +foo && null != foo.bar && baz; + +//valid +foo || {}; +foo || ({} as any); +(foo || { bar: 1 }).bar; +(undefined && (foo || {})).bar; +foo ||= bar; +foo ||= bar || {}; +foo ||= bar?.baz; +foo ||= bar?.baz || {}; +foo ||= bar?.baz?.buzz; +(foo1 ? foo2 : foo3 || {}).foo4; +(foo = 2 || {}).bar; +func(foo || {}).bar; +foo ?? {}; +foo ||= bar ?? {}; +foo && bar; +foo && foo; +foo || bar; +foo ?? bar; +foo || foo.bar; +foo ?? foo.bar; +file !== "index.ts" && file.endsWith(".ts"); +nextToken && sourceCode.isSpaceBetweenTokens(prevToken, nextToken); +result && this.options.shouldPreserveNodeMaps; +foo && fooBar.baz; +match && undefined !== match$1; +null !== foo && undefined !== foo; +undefined !== x["y"] && null !== x["y"]; + +foo["some long"] && foo["some long string"].baz; +foo[`some long`] && foo[`some long string`].baz; +foo["some long"] && foo["some long string"].baz; +foo[123] && foo[1234].baz; +foo[true] && foo[false].baz; +foo[12n] && foo[123n].baz; +foo[/\w+/] && foo[/ab+c/].baz; + +(foo || {})().bar; + +// FIXME: This should not generate a diagnostic +// (new foo() || {}).bar; diff --git a/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts.snap b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts.snap new file mode 100644 index 000000000000..33d8fabd76b8 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/yoda_expressions_validCases.ts.snap @@ -0,0 +1,56 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 152 +expression: yoda_expressions_validCases.ts +--- +# Input +```ts +/* should not generate diagnostics */ +// Do not simplify when a binary expression is used in the last expression +foo && null != foo.bar; +foo && undefined != foo.bar; +foo && null != foo.bar && baz; + +//valid +foo || {}; +foo || ({} as any); +(foo || { bar: 1 }).bar; +(undefined && (foo || {})).bar; +foo ||= bar; +foo ||= bar || {}; +foo ||= bar?.baz; +foo ||= bar?.baz || {}; +foo ||= bar?.baz?.buzz; +(foo1 ? foo2 : foo3 || {}).foo4; +(foo = 2 || {}).bar; +func(foo || {}).bar; +foo ?? {}; +foo ||= bar ?? {}; +foo && bar; +foo && foo; +foo || bar; +foo ?? bar; +foo || foo.bar; +foo ?? foo.bar; +file !== "index.ts" && file.endsWith(".ts"); +nextToken && sourceCode.isSpaceBetweenTokens(prevToken, nextToken); +result && this.options.shouldPreserveNodeMaps; +foo && fooBar.baz; +match && undefined !== match$1; +null !== foo && undefined !== foo; +undefined !== x["y"] && null !== x["y"]; + +foo["some long"] && foo["some long string"].baz; +foo[`some long`] && foo[`some long string`].baz; +foo["some long"] && foo["some long string"].baz; +foo[123] && foo[1234].baz; +foo[true] && foo[false].baz; +foo[12n] && foo[123n].baz; +foo[/\w+/] && foo[/ab+c/].baz; + +(foo || {})().bar; + +// FIXME: This should not generate a diagnostic +// (new foo() || {}).bar; + +``` diff --git a/crates/biome_js_syntax/src/expr_ext.rs b/crates/biome_js_syntax/src/expr_ext.rs index c1396a950376..74baa0e2c468 100644 --- a/crates/biome_js_syntax/src/expr_ext.rs +++ b/crates/biome_js_syntax/src/expr_ext.rs @@ -320,24 +320,52 @@ impl JsBinaryExpression { ) } - /// Whether this is a comparison operation similar to the optional chain + /// Extract the left or right operand of an optional chain-like expression. /// ```js - /// foo !== undefined; - /// foo != undefined; - /// foo !== null; - /// foo != null; + /// foo !== undefined; // -> Some(foo) + /// foo != undefined; // -> Some(foo) + /// foo !== null; // -> Some(foo) + /// foo != null; // -> Some(foo) + /// undefined !== foo; // -> Some(foo) + /// undefined != foo; // -> Some(foo) + /// null !== foo; // -> Some(foo) + /// null != foo; // -> Some(foo) + /// foo !== bar; // -> None + /// foo != bar; // -> None + /// undefined !== null; // -> None + /// undefined != null; // -> None + /// null !== undefined; // -> None + /// null != undefined; // -> None + /// undefined !== undefined; // -> None + /// undefined != undefined; // -> None + /// null !== null; // -> None + /// null != null; // -> None ///``` - pub fn is_optional_chain_like(&self) -> SyntaxResult { + pub fn extract_optional_chain_like(&self) -> SyntaxResult> { + fn is_nullish(expression: &AnyJsExpression) -> bool { + expression + .as_static_value() + .is_some_and(|x| x.is_null_or_undefined()) + } if matches!( self.operator(), Ok(JsBinaryOperator::StrictInequality | JsBinaryOperator::Inequality) ) { - Ok(self - .right()? - .as_static_value() - .is_some_and(|x| x.is_null_or_undefined())) + let left = self.left()?; + let right = self.right()?; + let left_is_nullish = is_nullish(&left); + let right_is_nullish = is_nullish(&right); + // right only nullish: `foo !== undefined` -> return foo (left) + if !left_is_nullish && right_is_nullish { + return Ok(Some(left)); + } + // left only nullish: `undefined !== foo` -> return foo (right) + if left_is_nullish && !right_is_nullish { + return Ok(Some(right)); + } + Ok(None) } else { - Ok(false) + Ok(None) } } } From f2296b32116e474deb089a71ab366f90b4943f87 Mon Sep 17 00:00:00 2001 From: qraqras Date: Thu, 4 Sep 2025 09:32:07 +0000 Subject: [PATCH 2/2] refactor: applied linting --- .../src/lint/complexity/use_optional_chain.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs index 394bf3e11a7a..54f69aff8610 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs @@ -263,10 +263,10 @@ impl Rule for UseOptionalChain { /// Normalize optional chain like. /// E.g. `foo != null` is normalized to `foo` fn normalized_optional_chain_like(expression: AnyJsExpression) -> SyntaxResult { - if let AnyJsExpression::JsBinaryExpression(binary_expression) = &expression { - if let Some(expr) = binary_expression.extract_optional_chain_like()? { - return Ok(expr); - } + if let AnyJsExpression::JsBinaryExpression(binary_expression) = &expression + && let Some(expr) = binary_expression.extract_optional_chain_like()? + { + return Ok(expr); } Ok(expression) }