Skip to content

Commit d03c6cd

Browse files
authored
feat(linter): implement no-nested-ternary (#4067)
1 parent ab0f96e commit d03c6cd

File tree

12 files changed

+264
-50
lines changed

12 files changed

+264
-50
lines changed

crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_configuration/src/analyzer/linter/rules.rs

Lines changed: 69 additions & 50 deletions
Large diffs are not rendered by default.

crates/biome_diagnostics_categories/src/categories.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ define_categories! {
151151
"lint/nursery/noIrregularWhitespace": "https://biomejs.dev/linter/rules/no-irregular-whitespace",
152152
"lint/nursery/noMissingGenericFamilyKeyword": "https://biomejs.dev/linter/rules/no-missing-generic-family-keyword",
153153
"lint/nursery/noMissingVarFunction": "https://biomejs.dev/linter/rules/no-missing-var-function",
154+
"lint/nursery/noNestedTernary": "https://biomejs.dev/linter/rules/no-nested-ternary",
154155
"lint/nursery/noOctalEscape": "https://biomejs.dev/linter/rules/no-octal-escape",
155156
"lint/nursery/noProcessEnv": "https://biomejs.dev/linter/rules/no-process-env",
156157
"lint/nursery/noReactSpecificProps": "https://biomejs.dev/linter/rules/no-react-specific-props",

crates/biome_js_analyze/src/lint/nursery.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod no_dynamic_namespace_import_access;
88
pub mod no_enum;
99
pub mod no_exported_imports;
1010
pub mod no_irregular_whitespace;
11+
pub mod no_nested_ternary;
1112
pub mod no_octal_escape;
1213
pub mod no_process_env;
1314
pub mod no_restricted_imports;
@@ -38,6 +39,7 @@ declare_lint_group! {
3839
self :: no_enum :: NoEnum ,
3940
self :: no_exported_imports :: NoExportedImports ,
4041
self :: no_irregular_whitespace :: NoIrregularWhitespace ,
42+
self :: no_nested_ternary :: NoNestedTernary ,
4143
self :: no_octal_escape :: NoOctalEscape ,
4244
self :: no_process_env :: NoProcessEnv ,
4345
self :: no_restricted_imports :: NoRestrictedImports ,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use biome_analyze::{
2+
context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
3+
};
4+
use biome_console::markup;
5+
use biome_js_syntax::{AnyJsExpression, JsConditionalExpression};
6+
use biome_rowan::{AstNode, TextRange};
7+
8+
declare_lint_rule! {
9+
/// Disallow nested ternary expressions.
10+
///
11+
/// Nesting ternary expressions can make code more difficult to understand.
12+
///
13+
/// ## Examples
14+
///
15+
/// ### Invalid
16+
///
17+
/// ```js,expect_diagnostic
18+
/// const thing = foo ? bar : baz === qux ? quxx : foobar;
19+
/// ```
20+
///
21+
/// ```js,expect_diagnostic
22+
/// foo ? baz === qux ? quxx() : foobar() : bar();
23+
/// ```
24+
///
25+
/// ### Valid
26+
///
27+
/// ```js
28+
/// const thing = foo ? bar : foobar;
29+
/// ```
30+
///
31+
/// ```js
32+
/// let thing;
33+
///
34+
/// if (foo) {
35+
/// thing = bar;
36+
/// } else if (baz === qux) {
37+
/// thing = quxx;
38+
/// } else {
39+
/// thing = foobar;
40+
/// }
41+
/// ```
42+
///
43+
pub NoNestedTernary {
44+
version: "next",
45+
name: "noNestedTernary",
46+
language: "js",
47+
recommended: false,
48+
sources: &[RuleSource::Eslint("no-nested-ternary")],
49+
}
50+
}
51+
52+
impl Rule for NoNestedTernary {
53+
type Query = Ast<JsConditionalExpression>;
54+
type State = TextRange;
55+
type Signals = Option<Self::State>;
56+
type Options = ();
57+
58+
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
59+
let node = ctx.query();
60+
let alternate = node.alternate().ok()?;
61+
let consequent = node.consequent().ok()?;
62+
63+
if let AnyJsExpression::JsConditionalExpression(expr) = consequent {
64+
return Some(expr.range());
65+
}
66+
67+
if let AnyJsExpression::JsConditionalExpression(expr) = alternate {
68+
return Some(expr.range());
69+
}
70+
71+
None
72+
}
73+
74+
fn diagnostic(_: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
75+
Some(
76+
RuleDiagnostic::new(
77+
rule_category!(),
78+
state,
79+
markup! {
80+
"Do not nest ternary expressions."
81+
},
82+
)
83+
.note(markup! {
84+
"Nesting ternary expressions can make code more difficult to understand."
85+
})
86+
.note(markup! {
87+
"Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand."
88+
}),
89+
)
90+
}
91+
}

crates/biome_js_analyze/src/options.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var thing = foo ? bar : baz === qux ? quxx : foobar;
2+
3+
foo ? baz === qux ? quxx() : foobar() : bar();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
source: crates/biome_js_analyze/tests/spec_tests.rs
3+
expression: invalid.js
4+
---
5+
# Input
6+
```jsx
7+
var thing = foo ? bar : baz === qux ? quxx : foobar;
8+
9+
foo ? baz === qux ? quxx() : foobar() : bar();
10+
```
11+
12+
# Diagnostics
13+
```
14+
invalid.js:1:25 lint/nursery/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
15+
16+
! Do not nest ternary expressions.
17+
18+
> 1 │ var thing = foo ? bar : baz === qux ? quxx : foobar;
19+
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
2 │
21+
3 │ foo ? baz === qux ? quxx() : foobar() : bar();
22+
23+
i Nesting ternary expressions can make code more difficult to understand.
24+
25+
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
26+
27+
28+
```
29+
30+
```
31+
invalid.js:3:7 lint/nursery/noNestedTernary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
32+
33+
! Do not nest ternary expressions.
34+
35+
1 │ var thing = foo ? bar : baz === qux ? quxx : foobar;
36+
2 │
37+
> 3 │ foo ? baz === qux ? quxx() : foobar() : bar();
38+
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
40+
i Nesting ternary expressions can make code more difficult to understand.
41+
42+
i Convert nested ternary expression into if-else statements or separate the conditions to make the logic easier to understand.
43+
44+
45+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* should not generate diagnostics */
2+
const thing = foo ? bar : foobar;
3+
4+
let thing;
5+
6+
if (foo) {
7+
thing = bar;
8+
} else if (baz === qux) {
9+
thing = quxx;
10+
} else {
11+
thing = foobar;
12+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: crates/biome_js_analyze/tests/spec_tests.rs
3+
expression: valid.js
4+
---
5+
# Input
6+
```jsx
7+
/* should not generate diagnostics */
8+
const thing = foo ? bar : foobar;
9+
10+
let thing;
11+
12+
if (foo) {
13+
thing = bar;
14+
} else if (baz === qux) {
15+
thing = quxx;
16+
} else {
17+
thing = foobar;
18+
}
19+
```

packages/@biomejs/backend-jsonrpc/src/workspace.ts

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@biomejs/biome/configuration_schema.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)