Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/fix-nested-ternary-parentheses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@biomejs/biome": patch
---

Fixed [#8045](https://github.com/biomejs/biome/issues/8045): The [`noNestedTernary`](https://biomejs.dev/linter/rules/no-nested-ternary/) rule now correctly detects nested ternary expressions even when they are wrapped in parentheses (e.g. `foo ? (bar ? 1 : 2) : 3`).

Previously, the rule would not flag nested ternaries like `foo ? (bar ? 1 : 2) : 3` because the parentheses prevented detection. The rule now looks through parentheses to identify nested conditionals.

**Previously not detected (now flagged):**

```js
const result = foo ? (bar ? 1 : 2) : 3;
```

**Still valid (non-nested with parentheses):**

```js
const result = (foo ? bar : baz);
```
4 changes: 2 additions & 2 deletions crates/biome_js_analyze/src/lint/style/no_nested_ternary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ impl Rule for NoNestedTernary {
let alternate = node.alternate().ok()?;
let consequent = node.consequent().ok()?;

if let AnyJsExpression::JsConditionalExpression(expr) = consequent {
if let AnyJsExpression::JsConditionalExpression(expr) = consequent.omit_parentheses() {
return Some(expr.range());
}

if let AnyJsExpression::JsConditionalExpression(expr) = alternate {
if let AnyJsExpression::JsConditionalExpression(expr) = alternate.omit_parentheses() {
return Some(expr.range());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
var thing = foo ? bar : baz === qux ? quxx : foobar;

foo ? baz === qux ? quxx() : foobar() : bar();
foo ? baz === qux ? quxx() : foobar() : bar();

var thing1 = foo ? (bar ? 1 : 2) : 3;

var thing2 = foo ? 1 : (bar ? 2 : 3);

var thing3 = foo ? ((bar ? 1 : 2)) : 3;

var thing4 = foo ? 1 : ((bar ? 2 : 3));

var thing5 = foo ? (baz === qux ? quxx : foobar) : bar;

const case1 = val ? (Math.random() ? 1 : 2) : 3;

const case2 = val ? 1 : Math.random() ? 2 : 3;

const case3 = (val ? (Math.random() ? 1 : 2) : 3);
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ expression: invalid.js
var thing = foo ? bar : baz === qux ? quxx : foobar;
foo ? baz === qux ? quxx() : foobar() : bar();
var thing1 = foo ? (bar ? 1 : 2) : 3;
var thing2 = foo ? 1 : (bar ? 2 : 3);
var thing3 = foo ? ((bar ? 1 : 2)) : 3;
var thing4 = foo ? 1 : ((bar ? 2 : 3));
var thing5 = foo ? (baz === qux ? quxx : foobar) : bar;
const case1 = val ? (Math.random() ? 1 : 2) : 3;
const case2 = val ? 1 : Math.random() ? 2 : 3;
const case3 = (val ? (Math.random() ? 1 : 2) : 3);
```

# Diagnostics
Expand Down Expand Up @@ -36,6 +53,159 @@ invalid.js:3:7 lint/style/noNestedTernary ━━━━━━━━━━━━
2 │
> 3 │ foo ? baz === qux ? quxx() : foobar() : bar();
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 │
5 │ var thing1 = foo ? (bar ? 1 : 2) : 3;
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:5:21 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
3 │ foo ? baz === qux ? quxx() : foobar() : bar();
4 │
> 5 │ var thing1 = foo ? (bar ? 1 : 2) : 3;
│ ^^^^^^^^^^^
6 │
7 │ var thing2 = foo ? 1 : (bar ? 2 : 3);
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:7:25 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
5 │ var thing1 = foo ? (bar ? 1 : 2) : 3;
6 │
> 7 │ var thing2 = foo ? 1 : (bar ? 2 : 3);
│ ^^^^^^^^^^^
8 │
9 │ var thing3 = foo ? ((bar ? 1 : 2)) : 3;
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:9:22 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
7 │ var thing2 = foo ? 1 : (bar ? 2 : 3);
8 │
> 9 │ var thing3 = foo ? ((bar ? 1 : 2)) : 3;
│ ^^^^^^^^^^^
10 │
11 │ var thing4 = foo ? 1 : ((bar ? 2 : 3));
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:11:26 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
9 │ var thing3 = foo ? ((bar ? 1 : 2)) : 3;
10 │
> 11 │ var thing4 = foo ? 1 : ((bar ? 2 : 3));
│ ^^^^^^^^^^^
12 │
13 │ var thing5 = foo ? (baz === qux ? quxx : foobar) : bar;
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:13:21 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
11 │ var thing4 = foo ? 1 : ((bar ? 2 : 3));
12 │
> 13 │ var thing5 = foo ? (baz === qux ? quxx : foobar) : bar;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
14 │
15 │ const case1 = val ? (Math.random() ? 1 : 2) : 3;
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:15:22 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
13 │ var thing5 = foo ? (baz === qux ? quxx : foobar) : bar;
14 │
> 15 │ const case1 = val ? (Math.random() ? 1 : 2) : 3;
│ ^^^^^^^^^^^^^^^^^^^^^
16 │
17 │ const case2 = val ? 1 : Math.random() ? 2 : 3;
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:17:25 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
15 │ const case1 = val ? (Math.random() ? 1 : 2) : 3;
16 │
> 17 │ const case2 = val ? 1 : Math.random() ? 2 : 3;
│ ^^^^^^^^^^^^^^^^^^^^^
18 │
19 │ const case3 = (val ? (Math.random() ? 1 : 2) : 3);
i Nesting ternary expressions can make code more difficult to understand.
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
```

```
invalid.js:19:23 lint/style/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Do not nest ternary expressions.
17 │ const case2 = val ? 1 : Math.random() ? 2 : 3;
18 │
> 19 │ const case3 = (val ? (Math.random() ? 1 : 2) : 3);
│ ^^^^^^^^^^^^^^^^^^^^^
20 │
i Nesting ternary expressions can make code more difficult to understand.
Expand Down
18 changes: 12 additions & 6 deletions crates/biome_js_analyze/tests/specs/style/noNestedTernary/valid.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
/* should not generate diagnostics */
const thing = foo ? bar : foobar;
const thing1 = foo ? bar : foobar;

let thing;
let thing2;

if (foo) {
thing = bar;
thing2 = bar;
} else if (baz === qux) {
thing = quxx;
thing2 = quxx;
} else {
thing = foobar;
}
thing2 = foobar;
}

const thing3 = (foo ? bar : baz);

const thing4 = foo ? (bar) : (baz);

const thing5 = ((foo ? bar : baz));
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: valid.js
snapshot_kind: text
---
# Input
```js
/* should not generate diagnostics */
const thing = foo ? bar : foobar;
const thing1 = foo ? bar : foobar;
let thing;
let thing2;
if (foo) {
thing = bar;
thing2 = bar;
} else if (baz === qux) {
thing = quxx;
thing2 = quxx;
} else {
thing = foobar;
thing2 = foobar;
}
const thing3 = (foo ? bar : baz);
const thing4 = foo ? (bar) : (baz);
const thing5 = ((foo ? bar : baz));
```