From 58517dce3ab478319124855f6a82c907ba1f40ed Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 8 Jun 2024 10:57:23 +0900 Subject: [PATCH 01/38] feat(biome_js_analyzer): `useTrimStartEnd` --- .../migrate/eslint_any_rule_to_biome.rs | 4 +- .../biome_configuration/src/linter/rules.rs | 94 +-------- .../src/categories.rs | 2 +- crates/biome_js_analyze/src/lint/nursery.rs | 4 +- .../src/lint/nursery/use_trim_start_end.rs | 121 +++++++++++ crates/biome_js_analyze/src/options.rs | 2 + .../specs/nursery/useTrimStartEnd/invalid.js | 11 + .../nursery/useTrimStartEnd/invalid.js.snap | 194 ++++++++++++++++++ .../specs/nursery/useTrimStartEnd/valid.js | 26 +++ .../nursery/useTrimStartEnd/valid.js.snap | 25 +++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 6 +- .../@biomejs/biome/configuration_schema.json | 6 +- 12 files changed, 397 insertions(+), 98 deletions(-) create mode 100644 crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index fb664749979..dee99903794 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1446,12 +1446,12 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.use_number_namespace.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } - "unicorn/prefer-string-slice" => { + "unicorn/prefer-string-trim-start-end" => { if !options.include_nursery { return false; } let group = rules.nursery.get_or_insert_with(Default::default); - let rule = group.no_substr.get_or_insert(Default::default()); + let rule = group.use_trim_start_end.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } "unicorn/require-number-to-fixed-digits-argument" => { diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 011c9ab5afd..428779ce0b4 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,9 +2972,9 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Use valid values for the autocomplete attribute on input elements."] + #[doc = "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_valid_autocomplete: Option>, + pub use_trim_start_end: Option>, } impl DeserializableValidator for Nursery { fn validate( @@ -3044,7 +3044,7 @@ impl Nursery { "useThrowNewError", "useThrowOnlyError", "useTopLevelRegex", - "useValidAutocomplete", + "useTrimStartEnd", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ "noDoneCallback", @@ -3380,51 +3380,11 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); - } - } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); - } - } - if let Some(rule) = self.use_semantic_elements.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); - } - } - if let Some(rule) = self.use_sorted_classes.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); - } - } - if let Some(rule) = self.use_throw_new_error.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); - } - } - if let Some(rule) = self.use_throw_only_error.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); - } - } - if let Some(rule) = self.use_top_level_regex.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); - } - } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); - } - } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3644,51 +3604,11 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); - } - } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); - } - } - if let Some(rule) = self.use_semantic_elements.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); - } - } - if let Some(rule) = self.use_sorted_classes.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); - } - } - if let Some(rule) = self.use_throw_new_error.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); - } - } - if let Some(rule) = self.use_throw_only_error.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); - } - } - if let Some(rule) = self.use_top_level_regex.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); - } - } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); - } - } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3929,8 +3849,8 @@ impl Nursery { .use_top_level_regex .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useValidAutocomplete" => self - .use_valid_autocomplete + "useTrimStartEnd" => self + .use_trim_start_end .as_ref() .map(|conf| (conf.level(), conf.get_options())), _ => None, diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index a39a331e059..1726324443e 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -168,7 +168,7 @@ define_categories! { "lint/nursery/useThrowNewError": "https://biomejs.dev/linter/rules/use-throw-new-error", "lint/nursery/useThrowOnlyError": "https://biomejs.dev/linter/rules/use-throw-only-error", "lint/nursery/useTopLevelRegex": "https://biomejs.dev/linter/rules/use-top-level-regex", - "lint/nursery/useValidAutocomplete": "https://biomejs.dev/linter/rules/use-valid-autocomplete", + "lint/nursery/useTrimStartEnd": "https://biomejs.dev/linter/rules/use-trim-start-end", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", "lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 52ac858a369..02eecf3fc08 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -33,7 +33,7 @@ pub mod use_sorted_classes; pub mod use_throw_new_error; pub mod use_throw_only_error; pub mod use_top_level_regex; -pub mod use_valid_autocomplete; +pub mod use_trim_start_end; declare_lint_group! { pub Nursery { @@ -70,7 +70,7 @@ declare_lint_group! { self :: use_throw_new_error :: UseThrowNewError , self :: use_throw_only_error :: UseThrowOnlyError , self :: use_top_level_regex :: UseTopLevelRegex , - self :: use_valid_autocomplete :: UseValidAutocomplete , + self :: use_trim_start_end :: UseTrimStartEnd , ] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs new file mode 100644 index 00000000000..a86a1409586 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -0,0 +1,121 @@ +use biome_analyze::{ + context::RuleContext, declare_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, + RuleSource, +}; +use biome_console::markup; +use biome_js_factory::make; +use biome_js_syntax::JsCallExpression; +use biome_rowan::{BatchMutationExt, TextRange, TokenText}; + +use crate::JsRuleAction; + +declare_rule! { + /// Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). + /// + /// While `trimLeft()` and `trimRight()` are aliases for `trimStart()` and `trimEnd()`, + /// using the latter helps maintain consistency and uses direction-independent wording. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// const foo = bar.trimLeft(); + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = bar.trimRight(); + /// ``` + /// + /// ### Valid + /// + /// ```js + /// const foo = bar.trimStart(); + /// ``` + /// + /// ```js + /// const foo = bar.trimEnd(); + /// ``` + /// + pub UseTrimStartEnd { + version: "next", + name: "useTrimStartEnd", + language: "js", + recommended: false, + sources: &[RuleSource::EslintUnicorn("prefer-string-trim-start-end")], + fix_kind: FixKind::Safe, + } +} + +#[derive(Debug, Clone)] +pub struct UseTrimStartEndState { + member_name: TokenText, + span: TextRange, + replaced_member_name: &'static str, +} + +impl Rule for UseTrimStartEnd { + type Query = Ast; + type State = UseTrimStartEndState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + let callee = node.callee().ok()?; + let expression = callee.as_js_static_member_expression()?; + let value_token = expression.member().ok()?.value_token().ok()?; + let name = value_token.text_trimmed(); + let suggested_name = match name { + "trimLeft" => Some("trimStart"), + "trimRight" => Some("trimEnd"), + _ => None, + }?; + + Some(UseTrimStartEndState { + member_name: value_token.token_text_trimmed(), + span: value_token.text_range(), + replaced_member_name: suggested_name, + }) + } + + fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { + let member_name = state.member_name.text(); + let replaced_member_name = state.replaced_member_name; + let diagnostic_message = markup! { + "Use "{member_name}" instead of "{replaced_member_name}"." + } + .to_owned(); + let note_message = { + markup! { + ""{member_name}"() is an alias for "{replaced_member_name}"." + } + .to_owned() + }; + Some( + RuleDiagnostic::new(rule_category!(), state.span, diagnostic_message) + .note(note_message), + ) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + let callee = node.callee().ok()?; + let expression = callee.as_js_static_member_expression()?; + let member = expression.member().ok()?; + let member_name = state.member_name.text(); + let replaced_member_name = state.replaced_member_name; + + let mut mutation = ctx.root().begin(); + let replaced_function = make::js_name(make::ident(replaced_member_name)); + mutation.replace_element(member.into(), replaced_function.into()); + + Some(JsRuleAction::new( + ActionCategory::QuickFix, + ctx.metadata().applicability(), + markup! { "Replace "{member_name}" with "{replaced_member_name}"." } + .to_owned(), + mutation, + )) + } +} diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 6c86ed8b82c..9192f5a57b2 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -357,6 +357,8 @@ pub type UseThrowOnlyError = ::Options; pub type UseTopLevelRegex = ::Options; +pub type UseTrimStartEnd = + ::Options; pub type UseValidAnchor = ::Options; pub type UseValidAriaProps = diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js new file mode 100644 index 00000000000..4587a117643 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -0,0 +1,11 @@ +foo.trimLeft(); +foo.trimRight(); +trimLeft.trimRight(); +foo.trimLeft.trimRight(); +"foo".trimLeft(); +foo + // comment + .trimRight /* comment */ + /* comment */ + (); +foo?.trimLeft(); diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap new file mode 100644 index 00000000000..853852b6635 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -0,0 +1,194 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.js +--- +# Input +```jsx +foo.trimLeft(); +foo.trimRight(); +trimLeft.trimRight(); +foo.trimLeft.trimRight(); +"foo".trimLeft(); +foo + // comment + .trimRight /* comment */ + /* comment */ + (); +foo?.trimLeft(); + +``` + +# Diagnostics +``` +invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimLeft instead of trimStart. + + > 1 │ foo.trimLeft(); + │ ^^^^^^^^ + 2 │ foo.trimRight(); + 3 │ trimLeft.trimRight(); + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 1 │ - foo.trimLeft(); + 1 │ + foo.trimStart(); + 2 2 │ foo.trimRight(); + 3 3 │ trimLeft.trimRight(); + + +``` + +``` +invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimRight instead of trimEnd. + + 1 │ foo.trimLeft(); + > 2 │ foo.trimRight(); + │ ^^^^^^^^^ + 3 │ trimLeft.trimRight(); + 4 │ foo.trimLeft.trimRight(); + + i trimRight() is an alias for trimEnd. + + i Safe fix: Replace trimRight with trimEnd. + + 1 1 │ foo.trimLeft(); + 2 │ - foo.trimRight(); + 2 │ + foo.trimEnd(); + 3 3 │ trimLeft.trimRight(); + 4 4 │ foo.trimLeft.trimRight(); + + +``` + +``` +invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimRight instead of trimEnd. + + 1 │ foo.trimLeft(); + 2 │ foo.trimRight(); + > 3 │ trimLeft.trimRight(); + │ ^^^^^^^^^ + 4 │ foo.trimLeft.trimRight(); + 5 │ "foo".trimLeft(); + + i trimRight() is an alias for trimEnd. + + i Safe fix: Replace trimRight with trimEnd. + + 1 1 │ foo.trimLeft(); + 2 2 │ foo.trimRight(); + 3 │ - trimLeft.trimRight(); + 3 │ + trimLeft.trimEnd(); + 4 4 │ foo.trimLeft.trimRight(); + 5 5 │ "foo".trimLeft(); + + +``` + +``` +invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimRight instead of trimEnd. + + 2 │ foo.trimRight(); + 3 │ trimLeft.trimRight(); + > 4 │ foo.trimLeft.trimRight(); + │ ^^^^^^^^^ + 5 │ "foo".trimLeft(); + 6 │ foo + + i trimRight() is an alias for trimEnd. + + i Safe fix: Replace trimRight with trimEnd. + + 2 2 │ foo.trimRight(); + 3 3 │ trimLeft.trimRight(); + 4 │ - foo.trimLeft.trimRight(); + 4 │ + foo.trimLeft.trimEnd(); + 5 5 │ "foo".trimLeft(); + 6 6 │ foo + + +``` + +``` +invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimLeft instead of trimStart. + + 3 │ trimLeft.trimRight(); + 4 │ foo.trimLeft.trimRight(); + > 5 │ "foo".trimLeft(); + │ ^^^^^^^^ + 6 │ foo + 7 │ // comment + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 3 3 │ trimLeft.trimRight(); + 4 4 │ foo.trimLeft.trimRight(); + 5 │ - "foo".trimLeft(); + 5 │ + "foo".trimStart(); + 6 6 │ foo + 7 7 │ // comment + + +``` + +``` +invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimRight instead of trimEnd. + + 6 │ foo + 7 │ // comment + > 8 │ .trimRight /* comment */ + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ /* comment */ + 10 │ (); + + i trimRight() is an alias for trimEnd. + + i Safe fix: Replace trimRight with trimEnd. + + 6 6 │ foo + 7 7 │ // comment + 8 │ - → .trimRight·/*·comment·*/ + 8 │ + → .trimEnd·/*·comment·*/ + 9 9 │ /* comment */ + 10 10 │ (); + + +``` + +``` +invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimLeft instead of trimStart. + + 9 │ /* comment */ + 10 │ (); + > 11 │ foo?.trimLeft(); + │ ^^^^^^^^ + 12 │ + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 9 9 │ /* comment */ + 10 10 │ (); + 11 │ - foo?.trimLeft(); + 11 │ + foo?.trimStart(); + 12 12 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js new file mode 100644 index 00000000000..d977fd94918 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -0,0 +1,26 @@ +const foo = bar.trimStart(); +const foo = bar.trimEnd(); +bar.trimStart?.() +bar['trimStart']() +foo.trimStart(), +foo.trimStart?.(), +foo.trimEnd(), +// Not `CallExpression` +new foo.trimLeft();, +// Not `MemberExpression` +trimLeft();, +// `callee.property` is not a `Identifier` +foo[\'trimLeft\']();', +// Computed +foo[trimLeft]();, +// Not `trimLeft`/`trimRight` +foo.bar();, +// More argument(s) +foo.trimLeft(extra);, +foo.trimLeft(...argumentsArray), +// `trimLeft` is in argument +foo.bar(trimLeft), +foo.bar(foo.trimLeft), +// `trimLeft` is in `MemberExpression.object` +trimLeft.foo(), +foo.trimLeft.bar(), diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index c003376f178..59f9ec6ebad 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -8,5 +8,30 @@ const foo = bar.trimStart(); const foo = bar.trimEnd(); bar.trimStart?.() bar['trimStart']() +<<<<<<< HEAD +======= +foo.trimStart(), +foo.trimStart?.(), +foo.trimEnd(), +// Not `CallExpression` +new foo.trimLeft();, +// Not `MemberExpression` +trimLeft();, +// `callee.property` is not a `Identifier` +foo[\'trimLeft\']();', +// Computed +foo[trimLeft]();, +// Not `trimLeft`/`trimRight` +foo.bar();, +// More argument(s) +foo.trimLeft(extra);, +foo.trimLeft(...argumentsArray), +// `trimLeft` is in argument +foo.bar(trimLeft), +foo.bar(foo.trimLeft), +// `trimLeft` is in `MemberExpression.object` +trimLeft.foo(), +foo.trimLeft.bar(), +>>>>>>> f5418c0e88 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8144222bda1..bb4cc72eefe 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,9 +1258,9 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Use valid values for the autocomplete attribute on input elements. + * Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). */ - useValidAutocomplete?: RuleConfiguration_for_UseValidAutocompleteOptions; + useTrimStartEnd?: RuleFixConfiguration_for_Null; } /** * A list of rules that belong to this group @@ -2546,7 +2546,7 @@ export type Category = | "lint/nursery/useThrowNewError" | "lint/nursery/useThrowOnlyError" | "lint/nursery/useTopLevelRegex" - | "lint/nursery/useValidAutocomplete" + | "lint/nursery/useTrimStartEnd" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" | "lint/performance/noDelete" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index b7e3473bee3..6918994b745 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2119,10 +2119,10 @@ { "type": "null" } ] }, - "useValidAutocomplete": { - "description": "Use valid values for the autocomplete attribute on input elements.", + "useTrimStartEnd": { + "description": "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight().", "anyOf": [ - { "$ref": "#/definitions/UseValidAutocompleteConfiguration" }, + { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } ] } From 00261f299be7af0911d2a70de76b49f150020940 Mon Sep 17 00:00:00 2001 From: chansuke Date: Mon, 10 Jun 2024 22:23:49 +0900 Subject: [PATCH 02/38] fix: gen-lint --- .../migrate/eslint_any_rule_to_biome.rs | 8 +++ .../biome_configuration/src/linter/rules.rs | 52 ++++++++++++++++++- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 2 + .../nursery/useTrimStartEnd/valid.js.snap | 3 -- .../@biomejs/backend-jsonrpc/src/workspace.ts | 5 ++ .../@biomejs/biome/configuration_schema.json | 7 +++ 7 files changed, 73 insertions(+), 5 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index dee99903794..c346f31c5b7 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1446,6 +1446,14 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.use_number_namespace.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } + "unicorn/prefer-string-slice" => { + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group.no_substr.get_or_insert(Default::default()); + rule.set_level(rule_severity.into()); + } "unicorn/prefer-string-trim-start-end" => { if !options.include_nursery { return false; diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 428779ce0b4..261c232d8ab 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2975,6 +2975,9 @@ pub struct Nursery { #[doc = "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] pub use_trim_start_end: Option>, + #[doc = "Use valid values for the autocomplete attribute on input elements."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_valid_autocomplete: Option>, } impl DeserializableValidator for Nursery { fn validate( @@ -3045,6 +3048,7 @@ impl Nursery { "useThrowOnlyError", "useTopLevelRegex", "useTrimStartEnd", + "useValidAutocomplete", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ "noDoneCallback", @@ -3380,11 +3384,31 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } + if let Some(rule) = self.use_throw_only_error.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + } + } + if let Some(rule) = self.use_top_level_regex.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + } + } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3604,11 +3628,31 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } + if let Some(rule) = self.use_throw_only_error.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + } + } + if let Some(rule) = self.use_top_level_regex.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + } + } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3853,6 +3897,10 @@ impl Nursery { .use_trim_start_end .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useValidAutocomplete" => self + .use_valid_autocomplete + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), _ => None, } } diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 1726324443e..0de83207de2 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -169,6 +169,7 @@ define_categories! { "lint/nursery/useThrowOnlyError": "https://biomejs.dev/linter/rules/use-throw-only-error", "lint/nursery/useTopLevelRegex": "https://biomejs.dev/linter/rules/use-top-level-regex", "lint/nursery/useTrimStartEnd": "https://biomejs.dev/linter/rules/use-trim-start-end", + "lint/nursery/useValidAutocomplete": "https://biomejs.dev/linter/rules/use-valid-autocomplete", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", "lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 02eecf3fc08..eae84e70891 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -34,6 +34,7 @@ pub mod use_throw_new_error; pub mod use_throw_only_error; pub mod use_top_level_regex; pub mod use_trim_start_end; +pub mod use_valid_autocomplete; declare_lint_group! { pub Nursery { @@ -71,6 +72,7 @@ declare_lint_group! { self :: use_throw_only_error :: UseThrowOnlyError , self :: use_top_level_regex :: UseTopLevelRegex , self :: use_trim_start_end :: UseTrimStartEnd , + self :: use_valid_autocomplete :: UseValidAutocomplete , ] } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index 59f9ec6ebad..b9c4df4ae93 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -8,8 +8,6 @@ const foo = bar.trimStart(); const foo = bar.trimEnd(); bar.trimStart?.() bar['trimStart']() -<<<<<<< HEAD -======= foo.trimStart(), foo.trimStart?.(), foo.trimEnd(), @@ -32,6 +30,5 @@ foo.bar(foo.trimLeft), // `trimLeft` is in `MemberExpression.object` trimLeft.foo(), foo.trimLeft.bar(), ->>>>>>> f5418c0e88 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index bb4cc72eefe..38222fa00ee 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1261,6 +1261,10 @@ export interface Nursery { * Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). */ useTrimStartEnd?: RuleFixConfiguration_for_Null; + /** + * Use valid values for the autocomplete attribute on input elements. + */ + useValidAutocomplete?: RuleConfiguration_for_UseValidAutocompleteOptions; } /** * A list of rules that belong to this group @@ -2547,6 +2551,7 @@ export type Category = | "lint/nursery/useThrowOnlyError" | "lint/nursery/useTopLevelRegex" | "lint/nursery/useTrimStartEnd" + | "lint/nursery/useValidAutocomplete" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" | "lint/performance/noDelete" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 6918994b745..b048258afed 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2125,6 +2125,13 @@ { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } ] + }, + "useValidAutocomplete": { + "description": "Use valid values for the autocomplete attribute on input elements.", + "anyOf": [ + { "$ref": "#/definitions/UseValidAutocompleteConfiguration" }, + { "type": "null" } + ] } }, "additionalProperties": false From ec1871a85f9a32d96d6498ebddcb6c18aa00eec4 Mon Sep 17 00:00:00 2001 From: chansuke Date: Thu, 13 Jun 2024 12:47:24 +0900 Subject: [PATCH 03/38] refactor: improve declaration of the rule --- .../biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index a86a1409586..88110d3616a 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -10,10 +10,10 @@ use biome_rowan::{BatchMutationExt, TextRange, TokenText}; use crate::JsRuleAction; declare_rule! { - /// Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). + /// Enforce the use of `trimStart()` and `trimEnd()` over `trimLeft()` and `trimRight()`. /// /// While `trimLeft()` and `trimRight()` are aliases for `trimStart()` and `trimEnd()`, - /// using the latter helps maintain consistency and uses direction-independent wording. + /// only using the latter pair ensures consistency and is preferable for their direction-independent wording. /// /// ## Examples /// From 7b60bba21340769863ae4e362e90f033873f5cc0 Mon Sep 17 00:00:00 2001 From: chansuke Date: Thu, 13 Jun 2024 15:14:34 +0900 Subject: [PATCH 04/38] chore: run gen-lint --- crates/biome_configuration/src/linter/rules.rs | 2 +- packages/@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- packages/@biomejs/biome/configuration_schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 261c232d8ab..0de1d4ef2e6 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,7 +2972,7 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight()."] + #[doc = "Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] pub use_trim_start_end: Option>, #[doc = "Use valid values for the autocomplete attribute on input elements."] diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 38222fa00ee..7d7228d170d 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,7 +1258,7 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). + * Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight(). */ useTrimStartEnd?: RuleFixConfiguration_for_Null; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index b048258afed..b67f902f3b5 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2120,7 +2120,7 @@ ] }, "useTrimStartEnd": { - "description": "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight().", + "description": "Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight().", "anyOf": [ { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } From 10077ff1e773ff2dfba347833ca616a065a10e5b Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 01:53:17 +0900 Subject: [PATCH 05/38] fix: remove illegal syntax test cases --- .../specs/nursery/useTrimStartEnd/invalid.js | 14 +-- .../nursery/useTrimStartEnd/invalid.js.snap | 114 +++++++++--------- .../specs/nursery/useTrimStartEnd/valid.js | 33 +++-- .../nursery/useTrimStartEnd/valid.js.snap | 33 +++-- 4 files changed, 96 insertions(+), 98 deletions(-) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js index 4587a117643..2e997511f14 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -1,11 +1,11 @@ -foo.trimLeft(); -foo.trimRight(); -trimLeft.trimRight(); -foo.trimLeft.trimRight(); -"foo".trimLeft(); +foo.trimLeft() +foo.trimRight() +trimLeft.trimRight() +foo.trimLeft.trimRight() +"foo".trimLeft() foo // comment .trimRight /* comment */ /* comment */ - (); -foo?.trimLeft(); + () +foo?.trimLeft() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 853852b6635..4627a2aa486 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -4,17 +4,17 @@ expression: invalid.js --- # Input ```jsx -foo.trimLeft(); -foo.trimRight(); -trimLeft.trimRight(); -foo.trimLeft.trimRight(); -"foo".trimLeft(); +foo.trimLeft() +foo.trimRight() +trimLeft.trimRight() +foo.trimLeft.trimRight() +"foo".trimLeft() foo // comment .trimRight /* comment */ /* comment */ - (); -foo?.trimLeft(); + () +foo?.trimLeft() ``` @@ -24,19 +24,19 @@ invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimLeft instead of trimStart. - > 1 │ foo.trimLeft(); + > 1 │ foo.trimLeft() │ ^^^^^^^^ - 2 │ foo.trimRight(); - 3 │ trimLeft.trimRight(); + 2 │ foo.trimRight() + 3 │ trimLeft.trimRight() i trimLeft() is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. - 1 │ - foo.trimLeft(); - 1 │ + foo.trimStart(); - 2 2 │ foo.trimRight(); - 3 3 │ trimLeft.trimRight(); + 1 │ - foo.trimLeft() + 1 │ + foo.trimStart() + 2 2 │ foo.trimRight() + 3 3 │ trimLeft.trimRight() ``` @@ -46,21 +46,21 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimRight instead of trimEnd. - 1 │ foo.trimLeft(); - > 2 │ foo.trimRight(); + 1 │ foo.trimLeft() + > 2 │ foo.trimRight() │ ^^^^^^^^^ - 3 │ trimLeft.trimRight(); - 4 │ foo.trimLeft.trimRight(); + 3 │ trimLeft.trimRight() + 4 │ foo.trimLeft.trimRight() i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. - 1 1 │ foo.trimLeft(); - 2 │ - foo.trimRight(); - 2 │ + foo.trimEnd(); - 3 3 │ trimLeft.trimRight(); - 4 4 │ foo.trimLeft.trimRight(); + 1 1 │ foo.trimLeft() + 2 │ - foo.trimRight() + 2 │ + foo.trimEnd() + 3 3 │ trimLeft.trimRight() + 4 4 │ foo.trimLeft.trimRight() ``` @@ -70,23 +70,23 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimRight instead of trimEnd. - 1 │ foo.trimLeft(); - 2 │ foo.trimRight(); - > 3 │ trimLeft.trimRight(); + 1 │ foo.trimLeft() + 2 │ foo.trimRight() + > 3 │ trimLeft.trimRight() │ ^^^^^^^^^ - 4 │ foo.trimLeft.trimRight(); - 5 │ "foo".trimLeft(); + 4 │ foo.trimLeft.trimRight() + 5 │ "foo".trimLeft() i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. - 1 1 │ foo.trimLeft(); - 2 2 │ foo.trimRight(); - 3 │ - trimLeft.trimRight(); - 3 │ + trimLeft.trimEnd(); - 4 4 │ foo.trimLeft.trimRight(); - 5 5 │ "foo".trimLeft(); + 1 1 │ foo.trimLeft() + 2 2 │ foo.trimRight() + 3 │ - trimLeft.trimRight() + 3 │ + trimLeft.trimEnd() + 4 4 │ foo.trimLeft.trimRight() + 5 5 │ "foo".trimLeft() ``` @@ -96,22 +96,22 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimRight instead of trimEnd. - 2 │ foo.trimRight(); - 3 │ trimLeft.trimRight(); - > 4 │ foo.trimLeft.trimRight(); + 2 │ foo.trimRight() + 3 │ trimLeft.trimRight() + > 4 │ foo.trimLeft.trimRight() │ ^^^^^^^^^ - 5 │ "foo".trimLeft(); + 5 │ "foo".trimLeft() 6 │ foo i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. - 2 2 │ foo.trimRight(); - 3 3 │ trimLeft.trimRight(); - 4 │ - foo.trimLeft.trimRight(); - 4 │ + foo.trimLeft.trimEnd(); - 5 5 │ "foo".trimLeft(); + 2 2 │ foo.trimRight() + 3 3 │ trimLeft.trimRight() + 4 │ - foo.trimLeft.trimRight() + 4 │ + foo.trimLeft.trimEnd() + 5 5 │ "foo".trimLeft() 6 6 │ foo @@ -122,9 +122,9 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimLeft instead of trimStart. - 3 │ trimLeft.trimRight(); - 4 │ foo.trimLeft.trimRight(); - > 5 │ "foo".trimLeft(); + 3 │ trimLeft.trimRight() + 4 │ foo.trimLeft.trimRight() + > 5 │ "foo".trimLeft() │ ^^^^^^^^ 6 │ foo 7 │ // comment @@ -133,10 +133,10 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ i Safe fix: Replace trimLeft with trimStart. - 3 3 │ trimLeft.trimRight(); - 4 4 │ foo.trimLeft.trimRight(); - 5 │ - "foo".trimLeft(); - 5 │ + "foo".trimStart(); + 3 3 │ trimLeft.trimRight() + 4 4 │ foo.trimLeft.trimRight() + 5 │ - "foo".trimLeft() + 5 │ + "foo".trimStart() 6 6 │ foo 7 7 │ // comment @@ -153,7 +153,7 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ > 8 │ .trimRight /* comment */ │ ^^^^^^^^^^^^^^^^^^^^^^^ 9 │ /* comment */ - 10 │ (); + 10 │ () i trimRight() is an alias for trimEnd. @@ -164,7 +164,7 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 8 │ - → .trimRight·/*·comment·*/ 8 │ + → .trimEnd·/*·comment·*/ 9 9 │ /* comment */ - 10 10 │ (); + 10 10 │ () ``` @@ -175,8 +175,8 @@ invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ! Use trimLeft instead of trimStart. 9 │ /* comment */ - 10 │ (); - > 11 │ foo?.trimLeft(); + 10 │ () + > 11 │ foo?.trimLeft() │ ^^^^^^^^ 12 │ @@ -185,9 +185,9 @@ invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ i Safe fix: Replace trimLeft with trimStart. 9 9 │ /* comment */ - 10 10 │ (); - 11 │ - foo?.trimLeft(); - 11 │ + foo?.trimStart(); + 10 10 │ () + 11 │ - foo?.trimLeft() + 11 │ + foo?.trimStart() 12 12 │ diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index d977fd94918..b3b6241b9a9 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -1,26 +1,25 @@ -const foo = bar.trimStart(); -const foo = bar.trimEnd(); +const foo = bar.trimStart() +const foo = bar.trimEnd() bar.trimStart?.() -bar['trimStart']() -foo.trimStart(), -foo.trimStart?.(), -foo.trimEnd(), +foo.trimStart() +foo.trimStart?.() +foo.trimEnd() // Not `CallExpression` -new foo.trimLeft();, +new foo.trimLeft() // Not `MemberExpression` -trimLeft();, +trimLeft() // `callee.property` is not a `Identifier` -foo[\'trimLeft\']();', +foo[\'trimLeft\']() // Computed -foo[trimLeft]();, +foo[trimLeft]() // Not `trimLeft`/`trimRight` -foo.bar();, +foo.bar() // More argument(s) -foo.trimLeft(extra);, -foo.trimLeft(...argumentsArray), +foo.trimLeft(extra) +foo.trimLeft(...argumentsArray) // `trimLeft` is in argument -foo.bar(trimLeft), -foo.bar(foo.trimLeft), +foo.bar(trimLeft) +foo.bar(foo.trimLeft) // `trimLeft` is in `MemberExpression.object` -trimLeft.foo(), -foo.trimLeft.bar(), +trimLeft.foo() +foo.trimLeft.bar() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index b9c4df4ae93..b04cd9a8294 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -4,31 +4,30 @@ expression: valid.js --- # Input ```jsx -const foo = bar.trimStart(); -const foo = bar.trimEnd(); +const foo = bar.trimStart() +const foo = bar.trimEnd() bar.trimStart?.() -bar['trimStart']() -foo.trimStart(), -foo.trimStart?.(), -foo.trimEnd(), +foo.trimStart() +foo.trimStart?.() +foo.trimEnd() // Not `CallExpression` -new foo.trimLeft();, +new foo.trimLeft() // Not `MemberExpression` -trimLeft();, +trimLeft() // `callee.property` is not a `Identifier` -foo[\'trimLeft\']();', +foo[\'trimLeft\']() // Computed -foo[trimLeft]();, +foo[trimLeft]() // Not `trimLeft`/`trimRight` -foo.bar();, +foo.bar() // More argument(s) -foo.trimLeft(extra);, -foo.trimLeft(...argumentsArray), +foo.trimLeft(extra) +foo.trimLeft(...argumentsArray) // `trimLeft` is in argument -foo.bar(trimLeft), -foo.bar(foo.trimLeft), +foo.bar(trimLeft) +foo.bar(foo.trimLeft) // `trimLeft` is in `MemberExpression.object` -trimLeft.foo(), -foo.trimLeft.bar(), +trimLeft.foo() +foo.trimLeft.bar() ``` From 31be2ef38d116af259f5f615a7c365a88569794f Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 01:56:15 +0900 Subject: [PATCH 06/38] docs: update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e25a3e48586..a22da1a36ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -252,6 +252,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b #### New features - Add [nursery/useValidAutocomplete](https://biomejs.dev/linter/rules/use-valid-autocomplete/). Contributed by @unvalley +- Add [nursery/noSubstr](https://biomejs.dev/linter/rules/no-substr/). Contributed by @chansuke +- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke #### Bug fixes From 0adcc3921391ea4684f0bad29fbc1e586090b1a0 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 10:32:08 +0900 Subject: [PATCH 07/38] chore: update testcase --- .../tests/specs/nursery/useTrimStartEnd/invalid.js | 3 +++ .../tests/specs/nursery/useTrimStartEnd/valid.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js index 2e997511f14..5bed3cd9475 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -9,3 +9,6 @@ foo /* comment */ () foo?.trimLeft() +bar['trimLeft']() +bar["trimLeft"]() +bar[`trimLeft`]() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index b3b6241b9a9..78573ce1977 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -23,3 +23,8 @@ foo.bar(foo.trimLeft) // `trimLeft` is in `MemberExpression.object` trimLeft.foo() foo.trimLeft.bar() + +bar['trimStart']() +bar["trimStart"]() +bar[`trimStart`]() + From ec91114c275eeed76488e84d4ebd5b8f215617e0 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 10:56:35 +0900 Subject: [PATCH 08/38] fix: show replaced member name --- .../src/lint/nursery/use_trim_start_end.rs | 2 +- .../nursery/useTrimStartEnd/invalid.js.snap | 23 +++++--- .../specs/nursery/useTrimStartEnd/valid.js | 3 +- .../nursery/useTrimStartEnd/valid.js.snap | 59 ++++++++++++++++++- 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 88110d3616a..816ec231a76 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -83,7 +83,7 @@ impl Rule for UseTrimStartEnd { let member_name = state.member_name.text(); let replaced_member_name = state.replaced_member_name; let diagnostic_message = markup! { - "Use "{member_name}" instead of "{replaced_member_name}"." + "Use "{replaced_member_name}" instead of "{member_name}"." } .to_owned(); let note_message = { diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 4627a2aa486..12595e6bb30 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -15,6 +15,9 @@ foo /* comment */ () foo?.trimLeft() +bar['trimLeft']() +bar["trimLeft"]() +bar[`trimLeft`]() ``` @@ -22,7 +25,7 @@ foo?.trimLeft() ``` invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimLeft instead of trimStart. + ! Use trimStart instead of trimLeft. > 1 │ foo.trimLeft() │ ^^^^^^^^ @@ -44,7 +47,7 @@ invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimRight instead of trimEnd. + ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() > 2 │ foo.trimRight() @@ -68,7 +71,7 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimRight instead of trimEnd. + ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() 2 │ foo.trimRight() @@ -94,7 +97,7 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimRight instead of trimEnd. + ! Use trimEnd instead of trimRight. 2 │ foo.trimRight() 3 │ trimLeft.trimRight() @@ -120,7 +123,7 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimLeft instead of trimStart. + ! Use trimStart instead of trimLeft. 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() @@ -146,7 +149,7 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimRight instead of trimEnd. + ! Use trimEnd instead of trimRight. 6 │ foo 7 │ // comment @@ -172,13 +175,14 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use trimLeft instead of trimStart. + ! Use trimStart instead of trimLeft. 9 │ /* comment */ 10 │ () > 11 │ foo?.trimLeft() │ ^^^^^^^^ - 12 │ + 12 │ bar['trimLeft']() + 13 │ bar["trimLeft"]() i trimLeft() is an alias for trimStart. @@ -188,7 +192,8 @@ invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 10 10 │ () 11 │ - foo?.trimLeft() 11 │ + foo?.trimStart() - 12 12 │ + 12 12 │ bar['trimLeft']() + 13 13 │ bar["trimLeft"]() ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index 78573ce1977..84d5c03c5f0 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -9,7 +9,7 @@ new foo.trimLeft() // Not `MemberExpression` trimLeft() // `callee.property` is not a `Identifier` -foo[\'trimLeft\']() +foo['trimLeft']() // Computed foo[trimLeft]() // Not `trimLeft`/`trimRight` @@ -23,7 +23,6 @@ foo.bar(foo.trimLeft) // `trimLeft` is in `MemberExpression.object` trimLeft.foo() foo.trimLeft.bar() - bar['trimStart']() bar["trimStart"]() bar[`trimStart`]() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index b04cd9a8294..90d275ca79e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -15,7 +15,7 @@ new foo.trimLeft() // Not `MemberExpression` trimLeft() // `callee.property` is not a `Identifier` -foo[\'trimLeft\']() +foo['trimLeft']() // Computed foo[trimLeft]() // Not `trimLeft`/`trimRight` @@ -29,5 +29,62 @@ foo.bar(foo.trimLeft) // `trimLeft` is in `MemberExpression.object` trimLeft.foo() foo.trimLeft.bar() +bar['trimStart']() +bar["trimStart"]() +bar[`trimStart`]() + + +``` + +# Diagnostics +``` +valid.js:18:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimLeft. + + 16 │ foo.bar() + 17 │ // More argument(s) + > 18 │ foo.trimLeft(extra) + │ ^^^^^^^^ + 19 │ foo.trimLeft(...argumentsArray) + 20 │ // `trimLeft` is in argument + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 16 16 │ foo.bar() + 17 17 │ // More argument(s) + 18 │ - foo.trimLeft(extra) + 18 │ + foo.trimStart(extra) + 19 19 │ foo.trimLeft(...argumentsArray) + 20 20 │ // `trimLeft` is in argument + + +``` + +``` +valid.js:19:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimLeft. + + 17 │ // More argument(s) + 18 │ foo.trimLeft(extra) + > 19 │ foo.trimLeft(...argumentsArray) + │ ^^^^^^^^ + 20 │ // `trimLeft` is in argument + 21 │ foo.bar(trimLeft) + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 17 17 │ // More argument(s) + 18 18 │ foo.trimLeft(extra) + 19 │ - foo.trimLeft(...argumentsArray) + 19 │ + foo.trimStart(...argumentsArray) + 20 20 │ // `trimLeft` is in argument + 21 21 │ foo.bar(trimLeft) + ``` From baf0b93288ff30fdbdcd65b1c5605a965ece5c13 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 12:25:35 +0900 Subject: [PATCH 09/38] chore: add `String.` --- .../biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 816ec231a76..f3c9189486b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -10,9 +10,9 @@ use biome_rowan::{BatchMutationExt, TextRange, TokenText}; use crate::JsRuleAction; declare_rule! { - /// Enforce the use of `trimStart()` and `trimEnd()` over `trimLeft()` and `trimRight()`. + /// Enforce the use of `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`. /// - /// While `trimLeft()` and `trimRight()` are aliases for `trimStart()` and `trimEnd()`, + /// While `String.trimLeft()` and `String.trimRight()` are aliases for `String.trimStart()` and `String.trimEnd()`, /// only using the latter pair ensures consistency and is preferable for their direction-independent wording. /// /// ## Examples From 7d690a8e1813c0ce6099752be43e4011318d6a9c Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 12:58:13 +0900 Subject: [PATCH 10/38] fix: add logic for parameters --- .../src/lint/nursery/use_trim_start_end.rs | 18 ++++++- .../nursery/useTrimStartEnd/valid.js.snap | 53 ------------------- 2 files changed, 17 insertions(+), 54 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index f3c9189486b..82f13654d1d 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -5,7 +5,7 @@ use biome_analyze::{ use biome_console::markup; use biome_js_factory::make; use biome_js_syntax::JsCallExpression; -use biome_rowan::{BatchMutationExt, TextRange, TokenText}; +use biome_rowan::{AstSeparatedList, BatchMutationExt, TextRange, TokenText}; use crate::JsRuleAction; @@ -15,6 +15,11 @@ declare_rule! { /// While `String.trimLeft()` and `String.trimRight()` are aliases for `String.trimStart()` and `String.trimEnd()`, /// only using the latter pair ensures consistency and is preferable for their direction-independent wording. /// + /// Note that `String.trimStart()` and `String.trimEnd()` methods do not take any parameters. Any arguments passed to these methods will be ignored. + /// See the MDN documentation for more details: + /// - [String.prototype.trimStart()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart) + /// - [String.prototype.trimEnd()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) + /// /// ## Examples /// /// ### Invalid @@ -62,6 +67,17 @@ impl Rule for UseTrimStartEnd { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); + let arguments = node.arguments().ok()?; + let args = arguments.args(); + + if !args.is_empty() { + // If arguments are present, it suggests this function call may not be intended for `String.trimStart()` or `String.trimEnd()`, + // as these methods do not accept parameters according to the specification: + // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart#parameters + // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd#parameters + return None; + } + let callee = node.callee().ok()?; let expression = callee.as_js_static_member_expression()?; let value_token = expression.member().ok()?.value_token().ok()?; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index 90d275ca79e..a6aa3754dff 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -34,57 +34,4 @@ bar["trimStart"]() bar[`trimStart`]() -``` - -# Diagnostics -``` -valid.js:18:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimLeft. - - 16 │ foo.bar() - 17 │ // More argument(s) - > 18 │ foo.trimLeft(extra) - │ ^^^^^^^^ - 19 │ foo.trimLeft(...argumentsArray) - 20 │ // `trimLeft` is in argument - - i trimLeft() is an alias for trimStart. - - i Safe fix: Replace trimLeft with trimStart. - - 16 16 │ foo.bar() - 17 17 │ // More argument(s) - 18 │ - foo.trimLeft(extra) - 18 │ + foo.trimStart(extra) - 19 19 │ foo.trimLeft(...argumentsArray) - 20 20 │ // `trimLeft` is in argument - - -``` - -``` -valid.js:19:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimLeft. - - 17 │ // More argument(s) - 18 │ foo.trimLeft(extra) - > 19 │ foo.trimLeft(...argumentsArray) - │ ^^^^^^^^ - 20 │ // `trimLeft` is in argument - 21 │ foo.bar(trimLeft) - - i trimLeft() is an alias for trimStart. - - i Safe fix: Replace trimLeft with trimStart. - - 17 17 │ // More argument(s) - 18 18 │ foo.trimLeft(extra) - 19 │ - foo.trimLeft(...argumentsArray) - 19 │ + foo.trimStart(...argumentsArray) - 20 20 │ // `trimLeft` is in argument - 21 21 │ foo.bar(trimLeft) - - ``` From 5325cd3dffae5a4573d7179553ac649394c33c09 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 14 Jun 2024 13:10:57 +0900 Subject: [PATCH 11/38] chore: run gen-lint --- crates/biome_configuration/src/linter/rules.rs | 2 +- packages/@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- packages/@biomejs/biome/configuration_schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 0de1d4ef2e6..752f0cf1e34 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,7 +2972,7 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight()."] + #[doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] pub use_trim_start_end: Option>, #[doc = "Use valid values for the autocomplete attribute on input elements."] diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 7d7228d170d..8839ae63ed1 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,7 +1258,7 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight(). + * Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight(). */ useTrimStartEnd?: RuleFixConfiguration_for_Null; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index b67f902f3b5..952406d91a2 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2120,7 +2120,7 @@ ] }, "useTrimStartEnd": { - "description": "Enforce the use of trimStart() and trimEnd() over trimLeft() and trimRight().", + "description": "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight().", "anyOf": [ { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } From c4c2edd073d75bd73a56230ebd63b97f73c65d20 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 15 Jun 2024 19:37:15 +0900 Subject: [PATCH 12/38] fix: add logic for computed memeber expression --- .../src/lint/nursery/use_trim_start_end.rs | 84 ++++-- .../nursery/useTrimStartEnd/invalid.js.snap | 76 +++++ .../nursery/useTrimStartEnd/valid.js.snap | 263 ++++++++++++++++++ 3 files changed, 403 insertions(+), 20 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 82f13654d1d..6f4a78912aa 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -4,8 +4,10 @@ use biome_analyze::{ }; use biome_console::markup; use biome_js_factory::make; -use biome_js_syntax::JsCallExpression; -use biome_rowan::{AstSeparatedList, BatchMutationExt, TextRange, TokenText}; +use biome_js_syntax::{AnyJsExpression, JsCallExpression, JsLanguage}; +use biome_rowan::{ + AstSeparatedList, BatchMutationExt, NodeOrToken, SyntaxToken, TextRange, TokenText, +}; use crate::JsRuleAction; @@ -56,7 +58,7 @@ declare_rule! { pub struct UseTrimStartEndState { member_name: TokenText, span: TextRange, - replaced_member_name: &'static str, + replaced_member_name: String, } impl Rule for UseTrimStartEnd { @@ -79,25 +81,20 @@ impl Rule for UseTrimStartEnd { } let callee = node.callee().ok()?; - let expression = callee.as_js_static_member_expression()?; - let value_token = expression.member().ok()?.value_token().ok()?; - let name = value_token.text_trimmed(); - let suggested_name = match name { - "trimLeft" => Some("trimStart"), - "trimRight" => Some("trimEnd"), - _ => None, - }?; + let token = generate_syntax_token(callee)?; + let suggested_name = suggested_name(token.clone()); Some(UseTrimStartEndState { - member_name: value_token.token_text_trimmed(), - span: value_token.text_range(), + member_name: token.token_text_trimmed(), + span: token.text_range(), replaced_member_name: suggested_name, }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name; + let replaced_member_name = state.replaced_member_name.clone(); + let diagnostic_message = markup! { "Use "{replaced_member_name}" instead of "{member_name}"." } @@ -108,6 +105,7 @@ impl Rule for UseTrimStartEnd { } .to_owned() }; + Some( RuleDiagnostic::new(rule_category!(), state.span, diagnostic_message) .note(note_message), @@ -117,14 +115,13 @@ impl Rule for UseTrimStartEnd { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); let callee = node.callee().ok()?; - let expression = callee.as_js_static_member_expression()?; - let member = expression.member().ok()?; - let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name; + let token = generate_syntax_token(callee)?; + let member_name = state.member_name.text(); + let replaced_member_name = state.replaced_member_name.clone(); + let replaced_function = make::js_name(make::ident(&replaced_member_name)); let mut mutation = ctx.root().begin(); - let replaced_function = make::js_name(make::ident(replaced_member_name)); - mutation.replace_element(member.into(), replaced_function.into()); + mutation.replace_element(NodeOrToken::Token(token), replaced_function.into()); Some(JsRuleAction::new( ActionCategory::QuickFix, @@ -135,3 +132,50 @@ impl Rule for UseTrimStartEnd { )) } } + +fn generate_syntax_token(callee: AnyJsExpression) -> Option> { + let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { + let member = expression.member().ok()?; + match member { + AnyJsExpression::AnyJsLiteralExpression(literal) => literal.value_token().ok(), + AnyJsExpression::JsTemplateExpression(element) => { + element.elements().into_iter().find_map(|x| { + x.as_js_template_chunk_element() + .and_then(|chunk| chunk.template_chunk_token().ok()) + }) + } + _ => None, + } + } else if let AnyJsExpression::JsStaticMemberExpression(expression) = callee { + expression.member().ok()?.value_token().ok() + } else { + None + }; + token +} + +// Handle "'text'" and "\"text\"" and "text" cases +fn suggested_name(text: SyntaxToken) -> String { + let trimmed = text.text_trimmed(); + let first_char = trimmed.chars().next(); + let last_char = trimmed.chars().last(); + + let is_single_quoted = first_char == Some('\'') && last_char == Some('\''); + let is_double_quoted = first_char == Some('"') && last_char == Some('"'); + + let unquoted = trimmed.trim_matches(|c| c == '\'' || c == '"'); + + let cleaned = match unquoted { + "trimLeft" => "trimStart", + "trimRight" => "trimEnd", + _ => unquoted, + }; + + if is_single_quoted { + format!("'{}'", cleaned) + } else if is_double_quoted { + format!("\"{}\"", cleaned) + } else { + cleaned.to_string() + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 12595e6bb30..e102fd9e100 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -197,3 +197,79 @@ invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` + +``` +invalid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use 'trimStart' instead of 'trimLeft'. + + 10 │ () + 11 │ foo?.trimLeft() + > 12 │ bar['trimLeft']() + │ ^^^^^^^^^^ + 13 │ bar["trimLeft"]() + 14 │ bar[`trimLeft`]() + + i 'trimLeft'() is an alias for 'trimStart'. + + i Safe fix: Replace 'trimLeft' with 'trimStart'. + + 10 10 │ () + 11 11 │ foo?.trimLeft() + 12 │ - bar['trimLeft']() + 12 │ + bar['trimStart']() + 13 13 │ bar["trimLeft"]() + 14 14 │ bar[`trimLeft`]() + + +``` + +``` +invalid.js:13:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use "trimStart" instead of "trimLeft". + + 11 │ foo?.trimLeft() + 12 │ bar['trimLeft']() + > 13 │ bar["trimLeft"]() + │ ^^^^^^^^^^ + 14 │ bar[`trimLeft`]() + 15 │ + + i "trimLeft"() is an alias for "trimStart". + + i Safe fix: Replace "trimLeft" with "trimStart". + + 11 11 │ foo?.trimLeft() + 12 12 │ bar['trimLeft']() + 13 │ - bar["trimLeft"]() + 13 │ + bar["trimStart"]() + 14 14 │ bar[`trimLeft`]() + 15 15 │ + + +``` + +``` +invalid.js:14:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimLeft. + + 12 │ bar['trimLeft']() + 13 │ bar["trimLeft"]() + > 14 │ bar[`trimLeft`]() + │ ^^^^^^^^ + 15 │ + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 12 12 │ bar['trimLeft']() + 13 13 │ bar["trimLeft"]() + 14 │ - bar[`trimLeft`]() + 14 │ + bar[`trimStart`]() + 15 15 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index a6aa3754dff..0e5dd9693ae 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -34,4 +34,267 @@ bar["trimStart"]() bar[`trimStart`]() +``` + +# Diagnostics +``` +valid.js:1:17 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimStart. + + > 1 │ const foo = bar.trimStart() + │ ^^^^^^^^^ + 2 │ const foo = bar.trimEnd() + 3 │ bar.trimStart?.() + + i trimStart() is an alias for trimStart. + + i Safe fix: Replace trimStart with trimStart. + + + +``` + +``` +valid.js:2:17 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimEnd instead of trimEnd. + + 1 │ const foo = bar.trimStart() + > 2 │ const foo = bar.trimEnd() + │ ^^^^^^^ + 3 │ bar.trimStart?.() + 4 │ foo.trimStart() + + i trimEnd() is an alias for trimEnd. + + i Safe fix: Replace trimEnd with trimEnd. + + + +``` + +``` +valid.js:3:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimStart. + + 1 │ const foo = bar.trimStart() + 2 │ const foo = bar.trimEnd() + > 3 │ bar.trimStart?.() + │ ^^^^^^^^^ + 4 │ foo.trimStart() + 5 │ foo.trimStart?.() + + i trimStart() is an alias for trimStart. + + i Safe fix: Replace trimStart with trimStart. + + + +``` + +``` +valid.js:4:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimStart. + + 2 │ const foo = bar.trimEnd() + 3 │ bar.trimStart?.() + > 4 │ foo.trimStart() + │ ^^^^^^^^^ + 5 │ foo.trimStart?.() + 6 │ foo.trimEnd() + + i trimStart() is an alias for trimStart. + + i Safe fix: Replace trimStart with trimStart. + + + +``` + +``` +valid.js:5:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimStart. + + 3 │ bar.trimStart?.() + 4 │ foo.trimStart() + > 5 │ foo.trimStart?.() + │ ^^^^^^^^^ + 6 │ foo.trimEnd() + 7 │ // Not `CallExpression` + + i trimStart() is an alias for trimStart. + + i Safe fix: Replace trimStart with trimStart. + + + +``` + +``` +valid.js:6:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimEnd instead of trimEnd. + + 4 │ foo.trimStart() + 5 │ foo.trimStart?.() + > 6 │ foo.trimEnd() + │ ^^^^^^^ + 7 │ // Not `CallExpression` + 8 │ new foo.trimLeft() + + i trimEnd() is an alias for trimEnd. + + i Safe fix: Replace trimEnd with trimEnd. + + + +``` + +``` +valid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use 'trimStart' instead of 'trimLeft'. + + 10 │ trimLeft() + 11 │ // `callee.property` is not a `Identifier` + > 12 │ foo['trimLeft']() + │ ^^^^^^^^^^ + 13 │ // Computed + 14 │ foo[trimLeft]() + + i 'trimLeft'() is an alias for 'trimStart'. + + i Safe fix: Replace 'trimLeft' with 'trimStart'. + + 10 10 │ trimLeft() + 11 11 │ // `callee.property` is not a `Identifier` + 12 │ - foo['trimLeft']() + 12 │ + foo['trimStart']() + 13 13 │ // Computed + 14 14 │ foo[trimLeft]() + + +``` + +``` +valid.js:16:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use bar instead of bar. + + 14 │ foo[trimLeft]() + 15 │ // Not `trimLeft`/`trimRight` + > 16 │ foo.bar() + │ ^^^ + 17 │ // More argument(s) + 18 │ foo.trimLeft(extra) + + i bar() is an alias for bar. + + i Safe fix: Replace bar with bar. + + + +``` + +``` +valid.js:24:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use foo instead of foo. + + 22 │ foo.bar(foo.trimLeft) + 23 │ // `trimLeft` is in `MemberExpression.object` + > 24 │ trimLeft.foo() + │ ^^^ + 25 │ foo.trimLeft.bar() + 26 │ bar['trimStart']() + + i foo() is an alias for foo. + + i Safe fix: Replace foo with foo. + + + +``` + +``` +valid.js:25:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use bar instead of bar. + + 23 │ // `trimLeft` is in `MemberExpression.object` + 24 │ trimLeft.foo() + > 25 │ foo.trimLeft.bar() + │ ^^^ + 26 │ bar['trimStart']() + 27 │ bar["trimStart"]() + + i bar() is an alias for bar. + + i Safe fix: Replace bar with bar. + + + +``` + +``` +valid.js:26:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use 'trimStart' instead of 'trimStart'. + + 24 │ trimLeft.foo() + 25 │ foo.trimLeft.bar() + > 26 │ bar['trimStart']() + │ ^^^^^^^^^^^ + 27 │ bar["trimStart"]() + 28 │ bar[`trimStart`]() + + i 'trimStart'() is an alias for 'trimStart'. + + i Safe fix: Replace 'trimStart' with 'trimStart'. + + + +``` + +``` +valid.js:27:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use "trimStart" instead of "trimStart". + + 25 │ foo.trimLeft.bar() + 26 │ bar['trimStart']() + > 27 │ bar["trimStart"]() + │ ^^^^^^^^^^^ + 28 │ bar[`trimStart`]() + 29 │ + + i "trimStart"() is an alias for "trimStart". + + i Safe fix: Replace "trimStart" with "trimStart". + + + +``` + +``` +valid.js:28:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimStart. + + 26 │ bar['trimStart']() + 27 │ bar["trimStart"]() + > 28 │ bar[`trimStart`]() + │ ^^^^^^^^^ + 29 │ + + i trimStart() is an alias for trimStart. + + i Safe fix: Replace trimStart with trimStart. + + + ``` From c1d517875d939009a53da6a205aee2a8853db8f0 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 15 Jun 2024 20:34:50 +0900 Subject: [PATCH 13/38] refactor: change type of argument --- .../src/lint/nursery/use_trim_start_end.rs | 115 ++++---- .../nursery/useTrimStartEnd/invalid.js.snap | 76 ----- .../nursery/useTrimStartEnd/valid.js.snap | 263 ------------------ 3 files changed, 47 insertions(+), 407 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 6f4a78912aa..443601a704b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -4,10 +4,8 @@ use biome_analyze::{ }; use biome_console::markup; use biome_js_factory::make; -use biome_js_syntax::{AnyJsExpression, JsCallExpression, JsLanguage}; -use biome_rowan::{ - AstSeparatedList, BatchMutationExt, NodeOrToken, SyntaxToken, TextRange, TokenText, -}; +use biome_js_syntax::{AnyJsExpression, JsCallExpression}; +use biome_rowan::{AstSeparatedList, BatchMutationExt, NodeOrToken, TextRange}; use crate::JsRuleAction; @@ -56,9 +54,9 @@ declare_rule! { #[derive(Debug, Clone)] pub struct UseTrimStartEndState { - member_name: TokenText, + member_name: String, span: TextRange, - replaced_member_name: String, + replaced_member_name: &'static str, } impl Rule for UseTrimStartEnd { @@ -81,27 +79,47 @@ impl Rule for UseTrimStartEnd { } let callee = node.callee().ok()?; - let token = generate_syntax_token(callee)?; - let suggested_name = suggested_name(token.clone()); - + let (member_name, span, suggested_name) = match callee { + AnyJsExpression::JsComputedMemberExpression(callee) => { + let member = callee.member().ok()?; + let value = member.as_static_value()?; + let span = value.range(); + let member_name = value.as_string_constant()?.to_string(); + let suggested_name = match member_name.as_ref() { + "trimLeft" => Some("trimStart"), + "trimRight" => Some("trimEnd"), + _ => return None, + }; + (member_name, span, suggested_name) + } + AnyJsExpression::JsStaticMemberExpression(callee) => { + let token = callee.member().ok()?.value_token().ok()?; + let span = token.text_range(); + let member_name = token.text_trimmed().to_string(); + let suggested_name = match member_name.as_ref() { + "trimLeft" => Some("trimStart"), + "trimRight" => Some("trimEnd"), + _ => return None, + }; + (member_name, span, suggested_name) + } + _ => return None, + }; Some(UseTrimStartEndState { - member_name: token.token_text_trimmed(), - span: token.text_range(), - replaced_member_name: suggested_name, + member_name, + span, + replaced_member_name: suggested_name?, }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { - let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name.clone(); - let diagnostic_message = markup! { - "Use "{replaced_member_name}" instead of "{member_name}"." + "Use "{state.replaced_member_name}" instead of "{state.member_name}"." } .to_owned(); let note_message = { markup! { - ""{member_name}"() is an alias for "{replaced_member_name}"." + ""{state.member_name}"() is an alias for "{state.replaced_member_name}"." } .to_owned() }; @@ -115,10 +133,18 @@ impl Rule for UseTrimStartEnd { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); let callee = node.callee().ok()?; - let token = generate_syntax_token(callee)?; + let token = match callee { + AnyJsExpression::JsComputedMemberExpression(computed_expression) => computed_expression + .member() + .ok()? + .get_callee_object_name()?, + AnyJsExpression::JsStaticMemberExpression(static_expression) => { + static_expression.member().ok()?.value_token().ok()? + } + _ => return None, + }; - let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name.clone(); + let replaced_member_name = state.replaced_member_name; let replaced_function = make::js_name(make::ident(&replaced_member_name)); let mut mutation = ctx.root().begin(); mutation.replace_element(NodeOrToken::Token(token), replaced_function.into()); @@ -126,56 +152,9 @@ impl Rule for UseTrimStartEnd { Some(JsRuleAction::new( ActionCategory::QuickFix, ctx.metadata().applicability(), - markup! { "Replace "{member_name}" with "{replaced_member_name}"." } + markup! { "Replace "{state.member_name}" with "{replaced_member_name}"." } .to_owned(), mutation, )) } } - -fn generate_syntax_token(callee: AnyJsExpression) -> Option> { - let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { - let member = expression.member().ok()?; - match member { - AnyJsExpression::AnyJsLiteralExpression(literal) => literal.value_token().ok(), - AnyJsExpression::JsTemplateExpression(element) => { - element.elements().into_iter().find_map(|x| { - x.as_js_template_chunk_element() - .and_then(|chunk| chunk.template_chunk_token().ok()) - }) - } - _ => None, - } - } else if let AnyJsExpression::JsStaticMemberExpression(expression) = callee { - expression.member().ok()?.value_token().ok() - } else { - None - }; - token -} - -// Handle "'text'" and "\"text\"" and "text" cases -fn suggested_name(text: SyntaxToken) -> String { - let trimmed = text.text_trimmed(); - let first_char = trimmed.chars().next(); - let last_char = trimmed.chars().last(); - - let is_single_quoted = first_char == Some('\'') && last_char == Some('\''); - let is_double_quoted = first_char == Some('"') && last_char == Some('"'); - - let unquoted = trimmed.trim_matches(|c| c == '\'' || c == '"'); - - let cleaned = match unquoted { - "trimLeft" => "trimStart", - "trimRight" => "trimEnd", - _ => unquoted, - }; - - if is_single_quoted { - format!("'{}'", cleaned) - } else if is_double_quoted { - format!("\"{}\"", cleaned) - } else { - cleaned.to_string() - } -} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index e102fd9e100..12595e6bb30 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -197,79 +197,3 @@ invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` - -``` -invalid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use 'trimStart' instead of 'trimLeft'. - - 10 │ () - 11 │ foo?.trimLeft() - > 12 │ bar['trimLeft']() - │ ^^^^^^^^^^ - 13 │ bar["trimLeft"]() - 14 │ bar[`trimLeft`]() - - i 'trimLeft'() is an alias for 'trimStart'. - - i Safe fix: Replace 'trimLeft' with 'trimStart'. - - 10 10 │ () - 11 11 │ foo?.trimLeft() - 12 │ - bar['trimLeft']() - 12 │ + bar['trimStart']() - 13 13 │ bar["trimLeft"]() - 14 14 │ bar[`trimLeft`]() - - -``` - -``` -invalid.js:13:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use "trimStart" instead of "trimLeft". - - 11 │ foo?.trimLeft() - 12 │ bar['trimLeft']() - > 13 │ bar["trimLeft"]() - │ ^^^^^^^^^^ - 14 │ bar[`trimLeft`]() - 15 │ - - i "trimLeft"() is an alias for "trimStart". - - i Safe fix: Replace "trimLeft" with "trimStart". - - 11 11 │ foo?.trimLeft() - 12 12 │ bar['trimLeft']() - 13 │ - bar["trimLeft"]() - 13 │ + bar["trimStart"]() - 14 14 │ bar[`trimLeft`]() - 15 15 │ - - -``` - -``` -invalid.js:14:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimLeft. - - 12 │ bar['trimLeft']() - 13 │ bar["trimLeft"]() - > 14 │ bar[`trimLeft`]() - │ ^^^^^^^^ - 15 │ - - i trimLeft() is an alias for trimStart. - - i Safe fix: Replace trimLeft with trimStart. - - 12 12 │ bar['trimLeft']() - 13 13 │ bar["trimLeft"]() - 14 │ - bar[`trimLeft`]() - 14 │ + bar[`trimStart`]() - 15 15 │ - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index 0e5dd9693ae..a6aa3754dff 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -34,267 +34,4 @@ bar["trimStart"]() bar[`trimStart`]() -``` - -# Diagnostics -``` -valid.js:1:17 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimStart. - - > 1 │ const foo = bar.trimStart() - │ ^^^^^^^^^ - 2 │ const foo = bar.trimEnd() - 3 │ bar.trimStart?.() - - i trimStart() is an alias for trimStart. - - i Safe fix: Replace trimStart with trimStart. - - - -``` - -``` -valid.js:2:17 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimEnd instead of trimEnd. - - 1 │ const foo = bar.trimStart() - > 2 │ const foo = bar.trimEnd() - │ ^^^^^^^ - 3 │ bar.trimStart?.() - 4 │ foo.trimStart() - - i trimEnd() is an alias for trimEnd. - - i Safe fix: Replace trimEnd with trimEnd. - - - -``` - -``` -valid.js:3:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimStart. - - 1 │ const foo = bar.trimStart() - 2 │ const foo = bar.trimEnd() - > 3 │ bar.trimStart?.() - │ ^^^^^^^^^ - 4 │ foo.trimStart() - 5 │ foo.trimStart?.() - - i trimStart() is an alias for trimStart. - - i Safe fix: Replace trimStart with trimStart. - - - -``` - -``` -valid.js:4:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimStart. - - 2 │ const foo = bar.trimEnd() - 3 │ bar.trimStart?.() - > 4 │ foo.trimStart() - │ ^^^^^^^^^ - 5 │ foo.trimStart?.() - 6 │ foo.trimEnd() - - i trimStart() is an alias for trimStart. - - i Safe fix: Replace trimStart with trimStart. - - - -``` - -``` -valid.js:5:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimStart. - - 3 │ bar.trimStart?.() - 4 │ foo.trimStart() - > 5 │ foo.trimStart?.() - │ ^^^^^^^^^ - 6 │ foo.trimEnd() - 7 │ // Not `CallExpression` - - i trimStart() is an alias for trimStart. - - i Safe fix: Replace trimStart with trimStart. - - - -``` - -``` -valid.js:6:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimEnd instead of trimEnd. - - 4 │ foo.trimStart() - 5 │ foo.trimStart?.() - > 6 │ foo.trimEnd() - │ ^^^^^^^ - 7 │ // Not `CallExpression` - 8 │ new foo.trimLeft() - - i trimEnd() is an alias for trimEnd. - - i Safe fix: Replace trimEnd with trimEnd. - - - -``` - -``` -valid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use 'trimStart' instead of 'trimLeft'. - - 10 │ trimLeft() - 11 │ // `callee.property` is not a `Identifier` - > 12 │ foo['trimLeft']() - │ ^^^^^^^^^^ - 13 │ // Computed - 14 │ foo[trimLeft]() - - i 'trimLeft'() is an alias for 'trimStart'. - - i Safe fix: Replace 'trimLeft' with 'trimStart'. - - 10 10 │ trimLeft() - 11 11 │ // `callee.property` is not a `Identifier` - 12 │ - foo['trimLeft']() - 12 │ + foo['trimStart']() - 13 13 │ // Computed - 14 14 │ foo[trimLeft]() - - -``` - -``` -valid.js:16:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use bar instead of bar. - - 14 │ foo[trimLeft]() - 15 │ // Not `trimLeft`/`trimRight` - > 16 │ foo.bar() - │ ^^^ - 17 │ // More argument(s) - 18 │ foo.trimLeft(extra) - - i bar() is an alias for bar. - - i Safe fix: Replace bar with bar. - - - -``` - -``` -valid.js:24:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use foo instead of foo. - - 22 │ foo.bar(foo.trimLeft) - 23 │ // `trimLeft` is in `MemberExpression.object` - > 24 │ trimLeft.foo() - │ ^^^ - 25 │ foo.trimLeft.bar() - 26 │ bar['trimStart']() - - i foo() is an alias for foo. - - i Safe fix: Replace foo with foo. - - - -``` - -``` -valid.js:25:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use bar instead of bar. - - 23 │ // `trimLeft` is in `MemberExpression.object` - 24 │ trimLeft.foo() - > 25 │ foo.trimLeft.bar() - │ ^^^ - 26 │ bar['trimStart']() - 27 │ bar["trimStart"]() - - i bar() is an alias for bar. - - i Safe fix: Replace bar with bar. - - - -``` - -``` -valid.js:26:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use 'trimStart' instead of 'trimStart'. - - 24 │ trimLeft.foo() - 25 │ foo.trimLeft.bar() - > 26 │ bar['trimStart']() - │ ^^^^^^^^^^^ - 27 │ bar["trimStart"]() - 28 │ bar[`trimStart`]() - - i 'trimStart'() is an alias for 'trimStart'. - - i Safe fix: Replace 'trimStart' with 'trimStart'. - - - -``` - -``` -valid.js:27:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use "trimStart" instead of "trimStart". - - 25 │ foo.trimLeft.bar() - 26 │ bar['trimStart']() - > 27 │ bar["trimStart"]() - │ ^^^^^^^^^^^ - 28 │ bar[`trimStart`]() - 29 │ - - i "trimStart"() is an alias for "trimStart". - - i Safe fix: Replace "trimStart" with "trimStart". - - - -``` - -``` -valid.js:28:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimStart instead of trimStart. - - 26 │ bar['trimStart']() - 27 │ bar["trimStart"]() - > 28 │ bar[`trimStart`]() - │ ^^^^^^^^^ - 29 │ - - i trimStart() is an alias for trimStart. - - i Safe fix: Replace trimStart with trimStart. - - - ``` From 7d779a3200da57c854603684197a4ddae050c5d9 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 16:08:27 +0900 Subject: [PATCH 14/38] feat: implement action for computed member expression case --- .../src/lint/nursery/use_trim_start_end.rs | 159 ++++++++++++++++-- .../specs/nursery/useTrimStartEnd/invalid.js | 1 - .../nursery/useTrimStartEnd/invalid.js.snap | 71 ++++++-- .../specs/nursery/useTrimStartEnd/valid.js | 2 - .../nursery/useTrimStartEnd/valid.js.snap | 2 - 5 files changed, 201 insertions(+), 34 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 443601a704b..0e11c03fd8b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -3,9 +3,12 @@ use biome_analyze::{ RuleSource, }; use biome_console::markup; -use biome_js_factory::make; -use biome_js_syntax::{AnyJsExpression, JsCallExpression}; -use biome_rowan::{AstSeparatedList, BatchMutationExt, NodeOrToken, TextRange}; +use biome_js_factory::make::{self}; +use biome_js_syntax::{ + AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsTemplateElement, JsCallExpression, + JsLanguage, JsSyntaxKind, JsSyntaxToken, T, +}; +use biome_rowan::{AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; use crate::JsRuleAction; @@ -56,7 +59,7 @@ declare_rule! { pub struct UseTrimStartEndState { member_name: String, span: TextRange, - replaced_member_name: &'static str, + suggested_name: &'static str, } impl Rule for UseTrimStartEnd { @@ -108,18 +111,18 @@ impl Rule for UseTrimStartEnd { Some(UseTrimStartEndState { member_name, span, - replaced_member_name: suggested_name?, + suggested_name: suggested_name?, }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { let diagnostic_message = markup! { - "Use "{state.replaced_member_name}" instead of "{state.member_name}"." + "Use "{state.suggested_name}" instead of "{state.member_name}"." } .to_owned(); let note_message = { markup! { - ""{state.member_name}"() is an alias for "{state.replaced_member_name}"." + ""{state.member_name}"() is an alias for "{state.suggested_name}"." } .to_owned() }; @@ -133,21 +136,91 @@ impl Rule for UseTrimStartEnd { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); let callee = node.callee().ok()?; - let token = match callee { - AnyJsExpression::JsComputedMemberExpression(computed_expression) => computed_expression - .member() - .ok()? - .get_callee_object_name()?, - AnyJsExpression::JsStaticMemberExpression(static_expression) => { - static_expression.member().ok()?.value_token().ok()? + + let is_computed_member = callee.as_js_computed_member_expression().is_some(); + let is_template = if is_computed_member { + if let Ok(computed_member) = callee.as_js_computed_member_expression()?.member() { + computed_member.as_js_template_expression().is_some() + } else { + false } - _ => return None, + } else { + false + }; + + let token = generate_syntax_token(callee.clone())?; + let replaced_member_name = suggested_name(&token); + + let mut elements = vec![]; + let template_elements = AnyJsTemplateElement::from(make::js_template_chunk_element( + make::js_template_chunk(&replaced_member_name), + )); + elements.push(template_elements); + + let callee_object = match callee { + AnyJsExpression::JsStaticMemberExpression(ref expression) => { + expression.object().ok()? + } + AnyJsExpression::JsComputedMemberExpression(ref expression) => { + expression.object().ok()? + } + _ => unreachable!(), + }; + + let computed_member_expression = if is_template { + AnyJsExpression::JsTemplateExpression( + make::js_template_expression( + make::token(T!['`']), + make::js_template_element_list(elements), + make::token(T!['`']), + ) + .build(), + ) + } else { + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(JsSyntaxToken::new_detached( + // Need to handle "'text'" and "\"text\"". + // make::js_string_literal() call the function that format the text as below: + // > &format!("\"{text}\"") + JsSyntaxKind::JS_STRING_LITERAL, + &replaced_member_name, + [], + [], + )), + ), + ) + }; + + let call_expression = if is_computed_member { + AnyJsExpression::JsComputedMemberExpression( + make::js_computed_member_expression( + callee_object, + callee + .as_js_computed_member_expression()? + .l_brack_token() + .ok()?, + computed_member_expression, + callee + .as_js_computed_member_expression()? + .r_brack_token() + .ok()?, + ) + .build(), + ) + } else { + AnyJsExpression::JsStaticMemberExpression(make::js_static_member_expression( + callee_object, + callee + .as_js_static_member_expression()? + .operator_token() + .ok()?, + AnyJsName::JsName(make::js_name(make::ident(&replaced_member_name))), + )) }; - let replaced_member_name = state.replaced_member_name; - let replaced_function = make::js_name(make::ident(&replaced_member_name)); let mut mutation = ctx.root().begin(); - mutation.replace_element(NodeOrToken::Token(token), replaced_function.into()); + mutation.replace_node(callee, call_expression); Some(JsRuleAction::new( ActionCategory::QuickFix, @@ -158,3 +231,53 @@ impl Rule for UseTrimStartEnd { )) } } + +fn generate_syntax_token(callee: AnyJsExpression) -> Option> { + let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { + let member = expression.member().ok()?; + match member { + AnyJsExpression::AnyJsLiteralExpression(literal) => literal.value_token().ok(), + AnyJsExpression::JsTemplateExpression(element) => { + element.elements().into_iter().find_map(|x| { + x.as_js_template_chunk_element() + .and_then(|chunk| chunk.template_chunk_token().ok()) + }) + } + _ => None, + } + } else if let AnyJsExpression::JsStaticMemberExpression(expression) = callee { + expression.member().ok()?.value_token().ok() + } else { + None + }; + token +} + +// Handle "'text'" and "\"text\"" and "text" cases +fn suggested_name(text: &SyntaxToken) -> String { + let trimmed = text.text_trimmed(); + let first_char = trimmed.chars().next(); + let last_char = trimmed.chars().last(); + + let is_single_quoted = first_char == Some('\'') && last_char == Some('\''); + let is_double_quoted = first_char == Some('"') && last_char == Some('"'); + + let unquoted = if first_char.is_some() && last_char.is_some() { + trimmed.trim_matches(|c| c == '\'' || c == '"') + } else { + trimmed + }; + let cleaned = match unquoted { + "trimLeft" => "trimStart", + "trimRight" => "trimEnd", + _ => unquoted, + }; + + if is_single_quoted { + format!("'{}'", cleaned) + } else if is_double_quoted { + format!("\"{}\"", cleaned) + } else { + cleaned.to_string() + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js index 5bed3cd9475..d4edefaa8e6 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -8,7 +8,6 @@ foo .trimRight /* comment */ /* comment */ () -foo?.trimLeft() bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 12595e6bb30..7d71e8682cc 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -14,7 +14,6 @@ foo .trimRight /* comment */ /* comment */ () -foo?.trimLeft() bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() @@ -173,27 +172,77 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` ``` -invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:11:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use trimStart instead of trimLeft. 9 │ /* comment */ 10 │ () - > 11 │ foo?.trimLeft() - │ ^^^^^^^^ - 12 │ bar['trimLeft']() - 13 │ bar["trimLeft"]() + > 11 │ bar['trimLeft']() + │ ^^^^^^^^^^ + 12 │ bar["trimLeft"]() + 13 │ bar[`trimLeft`]() i trimLeft() is an alias for trimStart. - i Safe fix: Replace trimLeft with trimStart. + i Safe fix: Replace trimLeft with 'trimStart'. 9 9 │ /* comment */ 10 10 │ () - 11 │ - foo?.trimLeft() - 11 │ + foo?.trimStart() - 12 12 │ bar['trimLeft']() - 13 13 │ bar["trimLeft"]() + 11 │ - bar['trimLeft']() + 11 │ + bar['trimStart']() + 12 12 │ bar["trimLeft"]() + 13 13 │ bar[`trimLeft`]() + + +``` + +``` +invalid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimLeft. + + 10 │ () + 11 │ bar['trimLeft']() + > 12 │ bar["trimLeft"]() + │ ^^^^^^^^^^ + 13 │ bar[`trimLeft`]() + 14 │ + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with "trimStart". + + 10 10 │ () + 11 11 │ bar['trimLeft']() + 12 │ - bar["trimLeft"]() + 12 │ + bar["trimStart"]() + 13 13 │ bar[`trimLeft`]() + 14 14 │ + + +``` + +``` +invalid.js:13:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimStart instead of trimLeft. + + 11 │ bar['trimLeft']() + 12 │ bar["trimLeft"]() + > 13 │ bar[`trimLeft`]() + │ ^^^^^^^^ + 14 │ + + i trimLeft() is an alias for trimStart. + + i Safe fix: Replace trimLeft with trimStart. + + 11 11 │ bar['trimLeft']() + 12 12 │ bar["trimLeft"]() + 13 │ - bar[`trimLeft`]() + 13 │ + bar[`trimStart`]() + 14 14 │ ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index 84d5c03c5f0..7717392b5d4 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -8,8 +8,6 @@ foo.trimEnd() new foo.trimLeft() // Not `MemberExpression` trimLeft() -// `callee.property` is not a `Identifier` -foo['trimLeft']() // Computed foo[trimLeft]() // Not `trimLeft`/`trimRight` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index a6aa3754dff..b67a501ad30 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -14,8 +14,6 @@ foo.trimEnd() new foo.trimLeft() // Not `MemberExpression` trimLeft() -// `callee.property` is not a `Identifier` -foo['trimLeft']() // Computed foo[trimLeft]() // Not `trimLeft`/`trimRight` From 05b2a4ff19d73ed4430a7a992a3ae0895ebd7dfa Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 8 Jun 2024 10:57:23 +0900 Subject: [PATCH 15/38] feat(biome_js_analyzer): `useTrimStartEnd` --- .../migrate/eslint_any_rule_to_biome.rs | 4 +- .../biome_configuration/src/linter/rules.rs | 54 +------ .../src/categories.rs | 1 - crates/biome_js_analyze/src/lint/nursery.rs | 2 - .../src/lint/nursery/use_trim_start_end.rs | 68 +++++++++ .../specs/nursery/useTrimStartEnd/invalid.js | 13 ++ .../nursery/useTrimStartEnd/invalid.js.snap | 132 ++++++++++++++++++ .../specs/nursery/useTrimStartEnd/valid.js | 29 ++++ .../nursery/useTrimStartEnd/valid.js.snap | 1 - .../@biomejs/backend-jsonrpc/src/workspace.ts | 3 +- .../@biomejs/biome/configuration_schema.json | 9 +- 11 files changed, 249 insertions(+), 67 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index c346f31c5b7..1242d18e252 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1446,12 +1446,12 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.use_number_namespace.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } - "unicorn/prefer-string-slice" => { + "unicorn/prefer-string-trim-start-end" => { if !options.include_nursery { return false; } let group = rules.nursery.get_or_insert_with(Default::default); - let rule = group.no_substr.get_or_insert(Default::default()); + let rule = group.use_trim_start_end.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } "unicorn/prefer-string-trim-start-end" => { diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 752f0cf1e34..428779ce0b4 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,12 +2972,9 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight()."] + #[doc = "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] pub use_trim_start_end: Option>, - #[doc = "Use valid values for the autocomplete attribute on input elements."] - #[serde(skip_serializing_if = "Option::is_none")] - pub use_valid_autocomplete: Option>, } impl DeserializableValidator for Nursery { fn validate( @@ -3048,7 +3045,6 @@ impl Nursery { "useThrowOnlyError", "useTopLevelRegex", "useTrimStartEnd", - "useValidAutocomplete", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ "noDoneCallback", @@ -3384,29 +3380,9 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); - } - } - if let Some(rule) = self.use_throw_only_error.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); - } - } - if let Some(rule) = self.use_top_level_regex.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); - } - } if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); - } - } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } index_set @@ -3628,29 +3604,9 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); - } - } - if let Some(rule) = self.use_throw_only_error.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); - } - } - if let Some(rule) = self.use_top_level_regex.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); - } - } if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); - } - } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } index_set @@ -3897,10 +3853,6 @@ impl Nursery { .use_trim_start_end .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useValidAutocomplete" => self - .use_valid_autocomplete - .as_ref() - .map(|conf| (conf.level(), conf.get_options())), _ => None, } } diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 0de83207de2..1726324443e 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -169,7 +169,6 @@ define_categories! { "lint/nursery/useThrowOnlyError": "https://biomejs.dev/linter/rules/use-throw-only-error", "lint/nursery/useTopLevelRegex": "https://biomejs.dev/linter/rules/use-top-level-regex", "lint/nursery/useTrimStartEnd": "https://biomejs.dev/linter/rules/use-trim-start-end", - "lint/nursery/useValidAutocomplete": "https://biomejs.dev/linter/rules/use-valid-autocomplete", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", "lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index eae84e70891..02eecf3fc08 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -34,7 +34,6 @@ pub mod use_throw_new_error; pub mod use_throw_only_error; pub mod use_top_level_regex; pub mod use_trim_start_end; -pub mod use_valid_autocomplete; declare_lint_group! { pub Nursery { @@ -72,7 +71,6 @@ declare_lint_group! { self :: use_throw_only_error :: UseThrowOnlyError , self :: use_top_level_regex :: UseTopLevelRegex , self :: use_trim_start_end :: UseTrimStartEnd , - self :: use_valid_autocomplete :: UseValidAutocomplete , ] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 0e11c03fd8b..1c24d7a09cf 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -3,16 +3,23 @@ use biome_analyze::{ RuleSource, }; use biome_console::markup; +<<<<<<< HEAD use biome_js_factory::make::{self}; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsTemplateElement, JsCallExpression, JsLanguage, JsSyntaxKind, JsSyntaxToken, T, }; use biome_rowan::{AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; +======= +use biome_js_factory::make; +use biome_js_syntax::JsCallExpression; +use biome_rowan::{BatchMutationExt, TextRange, TokenText}; +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) use crate::JsRuleAction; declare_rule! { +<<<<<<< HEAD /// Enforce the use of `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`. /// /// While `String.trimLeft()` and `String.trimRight()` are aliases for `String.trimStart()` and `String.trimEnd()`, @@ -22,6 +29,12 @@ declare_rule! { /// See the MDN documentation for more details: /// - [String.prototype.trimStart()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart) /// - [String.prototype.trimEnd()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) +======= + /// Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). + /// + /// While `trimLeft()` and `trimRight()` are aliases for `trimStart()` and `trimEnd()`, + /// using the latter helps maintain consistency and uses direction-independent wording. +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) /// /// ## Examples /// @@ -57,9 +70,15 @@ declare_rule! { #[derive(Debug, Clone)] pub struct UseTrimStartEndState { +<<<<<<< HEAD member_name: String, span: TextRange, suggested_name: &'static str, +======= + member_name: TokenText, + span: TextRange, + replaced_member_name: &'static str, +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) } impl Rule for UseTrimStartEnd { @@ -70,6 +89,7 @@ impl Rule for UseTrimStartEnd { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); +<<<<<<< HEAD let arguments = node.arguments().ok()?; let args = arguments.args(); @@ -112,21 +132,51 @@ impl Rule for UseTrimStartEnd { member_name, span, suggested_name: suggested_name?, +======= + let callee = node.callee().ok()?; + let expression = callee.as_js_static_member_expression()?; + let value_token = expression.member().ok()?.value_token().ok()?; + let name = value_token.text_trimmed(); + let suggested_name = match name { + "trimLeft" => Some("trimStart"), + "trimRight" => Some("trimEnd"), + _ => None, + }?; + + Some(UseTrimStartEndState { + member_name: value_token.token_text_trimmed(), + span: value_token.text_range(), + replaced_member_name: suggested_name, +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { +<<<<<<< HEAD let diagnostic_message = markup! { "Use "{state.suggested_name}" instead of "{state.member_name}"." +======= + let member_name = state.member_name.text(); + let replaced_member_name = state.replaced_member_name; + let diagnostic_message = markup! { + "Use "{member_name}" instead of "{replaced_member_name}"." +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) } .to_owned(); let note_message = { markup! { +<<<<<<< HEAD ""{state.member_name}"() is an alias for "{state.suggested_name}"." } .to_owned() }; +======= + ""{member_name}"() is an alias for "{replaced_member_name}"." + } + .to_owned() + }; +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) Some( RuleDiagnostic::new(rule_category!(), state.span, diagnostic_message) .note(note_message), @@ -136,6 +186,7 @@ impl Rule for UseTrimStartEnd { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); let callee = node.callee().ok()?; +<<<<<<< HEAD let is_computed_member = callee.as_js_computed_member_expression().is_some(); let is_template = if is_computed_member { @@ -221,16 +272,31 @@ impl Rule for UseTrimStartEnd { let mut mutation = ctx.root().begin(); mutation.replace_node(callee, call_expression); +======= + let expression = callee.as_js_static_member_expression()?; + let member = expression.member().ok()?; + let member_name = state.member_name.text(); + let replaced_member_name = state.replaced_member_name; + + let mut mutation = ctx.root().begin(); + let replaced_function = make::js_name(make::ident(replaced_member_name)); + mutation.replace_element(member.into(), replaced_function.into()); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) Some(JsRuleAction::new( ActionCategory::QuickFix, ctx.metadata().applicability(), +<<<<<<< HEAD markup! { "Replace "{state.member_name}" with "{replaced_member_name}"." } +======= + markup! { "Replace "{member_name}" with "{replaced_member_name}"." } +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) .to_owned(), mutation, )) } } +<<<<<<< HEAD fn generate_syntax_token(callee: AnyJsExpression) -> Option> { let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { @@ -281,3 +347,5 @@ fn suggested_name(text: &SyntaxToken) -> String { cleaned.to_string() } } +======= +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js index d4edefaa8e6..869157d9830 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -1,13 +1,26 @@ +<<<<<<< HEAD foo.trimLeft() foo.trimRight() trimLeft.trimRight() foo.trimLeft.trimRight() "foo".trimLeft() +======= +foo.trimLeft(); +foo.trimRight(); +trimLeft.trimRight(); +foo.trimLeft.trimRight(); +"foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) foo // comment .trimRight /* comment */ /* comment */ +<<<<<<< HEAD () bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() +======= + (); +foo?.trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 7d71e8682cc..8648407add3 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -4,19 +4,32 @@ expression: invalid.js --- # Input ```jsx +<<<<<<< HEAD foo.trimLeft() foo.trimRight() trimLeft.trimRight() foo.trimLeft.trimRight() "foo".trimLeft() +======= +foo.trimLeft(); +foo.trimRight(); +trimLeft.trimRight(); +foo.trimLeft.trimRight(); +"foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) foo // comment .trimRight /* comment */ /* comment */ +<<<<<<< HEAD () bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() +======= + (); +foo?.trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -24,21 +37,37 @@ bar[`trimLeft`]() ``` invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimStart instead of trimLeft. > 1 │ foo.trimLeft() │ ^^^^^^^^ 2 │ foo.trimRight() 3 │ trimLeft.trimRight() +======= + ! Use trimLeft instead of trimStart. + + > 1 │ foo.trimLeft(); + │ ^^^^^^^^ + 2 │ foo.trimRight(); + 3 │ trimLeft.trimRight(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimLeft() is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. +<<<<<<< HEAD 1 │ - foo.trimLeft() 1 │ + foo.trimStart() 2 2 │ foo.trimRight() 3 3 │ trimLeft.trimRight() +======= + 1 │ - foo.trimLeft(); + 1 │ + foo.trimStart(); + 2 2 │ foo.trimRight(); + 3 3 │ trimLeft.trimRight(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -46,6 +75,7 @@ invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() @@ -53,16 +83,33 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ │ ^^^^^^^^^ 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() +======= + ! Use trimRight instead of trimEnd. + + 1 │ foo.trimLeft(); + > 2 │ foo.trimRight(); + │ ^^^^^^^^^ + 3 │ trimLeft.trimRight(); + 4 │ foo.trimLeft.trimRight(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. +<<<<<<< HEAD 1 1 │ foo.trimLeft() 2 │ - foo.trimRight() 2 │ + foo.trimEnd() 3 3 │ trimLeft.trimRight() 4 4 │ foo.trimLeft.trimRight() +======= + 1 1 │ foo.trimLeft(); + 2 │ - foo.trimRight(); + 2 │ + foo.trimEnd(); + 3 3 │ trimLeft.trimRight(); + 4 4 │ foo.trimLeft.trimRight(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -70,6 +117,7 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() @@ -78,17 +126,36 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ │ ^^^^^^^^^ 4 │ foo.trimLeft.trimRight() 5 │ "foo".trimLeft() +======= + ! Use trimRight instead of trimEnd. + + 1 │ foo.trimLeft(); + 2 │ foo.trimRight(); + > 3 │ trimLeft.trimRight(); + │ ^^^^^^^^^ + 4 │ foo.trimLeft.trimRight(); + 5 │ "foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. +<<<<<<< HEAD 1 1 │ foo.trimLeft() 2 2 │ foo.trimRight() 3 │ - trimLeft.trimRight() 3 │ + trimLeft.trimEnd() 4 4 │ foo.trimLeft.trimRight() 5 5 │ "foo".trimLeft() +======= + 1 1 │ foo.trimLeft(); + 2 2 │ foo.trimRight(); + 3 │ - trimLeft.trimRight(); + 3 │ + trimLeft.trimEnd(); + 4 4 │ foo.trimLeft.trimRight(); + 5 5 │ "foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -96,6 +163,7 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimEnd instead of trimRight. 2 │ foo.trimRight() @@ -103,17 +171,34 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ > 4 │ foo.trimLeft.trimRight() │ ^^^^^^^^^ 5 │ "foo".trimLeft() +======= + ! Use trimRight instead of trimEnd. + + 2 │ foo.trimRight(); + 3 │ trimLeft.trimRight(); + > 4 │ foo.trimLeft.trimRight(); + │ ^^^^^^^^^ + 5 │ "foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 │ foo i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. +<<<<<<< HEAD 2 2 │ foo.trimRight() 3 3 │ trimLeft.trimRight() 4 │ - foo.trimLeft.trimRight() 4 │ + foo.trimLeft.trimEnd() 5 5 │ "foo".trimLeft() +======= + 2 2 │ foo.trimRight(); + 3 3 │ trimLeft.trimRight(); + 4 │ - foo.trimLeft.trimRight(); + 4 │ + foo.trimLeft.trimEnd(); + 5 5 │ "foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 6 │ foo @@ -122,11 +207,19 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimStart instead of trimLeft. 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() > 5 │ "foo".trimLeft() +======= + ! Use trimLeft instead of trimStart. + + 3 │ trimLeft.trimRight(); + 4 │ foo.trimLeft.trimRight(); + > 5 │ "foo".trimLeft(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) │ ^^^^^^^^ 6 │ foo 7 │ // comment @@ -135,10 +228,17 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ i Safe fix: Replace trimLeft with trimStart. +<<<<<<< HEAD 3 3 │ trimLeft.trimRight() 4 4 │ foo.trimLeft.trimRight() 5 │ - "foo".trimLeft() 5 │ + "foo".trimStart() +======= + 3 3 │ trimLeft.trimRight(); + 4 4 │ foo.trimLeft.trimRight(); + 5 │ - "foo".trimLeft(); + 5 │ + "foo".trimStart(); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 6 │ foo 7 7 │ // comment @@ -148,14 +248,22 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +<<<<<<< HEAD ! Use trimEnd instead of trimRight. +======= + ! Use trimRight instead of trimEnd. +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 │ foo 7 │ // comment > 8 │ .trimRight /* comment */ │ ^^^^^^^^^^^^^^^^^^^^^^^ 9 │ /* comment */ +<<<<<<< HEAD 10 │ () +======= + 10 │ (); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. @@ -166,12 +274,17 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 8 │ - → .trimRight·/*·comment·*/ 8 │ + → .trimEnd·/*·comment·*/ 9 9 │ /* comment */ +<<<<<<< HEAD 10 10 │ () +======= + 10 10 │ (); +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` ``` +<<<<<<< HEAD invalid.js:11:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use trimStart instead of trimLeft. @@ -233,16 +346,35 @@ invalid.js:13:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ > 13 │ bar[`trimLeft`]() │ ^^^^^^^^ 14 │ +======= +invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use trimLeft instead of trimStart. + + 9 │ /* comment */ + 10 │ (); + > 11 │ foo?.trimLeft(); + │ ^^^^^^^^ + 12 │ +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimLeft() is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. +<<<<<<< HEAD 11 11 │ bar['trimLeft']() 12 12 │ bar["trimLeft"]() 13 │ - bar[`trimLeft`]() 13 │ + bar[`trimStart`]() 14 14 │ +======= + 9 9 │ /* comment */ + 10 10 │ (); + 11 │ - foo?.trimLeft(); + 11 │ + foo?.trimStart(); + 12 12 │ +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index 7717392b5d4..b8f5f50832c 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -1,3 +1,4 @@ +<<<<<<< HEAD const foo = bar.trimStart() const foo = bar.trimEnd() bar.trimStart?.() @@ -25,3 +26,31 @@ bar['trimStart']() bar["trimStart"]() bar[`trimStart`]() +======= +const foo = bar.trimStart(); +const foo = bar.trimEnd(); +bar.trimStart?.() +bar['trimStart']() +foo.trimStart(), +foo.trimStart?.(), +foo.trimEnd(), +// Not `CallExpression` +new foo.trimLeft();, +// Not `MemberExpression` +trimLeft();, +// `callee.property` is not a `Identifier` +foo[\'trimLeft\']();', +// Computed +foo[trimLeft]();, +// Not `trimLeft`/`trimRight` +foo.bar();, +// More argument(s) +foo.trimLeft(extra);, +foo.trimLeft(...argumentsArray), +// `trimLeft` is in argument +foo.bar(trimLeft), +foo.bar(foo.trimLeft), +// `trimLeft` is in `MemberExpression.object` +trimLeft.foo(), +foo.trimLeft.bar(), +>>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index b67a501ad30..cc163b68c80 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -31,5 +31,4 @@ bar['trimStart']() bar["trimStart"]() bar[`trimStart`]() - ``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8839ae63ed1..165a6fb4575 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1264,7 +1264,7 @@ export interface Nursery { /** * Use valid values for the autocomplete attribute on input elements. */ - useValidAutocomplete?: RuleConfiguration_for_UseValidAutocompleteOptions; + useTrimStartEnd?: RuleFixConfiguration_for_Null; } /** * A list of rules that belong to this group @@ -2551,7 +2551,6 @@ export type Category = | "lint/nursery/useThrowOnlyError" | "lint/nursery/useTopLevelRegex" | "lint/nursery/useTrimStartEnd" - | "lint/nursery/useValidAutocomplete" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" | "lint/performance/noDelete" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 952406d91a2..6918994b745 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2120,18 +2120,11 @@ ] }, "useTrimStartEnd": { - "description": "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight().", + "description": "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight().", "anyOf": [ { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } ] - }, - "useValidAutocomplete": { - "description": "Use valid values for the autocomplete attribute on input elements.", - "anyOf": [ - { "$ref": "#/definitions/UseValidAutocompleteConfiguration" }, - { "type": "null" } - ] } }, "additionalProperties": false From 30681116f5f55f0de624a328d6d1d8f602045b33 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 16:44:57 +0900 Subject: [PATCH 16/38] feat: implement the action logic for computed expression --- .../migrate/eslint_any_rule_to_biome.rs | 12 +- .../biome_configuration/src/linter/rules.rs | 66 ++++++++- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 2 + .../src/lint/nursery/use_trim_start_end.rs | 68 --------- .../specs/nursery/useTrimStartEnd/invalid.js | 13 -- .../nursery/useTrimStartEnd/invalid.js.snap | 132 ------------------ .../specs/nursery/useTrimStartEnd/valid.js | 34 +---- .../nursery/useTrimStartEnd/valid.js.snap | 32 +++-- .../@biomejs/backend-jsonrpc/src/workspace.ts | 7 +- 10 files changed, 91 insertions(+), 276 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index 1242d18e252..fb664749979 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1446,20 +1446,12 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.use_number_namespace.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } - "unicorn/prefer-string-trim-start-end" => { + "unicorn/prefer-string-slice" => { if !options.include_nursery { return false; } let group = rules.nursery.get_or_insert_with(Default::default); - let rule = group.use_trim_start_end.get_or_insert(Default::default()); - rule.set_level(rule_severity.into()); - } - "unicorn/prefer-string-trim-start-end" => { - if !options.include_nursery { - return false; - } - let group = rules.nursery.get_or_insert_with(Default::default); - let rule = group.use_trim_start_end.get_or_insert(Default::default()); + let rule = group.no_substr.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } "unicorn/require-number-to-fixed-digits-argument" => { diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 428779ce0b4..17c6312c8d6 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,9 +2972,12 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight()."] + #[doc = "Succinct description of the rule."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_trim_start_end: Option>, + pub use_trim_start_end: Option>, + #[doc = "Use valid values for the autocomplete attribute on input elements."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_valid_autocomplete: Option>, } impl DeserializableValidator for Nursery { fn validate( @@ -3045,6 +3048,7 @@ impl Nursery { "useThrowOnlyError", "useTopLevelRegex", "useTrimStartEnd", + "useValidAutocomplete", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ "noDoneCallback", @@ -3380,11 +3384,36 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } + if let Some(rule) = self.use_throw_new_error.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + } + } + if let Some(rule) = self.use_throw_only_error.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + } + } + if let Some(rule) = self.use_top_level_regex.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + } + } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3604,11 +3633,36 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } + if let Some(rule) = self.use_throw_new_error.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + } + } + if let Some(rule) = self.use_throw_only_error.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + } + } + if let Some(rule) = self.use_top_level_regex.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + } + } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3853,6 +3907,10 @@ impl Nursery { .use_trim_start_end .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useValidAutocomplete" => self + .use_valid_autocomplete + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), _ => None, } } diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 1726324443e..0de83207de2 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -169,6 +169,7 @@ define_categories! { "lint/nursery/useThrowOnlyError": "https://biomejs.dev/linter/rules/use-throw-only-error", "lint/nursery/useTopLevelRegex": "https://biomejs.dev/linter/rules/use-top-level-regex", "lint/nursery/useTrimStartEnd": "https://biomejs.dev/linter/rules/use-trim-start-end", + "lint/nursery/useValidAutocomplete": "https://biomejs.dev/linter/rules/use-valid-autocomplete", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", "lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 02eecf3fc08..eae84e70891 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -34,6 +34,7 @@ pub mod use_throw_new_error; pub mod use_throw_only_error; pub mod use_top_level_regex; pub mod use_trim_start_end; +pub mod use_valid_autocomplete; declare_lint_group! { pub Nursery { @@ -71,6 +72,7 @@ declare_lint_group! { self :: use_throw_only_error :: UseThrowOnlyError , self :: use_top_level_regex :: UseTopLevelRegex , self :: use_trim_start_end :: UseTrimStartEnd , + self :: use_valid_autocomplete :: UseValidAutocomplete , ] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 1c24d7a09cf..0e11c03fd8b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -3,23 +3,16 @@ use biome_analyze::{ RuleSource, }; use biome_console::markup; -<<<<<<< HEAD use biome_js_factory::make::{self}; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsTemplateElement, JsCallExpression, JsLanguage, JsSyntaxKind, JsSyntaxToken, T, }; use biome_rowan::{AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; -======= -use biome_js_factory::make; -use biome_js_syntax::JsCallExpression; -use biome_rowan::{BatchMutationExt, TextRange, TokenText}; ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) use crate::JsRuleAction; declare_rule! { -<<<<<<< HEAD /// Enforce the use of `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`. /// /// While `String.trimLeft()` and `String.trimRight()` are aliases for `String.trimStart()` and `String.trimEnd()`, @@ -29,12 +22,6 @@ declare_rule! { /// See the MDN documentation for more details: /// - [String.prototype.trimStart()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart) /// - [String.prototype.trimEnd()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) -======= - /// Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight(). - /// - /// While `trimLeft()` and `trimRight()` are aliases for `trimStart()` and `trimEnd()`, - /// using the latter helps maintain consistency and uses direction-independent wording. ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) /// /// ## Examples /// @@ -70,15 +57,9 @@ declare_rule! { #[derive(Debug, Clone)] pub struct UseTrimStartEndState { -<<<<<<< HEAD member_name: String, span: TextRange, suggested_name: &'static str, -======= - member_name: TokenText, - span: TextRange, - replaced_member_name: &'static str, ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) } impl Rule for UseTrimStartEnd { @@ -89,7 +70,6 @@ impl Rule for UseTrimStartEnd { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); -<<<<<<< HEAD let arguments = node.arguments().ok()?; let args = arguments.args(); @@ -132,51 +112,21 @@ impl Rule for UseTrimStartEnd { member_name, span, suggested_name: suggested_name?, -======= - let callee = node.callee().ok()?; - let expression = callee.as_js_static_member_expression()?; - let value_token = expression.member().ok()?.value_token().ok()?; - let name = value_token.text_trimmed(); - let suggested_name = match name { - "trimLeft" => Some("trimStart"), - "trimRight" => Some("trimEnd"), - _ => None, - }?; - - Some(UseTrimStartEndState { - member_name: value_token.token_text_trimmed(), - span: value_token.text_range(), - replaced_member_name: suggested_name, ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { -<<<<<<< HEAD let diagnostic_message = markup! { "Use "{state.suggested_name}" instead of "{state.member_name}"." -======= - let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name; - let diagnostic_message = markup! { - "Use "{member_name}" instead of "{replaced_member_name}"." ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) } .to_owned(); let note_message = { markup! { -<<<<<<< HEAD ""{state.member_name}"() is an alias for "{state.suggested_name}"." } .to_owned() }; -======= - ""{member_name}"() is an alias for "{replaced_member_name}"." - } - .to_owned() - }; ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) Some( RuleDiagnostic::new(rule_category!(), state.span, diagnostic_message) .note(note_message), @@ -186,7 +136,6 @@ impl Rule for UseTrimStartEnd { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); let callee = node.callee().ok()?; -<<<<<<< HEAD let is_computed_member = callee.as_js_computed_member_expression().is_some(); let is_template = if is_computed_member { @@ -272,31 +221,16 @@ impl Rule for UseTrimStartEnd { let mut mutation = ctx.root().begin(); mutation.replace_node(callee, call_expression); -======= - let expression = callee.as_js_static_member_expression()?; - let member = expression.member().ok()?; - let member_name = state.member_name.text(); - let replaced_member_name = state.replaced_member_name; - - let mut mutation = ctx.root().begin(); - let replaced_function = make::js_name(make::ident(replaced_member_name)); - mutation.replace_element(member.into(), replaced_function.into()); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) Some(JsRuleAction::new( ActionCategory::QuickFix, ctx.metadata().applicability(), -<<<<<<< HEAD markup! { "Replace "{state.member_name}" with "{replaced_member_name}"." } -======= - markup! { "Replace "{member_name}" with "{replaced_member_name}"." } ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) .to_owned(), mutation, )) } } -<<<<<<< HEAD fn generate_syntax_token(callee: AnyJsExpression) -> Option> { let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { @@ -347,5 +281,3 @@ fn suggested_name(text: &SyntaxToken) -> String { cleaned.to_string() } } -======= ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js index 869157d9830..d4edefaa8e6 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js @@ -1,26 +1,13 @@ -<<<<<<< HEAD foo.trimLeft() foo.trimRight() trimLeft.trimRight() foo.trimLeft.trimRight() "foo".trimLeft() -======= -foo.trimLeft(); -foo.trimRight(); -trimLeft.trimRight(); -foo.trimLeft.trimRight(); -"foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) foo // comment .trimRight /* comment */ /* comment */ -<<<<<<< HEAD () bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() -======= - (); -foo?.trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 8648407add3..7d71e8682cc 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -4,32 +4,19 @@ expression: invalid.js --- # Input ```jsx -<<<<<<< HEAD foo.trimLeft() foo.trimRight() trimLeft.trimRight() foo.trimLeft.trimRight() "foo".trimLeft() -======= -foo.trimLeft(); -foo.trimRight(); -trimLeft.trimRight(); -foo.trimLeft.trimRight(); -"foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) foo // comment .trimRight /* comment */ /* comment */ -<<<<<<< HEAD () bar['trimLeft']() bar["trimLeft"]() bar[`trimLeft`]() -======= - (); -foo?.trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -37,37 +24,21 @@ foo?.trimLeft(); ``` invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimStart instead of trimLeft. > 1 │ foo.trimLeft() │ ^^^^^^^^ 2 │ foo.trimRight() 3 │ trimLeft.trimRight() -======= - ! Use trimLeft instead of trimStart. - - > 1 │ foo.trimLeft(); - │ ^^^^^^^^ - 2 │ foo.trimRight(); - 3 │ trimLeft.trimRight(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimLeft() is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. -<<<<<<< HEAD 1 │ - foo.trimLeft() 1 │ + foo.trimStart() 2 2 │ foo.trimRight() 3 3 │ trimLeft.trimRight() -======= - 1 │ - foo.trimLeft(); - 1 │ + foo.trimStart(); - 2 2 │ foo.trimRight(); - 3 3 │ trimLeft.trimRight(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -75,7 +46,6 @@ invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() @@ -83,33 +53,16 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ │ ^^^^^^^^^ 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() -======= - ! Use trimRight instead of trimEnd. - - 1 │ foo.trimLeft(); - > 2 │ foo.trimRight(); - │ ^^^^^^^^^ - 3 │ trimLeft.trimRight(); - 4 │ foo.trimLeft.trimRight(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. -<<<<<<< HEAD 1 1 │ foo.trimLeft() 2 │ - foo.trimRight() 2 │ + foo.trimEnd() 3 3 │ trimLeft.trimRight() 4 4 │ foo.trimLeft.trimRight() -======= - 1 1 │ foo.trimLeft(); - 2 │ - foo.trimRight(); - 2 │ + foo.trimEnd(); - 3 3 │ trimLeft.trimRight(); - 4 4 │ foo.trimLeft.trimRight(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -117,7 +70,6 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimEnd instead of trimRight. 1 │ foo.trimLeft() @@ -126,36 +78,17 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ │ ^^^^^^^^^ 4 │ foo.trimLeft.trimRight() 5 │ "foo".trimLeft() -======= - ! Use trimRight instead of trimEnd. - - 1 │ foo.trimLeft(); - 2 │ foo.trimRight(); - > 3 │ trimLeft.trimRight(); - │ ^^^^^^^^^ - 4 │ foo.trimLeft.trimRight(); - 5 │ "foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. -<<<<<<< HEAD 1 1 │ foo.trimLeft() 2 2 │ foo.trimRight() 3 │ - trimLeft.trimRight() 3 │ + trimLeft.trimEnd() 4 4 │ foo.trimLeft.trimRight() 5 5 │ "foo".trimLeft() -======= - 1 1 │ foo.trimLeft(); - 2 2 │ foo.trimRight(); - 3 │ - trimLeft.trimRight(); - 3 │ + trimLeft.trimEnd(); - 4 4 │ foo.trimLeft.trimRight(); - 5 5 │ "foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` @@ -163,7 +96,6 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimEnd instead of trimRight. 2 │ foo.trimRight() @@ -171,34 +103,17 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ > 4 │ foo.trimLeft.trimRight() │ ^^^^^^^^^ 5 │ "foo".trimLeft() -======= - ! Use trimRight instead of trimEnd. - - 2 │ foo.trimRight(); - 3 │ trimLeft.trimRight(); - > 4 │ foo.trimLeft.trimRight(); - │ ^^^^^^^^^ - 5 │ "foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 │ foo i trimRight() is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. -<<<<<<< HEAD 2 2 │ foo.trimRight() 3 3 │ trimLeft.trimRight() 4 │ - foo.trimLeft.trimRight() 4 │ + foo.trimLeft.trimEnd() 5 5 │ "foo".trimLeft() -======= - 2 2 │ foo.trimRight(); - 3 3 │ trimLeft.trimRight(); - 4 │ - foo.trimLeft.trimRight(); - 4 │ + foo.trimLeft.trimEnd(); - 5 5 │ "foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 6 │ foo @@ -207,19 +122,11 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimStart instead of trimLeft. 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() > 5 │ "foo".trimLeft() -======= - ! Use trimLeft instead of trimStart. - - 3 │ trimLeft.trimRight(); - 4 │ foo.trimLeft.trimRight(); - > 5 │ "foo".trimLeft(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) │ ^^^^^^^^ 6 │ foo 7 │ // comment @@ -228,17 +135,10 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ i Safe fix: Replace trimLeft with trimStart. -<<<<<<< HEAD 3 3 │ trimLeft.trimRight() 4 4 │ foo.trimLeft.trimRight() 5 │ - "foo".trimLeft() 5 │ + "foo".trimStart() -======= - 3 3 │ trimLeft.trimRight(); - 4 4 │ foo.trimLeft.trimRight(); - 5 │ - "foo".trimLeft(); - 5 │ + "foo".trimStart(); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 6 │ foo 7 7 │ // comment @@ -248,22 +148,14 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ ``` invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -<<<<<<< HEAD ! Use trimEnd instead of trimRight. -======= - ! Use trimRight instead of trimEnd. ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) 6 │ foo 7 │ // comment > 8 │ .trimRight /* comment */ │ ^^^^^^^^^^^^^^^^^^^^^^^ 9 │ /* comment */ -<<<<<<< HEAD 10 │ () -======= - 10 │ (); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimRight() is an alias for trimEnd. @@ -274,17 +166,12 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 8 │ - → .trimRight·/*·comment·*/ 8 │ + → .trimEnd·/*·comment·*/ 9 9 │ /* comment */ -<<<<<<< HEAD 10 10 │ () -======= - 10 10 │ (); ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` ``` -<<<<<<< HEAD invalid.js:11:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use trimStart instead of trimLeft. @@ -346,35 +233,16 @@ invalid.js:13:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ > 13 │ bar[`trimLeft`]() │ ^^^^^^^^ 14 │ -======= -invalid.js:11:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use trimLeft instead of trimStart. - - 9 │ /* comment */ - 10 │ (); - > 11 │ foo?.trimLeft(); - │ ^^^^^^^^ - 12 │ ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) i trimLeft() is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. -<<<<<<< HEAD 11 11 │ bar['trimLeft']() 12 12 │ bar["trimLeft"]() 13 │ - bar[`trimLeft`]() 13 │ + bar[`trimStart`]() 14 14 │ -======= - 9 9 │ /* comment */ - 10 10 │ (); - 11 │ - foo?.trimLeft(); - 11 │ + foo?.trimStart(); - 12 12 │ ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js index b8f5f50832c..0ee8a8cb289 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js @@ -1,36 +1,6 @@ -<<<<<<< HEAD -const foo = bar.trimStart() -const foo = bar.trimEnd() -bar.trimStart?.() -foo.trimStart() -foo.trimStart?.() -foo.trimEnd() -// Not `CallExpression` -new foo.trimLeft() -// Not `MemberExpression` -trimLeft() -// Computed -foo[trimLeft]() -// Not `trimLeft`/`trimRight` -foo.bar() -// More argument(s) -foo.trimLeft(extra) -foo.trimLeft(...argumentsArray) -// `trimLeft` is in argument -foo.bar(trimLeft) -foo.bar(foo.trimLeft) -// `trimLeft` is in `MemberExpression.object` -trimLeft.foo() -foo.trimLeft.bar() -bar['trimStart']() -bar["trimStart"]() -bar[`trimStart`]() - -======= const foo = bar.trimStart(); const foo = bar.trimEnd(); bar.trimStart?.() -bar['trimStart']() foo.trimStart(), foo.trimStart?.(), foo.trimEnd(), @@ -53,4 +23,6 @@ foo.bar(foo.trimLeft), // `trimLeft` is in `MemberExpression.object` trimLeft.foo(), foo.trimLeft.bar(), ->>>>>>> 0ee594f175 (feat(biome_js_analyzer): `useTrimStartEnd`) +bar['trimStart']() +bar["trimStart"]() +bar[`trimStart`]() diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap index cc163b68c80..7bebf5c3c19 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/valid.js.snap @@ -4,29 +4,31 @@ expression: valid.js --- # Input ```jsx -const foo = bar.trimStart() -const foo = bar.trimEnd() +const foo = bar.trimStart(); +const foo = bar.trimEnd(); bar.trimStart?.() -foo.trimStart() -foo.trimStart?.() -foo.trimEnd() +foo.trimStart(), +foo.trimStart?.(), +foo.trimEnd(), // Not `CallExpression` -new foo.trimLeft() +new foo.trimLeft();, // Not `MemberExpression` -trimLeft() +trimLeft();, +// `callee.property` is not a `Identifier` +foo[\'trimLeft\']();', // Computed -foo[trimLeft]() +foo[trimLeft]();, // Not `trimLeft`/`trimRight` -foo.bar() +foo.bar();, // More argument(s) -foo.trimLeft(extra) -foo.trimLeft(...argumentsArray) +foo.trimLeft(extra);, +foo.trimLeft(...argumentsArray), // `trimLeft` is in argument -foo.bar(trimLeft) -foo.bar(foo.trimLeft) +foo.bar(trimLeft), +foo.bar(foo.trimLeft), // `trimLeft` is in `MemberExpression.object` -trimLeft.foo() -foo.trimLeft.bar() +trimLeft.foo(), +foo.trimLeft.bar(), bar['trimStart']() bar["trimStart"]() bar[`trimStart`]() diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 165a6fb4575..ae51ce7c135 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,13 +1258,13 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight(). + * Succinct description of the rule. */ - useTrimStartEnd?: RuleFixConfiguration_for_Null; + useTrimStartEnd?: RuleConfiguration_for_Null; /** * Use valid values for the autocomplete attribute on input elements. */ - useTrimStartEnd?: RuleFixConfiguration_for_Null; + useValidAutocomplete?: RuleConfiguration_for_UseValidAutocompleteOptions; } /** * A list of rules that belong to this group @@ -2551,6 +2551,7 @@ export type Category = | "lint/nursery/useThrowOnlyError" | "lint/nursery/useTopLevelRegex" | "lint/nursery/useTrimStartEnd" + | "lint/nursery/useValidAutocomplete" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" | "lint/performance/noDelete" From 25e69a0a801f2d9c443338e4d2e08017f2a8ecc4 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 16:48:57 +0900 Subject: [PATCH 17/38] chore: run gen-lint --- .../src/execute/migrate/eslint_any_rule_to_biome.rs | 8 ++++++++ crates/biome_configuration/src/linter/rules.rs | 4 ++-- packages/@biomejs/backend-jsonrpc/src/workspace.ts | 4 ++-- packages/@biomejs/biome/configuration_schema.json | 9 ++++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index fb664749979..c346f31c5b7 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1454,6 +1454,14 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.no_substr.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } + "unicorn/prefer-string-trim-start-end" => { + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group.use_trim_start_end.get_or_insert(Default::default()); + rule.set_level(rule_severity.into()); + } "unicorn/require-number-to-fixed-digits-argument" => { if !options.include_nursery { return false; diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 17c6312c8d6..e909ce83e19 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,9 +2972,9 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Succinct description of the rule."] + #[doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_trim_start_end: Option>, + pub use_trim_start_end: Option>, #[doc = "Use valid values for the autocomplete attribute on input elements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_valid_autocomplete: Option>, diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index ae51ce7c135..8839ae63ed1 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,9 +1258,9 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Succinct description of the rule. + * Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight(). */ - useTrimStartEnd?: RuleConfiguration_for_Null; + useTrimStartEnd?: RuleFixConfiguration_for_Null; /** * Use valid values for the autocomplete attribute on input elements. */ diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 6918994b745..952406d91a2 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2120,11 +2120,18 @@ ] }, "useTrimStartEnd": { - "description": "Enforce the use of trimStart() over trimLeft() and trimeEnd() over trimRight().", + "description": "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight().", "anyOf": [ { "$ref": "#/definitions/RuleFixConfiguration" }, { "type": "null" } ] + }, + "useValidAutocomplete": { + "description": "Use valid values for the autocomplete attribute on input elements.", + "anyOf": [ + { "$ref": "#/definitions/UseValidAutocompleteConfiguration" }, + { "type": "null" } + ] } }, "additionalProperties": false From 0a470c56f005b14b31a71d0bd17b3303930a66c8 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 17:07:24 +0900 Subject: [PATCH 18/38] chore: fix wording --- crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 0e11c03fd8b..878c1714a1d 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -122,7 +122,7 @@ impl Rule for UseTrimStartEnd { .to_owned(); let note_message = { markup! { - ""{state.member_name}"() is an alias for "{state.suggested_name}"." + ""{state.member_name}" is an alias for "{state.suggested_name}"." } .to_owned() }; From 4bc971350f3a8ead12f5c204b5bbb03b593d8d78 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 17:18:34 +0900 Subject: [PATCH 19/38] chore: update snapshot --- .../src/lint/nursery/use_trim_start_end.rs | 18 +++++++++++++++--- .../nursery/useTrimStartEnd/invalid.js.snap | 18 +++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 878c1714a1d..b89fd823096 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -6,7 +6,7 @@ use biome_console::markup; use biome_js_factory::make::{self}; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsTemplateElement, JsCallExpression, - JsLanguage, JsSyntaxKind, JsSyntaxToken, T, + JsLanguage, JsSyntaxKind, JsSyntaxToken, }; use biome_rowan::{AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; @@ -170,9 +170,21 @@ impl Rule for UseTrimStartEnd { let computed_member_expression = if is_template { AnyJsExpression::JsTemplateExpression( make::js_template_expression( - make::token(T!['`']), + callee + .as_js_computed_member_expression()? + .member() + .ok()? + .as_js_template_expression()? + .l_tick_token() + .ok()?, make::js_template_element_list(elements), - make::token(T!['`']), + callee + .as_js_computed_member_expression()? + .member() + .ok()? + .as_js_template_expression()? + .r_tick_token() + .ok()?, ) .build(), ) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap index 7d71e8682cc..1a3afc2fb5b 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useTrimStartEnd/invalid.js.snap @@ -31,7 +31,7 @@ invalid.js:1:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 2 │ foo.trimRight() 3 │ trimLeft.trimRight() - i trimLeft() is an alias for trimStart. + i trimLeft is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. @@ -54,7 +54,7 @@ invalid.js:2:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 3 │ trimLeft.trimRight() 4 │ foo.trimLeft.trimRight() - i trimRight() is an alias for trimEnd. + i trimRight is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. @@ -79,7 +79,7 @@ invalid.js:3:10 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 4 │ foo.trimLeft.trimRight() 5 │ "foo".trimLeft() - i trimRight() is an alias for trimEnd. + i trimRight is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. @@ -105,7 +105,7 @@ invalid.js:4:14 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 5 │ "foo".trimLeft() 6 │ foo - i trimRight() is an alias for trimEnd. + i trimRight is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. @@ -131,7 +131,7 @@ invalid.js:5:7 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 6 │ foo 7 │ // comment - i trimLeft() is an alias for trimStart. + i trimLeft is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. @@ -157,7 +157,7 @@ invalid.js:8:3 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 9 │ /* comment */ 10 │ () - i trimRight() is an alias for trimEnd. + i trimRight is an alias for trimEnd. i Safe fix: Replace trimRight with trimEnd. @@ -183,7 +183,7 @@ invalid.js:11:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 12 │ bar["trimLeft"]() 13 │ bar[`trimLeft`]() - i trimLeft() is an alias for trimStart. + i trimLeft is an alias for trimStart. i Safe fix: Replace trimLeft with 'trimStart'. @@ -209,7 +209,7 @@ invalid.js:12:5 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ 13 │ bar[`trimLeft`]() 14 │ - i trimLeft() is an alias for trimStart. + i trimLeft is an alias for trimStart. i Safe fix: Replace trimLeft with "trimStart". @@ -234,7 +234,7 @@ invalid.js:13:6 lint/nursery/useTrimStartEnd FIXABLE ━━━━━━━━ │ ^^^^^^^^ 14 │ - i trimLeft() is an alias for trimStart. + i trimLeft is an alias for trimStart. i Safe fix: Replace trimLeft with trimStart. From 21a0728a622d1da44d8018bc719aa1efe4055e40 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 19:48:13 +0900 Subject: [PATCH 20/38] chore: add comment --- crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index b89fd823096..82c301116c8 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -147,7 +147,7 @@ impl Rule for UseTrimStartEnd { } else { false }; - + // Need to keep the original token to replace it with the new token. let token = generate_syntax_token(callee.clone())?; let replaced_member_name = suggested_name(&token); From 2c0fdf65172d14c76743f2546a657f4d5e193553 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 23 Jun 2024 19:50:22 +0900 Subject: [PATCH 21/38] chore: add more description --- crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 82c301116c8..d231109ae04 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -148,6 +148,7 @@ impl Rule for UseTrimStartEnd { false }; // Need to keep the original token to replace it with the new token. + // `.as_static_value()` strips the information of tick tokens. let token = generate_syntax_token(callee.clone())?; let replaced_member_name = suggested_name(&token); From 3c498795e113291e75280a32f847a797d52b4c08 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 29 Jun 2024 16:52:12 +0900 Subject: [PATCH 22/38] chore: update changelog entry --- CHANGELOG.md | 8 +++- .../biome_configuration/src/linter/rules.rs | 48 +++++++++++++------ .../@biomejs/backend-jsonrpc/src/workspace.ts | 4 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a22da1a36ec..53608d4d0df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ### Linter +<<<<<<< HEAD #### Enhancements - [noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) now reports direct use of an enum member before its declaration. @@ -100,6 +101,11 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ``` Contributed by @Conaclos +======= +#### New features + +- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke +>>>>>>> 5b73c02b47 (chore: update changelog entry) ### Parser @@ -252,8 +258,6 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b #### New features - Add [nursery/useValidAutocomplete](https://biomejs.dev/linter/rules/use-valid-autocomplete/). Contributed by @unvalley -- Add [nursery/noSubstr](https://biomejs.dev/linter/rules/no-substr/). Contributed by @chansuke -- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke #### Bug fixes diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index e909ce83e19..e1d98186263 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2972,9 +2972,9 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight()."] + #[doc = "Succinct description of the rule."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_trim_start_end: Option>, + pub use_trim_start_end: Option>, #[doc = "Use valid values for the autocomplete attribute on input elements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_valid_autocomplete: Option>, @@ -3384,36 +3384,46 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3633,36 +3643,46 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8839ae63ed1..ae51ce7c135 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1258,9 +1258,9 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight(). + * Succinct description of the rule. */ - useTrimStartEnd?: RuleFixConfiguration_for_Null; + useTrimStartEnd?: RuleConfiguration_for_Null; /** * Use valid values for the autocomplete attribute on input elements. */ From a168e7494ccc3ca61c4adc83e273ff1a16ec2575 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 29 Jun 2024 16:54:24 +0900 Subject: [PATCH 23/38] chore: run gen-lint --- .../biome_configuration/src/linter/rules.rs | 217 +++++++----------- crates/biome_js_analyze/src/lint/nursery.rs | 2 - crates/biome_js_analyze/src/options.rs | 2 - .../@biomejs/backend-jsonrpc/src/workspace.ts | 12 +- 4 files changed, 89 insertions(+), 144 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index e1d98186263..2e73e1ed29b 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -8,7 +8,6 @@ use biome_css_analyze::options::*; use biome_deserialize::{DeserializableValidator, DeserializationDiagnostic}; use biome_deserialize_macros::{Deserializable, Merge}; use biome_diagnostics::{Category, Severity}; -use biome_graphql_analyze::options::*; use biome_js_analyze::options::*; use biome_json_analyze::options::*; use biome_rowan::TextRange; @@ -2846,9 +2845,6 @@ pub struct Nursery { #[doc = "Disallow variables from evolving into any type through reassignments."] #[serde(skip_serializing_if = "Option::is_none")] pub no_evolving_types: Option>, - #[doc = "Disallow exporting an imported variable."] - #[serde(skip_serializing_if = "Option::is_none")] - pub no_exported_imports: Option>, #[doc = "Disallow invalid !important within keyframe declarations"] #[serde(skip_serializing_if = "Option::is_none")] pub no_important_in_keyframe: Option>, @@ -2932,9 +2928,6 @@ pub struct Nursery { #[doc = "Require the default clause in switch statements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_default_switch_clause: Option>, - #[doc = "Require specifying the reason argument when using @deprecated directive"] - #[serde(skip_serializing_if = "Option::is_none")] - pub use_deprecated_reason: Option>, #[doc = "Enforce passing a message value when creating a built-in error."] #[serde(skip_serializing_if = "Option::is_none")] pub use_error_message: Option>, @@ -2972,9 +2965,9 @@ pub struct Nursery { #[doc = "Require regex literals to be declared at the top level."] #[serde(skip_serializing_if = "Option::is_none")] pub use_top_level_regex: Option>, - #[doc = "Succinct description of the rule."] + #[doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight()."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_trim_start_end: Option>, + pub use_trim_start_end: Option>, #[doc = "Use valid values for the autocomplete attribute on input elements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_valid_autocomplete: Option>, @@ -3007,7 +3000,6 @@ impl Nursery { "noDynamicNamespaceImportAccess", "noEmptyBlock", "noEvolvingTypes", - "noExportedImports", "noImportantInKeyframe", "noInvalidDirectionInLinearGradient", "noInvalidPositionAtImportRule", @@ -3034,7 +3026,6 @@ impl Nursery { "useConsistentGridAreas", "useDateNow", "useDefaultSwitchClause", - "useDeprecatedReason", "useErrorMessage", "useExplicitLengthCheck", "useFocusableInteractive", @@ -3084,21 +3075,18 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3150,9 +3138,6 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3214,214 +3199,204 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_empty_block.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_evolving_types.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_exported_imports.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); - } - } - if let Some(rule) = self.use_generic_font_names.as_ref() { - if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); - } - } if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } if let Some(rule) = self.use_valid_autocomplete.as_ref() { if rule.is_enabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } index_set @@ -3473,214 +3448,204 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_empty_block.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_evolving_types.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_exported_imports.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); - } - } - if let Some(rule) = self.use_generic_font_names.as_ref() { - if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); - } - } if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } if let Some(rule) = self.use_valid_autocomplete.as_ref() { if rule.is_disabled() { - index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } index_set @@ -3763,10 +3728,6 @@ impl Nursery { .no_evolving_types .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "noExportedImports" => self - .no_exported_imports - .as_ref() - .map(|conf| (conf.level(), conf.get_options())), "noImportantInKeyframe" => self .no_important_in_keyframe .as_ref() @@ -3871,10 +3832,6 @@ impl Nursery { .use_default_switch_clause .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useDeprecatedReason" => self - .use_deprecated_reason - .as_ref() - .map(|conf| (conf.level(), conf.get_options())), "useErrorMessage" => self .use_error_message .as_ref() diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index eae84e70891..ec349c263e5 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -7,7 +7,6 @@ pub mod no_done_callback; pub mod no_duplicate_else_if; pub mod no_dynamic_namespace_import_access; pub mod no_evolving_types; -pub mod no_exported_imports; pub mod no_label_without_control; pub mod no_misplaced_assertion; pub mod no_react_specific_props; @@ -45,7 +44,6 @@ declare_lint_group! { self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_evolving_types :: NoEvolvingTypes , - self :: no_exported_imports :: NoExportedImports , self :: no_label_without_control :: NoLabelWithoutControl , self :: no_misplaced_assertion :: NoMisplacedAssertion , self :: no_react_specific_props :: NoReactSpecificProps , diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 9192f5a57b2..eeb0bb68357 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -84,8 +84,6 @@ pub type NoExcessiveCognitiveComplexity = < lint :: complexity :: no_excessive_c pub type NoExcessiveNestedTestSuites = < lint :: complexity :: no_excessive_nested_test_suites :: NoExcessiveNestedTestSuites as biome_analyze :: Rule > :: Options ; pub type NoExplicitAny = ::Options; -pub type NoExportedImports = - ::Options; pub type NoExportsInTest = ::Options; pub type NoExtraBooleanCast = diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index ae51ce7c135..bee36c7ea9e 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1093,10 +1093,6 @@ export interface Nursery { * Disallow variables from evolving into any type through reassignments. */ noEvolvingTypes?: RuleConfiguration_for_Null; - /** - * Disallow exporting an imported variable. - */ - noExportedImports?: RuleConfiguration_for_Null; /** * Disallow invalid !important within keyframe declarations */ @@ -1205,10 +1201,6 @@ export interface Nursery { * Require the default clause in switch statements. */ useDefaultSwitchClause?: RuleConfiguration_for_Null; - /** - * Require specifying the reason argument when using @deprecated directive - */ - useDeprecatedReason?: RuleConfiguration_for_Null; /** * Enforce passing a message value when creating a built-in error. */ @@ -1258,9 +1250,9 @@ export interface Nursery { */ useTopLevelRegex?: RuleConfiguration_for_Null; /** - * Succinct description of the rule. + * Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight(). */ - useTrimStartEnd?: RuleConfiguration_for_Null; + useTrimStartEnd?: RuleFixConfiguration_for_Null; /** * Use valid values for the autocomplete attribute on input elements. */ From 8509590cfc81daab9a6f55268e7b1f4e8dc73e2b Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 29 Jun 2024 17:42:14 +0900 Subject: [PATCH 24/38] refactor: update suggenst name handling --- .../src/lint/nursery/use_trim_start_end.rs | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index d231109ae04..37d75dd6cf4 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -59,7 +59,7 @@ declare_rule! { pub struct UseTrimStartEndState { member_name: String, span: TextRange, - suggested_name: &'static str, + suggested_name: String, } impl Rule for UseTrimStartEnd { @@ -88,22 +88,14 @@ impl Rule for UseTrimStartEnd { let value = member.as_static_value()?; let span = value.range(); let member_name = value.as_string_constant()?.to_string(); - let suggested_name = match member_name.as_ref() { - "trimLeft" => Some("trimStart"), - "trimRight" => Some("trimEnd"), - _ => return None, - }; + let suggested_name = generate_suggested_name(&member_name); (member_name, span, suggested_name) } AnyJsExpression::JsStaticMemberExpression(callee) => { let token = callee.member().ok()?.value_token().ok()?; let span = token.text_range(); let member_name = token.text_trimmed().to_string(); - let suggested_name = match member_name.as_ref() { - "trimLeft" => Some("trimStart"), - "trimRight" => Some("trimEnd"), - _ => return None, - }; + let suggested_name = generate_suggested_name(&member_name); (member_name, span, suggested_name) } _ => return None, @@ -280,17 +272,21 @@ fn suggested_name(text: &SyntaxToken) -> String { } else { trimmed }; - let cleaned = match unquoted { - "trimLeft" => "trimStart", - "trimRight" => "trimEnd", - _ => unquoted, - }; + let suggested_name = generate_suggested_name(unquoted).unwrap(); if is_single_quoted { - format!("'{}'", cleaned) + format!("'{}'", suggested_name) } else if is_double_quoted { - format!("\"{}\"", cleaned) + format!("\"{}\"", suggested_name) } else { - cleaned.to_string() + suggested_name.to_string() + } +} + +fn generate_suggested_name(member_name: &str) -> Option { + match member_name { + "trimLeft" => Some("trimStart".to_string()), + "trimRight" => Some("trimEnd".to_string()), + _ => None, } } From f4f3017ee96be8cc331e3465109872129fa8ecbe Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 29 Jun 2024 17:51:56 +0900 Subject: [PATCH 25/38] chore: run gen-lint --- .../biome_configuration/src/linter/rules.rs | 213 +++++++++++------- crates/biome_js_analyze/src/lint/nursery.rs | 2 + crates/biome_js_analyze/src/options.rs | 2 + .../@biomejs/backend-jsonrpc/src/workspace.ts | 8 + 4 files changed, 138 insertions(+), 87 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 2e73e1ed29b..009adc32143 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -8,6 +8,7 @@ use biome_css_analyze::options::*; use biome_deserialize::{DeserializableValidator, DeserializationDiagnostic}; use biome_deserialize_macros::{Deserializable, Merge}; use biome_diagnostics::{Category, Severity}; +use biome_graphql_analyze::options::*; use biome_js_analyze::options::*; use biome_json_analyze::options::*; use biome_rowan::TextRange; @@ -2845,6 +2846,9 @@ pub struct Nursery { #[doc = "Disallow variables from evolving into any type through reassignments."] #[serde(skip_serializing_if = "Option::is_none")] pub no_evolving_types: Option>, + #[doc = "Disallow exporting an imported variable."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_exported_imports: Option>, #[doc = "Disallow invalid !important within keyframe declarations"] #[serde(skip_serializing_if = "Option::is_none")] pub no_important_in_keyframe: Option>, @@ -2928,6 +2932,9 @@ pub struct Nursery { #[doc = "Require the default clause in switch statements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_default_switch_clause: Option>, + #[doc = "Require specifying the reason argument when using @deprecated directive"] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_deprecated_reason: Option>, #[doc = "Enforce passing a message value when creating a built-in error."] #[serde(skip_serializing_if = "Option::is_none")] pub use_error_message: Option>, @@ -3000,6 +3007,7 @@ impl Nursery { "noDynamicNamespaceImportAccess", "noEmptyBlock", "noEvolvingTypes", + "noExportedImports", "noImportantInKeyframe", "noInvalidDirectionInLinearGradient", "noInvalidPositionAtImportRule", @@ -3026,6 +3034,7 @@ impl Nursery { "useConsistentGridAreas", "useDateNow", "useDefaultSwitchClause", + "useDeprecatedReason", "useErrorMessage", "useExplicitLengthCheck", "useFocusableInteractive", @@ -3074,19 +3083,19 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3138,6 +3147,8 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3199,206 +3210,216 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_exported_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_semantic_elements.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3448,206 +3469,216 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_exported_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_semantic_elements.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3728,6 +3759,10 @@ impl Nursery { .no_evolving_types .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "noExportedImports" => self + .no_exported_imports + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "noImportantInKeyframe" => self .no_important_in_keyframe .as_ref() @@ -3832,6 +3867,10 @@ impl Nursery { .use_default_switch_clause .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useDeprecatedReason" => self + .use_deprecated_reason + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useErrorMessage" => self .use_error_message .as_ref() diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index ec349c263e5..eae84e70891 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -7,6 +7,7 @@ pub mod no_done_callback; pub mod no_duplicate_else_if; pub mod no_dynamic_namespace_import_access; pub mod no_evolving_types; +pub mod no_exported_imports; pub mod no_label_without_control; pub mod no_misplaced_assertion; pub mod no_react_specific_props; @@ -44,6 +45,7 @@ declare_lint_group! { self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_evolving_types :: NoEvolvingTypes , + self :: no_exported_imports :: NoExportedImports , self :: no_label_without_control :: NoLabelWithoutControl , self :: no_misplaced_assertion :: NoMisplacedAssertion , self :: no_react_specific_props :: NoReactSpecificProps , diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index eeb0bb68357..9192f5a57b2 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -84,6 +84,8 @@ pub type NoExcessiveCognitiveComplexity = < lint :: complexity :: no_excessive_c pub type NoExcessiveNestedTestSuites = < lint :: complexity :: no_excessive_nested_test_suites :: NoExcessiveNestedTestSuites as biome_analyze :: Rule > :: Options ; pub type NoExplicitAny = ::Options; +pub type NoExportedImports = + ::Options; pub type NoExportsInTest = ::Options; pub type NoExtraBooleanCast = diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index bee36c7ea9e..8839ae63ed1 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1093,6 +1093,10 @@ export interface Nursery { * Disallow variables from evolving into any type through reassignments. */ noEvolvingTypes?: RuleConfiguration_for_Null; + /** + * Disallow exporting an imported variable. + */ + noExportedImports?: RuleConfiguration_for_Null; /** * Disallow invalid !important within keyframe declarations */ @@ -1201,6 +1205,10 @@ export interface Nursery { * Require the default clause in switch statements. */ useDefaultSwitchClause?: RuleConfiguration_for_Null; + /** + * Require specifying the reason argument when using @deprecated directive + */ + useDeprecatedReason?: RuleConfiguration_for_Null; /** * Enforce passing a message value when creating a built-in error. */ From 284f36637795839548206e0a93350565b11b4d1f Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 5 Jul 2024 00:13:57 +0900 Subject: [PATCH 26/38] fix: remove redundant allocation --- .../src/lint/nursery/use_trim_start_end.rs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 37d75dd6cf4..1e8d1c92cb8 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -59,7 +59,6 @@ declare_rule! { pub struct UseTrimStartEndState { member_name: String, span: TextRange, - suggested_name: String, } impl Rule for UseTrimStartEnd { @@ -82,39 +81,36 @@ impl Rule for UseTrimStartEnd { } let callee = node.callee().ok()?; - let (member_name, span, suggested_name) = match callee { + let (member_name, span) = match callee { AnyJsExpression::JsComputedMemberExpression(callee) => { let member = callee.member().ok()?; let value = member.as_static_value()?; let span = value.range(); let member_name = value.as_string_constant()?.to_string(); - let suggested_name = generate_suggested_name(&member_name); - (member_name, span, suggested_name) + (member_name, span) } AnyJsExpression::JsStaticMemberExpression(callee) => { let token = callee.member().ok()?.value_token().ok()?; let span = token.text_range(); let member_name = token.text_trimmed().to_string(); - let suggested_name = generate_suggested_name(&member_name); - (member_name, span, suggested_name) + (member_name, span) } _ => return None, }; - Some(UseTrimStartEndState { - member_name, - span, - suggested_name: suggested_name?, - }) + + Some(UseTrimStartEndState { member_name, span }) } fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { + let suggested_name = generate_suggested_name(&state.member_name)?; + let diagnostic_message = markup! { - "Use "{state.suggested_name}" instead of "{state.member_name}"." + "Use "{suggested_name}" instead of "{state.member_name}"." } .to_owned(); let note_message = { markup! { - ""{state.member_name}" is an alias for "{state.suggested_name}"." + ""{state.member_name}" is an alias for "{suggested_name}"." } .to_owned() }; @@ -283,10 +279,10 @@ fn suggested_name(text: &SyntaxToken) -> String { } } -fn generate_suggested_name(member_name: &str) -> Option { +fn generate_suggested_name(member_name: &str) -> Option<&str> { match member_name { - "trimLeft" => Some("trimStart".to_string()), - "trimRight" => Some("trimEnd".to_string()), + "trimLeft" => Some("trimStart"), + "trimRight" => Some("trimEnd"), _ => None, } } From 8a684bab719bbbc90ed5b075db1f0d0862731218 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 5 Jul 2024 00:39:10 +0900 Subject: [PATCH 27/38] fix: handle error --- .../src/lint/nursery/use_trim_start_end.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 1e8d1c92cb8..cd4874a7773 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -138,11 +138,11 @@ impl Rule for UseTrimStartEnd { // Need to keep the original token to replace it with the new token. // `.as_static_value()` strips the information of tick tokens. let token = generate_syntax_token(callee.clone())?; - let replaced_member_name = suggested_name(&token); + let replaced_member_name = suggested_name(&token)?; let mut elements = vec![]; let template_elements = AnyJsTemplateElement::from(make::js_template_chunk_element( - make::js_template_chunk(&replaced_member_name), + make::js_template_chunk(replaced_member_name.as_str()), )); elements.push(template_elements); @@ -255,7 +255,7 @@ fn generate_syntax_token(callee: AnyJsExpression) -> Option) -> String { +fn suggested_name(text: &SyntaxToken) -> Option { let trimmed = text.text_trimmed(); let first_char = trimmed.chars().next(); let last_char = trimmed.chars().last(); @@ -268,15 +268,16 @@ fn suggested_name(text: &SyntaxToken) -> String { } else { trimmed }; - let suggested_name = generate_suggested_name(unquoted).unwrap(); - if is_single_quoted { - format!("'{}'", suggested_name) - } else if is_double_quoted { - format!("\"{}\"", suggested_name) - } else { - suggested_name.to_string() - } + generate_suggested_name(unquoted).map(|suggested_name| { + if is_single_quoted { + format!("'{}'", suggested_name) + } else if is_double_quoted { + format!("\"{}\"", suggested_name) + } else { + suggested_name.to_string() + } + }) } fn generate_suggested_name(member_name: &str) -> Option<&str> { From 3c2285fffd499775ee104d11282f62f60ffeac8f Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 6 Jul 2024 19:45:19 +0900 Subject: [PATCH 28/38] refactor: use `can_cast` --- .../src/lint/nursery/use_trim_start_end.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index cd4874a7773..d0660e0b3f3 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -6,9 +6,9 @@ use biome_console::markup; use biome_js_factory::make::{self}; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsName, AnyJsTemplateElement, JsCallExpression, - JsLanguage, JsSyntaxKind, JsSyntaxToken, + JsComputedMemberExpression, JsLanguage, JsSyntaxKind, JsSyntaxToken, JsTemplateExpression, }; -use biome_rowan::{AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; +use biome_rowan::{AstNode, AstSeparatedList, BatchMutationExt, SyntaxToken, TextRange}; use crate::JsRuleAction; @@ -125,10 +125,10 @@ impl Rule for UseTrimStartEnd { let node = ctx.query(); let callee = node.callee().ok()?; - let is_computed_member = callee.as_js_computed_member_expression().is_some(); + let is_computed_member = JsComputedMemberExpression::can_cast(callee.syntax().kind()); let is_template = if is_computed_member { if let Ok(computed_member) = callee.as_js_computed_member_expression()?.member() { - computed_member.as_js_template_expression().is_some() + JsTemplateExpression::can_cast(computed_member.syntax().kind()) } else { false } From 19c7c1e2d993376f869ca5f4e3b71e96643b4aa5 Mon Sep 17 00:00:00 2001 From: chansuke Date: Tue, 9 Jul 2024 00:13:37 +0900 Subject: [PATCH 29/38] refactor: use variable for `.as_js_computed_member_expression()` --- .../src/lint/nursery/use_trim_start_end.rs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index d0660e0b3f3..1e8f4b3a233 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -126,8 +126,13 @@ impl Rule for UseTrimStartEnd { let callee = node.callee().ok()?; let is_computed_member = JsComputedMemberExpression::can_cast(callee.syntax().kind()); + let computed_member_expression_opt = if is_computed_member { + callee.as_js_computed_member_expression() + } else { + None + }; let is_template = if is_computed_member { - if let Ok(computed_member) = callee.as_js_computed_member_expression()?.member() { + if let Ok(computed_member) = computed_member_expression_opt?.member() { JsTemplateExpression::can_cast(computed_member.syntax().kind()) } else { false @@ -156,19 +161,17 @@ impl Rule for UseTrimStartEnd { _ => unreachable!(), }; - let computed_member_expression = if is_template { + let transformed_expression = if is_template { AnyJsExpression::JsTemplateExpression( make::js_template_expression( - callee - .as_js_computed_member_expression()? + computed_member_expression_opt? .member() .ok()? .as_js_template_expression()? .l_tick_token() .ok()?, make::js_template_element_list(elements), - callee - .as_js_computed_member_expression()? + computed_member_expression_opt? .member() .ok()? .as_js_template_expression()? @@ -197,15 +200,9 @@ impl Rule for UseTrimStartEnd { AnyJsExpression::JsComputedMemberExpression( make::js_computed_member_expression( callee_object, - callee - .as_js_computed_member_expression()? - .l_brack_token() - .ok()?, - computed_member_expression, - callee - .as_js_computed_member_expression()? - .r_brack_token() - .ok()?, + computed_member_expression_opt?.l_brack_token().ok()?, + transformed_expression, + computed_member_expression_opt?.r_brack_token().ok()?, ) .build(), ) From 63b8e89397b8fbb4d42f890bfc1b6e6e21922126 Mon Sep 17 00:00:00 2001 From: chansuke Date: Tue, 9 Jul 2024 00:25:23 +0900 Subject: [PATCH 30/38] refactor: use better naming --- .../biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 1e8f4b3a233..fc1357d8eca 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -142,7 +142,7 @@ impl Rule for UseTrimStartEnd { }; // Need to keep the original token to replace it with the new token. // `.as_static_value()` strips the information of tick tokens. - let token = generate_syntax_token(callee.clone())?; + let token = extract_token_from_expression(callee.clone())?; let replaced_member_name = suggested_name(&token)?; let mut elements = vec![]; @@ -230,7 +230,7 @@ impl Rule for UseTrimStartEnd { } } -fn generate_syntax_token(callee: AnyJsExpression) -> Option> { +fn extract_token_from_expression(callee: AnyJsExpression) -> Option> { let token = if let AnyJsExpression::JsComputedMemberExpression(expression) = callee { let member = expression.member().ok()?; match member { From fad4c9752989638aafd084bfec6a0540c35264a1 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 10 Jul 2024 00:18:13 +0900 Subject: [PATCH 31/38] chore: run gen-lint --- CHANGELOG.md | 7 +---- .../biome_configuration/src/linter/rules.rs | 30 ++++++++++++++++--- .../src/lint/nursery/use_trim_start_end.rs | 4 +-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53608d4d0df..a393d431c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,6 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ### Linter -<<<<<<< HEAD #### Enhancements - [noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) now reports direct use of an enum member before its declaration. @@ -77,6 +76,7 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b #### New features - Add support for GraphQL linting. Contributed by @ematipico +- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke #### Bug fixes @@ -101,11 +101,6 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ``` Contributed by @Conaclos -======= -#### New features - -- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke ->>>>>>> 5b73c02b47 (chore: update changelog entry) ### Parser diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 009adc32143..e9436f67626 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -3149,6 +3149,8 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3410,16 +3412,26 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3669,16 +3681,26 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_throw_only_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } + if let Some(rule) = self.use_trim_start_end.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); + } + } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index fc1357d8eca..f9544611e6d 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -1,5 +1,5 @@ use biome_analyze::{ - context::RuleContext, declare_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, + context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; @@ -12,7 +12,7 @@ use biome_rowan::{AstNode, AstSeparatedList, BatchMutationExt, SyntaxToken, Text use crate::JsRuleAction; -declare_rule! { +declare_lint_rule! { /// Enforce the use of `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`. /// /// While `String.trimLeft()` and `String.trimRight()` are aliases for `String.trimStart()` and `String.trimEnd()`, From e421346952ad5baf87844cab19bdb966aed53a2a Mon Sep 17 00:00:00 2001 From: chansuke Date: Thu, 11 Jul 2024 01:12:04 +0900 Subject: [PATCH 32/38] chore: gen-lint --- .../biome_configuration/src/linter/rules.rs | 177 +++++++++--------- 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index e9436f67626..ec57181b443 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -3083,19 +3083,22 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3212,202 +3215,202 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_exported_imports.as_ref() { + if let Some(rule) = self.no_empty_block.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_evolving_types.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_exported_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_semantic_elements.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } @@ -3481,202 +3484,202 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_exported_imports.as_ref() { + if let Some(rule) = self.no_empty_block.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_evolving_types.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { + if let Some(rule) = self.no_exported_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_label_without_control.as_ref() { + if let Some(rule) = self.no_invalid_direction_in_linear_gradient.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_invalid_position_at_import_rule.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_label_without_control.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_shorthand_property_overrides.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.no_unknown_property.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { + if let Some(rule) = self.no_unknown_media_feature_name.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_property.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { + if let Some(rule) = self.no_unknown_selector_pseudo_element.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unused_function_parameters.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_useless_string_concat.as_ref() { + if let Some(rule) = self.no_unmatchable_anb_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_unused_function_parameters.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_yoda_expression.as_ref() { + if let Some(rule) = self.no_useless_string_concat.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { + if let Some(rule) = self.no_yoda_expression.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_consistent_grid_areas.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_date_now.as_ref() { + if let Some(rule) = self.use_consistent_builtin_instantiation.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_consistent_grid_areas.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_date_now.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_error_message.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_explicit_length_check.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_focusable_interactive.as_ref() { + if let Some(rule) = self.use_error_message.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_explicit_length_check.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_import_extensions.as_ref() { + if let Some(rule) = self.use_focusable_interactive.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { + if let Some(rule) = self.use_import_extensions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_semantic_elements.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_number_to_fixed_digits_argument.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_throw_new_error.as_ref() { + if let Some(rule) = self.use_semantic_elements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_throw_only_error.as_ref() { + if let Some(rule) = self.use_sorted_classes.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_top_level_regex.as_ref() { + if let Some(rule) = self.use_throw_new_error.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } From 67c00f5597b06ae19198c822fcb0a7521a45f1db Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 12 Jul 2024 19:56:28 +0900 Subject: [PATCH 33/38] chore: update rules.rs --- .../biome_configuration/src/linter/rules.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 24ec98a3596..57bd8de0003 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -3160,6 +3160,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3431,16 +3432,21 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3700,16 +3706,21 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_top_level_regex.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] From 37f32b0fb3473d376c43d58d5fddf83176ab9f45 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sun, 14 Jul 2024 17:41:34 +0900 Subject: [PATCH 34/38] reafactor: update quote handling --- .../src/lint/nursery/use_trim_start_end.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index f9544611e6d..8147e330d70 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -143,6 +143,9 @@ impl Rule for UseTrimStartEnd { // Need to keep the original token to replace it with the new token. // `.as_static_value()` strips the information of tick tokens. let token = extract_token_from_expression(callee.clone())?; + let unquoted = token.text().trim_matches('"'); + println!("unquoted: {:?}", unquoted); + println!("is_single: {:?}", ctx.as_preferred_quote().is_single()); let replaced_member_name = suggested_name(&token)?; let mut elements = vec![]; @@ -254,14 +257,14 @@ fn extract_token_from_expression(callee: AnyJsExpression) -> Option) -> Option { let trimmed = text.text_trimmed(); - let first_char = trimmed.chars().next(); - let last_char = trimmed.chars().last(); - let is_single_quoted = first_char == Some('\'') && last_char == Some('\''); - let is_double_quoted = first_char == Some('"') && last_char == Some('"'); + let is_single_quoted = trimmed.starts_with('\'') && trimmed.ends_with('\''); + let is_double_quoted = trimmed.starts_with('"') && trimmed.ends_with('"'); - let unquoted = if first_char.is_some() && last_char.is_some() { - trimmed.trim_matches(|c| c == '\'' || c == '"') + let unquoted = if is_single_quoted { + trimmed.trim_matches('\'') + } else if is_double_quoted { + trimmed.trim_matches('"') } else { trimmed }; From b3b0d2d0aa0925ca2723e31e6f7c4f16aaf06a06 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 17 Jul 2024 18:54:58 +0900 Subject: [PATCH 35/38] fix: remove print debug --- crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index 8147e330d70..d4b3daf99cf 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -144,8 +144,6 @@ impl Rule for UseTrimStartEnd { // `.as_static_value()` strips the information of tick tokens. let token = extract_token_from_expression(callee.clone())?; let unquoted = token.text().trim_matches('"'); - println!("unquoted: {:?}", unquoted); - println!("is_single: {:?}", ctx.as_preferred_quote().is_single()); let replaced_member_name = suggested_name(&token)?; let mut elements = vec![]; From f1f8db277d2aedc312c1ba772a4b188c45ff2740 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 17 Jul 2024 19:01:38 +0900 Subject: [PATCH 36/38] fix: remove unused variable --- crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index d4b3daf99cf..f83e69a42a2 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -143,7 +143,6 @@ impl Rule for UseTrimStartEnd { // Need to keep the original token to replace it with the new token. // `.as_static_value()` strips the information of tick tokens. let token = extract_token_from_expression(callee.clone())?; - let unquoted = token.text().trim_matches('"'); let replaced_member_name = suggested_name(&token)?; let mut elements = vec![]; From db74640ba8be66efc6dbc5eb49b8303e64b93f43 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 17 Jul 2024 21:56:20 +0900 Subject: [PATCH 37/38] fix: avoid using `unreachable` --- .../src/lint/nursery/use_trim_start_end.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs index f83e69a42a2..26d0774228e 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_trim_start_end.rs @@ -152,13 +152,9 @@ impl Rule for UseTrimStartEnd { elements.push(template_elements); let callee_object = match callee { - AnyJsExpression::JsStaticMemberExpression(ref expression) => { - expression.object().ok()? - } - AnyJsExpression::JsComputedMemberExpression(ref expression) => { - expression.object().ok()? - } - _ => unreachable!(), + AnyJsExpression::JsStaticMemberExpression(ref expression) => expression.object().ok(), + AnyJsExpression::JsComputedMemberExpression(ref expression) => expression.object().ok(), + _ => None, }; let transformed_expression = if is_template { @@ -199,7 +195,7 @@ impl Rule for UseTrimStartEnd { let call_expression = if is_computed_member { AnyJsExpression::JsComputedMemberExpression( make::js_computed_member_expression( - callee_object, + callee_object?, computed_member_expression_opt?.l_brack_token().ok()?, transformed_expression, computed_member_expression_opt?.r_brack_token().ok()?, @@ -208,7 +204,7 @@ impl Rule for UseTrimStartEnd { ) } else { AnyJsExpression::JsStaticMemberExpression(make::js_static_member_expression( - callee_object, + callee_object?, callee .as_js_static_member_expression()? .operator_token() From 99a6a88e69192c535da327f3f56bb5823042eb91 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 17 Jul 2024 22:07:41 +0900 Subject: [PATCH 38/38] chore: update rules.rs --- crates/biome_configuration/src/linter/rules.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 101aaf01521..8789170e1bd 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -3175,6 +3175,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3466,11 +3467,16 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3750,11 +3756,16 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"]