From 67a569869a99ca96a1337b062ce8cd10ffeccccb Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Thu, 31 Mar 2022 17:48:48 +0800 Subject: [PATCH 1/3] feat: add no-unneeded-ternary rule --- docs/rules/no_unneeded_ternary.md | 17 +++++ src/rules.rs | 2 + src/rules/no_unneeded_ternary.rs | 123 ++++++++++++++++++++++++++++++ www/public/docs.json | 5 ++ 4 files changed, 147 insertions(+) create mode 100644 docs/rules/no_unneeded_ternary.md create mode 100644 src/rules/no_unneeded_ternary.rs diff --git a/docs/rules/no_unneeded_ternary.md b/docs/rules/no_unneeded_ternary.md new file mode 100644 index 000000000..479aa2a88 --- /dev/null +++ b/docs/rules/no_unneeded_ternary.md @@ -0,0 +1,17 @@ +Disallows ternary operators when simpler alternatives exist. + +It's a common mistake to use a conditional expression to select between two +Boolean values instead of using ! to convert the test to a Boolean. + +### Invalid: + +```typescript +const foo = condition ? true : false; +``` + +### Valid: + +```typescript +const foo = condition ? x : y; +const foo = condition ? x : false; +``` diff --git a/src/rules.rs b/src/rules.rs index 311b6e365..c0e6f35e6 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -79,6 +79,7 @@ pub mod no_this_before_super; pub mod no_throw_literal; pub mod no_top_level_await; pub mod no_undef; +pub mod no_unneeded_ternary; pub mod no_unreachable; pub mod no_unsafe_finally; pub mod no_unsafe_negation; @@ -305,6 +306,7 @@ fn get_all_rules_raw() -> Vec> { no_throw_literal::NoThrowLiteral::new(), no_top_level_await::NoTopLevelAwait::new(), no_undef::NoUndef::new(), + no_unneeded_ternary::NoUnneededTernary::new(), no_unreachable::NoUnreachable::new(), no_unsafe_finally::NoUnsafeFinally::new(), no_unsafe_negation::NoUnsafeNegation::new(), diff --git a/src/rules/no_unneeded_ternary.rs b/src/rules/no_unneeded_ternary.rs new file mode 100644 index 000000000..038175388 --- /dev/null +++ b/src/rules/no_unneeded_ternary.rs @@ -0,0 +1,123 @@ +// Copyright 2020-2022 the Deno authors. All rights reserved. MIT license. +use super::{Context, LintRule}; +use crate::handler::{Handler, Traverse}; +use crate::{Program, ProgramRef}; +use deno_ast::swc::common::Spanned; +use deno_ast::view as ast_view; +use if_chain::if_chain; +use std::sync::Arc; + +#[derive(Debug)] +pub struct NoUnneededTernary; + +const CODE: &str = "no-unneeded-ternary"; +const MESSAGE: &str = + "Unnecessary use of boolean literals in conditional expression"; + +impl LintRule for NoUnneededTernary { + fn new() -> Arc { + Arc::new(NoUnneededTernary) + } + + fn code(&self) -> &'static str { + CODE + } + + fn lint_program(&self, _context: &mut Context, _program: ProgramRef<'_>) { + unreachable!(); + } + + fn lint_program_with_ast_view( + &self, + context: &mut Context, + program: Program<'_>, + ) { + NoUnneededTernaryHandler.traverse(program, context); + } + + #[cfg(feature = "docs")] + fn docs(&self) -> &'static str { + include_str!("../../docs/rules/no_unneeded_ternary.md") + } +} + +struct NoUnneededTernaryHandler; + +impl Handler for NoUnneededTernaryHandler { + fn cond_expr(&mut self, cond_expr: &ast_view::CondExpr, ctx: &mut Context) { + if_chain! { + if cond_expr.cons.is::(); + if cond_expr.alt.is::(); + then { + ctx.add_diagnostic(cond_expr.span(), CODE, MESSAGE); + } + } + } +} + +// https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-unneeded-ternary.js +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn no_unneeded_ternary_valid() { + assert_lint_ok! { + NoUnneededTernary, + r#"config.newIsCap = config.newIsCap !== false"#, + r#"var a = x === 2 ? 'Yes' : 'No';"#, + r#"var a = x === 2 ? true : 'No';"#, + r#"var a = x === 2 ? 'Yes' : false;"#, + r#"var a = x === 2 ? 'true' : 'false';"#, + }; + } + + #[test] + fn no_unneeded_ternary_invalid() { + assert_lint_err! { + NoUnneededTernary, + r#"x === 2 ? true : false;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"x >= 2 ? true : false;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"x ? true : false;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"x === 1 ? false : true;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"x != 1 ? false : true;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"foo() ? false : true;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + r#"!foo() ? false : true;"#: [ + { + col: 0, + message: MESSAGE, + }, + ], + }; + } +} diff --git a/www/public/docs.json b/www/public/docs.json index bf710341f..467f39a4c 100644 --- a/www/public/docs.json +++ b/www/public/docs.json @@ -473,6 +473,11 @@ "docs": "", "tags": [] }, + { + "code": "no-unneeded-ternary", + "docs": "Disallows ternary operators when simpler alternatives exist.\n\nIt's a common mistake to use a conditional expression to select between two\nBoolean values instead of using ! to convert the test to a Boolean.\n\n### Invalid:\n\n```typescript\nconst foo = condition ? true : false;\n```\n\n### Valid:\n\n```typescript\nconst foo = condition ? x : y;\nconst foo = condition ? x : false;\n```\n", + "tags": [] + }, { "code": "no-unreachable", "docs": "Disallows the unreachable code after the control flow statements.\n\nBecause the control flow statements (`return`, `throw`, `break` and `continue`)\nunconditionally exit a block of code, any statements after them cannot be\nexecuted.\n\n### Invalid:\n\n```typescript\nfunction foo() {\n return true;\n console.log(\"done\");\n}\n```\n\n```typescript\nfunction bar() {\n throw new Error(\"Oops!\");\n console.log(\"done\");\n}\n```\n\n```typescript\nwhile (value) {\n break;\n console.log(\"done\");\n}\n```\n\n```typescript\nthrow new Error(\"Oops!\");\nconsole.log(\"done\");\n```\n\n```typescript\nfunction baz() {\n if (Math.random() < 0.5) {\n return;\n } else {\n throw new Error();\n }\n console.log(\"done\");\n}\n```\n\n```typescript\nfor (;;) {}\nconsole.log(\"done\");\n```\n\n### Valid\n\n```typescript\nfunction foo() {\n return bar();\n function bar() {\n return 1;\n }\n}\n```\n", From 369004cb4c8592cd9b8902a133eb2729f8974ce5 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Wed, 6 Apr 2022 20:44:32 +0800 Subject: [PATCH 2/3] make doc more clear --- docs/rules/no_unneeded_ternary.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/rules/no_unneeded_ternary.md b/docs/rules/no_unneeded_ternary.md index 479aa2a88..552875bfb 100644 --- a/docs/rules/no_unneeded_ternary.md +++ b/docs/rules/no_unneeded_ternary.md @@ -6,12 +6,16 @@ Boolean values instead of using ! to convert the test to a Boolean. ### Invalid: ```typescript +// You don't need a ternary when a simple `const foo = !condition` will do +const foo = condition ? false : true; +// use `const foo = condition` instead const foo = condition ? true : false; ``` ### Valid: ```typescript -const foo = condition ? x : y; -const foo = condition ? x : false; +const foo = x === 2 ? "Yes" : "No"; +const foo = x === 2 ? "Yes" : false; +const foo = x === 2 ? true : "No"; ``` From 32826f0968c67efe5c774bfd001a2228b3c145a1 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Wed, 6 Apr 2022 20:49:38 +0800 Subject: [PATCH 3/3] update website --- www/public/docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/public/docs.json b/www/public/docs.json index 467f39a4c..ebac6c56d 100644 --- a/www/public/docs.json +++ b/www/public/docs.json @@ -475,7 +475,7 @@ }, { "code": "no-unneeded-ternary", - "docs": "Disallows ternary operators when simpler alternatives exist.\n\nIt's a common mistake to use a conditional expression to select between two\nBoolean values instead of using ! to convert the test to a Boolean.\n\n### Invalid:\n\n```typescript\nconst foo = condition ? true : false;\n```\n\n### Valid:\n\n```typescript\nconst foo = condition ? x : y;\nconst foo = condition ? x : false;\n```\n", + "docs": "Disallows ternary operators when simpler alternatives exist.\n\nIt's a common mistake to use a conditional expression to select between two\nBoolean values instead of using ! to convert the test to a Boolean.\n\n### Invalid:\n\n```typescript\n// You don't need a ternary when a simple `const foo = !condition` will do\nconst foo = condition ? false : true;\n// use `const foo = condition` instead\nconst foo = condition ? true : false;\n```\n\n### Valid:\n\n```typescript\nconst foo = x === 2 ? \"Yes\" : \"No\";\nconst foo = x === 2 ? \"Yes\" : false;\nconst foo = x === 2 ? true : \"No\";\n```\n", "tags": [] }, {