From 8445e7ebc91ab669882924d5fa1215025820f995 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 01:33:40 +0900 Subject: [PATCH 01/48] add: rule useAtIndex --- .../migrate/eslint_any_rule_to_biome.rs | 12 + .../src/analyzer/linter/rules.rs | 63 +- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 2 + .../src/lint/nursery/use_at_index.rs | 680 ++++++++++++++++++ crates/biome_js_analyze/src/options.rs | 1 + .../nursery/useAtIndex/charAtInvalid.json | 11 + .../useAtIndex/charAtInvalid.json.snap | 220 ++++++ .../specs/nursery/useAtIndex/charAtValid.json | 11 + .../nursery/useAtIndex/charAtValid.json.snap | 49 ++ .../checkAllIndexAccessInvalid.json | 8 + .../checkAllIndexAccessInvalid.json.snap | 148 ++++ .../checkAllIndexAccessInvalid.options.json | 16 + .../useAtIndex/checkAllIndexAccessValid.json | 5 + .../checkAllIndexAccessValid.json.snap | 19 + .../checkAllIndexAccessValid.options.json | 16 + .../nursery/useAtIndex/indexInvalid.json | 22 + .../nursery/useAtIndex/indexInvalid.json.snap | 484 +++++++++++++ .../specs/nursery/useAtIndex/indexValid.json | 16 + .../nursery/useAtIndex/indexValid.json.snap | 74 ++ .../nursery/useAtIndex/sliceInvalid.json | 28 + .../nursery/useAtIndex/sliceInvalid.json.snap | 628 ++++++++++++++++ .../specs/nursery/useAtIndex/sliceValid.json | 31 + .../nursery/useAtIndex/sliceValid.json.snap | 149 ++++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 25 + .../@biomejs/biome/configuration_schema.json | 38 + 26 files changed, 2735 insertions(+), 22 deletions(-) create mode 100644 crates/biome_js_analyze/src/lint/nursery/use_at_index.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap 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 16318d7cd703..e804e24520ad 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 @@ -1515,6 +1515,18 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.use_flat_map.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } + "unicorn/prefer-at" => { + if !options.include_inspired { + results.has_inspired_rules = true; + return false; + } + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group.use_at_index.get_or_insert(Default::default()); + rule.set_level(rule_severity.into()); + } "unicorn/prefer-date-now" => { let group = rules.complexity.get_or_insert_with(Default::default); let rule = group.use_date_now.get_or_insert(Default::default()); diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 7f9d335f686c..1b5c04f88464 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3359,6 +3359,9 @@ pub struct Nursery { #[serde(skip_serializing_if = "Option::is_none")] pub use_aria_props_supported_by_role: Option>, + #[doc = "Succinct description of the rule."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_at_index: Option>, #[doc = "Enforce declaring components only within modules that export React Components exclusively."] #[serde(skip_serializing_if = "Option::is_none")] pub use_component_export_only_modules: @@ -3440,6 +3443,7 @@ impl Nursery { "noValueAtRule", "useAdjacentOverloadSignatures", "useAriaPropsSupportedByRole", + "useAtIndex", "useComponentExportOnlyModules", "useConsistentCurlyBraces", "useConsistentMemberAccessibility", @@ -3473,9 +3477,9 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), - 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[31]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3512,6 +3516,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3648,56 +3653,61 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_component_export_only_modules.as_ref() { + if let Some(rule) = self.use_at_index.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_consistent_curly_braces.as_ref() { + if let Some(rule) = self.use_component_export_only_modules.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { + if let Some(rule) = self.use_consistent_curly_braces.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.use_explicit_function_return_type.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[28])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_explicit_function_return_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.use_sorted_classes.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[30])); } } - if let Some(rule) = self.use_strict_mode.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[31])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - 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[33])); } } + 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[34])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -3822,56 +3832,61 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_component_export_only_modules.as_ref() { + if let Some(rule) = self.use_at_index.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_consistent_curly_braces.as_ref() { + if let Some(rule) = self.use_component_export_only_modules.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { + if let Some(rule) = self.use_consistent_curly_braces.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.use_explicit_function_return_type.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[28])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_explicit_function_return_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.use_sorted_classes.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[30])); } } - if let Some(rule) = self.use_strict_mode.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[31])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - 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[33])); } } + 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[34])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -4004,6 +4019,10 @@ impl Nursery { .use_aria_props_supported_by_role .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useAtIndex" => self + .use_at_index + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useComponentExportOnlyModules" => self .use_component_export_only_modules .as_ref() diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index d39031092aa9..3d380af384af 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -177,6 +177,7 @@ define_categories! { "lint/nursery/noValueAtRule": "https://biomejs.dev/linter/rules/no-value-at-rule", "lint/nursery/useAdjacentOverloadSignatures": "https://biomejs.dev/linter/rules/use-adjacent-overload-signatures", "lint/nursery/useAriaPropsSupportedByRole": "https://biomejs.dev/linter/rules/use-aria-props-supported-by-role", + "lint/nursery/useAtIndex": "https://biomejs.dev/linter/rules/use-at-index", "lint/nursery/useBiomeSuppressionComment": "https://biomejs.dev/linter/rules/use-biome-suppression-comment", "lint/nursery/useComponentExportOnlyModules": "https://biomejs.dev/linter/rules/use-components-only-module", "lint/nursery/useConsistentCurlyBraces": "https://biomejs.dev/linter/rules/use-consistent-curly-braces", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index c808f8122d78..4228deb21b6d 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -20,6 +20,7 @@ pub mod no_template_curly_in_string; pub mod no_useless_escape_in_regex; pub mod use_adjacent_overload_signatures; pub mod use_aria_props_supported_by_role; +pub mod use_at_index; pub mod use_component_export_only_modules; pub mod use_consistent_curly_braces; pub mod use_consistent_member_accessibility; @@ -52,6 +53,7 @@ declare_lint_group! { self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , + self :: use_at_index :: UseAtIndex , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs new file mode 100644 index 000000000000..eb984965d7cc --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -0,0 +1,680 @@ +use ::serde::{Deserialize, Serialize}; +use biome_analyze::{ + context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, + RuleSource, RuleSourceKind, +}; +use biome_console::markup; +use biome_js_factory::make::{self}; +use biome_js_syntax::{ + AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, JsCallExpression, + JsComputedMemberExpression, JsParenthesizedExpression, JsUnaryExpression, T, +}; +use biome_rowan::{declare_node_union, AstNode, BatchMutationExt}; + +use crate::JsRuleAction; + +#[cfg(feature = "schemars")] +use schemars::JsonSchema; + +declare_lint_rule! { + /// Succinct description of the rule. + /// + /// Put context and details about the rule. + /// As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + /// + /// Try to stay consistent with the descriptions of implemented rules. + /// + /// ## Examples + /// + /// ### Invalid + /// + pub UseAtIndex { + version: "next", + name: "useAtIndex", + language: "js", + recommended: false, + sources: &[RuleSource::EslintUnicorn("prefer-at")], + source_kind: RuleSourceKind::Inspired, + fix_kind: FixKind::Unsafe, + } +} + +declare_node_union! { + pub AnyJsArrayAccess = JsComputedMemberExpression | JsCallExpression +} + +pub struct UseAtIndexState { + at_number_exp: AnyJsExpression, + error_type: ErrorType, + object: AnyJsExpression, +} + +pub enum ErrorType { + NegativeIndex, + IdIndex, + StringCharAtNegativeIndex, + StringCharAt, + Slice, + GetLastFunction, +} + +/// If the node is a parenthized expression, it returns the expression inside. +/// # Examples +/// ```js +/// a // Some(a) +/// (a) // Some(a) +/// (a + b) // Some(a + b) +/// ``` +fn solve_parenthesized_expression(node: &AnyJsExpression) -> Option { + if let AnyJsExpression::JsParenthesizedExpression(parenthesized_exp) = node { + let exp = parenthesized_exp.expression().ok()?; + solve_parenthesized_expression(&exp) + } else { + Some(node.clone()) + } +} + +/// Check if two expressions reference the same value. +/// Only literals are allowed for members. +/// # Examples +/// ```js +/// a == a +/// a.b == a.b +/// a?.b == a.b +/// a[0] == a[0] +/// a['b'] == a['b'] +/// ``` +fn is_same_reference(left: &AnyJsExpression, right: &AnyJsExpression) -> Option { + // solve JsParenthesizedExpression + let left = solve_parenthesized_expression(left)?; + let right = solve_parenthesized_expression(right)?; + match left { + // x[0] + AnyJsExpression::JsComputedMemberExpression(left) => match right { + AnyJsExpression::JsComputedMemberExpression(right) => { + let AnyJsExpression::AnyJsLiteralExpression(left_member) = + solve_parenthesized_expression(&left.member().ok()?)? + else { + return Some(false); + }; + let AnyJsExpression::AnyJsLiteralExpression(right_member) = + solve_parenthesized_expression(&right.member().ok()?)? + else { + return Some(false); + }; + if left_member.text() != right_member.text() { + return Some(false); + } + is_same_reference(&left.object().ok()?, &right.object().ok()?) + } + _ => Some(false), + }, + // x.y + AnyJsExpression::JsStaticMemberExpression(left) => match right { + AnyJsExpression::JsStaticMemberExpression(right) => { + let left_member = left.member().ok()?; + let right_member = right.member().ok()?; + if left_member.text() != right_member.text() { + Some(false) + } else { + is_same_reference(&left.object().ok()?, &right.object().ok()?) + } + } + _ => Some(false), + }, + // x + AnyJsExpression::JsIdentifierExpression(left) => match right { + AnyJsExpression::JsIdentifierExpression(right) => { + Some(left.name().ok()?.text() == right.name().ok()?.text()) + } + _ => Some(false), + }, + // this + AnyJsExpression::JsThisExpression(_) => match right { + AnyJsExpression::JsThisExpression(_) => Some(true), + _ => Some(false), + }, + _ => Some(false), + } +} + +/// If the node is a length method, it returns the object of interest. +fn get_length_node(node: &AnyJsExpression) -> Option { + let AnyJsExpression::JsStaticMemberExpression(node) = node else { + return None; + }; + let member_name = node.member().ok()?; + let member_name = member_name + .as_js_name()? + .value_token() + .ok()? + .token_text_trimmed(); + if member_name.text() != "length" { + return None; + } + node.object().ok() +} + +/// AnyJsExpressiion -> Some(i64) if the expression is an integer literal, otherwise None. +fn get_integer_from_literal(node: &AnyJsExpression) -> Option { + if let AnyJsExpression::JsUnaryExpression(unary) = node { + let token = unary.operator_token().ok()?; + if token.kind() != T![-] { + return None; + } + return get_integer_from_literal(&solve_parenthesized_expression(&unary.argument().ok()?)?) + .map(|num| -num); + } + let AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::JsNumberLiteralExpression( + number, + )) = node + else { + return None; + }; + let number = number.as_number()?; + if number.fract() == 0.0 { + Some(i64::try_from(number as i128).ok()?) + } else { + None + } +} + +/// If the node is a negative index, it returns the negative index. +/// # Examples +/// ```js +/// hoge[hoge.length - 0] // => None +/// hoge[hoge.length - 1] // => Some(-1) +/// hoge[fuga.length - 2] // => None +/// ``` +fn get_negative_index( + member: &AnyJsExpression, + object: &AnyJsExpression, +) -> Option { + let AnyJsExpression::JsBinaryExpression(member) = member else { + return None; + }; + let token = member.operator_token().ok()?; + if token.kind() != T![-] { + return None; + } + // left expression should be hoge.length + let left = solve_parenthesized_expression(&member.left().ok()?)?; + let length_parent = get_length_node(&left)?; + // left expression should be the same as the object + if !is_same_reference(object, &length_parent)? { + return None; + } + let number_exp = solve_parenthesized_expression(&member.right().ok()?)?; + // right expression should be integer + let number = get_integer_from_literal(&number_exp)?; + if number > 0 { + Some(AnyJsExpression::JsUnaryExpression( + make::js_unary_expression(make::token(T![-]), number_exp), + )) + } else { + None + } +} + +/// Is the node a child node of `delete`? +fn is_delete_child(node: &AnyJsExpression) -> Option { + node.syntax().parent()?.ancestors().find_map(|ancestor| { + if let Some(unary) = JsUnaryExpression::cast(ancestor.clone()) { + unary + .operator_token() + .ok() + .map(|token| token.kind() == T![delete]) + .or(Some(false)) + } else { + (!JsParenthesizedExpression::can_cast(ancestor.kind())).then_some(false) + } + }) +} + +fn make_number_literal(value: i64) -> AnyJsExpression { + AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::JsNumberLiteralExpression( + make::js_number_literal_expression(make::js_number_literal(value)), + )) +} + +/// check if the node is a zero-argument method. +// fn check_zero_arg_method(node: &JsCallExpression) -> Option<(String, AnyJsExpression)> { +// let member = node.callee().ok()?; +// match member { +// AnyJsExpression::JsStaticMemberExpression(member) => { +// let member_name = member.member().ok()?; +// let member_name = member_name.as_js_name()?.value_token().ok()?; +// let member_name = member_name.token_text_trimmed(); +// let args = node.arguments().ok()?.args(); +// if args.into_iter().count() != 0 { +// return None; +// } else { +// Some((member_name.text().to_string(), member.object().ok()?)) +// } + +// }, +// _ => None, +// } +// } + +/// check if the node is a slice +/// # Examples +/// ```js +/// .slice(0)[0] +/// .slice(0, 1).pop(0) +/// ``` +fn check_get_element_by_slice(node: &AnyJsExpression) -> Option { + if is_delete_child(node).unwrap_or(false) { + return None; + } + // selector + let (selected_exp, at_value): (AnyJsExpression, i64) = match node { + // .pop() or .shift() + AnyJsExpression::JsCallExpression(call_exp) => { + let arg_length = call_exp.arguments().ok()?.args().into_iter().count(); + if arg_length != 0 { + return None; + } + let member = solve_parenthesized_expression(&call_exp.callee().ok()?)?; + let AnyJsExpression::JsStaticMemberExpression(member) = member else { + return None; + }; + if call_exp.is_optional_chain() || member.is_optional_chain() { + return None; + } + let member_name = member + .member() + .ok()? + .as_js_name()? + .value_token() + .ok()? + .token_text_trimmed(); + let object = solve_parenthesized_expression(&member.object().ok()?)?; + if member_name == "pop" { + (object, -1) + } else if member_name == "shift" { + (object, 0) + } else { + return None; + } + } + AnyJsExpression::JsComputedMemberExpression(member) => { + let object = solve_parenthesized_expression(&member.object().ok()?)?; + if member.is_optional_chain() { + return None; + } + let value = + get_integer_from_literal(&solve_parenthesized_expression(&member.member().ok()?)?)?; + // enable only x[0] + if value != 0 { + return None; + } + (object, value) + } + _ => return None, + }; + // .slice(0,1) + let AnyJsExpression::JsCallExpression(call_exp) = selected_exp else { + return None; + }; + let AnyJsExpression::JsStaticMemberExpression(member) = call_exp.callee().ok()? else { + return None; + }; + let member_name = member + .member() + .ok()? + .as_js_name()? + .value_token() + .ok()? + .token_text_trimmed(); + if member_name != "slice" { + return None; + } + // arg length should be 1 or 2 + let args: Vec<_> = call_exp + .arguments() + .ok()? + .args() + .into_iter() + .flatten() + .collect(); + if args.is_empty() || args.len() > 2 { + return None; + } + let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + return None; + }; + let start_exp = solve_parenthesized_expression(arg0)?; + let start_index = get_integer_from_literal(&start_exp)?; + + let sliced_exp = member.object().ok()?; + + if args.len() == 1 { + if (at_value == 0) || (start_index == -1 && at_value == -1) { + return Some(UseAtIndexState { + at_number_exp: start_exp.trim_trivia()?, + error_type: ErrorType::Slice, + object: sliced_exp, + }); + } + return None; + } + let AnyJsCallArgument::AnyJsExpression(arg1) = &args[1] else { + return None; + }; + let end_exp = solve_parenthesized_expression(arg1)?; + let end_index = get_integer_from_literal(&end_exp)?; + // enable only x.slice(2, 4) + if start_index * end_index >= 0 && start_index < end_index { + if at_value == 0 { + Some(UseAtIndexState { + at_number_exp: start_exp.trim_trivia()?, + error_type: ErrorType::Slice, + object: sliced_exp, + }) + } else { + Some(UseAtIndexState { + at_number_exp: make_number_literal(end_index - 1), + error_type: ErrorType::Slice, + object: sliced_exp, + }) + } + } else { + None + } +} + +/// make `object.at(arg)` +fn make_at_method(object: AnyJsExpression, arg: AnyJsExpression) -> JsCallExpression { + let at_member = make::js_static_member_expression( + object, + make::token(T![.]), + make::js_name(make::ident("at")).into(), + ); + let args = make::js_call_arguments( + make::token(T!['(']), + make::js_call_argument_list([AnyJsCallArgument::AnyJsExpression(arg)], []), + make::token(T![')']), + ); + make::js_call_expression(at_member.into(), args).build() +} + +#[derive( + Clone, + Debug, + Default, + biome_deserialize_macros::Deserializable, + Deserialize, + Serialize, + Eq, + PartialEq, +)] +#[cfg_attr(feature = "schemars", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct UseAtIndexOptions { + // Force the use of the `.at()` method in cases other than positive integers. + pub check_all_index_access: bool, +} + +impl Rule for UseAtIndex { + type Query = Ast; + type State = UseAtIndexState; + type Signals = Option; + type Options = UseAtIndexOptions; + + fn run(ctx: &RuleContext) -> Self::Signals { + let exp = ctx.query(); + + let result: Option = match exp { + // hoge[a] + AnyJsArrayAccess::JsComputedMemberExpression(exp) => { + // check slice + if let Some(slice_err) = check_get_element_by_slice( + &AnyJsExpression::JsComputedMemberExpression(exp.clone()), + ) { + return Some(slice_err); + } + // invalid optional chain + if exp.is_optional_chain() { + return None; + } + // invalid mutable case + if is_delete_child(&AnyJsExpression::JsComputedMemberExpression(exp.clone())) + .unwrap_or(false) + { + return None; + } + // check member + let member = solve_parenthesized_expression(&exp.member().ok()?)?; + match member.clone() { + // hoge[hoge.length - 1] + AnyJsExpression::JsBinaryExpression(_binary) => Some(UseAtIndexState { + at_number_exp: get_negative_index( + &member, + &solve_parenthesized_expression(&exp.object().ok()?)?, + )?, + error_type: ErrorType::NegativeIndex, + object: exp.object().ok()?, + }), + // hoge[1] + AnyJsExpression::AnyJsLiteralExpression(member) => { + let AnyJsLiteralExpression::JsNumberLiteralExpression(member) = member + else { + return None; + }; + let value_token = member.value_token().ok()?; + let number = value_token.text_trimmed(); + if let Ok(number) = number.parse::() { + if number >= 0 { + let option = ctx.options(); + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: make_number_literal(number), + error_type: ErrorType::IdIndex, + object: exp.object().ok()?, + }) + } else { + None + } + } else { + None + } + } + _ => None, + } + } + // hoge.fuga() + AnyJsArrayAccess::JsCallExpression(call_exp) => { + // check slice + if let Some(slice_err) = + check_get_element_by_slice(&AnyJsExpression::JsCallExpression(call_exp.clone())) + { + return Some(slice_err); + } + + if call_exp.is_optional_chain() { + return None; + } + + let member = solve_parenthesized_expression(&call_exp.callee().ok()?)?; + match member { + AnyJsExpression::JsStaticMemberExpression(member) => { + if member.is_optional_chain() { + return None; + } + let member_name = member + .member() + .ok()? + .as_js_name()? + .value_token() + .ok()? + .token_text_trimmed(); + match member_name.text() { + "last" => { + let args: Vec<_> = call_exp + .arguments() + .ok()? + .args() + .into_iter() + .flatten() + .collect(); + if args.len() != 1 { + return None; + } + let object = member.object().ok()?; + let AnyJsExpression::JsIdentifierExpression(object) = object else { + return None; + }; + let lodash_function = ["_", "lodash", "underscore"]; + let object_name = object.syntax().text().to_string(); + if lodash_function.contains(&object_name.as_str()) { + let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + return None; + }; + Some(UseAtIndexState { + at_number_exp: make_number_literal(-1), + error_type: ErrorType::GetLastFunction, + object: solve_parenthesized_expression(arg0)?, + }) + } else { + None + } + } + "charAt" => { + let args: Vec<_> = call_exp + .arguments() + .ok()? + .args() + .into_iter() + .flatten() + .collect(); + if args.len() != 1 { + return None; + } + let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + return None; + }; + let core_arg0 = solve_parenthesized_expression(arg0)?; + let char_at_parent = + &solve_parenthesized_expression(&member.object().ok()?)?; + match core_arg0.clone() { + // hoge.charAt(hoge.length - 1) + AnyJsExpression::JsBinaryExpression(_) => { + let at_number_exp_2 = + get_negative_index(&core_arg0, char_at_parent)?; + Some(UseAtIndexState { + at_number_exp: at_number_exp_2, + error_type: ErrorType::StringCharAtNegativeIndex, + object: char_at_parent.clone(), + }) + } + // hoge.charAt(1) + AnyJsExpression::AnyJsLiteralExpression(_member) => { + let number = get_integer_from_literal(&core_arg0)?; + let option = ctx.options(); + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: make_number_literal(number), + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }) + } + _ => None, + } + } + //"lastIndexOf" => Some(ErrorType::GetLastFunction), + _ => None, + } + } + _ => return None, + } + } + }; + result + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + Some(RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "Replace index references with "".at()""." + }, + ).note( + match state.error_type { + ErrorType::NegativeIndex => { + markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." } + } + ErrorType::IdIndex => { + markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." } + } + ErrorType::StringCharAtNegativeIndex => { + markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." } + } + ErrorType::StringCharAt => { + markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." } + } + ErrorType::Slice => { + markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)[0]""." } + } + ErrorType::GetLastFunction => { + markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." } + } + } + )) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + let mut mutation = ctx.root().begin(); + let prev_node = match node { + AnyJsArrayAccess::JsComputedMemberExpression(node) => { + AnyJsExpression::JsComputedMemberExpression(node.clone()) + } + AnyJsArrayAccess::JsCallExpression(node) => { + AnyJsExpression::JsCallExpression(node.clone()) + } + }; + let UseAtIndexState { + at_number_exp, + error_type: _, + object, + } = state; + let object = match object { + AnyJsExpression::JsArrayExpression(exp) => { + AnyJsExpression::JsArrayExpression(exp.clone()) + } + AnyJsExpression::JsCallExpression(exp) => { + AnyJsExpression::JsCallExpression(exp.clone()) + } + AnyJsExpression::JsComputedMemberExpression(exp) => { + AnyJsExpression::JsComputedMemberExpression(exp.clone()) + } + AnyJsExpression::JsIdentifierExpression(exp) => { + AnyJsExpression::JsIdentifierExpression(exp.clone().trim_trivia()?) + } + AnyJsExpression::JsParenthesizedExpression(exp) => { + AnyJsExpression::JsParenthesizedExpression(exp.clone()) + } + AnyJsExpression::JsStaticMemberExpression(exp) => { + AnyJsExpression::JsStaticMemberExpression(exp.clone()) + } + _ => AnyJsExpression::JsParenthesizedExpression(make::js_parenthesized_expression( + make::token(T!['(']), + object.clone(), + make::token(T![')']), + )), + }; + + mutation.replace_node( + prev_node, + AnyJsExpression::JsCallExpression(make_at_method(object, at_number_exp.clone())), + ); + + Some(JsRuleAction::new( + ActionCategory::QuickFix, + ctx.metadata().applicability(), + markup! { "Replace index references with "".at()""." }.to_owned(), + mutation, + )) + } +} diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 8babc8375238..32283ef96874 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -284,6 +284,7 @@ pub type UseArrowFunction = ::Options; pub type UseAsConstAssertion = ::Options; +pub type UseAtIndex = ::Options; pub type UseAwait = ::Options; pub type UseBlockStatements = ::Options; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json new file mode 100644 index 000000000000..bd7b3e789a7a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json @@ -0,0 +1,11 @@ +[ + "string.charAt(string.length - 1);", + "string.charAt(string.length - 0o11);", + "some.string.charAt(some.string.length - 1);", + "string.charAt((( string.length )) - 0xFF);", + "string.charAt(string.length - (( 1 )));", + "string.charAt((( string.length - 1 )));", + "(( string )).charAt(string.length - 1);", + "(( string.charAt ))(string.length - 1);", + "(( string.charAt(string.length - 1) ));" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap new file mode 100644 index 000000000000..33075c836ddd --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap @@ -0,0 +1,220 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: charAtInvalid.json +--- +# Input +```cjs +string.charAt(string.length - 1); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +string.charAt(string.length - 0o11); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - 0o11); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·0o11); + + string.at(-0o11); + + +``` + +# Input +```cjs +some.string.charAt(some.string.length - 1); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ some.string.charAt(some.string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - some.string.charAt(some.string.length·-·1); + + some.string.at(-1); + + +``` + +# Input +```cjs +string.charAt((( string.length )) - 0xFF); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt((( string.length )) - 0xFF); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(((·string.length·))·-·0xFF); + + string.at(-0xFF); + + +``` + +# Input +```cjs +string.charAt(string.length - (( 1 ))); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - (( 1 ))); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·((·1·))); + + string.at(-1·); + + +``` + +# Input +```cjs +string.charAt((( string.length - 1 ))); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt((( string.length - 1 ))); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(((·string.length·-·1·))); + + string.at(-1·); + + +``` + +# Input +```cjs +(( string )).charAt(string.length - 1); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string )).charAt(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string·)).charAt(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +(( string.charAt ))(string.length - 1); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string.charAt ))(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string.charAt·))(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +(( string.charAt(string.length - 1) )); +``` + +# Diagnostics +``` +charAtInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string.charAt(string.length - 1) )); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string.charAt(string.length·-·1)·)); + + ((·string.at(-1)·)); + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json new file mode 100644 index 000000000000..c3bcee6b8eb2 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json @@ -0,0 +1,11 @@ +[ + "string.charAt(string.length - 0);", + "string.charAt(string.length + 1)", + "string.charAt(string.length + -1)", + "foo.charAt(bar.length - 1)", + "string?.charAt?.(string.length - 1);", + "string?.charAt(string.length - 1);", + "string.charAt(9);", + "string1.charAt(string2.length - 1);", + "string.charAt(hoge.string.length - 1)" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap new file mode 100644 index 000000000000..412b0f83e54a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap @@ -0,0 +1,49 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: charAtValid.json +--- +# Input +```cjs +string.charAt(string.length - 0); +``` + +# Input +```cjs +string.charAt(string.length + 1) +``` + +# Input +```cjs +string.charAt(string.length + -1) +``` + +# Input +```cjs +foo.charAt(bar.length - 1) +``` + +# Input +```cjs +string?.charAt?.(string.length - 1); +``` + +# Input +```cjs +string?.charAt(string.length - 1); +``` + +# Input +```cjs +string.charAt(9); +``` + +# Input +```cjs +string1.charAt(string2.length - 1); +``` + +# Input +```cjs +string.charAt(hoge.string.length - 1) +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json new file mode 100644 index 000000000000..c83de934a80d --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json @@ -0,0 +1,8 @@ +[ + "_.last(array)", + "lodash.last(array)", + "underscore.last(array)", + "_.last(new Array)", + "if (foo) _.last([bar])", + "function foo() {return _.last(arguments)}" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap new file mode 100644 index 000000000000..c9f1ae8296eb --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap @@ -0,0 +1,148 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: checkAllIndexAccessInvalid.json +--- +# Input +```cjs +_.last(array) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(array) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +lodash.last(array) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ lodash.last(array) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - lodash.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +underscore.last(array) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ underscore.last(array) + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - underscore.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +_.last(new Array) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(new Array) + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(new·Array) + + (new·Array).at(-1) + + +``` + +# Input +```cjs +if (foo) _.last([bar]) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ if (foo) _.last([bar]) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - if·(foo)·_.last([bar]) + + if·(foo)·[bar].at(-1) + + +``` + +# Input +```cjs +function foo() {return _.last(arguments)} +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ function foo() {return _.last(arguments)} + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - function·foo()·{return·_.last(arguments)} + + function·foo()·{return·arguments.at(-1)} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json new file mode 100644 index 000000000000..f1c0c4e50343 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useAtIndex": { + "level": "error", + "options": { + "checkAllIndexAccess": true + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json new file mode 100644 index 000000000000..2e173476eb5f --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json @@ -0,0 +1,5 @@ +[ + "new _.last(array)", + "_.last(array, 2)", + "_.last(...array)" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap new file mode 100644 index 000000000000..abeff66bb442 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap @@ -0,0 +1,19 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: checkAllIndexAccessValid.json +--- +# Input +```cjs +new _.last(array) +``` + +# Input +```cjs +_.last(array, 2) +``` + +# Input +```cjs +_.last(...array) +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json new file mode 100644 index 000000000000..f1c0c4e50343 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useAtIndex": { + "level": "error", + "options": { + "checkAllIndexAccess": true + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json new file mode 100644 index 000000000000..8155b7dfba80 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json @@ -0,0 +1,22 @@ +[ + "array[array.length - 1];", + "array[array.length -1];", + "array[array.length - /* comment */ 1];", + "array[array.length - 1.];", + "array[array.length - 0b1];", + "array[array.length - 9];", + "array[0][array[0].length - 1];", + "array[(( array.length )) - 1];", + "array[array.length - (( 1 ))];", + "array[(( array.length - 1 ))];", + "(( array ))[array.length - 1];", + "(( array[array.length - 1] ));", + "array[array.length - 1].pop().shift()[0];", + "a = array[array.length - 1]", + "const a = array[array.length - 1]", + "const {a = array[array.length - 1]} = {}", + "typeof array[array.length - 1]", + "function foo() {return arguments[arguments.length - 1]}", + "class Foo {bar; baz() {return this.bar[this.bar.length - 1]}}", + "class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}}" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap new file mode 100644 index 000000000000..42dc0b2fdba0 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap @@ -0,0 +1,484 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: indexInvalid.json +--- +# Input +```cjs +array[array.length - 1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length -1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length -1]; + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - /* comment */ 1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - /* comment */ 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·/*·comment·*/·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - 1.]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1.]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1.]; + + array.at(-1.); + + +``` + +# Input +```cjs +array[array.length - 0b1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 0b1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·0b1]; + + array.at(-0b1); + + +``` + +# Input +```cjs +array[array.length - 9]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 9]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·9]; + + array.at(-9); + + +``` + +# Input +```cjs +array[0][array[0].length - 1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[0][array[0].length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[0][array[0].length·-·1]; + + array[0].at(-1); + + +``` + +# Input +```cjs +array[(( array.length )) - 1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[(( array.length )) - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[((·array.length·))·-·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - (( 1 ))]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - (( 1 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·((·1·))]; + + array.at(-1·); + + +``` + +# Input +```cjs +array[(( array.length - 1 ))]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[(( array.length - 1 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[((·array.length·-·1·))]; + + array.at(-1·); + + +``` + +# Input +```cjs +(( array ))[array.length - 1]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array ))[array.length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array·))[array.length·-·1]; + + ((·array·)).at(-1); + + +``` + +# Input +```cjs +(( array[array.length - 1] )); +``` + +# Diagnostics +``` +indexInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array[array.length - 1] )); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array[array.length·-·1]·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +array[array.length - 1].pop().shift()[0]; +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1].pop().shift()[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1].pop().shift()[0]; + + array.at(-1).pop().shift()[0]; + + +``` + +# Input +```cjs +a = array[array.length - 1] +``` + +# Diagnostics +``` +indexInvalid.json:1:5 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ a = array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - a·=·array[array.length·-·1] + + a·=·array.at(-1) + + +``` + +# Input +```cjs +const a = array[array.length - 1] +``` + +# Diagnostics +``` +indexInvalid.json:1:11 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const a = array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - const·a·=·array[array.length·-·1] + + const·a·=·array.at(-1) + + +``` + +# Input +```cjs +const {a = array[array.length - 1]} = {} +``` + +# Diagnostics +``` +indexInvalid.json:1:12 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const {a = array[array.length - 1]} = {} + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - const·{a·=·array[array.length·-·1]}·=·{} + + const·{a·=·array.at(-1)}·=·{} + + +``` + +# Input +```cjs +typeof array[array.length - 1] +``` + +# Diagnostics +``` +indexInvalid.json:1:8 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ typeof array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - typeof·array[array.length·-·1] + + typeof·array.at(-1) + + +``` + +# Input +```cjs +function foo() {return arguments[arguments.length - 1]} +``` + +# Diagnostics +``` +indexInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ function foo() {return arguments[arguments.length - 1]} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - function·foo()·{return·arguments[arguments.length·-·1]} + + function·foo()·{return·arguments.at(-1)} + + +``` + +# Input +```cjs +class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} +``` + +# Diagnostics +``` +indexInvalid.json:1:31 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - class·Foo·{bar;·baz()·{return·this.bar[this.bar.length·-·1]}} + + class·Foo·{bar;·baz()·{return·this.bar.at(-1)}} + + +``` + +# Input +```cjs +class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} +``` + +# Diagnostics +``` +indexInvalid.json:1:32 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - class·Foo·{#bar;·baz()·{return·this.#bar[this.#bar.length·-·1]}} + + class·Foo·{#bar;·baz()·{return·this.#bar.at(-1)}} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json new file mode 100644 index 000000000000..9f352b6d5586 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json @@ -0,0 +1,16 @@ +[ + "array.at(-1)", + "array[array.length - 0];", + "array[array.length + 1]", + "array[array.length + -1]", + "foo[bar.length - 1]", + "array?.[array.length - 1];", + "array[array.length - 1] = 1", + "array[array.length - 1] %= 1", + "++ array[array.length - 1]", + "array[array.length - 1] --", + "delete array[array.length - 1]", + "class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}}", + "([array[array.length - 1]] = [])", + "({foo: array[array.length - 1] = 9} = {})" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap new file mode 100644 index 000000000000..43b532c1b1ea --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap @@ -0,0 +1,74 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: valid.json +--- +# Input +```cjs +array.at(-1) +``` + +# Input +```cjs +array[array.length - 0]; +``` + +# Input +```cjs +array[array.length + 1] +``` + +# Input +```cjs +array[array.length + -1] +``` + +# Input +```cjs +foo[bar.length - 1] +``` + +# Input +```cjs +array?.[array.length - 1]; +``` + +# Input +```cjs +array[array.length - 1] = 1 +``` + +# Input +```cjs +array[array.length - 1] %= 1 +``` + +# Input +```cjs +++ array[array.length - 1] +``` + +# Input +```cjs +array[array.length - 1] -- +``` + +# Input +```cjs +delete array[array.length - 1] +``` + +# Input +```cjs +class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}} +``` + +# Input +```cjs +([array[array.length - 1]] = []) +``` + +# Input +```cjs +({foo: array[array.length - 1] = 9} = {}) +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json new file mode 100644 index 000000000000..3ad787784f90 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json @@ -0,0 +1,28 @@ +[ + "array.slice(0)[0]", + "array.slice(-0)[0]", + "array.slice(-1)[0]", + "array.slice(-1).pop()", + "array.slice(-1.0).shift()", + "array.slice(-9)[0]", + "array.slice(-0xA)[0b000]", + "array.slice(-9).shift()", + "array.slice(-1)[(( 0 ))];", + "array.slice(-(( 1 )))[0];", + "array.slice((( -1 )))[0];", + "(( array.slice(-1) ))[0];", + "(( array )).slice(-1)[0];", + "(( array.slice(-1)[0] ));", + "(( array.slice(-1) )).pop();", + "(( array.slice(-1).pop ))();", + "(( array.slice(-1).pop() ));", + "array.slice(-1)[0].pop().shift().slice(-1)", + "array.slice(-9, -8)[0]", + "array.slice(-9, -0o10)[0]", + "array.slice(-9, -8).pop()", + "array.slice(-9, -8).shift()", + "array.slice((( -9 )), (( -8 )), ).shift()", + "(( array.slice(-9, -8).shift ))()", + "array.slice(-0o11, -7)[0]", + "array.slice(-9, 0)[0]" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap new file mode 100644 index 000000000000..fae514020f5c --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap @@ -0,0 +1,628 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: sliceInvalid.json +--- +# Input +```cjs +array.slice(0)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(0)[0] + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(0)[0] + + array.at(0) + + +``` + +# Input +```cjs +array.slice(-0)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0)[0] + + array.at(-0) + + +``` + +# Input +```cjs +array.slice(-1)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[0] + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1).pop() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1).pop() + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1.0).shift() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1.0).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1.0).shift() + + array.at(-1.0) + + +``` + +# Input +```cjs +array.slice(-9)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-0xA)[0b000] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0xA)[0b000] + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0xA)[0b000] + + array.at(-0xA) + + +``` + +# Input +```cjs +array.slice(-9).shift() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).shift() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-1)[(( 0 ))]; +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[(( 0 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[((·0·))]; + + array.at(-1); + + +``` + +# Input +```cjs +array.slice(-(( 1 )))[0]; +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-(( 1 )))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-((·1·)))[0]; + + array.at(-((·1·))); + + +``` + +# Input +```cjs +array.slice((( -1 )))[0]; +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice((( -1 )))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(((·-1·)))[0]; + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1) ))[0]; +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1) ))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)·))[0]; + + array.at(-1); + + +``` + +# Input +```cjs +(( array )).slice(-1)[0]; +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array )).slice(-1)[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array·)).slice(-1)[0]; + + ((·array·)).at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1)[0] )); +``` + +# Diagnostics +``` +sliceInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1)[0] )); + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)[0]·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +(( array.slice(-1) )).pop(); +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1) )).pop(); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)·)).pop(); + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1).pop ))(); +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1).pop ))(); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1).pop·))(); + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1).pop() )); +``` + +# Diagnostics +``` +sliceInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1).pop() )); + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1).pop()·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +array.slice(-1)[0].pop().shift().slice(-1) +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[0].pop().shift().slice(-1) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[0].pop().shift().slice(-1) + + array.at(-1).pop().shift().slice(-1) + + +``` + +# Input +```cjs +array.slice(-9, -8)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -0o10)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -0o10)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-0o10)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -8).pop() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8).pop() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8).pop() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -8).shift() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8).shift() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice((( -9 )), (( -8 )), ).shift() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice((( -9 )), (( -8 )), ).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(((·-9·)),·((·-8·)),·).shift() + + array.at(-9) + + +``` + +# Input +```cjs +(( array.slice(-9, -8).shift ))() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-9, -8).shift ))() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-9,·-8).shift·))() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-0o11, -7)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0o11, -7)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0o11,·-7)[0] + + array.at(-0o11) + + +``` + +# Input +```cjs +array.slice(-9, 0)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, 0)[0] + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·0)[0] + + array.at(-9) + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json new file mode 100644 index 000000000000..9626bcb85a60 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json @@ -0,0 +1,31 @@ +[ + "array.slice(-1)", + "new array.slice(-1)", + "array.slice(-9).pop()", + "array.slice(-1.1)[0]", + "array.slice(-1)?.[0]", + "array.slice?.(-1)[0]", + "array?.slice(-1)[0]", + "array.notSlice(-1)[0]", + "array.slice()[0]", + "array.slice(...[-1])[0]", + "array.slice(-1).shift?.()", + "array.slice(-1)?.shift()", + "array.slice(-1).shift(...[])", + "new array.slice(-1).shift()", + "array.slice(-1)[0] += 1", + "++ array.slice(-1)[0]", + "array.slice(-1)[0] --", + "delete array.slice(-1)[0]", + "array.slice(-9.1, -8.1)[0]", + "array.slice(-unknown, -unknown2)[0]", + "array.slice(-9.1, unknown)[0]", + "array.slice(-9, unknown).pop()", + "array.slice(-9, ...unknown)[0]", + "array.slice(...[-9], unknown)[0]", + "array.slice(-9, unknown)[0]", + "array.slice(-9, unknown).shift()", + "const KNOWN = -8; array.slice(-9, KNOWN).shift()", + "(( (( array.slice( ((-9)), ((unknown)), ).shift ))() ));", + "array.slice(-9, (a, really, _really, complicated, second) => argument)[0]" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap new file mode 100644 index 000000000000..961047c348e0 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap @@ -0,0 +1,149 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: sliceValid.json +--- +# Input +```cjs +array.slice(-1) +``` + +# Input +```cjs +new array.slice(-1) +``` + +# Input +```cjs +array.slice(-9).pop() +``` + +# Input +```cjs +array.slice(-1.1)[0] +``` + +# Input +```cjs +array.slice(-1)?.[0] +``` + +# Input +```cjs +array.slice?.(-1)[0] +``` + +# Input +```cjs +array?.slice(-1)[0] +``` + +# Input +```cjs +array.notSlice(-1)[0] +``` + +# Input +```cjs +array.slice()[0] +``` + +# Input +```cjs +array.slice(...[-1])[0] +``` + +# Input +```cjs +array.slice(-1).shift?.() +``` + +# Input +```cjs +array.slice(-1)?.shift() +``` + +# Input +```cjs +array.slice(-1).shift(...[]) +``` + +# Input +```cjs +new array.slice(-1).shift() +``` + +# Input +```cjs +array.slice(-1)[0] += 1 +``` + +# Input +```cjs +++ array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-1)[0] -- +``` + +# Input +```cjs +delete array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-9.1, -8.1)[0] +``` + +# Input +```cjs +array.slice(-unknown, -unknown2)[0] +``` + +# Input +```cjs +array.slice(-9.1, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).pop() +``` + +# Input +```cjs +array.slice(-9, ...unknown)[0] +``` + +# Input +```cjs +array.slice(...[-9], unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).shift() +``` + +# Input +```cjs +const KNOWN = -8; array.slice(-9, KNOWN).shift() +``` + +# Input +```cjs +(( (( array.slice( ((-9)), ((unknown)), ).shift ))() )); +``` + +# Input +```cjs +array.slice(-9, (a, really, _really, complicated, second) => argument)[0] +``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 81fa5a018fc5..cea301f977ca 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1318,6 +1318,10 @@ export interface Nursery { * Enforce that ARIA properties are valid for the roles that are supported by the element. */ useAriaPropsSupportedByRole?: RuleConfiguration_for_Null; + /** + * Succinct description of the rule. + */ + useAtIndex?: RuleFixConfiguration_for_UseAtIndexOptions; /** * Enforce declaring components only within modules that export React Components exclusively. */ @@ -1996,6 +2000,9 @@ export type RuleConfiguration_for_RestrictedImportsOptions = export type RuleFixConfiguration_for_NoRestrictedTypesOptions = | RulePlainConfiguration | RuleWithFixOptions_for_NoRestrictedTypesOptions; +export type RuleFixConfiguration_for_UseAtIndexOptions = + | RulePlainConfiguration + | RuleWithFixOptions_for_UseAtIndexOptions; export type RuleConfiguration_for_UseComponentExportOnlyModulesOptions = | RulePlainConfiguration | RuleWithOptions_for_UseComponentExportOnlyModulesOptions; @@ -2157,6 +2164,20 @@ export interface RuleWithFixOptions_for_NoRestrictedTypesOptions { */ options: NoRestrictedTypesOptions; } +export interface RuleWithFixOptions_for_UseAtIndexOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ + level: RulePlainConfiguration; + /** + * Rule's options + */ + options: UseAtIndexOptions; +} export interface RuleWithOptions_for_UseComponentExportOnlyModulesOptions { /** * The severity of the emitted diagnostics by the rule @@ -2345,6 +2366,9 @@ export interface RestrictedImportsOptions { export interface NoRestrictedTypesOptions { types: {}; } +export interface UseAtIndexOptions { + checkAllIndexAccess: boolean; +} export interface UseComponentExportOnlyModulesOptions { /** * Allows the export of constants. This option is for environments that support it, such as [Vite](https://vitejs.dev/) @@ -2874,6 +2898,7 @@ export type Category = | "lint/nursery/noValueAtRule" | "lint/nursery/useAdjacentOverloadSignatures" | "lint/nursery/useAriaPropsSupportedByRole" + | "lint/nursery/useAtIndex" | "lint/nursery/useBiomeSuppressionComment" | "lint/nursery/useComponentExportOnlyModules" | "lint/nursery/useConsistentCurlyBraces" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index b45098e9db28..b83acd87fde7 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2248,6 +2248,13 @@ { "type": "null" } ] }, + "useAtIndex": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/UseAtIndexConfiguration" }, + { "type": "null" } + ] + }, "useComponentExportOnlyModules": { "description": "Enforce declaring components only within modules that export React Components exclusively.", "anyOf": [ @@ -2853,6 +2860,25 @@ }, "additionalProperties": false }, + "RuleWithUseAtIndexOptions": { + "type": "object", + "required": ["level"], + "properties": { + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/UseAtIndexOptions" }] + } + }, + "additionalProperties": false + }, "RuleWithUseComponentExportOnlyModulesOptions": { "type": "object", "required": ["level"], @@ -3979,6 +4005,18 @@ } ] }, + "UseAtIndexConfiguration": { + "anyOf": [ + { "$ref": "#/definitions/RulePlainConfiguration" }, + { "$ref": "#/definitions/RuleWithUseAtIndexOptions" } + ] + }, + "UseAtIndexOptions": { + "type": "object", + "required": ["checkAllIndexAccess"], + "properties": { "checkAllIndexAccess": { "type": "boolean" } }, + "additionalProperties": false + }, "UseComponentExportOnlyModulesConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" }, From 52c2ac0dfd0910bc689bc2db06e31831d7a9d8d5 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 04:05:45 +0900 Subject: [PATCH 02/48] add: rule document --- .../src/analyzer/linter/rules.rs | 2 +- .../src/lint/nursery/use_at_index.rs | 69 +++++++++++++++++-- .../@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- .../@biomejs/biome/configuration_schema.json | 2 +- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 1b5c04f88464..bb4914c6aa00 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3359,7 +3359,7 @@ pub struct Nursery { #[serde(skip_serializing_if = "Option::is_none")] pub use_aria_props_supported_by_role: Option>, - #[doc = "Succinct description of the rule."] + #[doc = "Enforce using .at to retrieve elements."] #[serde(skip_serializing_if = "Option::is_none")] pub use_at_index: Option>, #[doc = "Enforce declaring components only within modules that export React Components exclusively."] diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index eb984965d7cc..242d7c324cb1 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -17,17 +17,78 @@ use crate::JsRuleAction; use schemars::JsonSchema; declare_lint_rule! { - /// Succinct description of the rule. + /// Enforce using .at to retrieve elements. /// - /// Put context and details about the rule. - /// As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + /// When extracting elements from an array, especially when retrieving from the end, `.at` is convenient. Replace the previously used syntax with `.at()`. /// - /// Try to stay consistent with the descriptions of implemented rules. + /// ## Options + /// + /// ### `checkAllIndexAccess` + /// + /// By default, only negative element accesses will use errors, but I will also generate errors for positive accesses. + /// + /// ```json,ignore + /// { + /// "//": "...", + /// "options": { + /// "checkAllIndexAccess": true + /// } + /// } + /// ``` /// /// ## Examples /// /// ### Invalid /// + /// ```js,expect_diagnostic + /// const foo = array[array.length - 1]; + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = array[array.length - 5]; + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = array.slice(-1)[0]; + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = array.slice(-1).pop(); + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = array.slice(-5).shift(); + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = string.charAt(string.length - 5); + /// ``` + /// + /// ```js,expect_diagnostic + /// const foo = lodash.last(array); + /// ``` + /// + /// ### Valid + /// + /// ```js + /// const foo = array.at(-1); + /// ``` + /// + /// ```js + /// const foo = array.at(-5); + /// ``` + /// + /// ```js + /// const foo = array[100]; + /// ``` + /// + /// ```js + /// const foo = array.at(array.length - 1); + /// ``` + /// + /// ```js + /// array[array.length - 1] = foo; + /// ``` pub UseAtIndex { version: "next", name: "useAtIndex", diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index cea301f977ca..4cce75b9cac5 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1319,7 +1319,7 @@ export interface Nursery { */ useAriaPropsSupportedByRole?: RuleConfiguration_for_Null; /** - * Succinct description of the rule. + * Enforce using .at to retrieve elements. */ useAtIndex?: RuleFixConfiguration_for_UseAtIndexOptions; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index b83acd87fde7..5152e698b42f 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2249,7 +2249,7 @@ ] }, "useAtIndex": { - "description": "Succinct description of the rule.", + "description": "Enforce using .at to retrieve elements.", "anyOf": [ { "$ref": "#/definitions/UseAtIndexConfiguration" }, { "type": "null" } From 34e32790f253b2938d8d85dd48fcc9565b2ecbf6 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 04:06:31 +0900 Subject: [PATCH 03/48] fix: add_option & better error message --- .../src/lint/nursery/use_at_index.rs | 188 ++++++++----- .../useAtIndex/charAtInvalid.json.snap | 4 +- .../checkAllIndexAccessInvalid.json | 20 +- .../checkAllIndexAccessInvalid.json.snap | 262 +++++++++++++++--- .../useAtIndex/checkAllIndexAccessValid.json | 9 +- .../checkAllIndexAccessValid.json.snap | 21 +- .../nursery/useAtIndex/indexInvalid.json.snap | 4 +- .../nursery/useAtIndex/lodashInvalid.json | 8 + .../useAtIndex/lodashInvalid.json.snap | 148 ++++++++++ .../specs/nursery/useAtIndex/lodashValid.json | 5 + .../nursery/useAtIndex/lodashValid.json.snap | 19 ++ .../nursery/useAtIndex/sliceInvalid.json | 3 +- .../nursery/useAtIndex/sliceInvalid.json.snap | 52 +++- .../useAtIndex/sliceValid.json.snap.new | 168 +++++++++++ 14 files changed, 774 insertions(+), 137 deletions(-) create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 242d7c324cb1..569f6523fdd9 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -115,7 +115,10 @@ pub enum ErrorType { IdIndex, StringCharAtNegativeIndex, StringCharAt, - Slice, + Slice(String), + SlicePop, + Slice2(String), + Slice2Pop, GetLastFunction, } @@ -298,26 +301,6 @@ fn make_number_literal(value: i64) -> AnyJsExpression { )) } -/// check if the node is a zero-argument method. -// fn check_zero_arg_method(node: &JsCallExpression) -> Option<(String, AnyJsExpression)> { -// let member = node.callee().ok()?; -// match member { -// AnyJsExpression::JsStaticMemberExpression(member) => { -// let member_name = member.member().ok()?; -// let member_name = member_name.as_js_name()?.value_token().ok()?; -// let member_name = member_name.token_text_trimmed(); -// let args = node.arguments().ok()?.args(); -// if args.into_iter().count() != 0 { -// return None; -// } else { -// Some((member_name.text().to_string(), member.object().ok()?)) -// } - -// }, -// _ => None, -// } -// } - /// check if the node is a slice /// # Examples /// ```js @@ -329,7 +312,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option return None; } // selector - let (selected_exp, at_value): (AnyJsExpression, i64) = match node { + let (selected_exp, at_value, taker): (AnyJsExpression, i64, &str) = match node { // .pop() or .shift() AnyJsExpression::JsCallExpression(call_exp) => { let arg_length = call_exp.arguments().ok()?.args().into_iter().count(); @@ -352,9 +335,9 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option .token_text_trimmed(); let object = solve_parenthesized_expression(&member.object().ok()?)?; if member_name == "pop" { - (object, -1) + (object, -1, ".pop()") } else if member_name == "shift" { - (object, 0) + (object, 0, ".shift()") } else { return None; } @@ -370,7 +353,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option if value != 0 { return None; } - (object, value) + (object, value, "[0]") } _ => return None, }; @@ -411,10 +394,17 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option let sliced_exp = member.object().ok()?; if args.len() == 1 { - if (at_value == 0) || (start_index == -1 && at_value == -1) { + if at_value == 0 { + return Some(UseAtIndexState { + at_number_exp: start_exp, + error_type: ErrorType::Slice(taker.to_string()), + object: sliced_exp, + }); + } + if start_index < 0 && at_value == -1 { return Some(UseAtIndexState { - at_number_exp: start_exp.trim_trivia()?, - error_type: ErrorType::Slice, + at_number_exp: make_number_literal(-1), + error_type: ErrorType::SlicePop, object: sliced_exp, }); } @@ -429,14 +419,14 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option if start_index * end_index >= 0 && start_index < end_index { if at_value == 0 { Some(UseAtIndexState { - at_number_exp: start_exp.trim_trivia()?, - error_type: ErrorType::Slice, + at_number_exp: start_exp, + error_type: ErrorType::Slice2(taker.to_string()), object: sliced_exp, }) } else { Some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), - error_type: ErrorType::Slice, + error_type: ErrorType::Slice2Pop, object: sliced_exp, }) } @@ -485,6 +475,7 @@ impl Rule for UseAtIndex { fn run(ctx: &RuleContext) -> Self::Signals { let exp = ctx.query(); + let option = ctx.options(); let result: Option = match exp { // hoge[a] @@ -509,14 +500,24 @@ impl Rule for UseAtIndex { let member = solve_parenthesized_expression(&exp.member().ok()?)?; match member.clone() { // hoge[hoge.length - 1] - AnyJsExpression::JsBinaryExpression(_binary) => Some(UseAtIndexState { - at_number_exp: get_negative_index( + AnyJsExpression::JsBinaryExpression(_binary) => { + let negative_index_exp = get_negative_index( &member, &solve_parenthesized_expression(&exp.object().ok()?)?, - )?, - error_type: ErrorType::NegativeIndex, - object: exp.object().ok()?, - }), + ); + if let Some(negative_index) = negative_index_exp { + return Some(UseAtIndexState { + at_number_exp: negative_index, + error_type: ErrorType::NegativeIndex, + object: exp.object().ok()?, + }); + } + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::NegativeIndex, + object: exp.object().ok()?, + }) + } // hoge[1] AnyJsExpression::AnyJsLiteralExpression(member) => { let AnyJsLiteralExpression::JsNumberLiteralExpression(member) = member @@ -524,23 +525,44 @@ impl Rule for UseAtIndex { return None; }; let value_token = member.value_token().ok()?; - let number = value_token.text_trimmed(); - if let Ok(number) = number.parse::() { - if number >= 0 { - let option = ctx.options(); - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: make_number_literal(number), - error_type: ErrorType::IdIndex, - object: exp.object().ok()?, - }) - } else { - None - } + let number = value_token.text_trimmed().parse::().ok()?; + if number >= 0 { + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: make_number_literal(number), + error_type: ErrorType::IdIndex, + object: exp.object().ok()?, + }) } else { None } } - _ => None, + AnyJsExpression::JsUnaryExpression(unary) => { + if !option.check_all_index_access { + return None; + } + // ignore -5 + let token = unary.operator_token().ok()?; + if token.kind() == T![-] { + if let Some(arg) = get_integer_from_literal( + &solve_parenthesized_expression(&unary.argument().ok()?)?, + ) { + if arg >= 0 { + return None; + } + } + } + Some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::IdIndex, + object: exp.object().ok()?, + }) + } + AnyJsExpression::JsIdentifierExpression(_) => None, + _ => option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::IdIndex, + object: exp.object().ok()?, + }), } } // hoge.fuga() @@ -620,25 +642,37 @@ impl Rule for UseAtIndex { match core_arg0.clone() { // hoge.charAt(hoge.length - 1) AnyJsExpression::JsBinaryExpression(_) => { - let at_number_exp_2 = - get_negative_index(&core_arg0, char_at_parent)?; - Some(UseAtIndexState { - at_number_exp: at_number_exp_2, - error_type: ErrorType::StringCharAtNegativeIndex, - object: char_at_parent.clone(), - }) + let at_number_exp = + get_negative_index(&core_arg0, char_at_parent); + if let Some(at_number_exp) = at_number_exp { + Some(UseAtIndexState { + at_number_exp, + error_type: ErrorType::StringCharAtNegativeIndex, + object: char_at_parent.clone(), + }) + } else { + option.check_all_index_access.then_some( + UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }, + ) + } } // hoge.charAt(1) AnyJsExpression::AnyJsLiteralExpression(_member) => { - let number = get_integer_from_literal(&core_arg0)?; - let option = ctx.options(); option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: make_number_literal(number), + at_number_exp: core_arg0, error_type: ErrorType::StringCharAt, object: char_at_parent.clone(), }) } - _ => None, + _ => option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }), } } //"lastIndexOf" => Some(ErrorType::GetLastFunction), @@ -659,26 +693,35 @@ impl Rule for UseAtIndex { node.range(), markup! { "Replace index references with "".at()""." - }, + }.to_owned(), ).note( - match state.error_type { + match &state.error_type { ErrorType::NegativeIndex => { - markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." } + markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." }.to_owned() } ErrorType::IdIndex => { - markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." } + markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." }.to_owned() } ErrorType::StringCharAtNegativeIndex => { - markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." } + markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." }.to_owned() } ErrorType::StringCharAt => { - markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." } + markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." }.to_owned() + } + ErrorType::Slice(taker) => { + markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)"{taker}"." }.to_owned() } - ErrorType::Slice => { - markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)[0]""." } + ErrorType::SlicePop => { + markup! { "Prefer ""X.at(-1)"" over ""X.slice(-a).pop()""." }.to_owned() + } + ErrorType::Slice2(taker) => { + markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y, a)"{taker}"." }.to_owned() + } + ErrorType::Slice2Pop => { + markup! { "Prefer ""X.at(Y - 1)"" over ""X.slice(a, Y).pop()""." }.to_owned() } ErrorType::GetLastFunction => { - markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." } + markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() } } )) @@ -728,7 +771,10 @@ impl Rule for UseAtIndex { mutation.replace_node( prev_node, - AnyJsExpression::JsCallExpression(make_at_method(object, at_number_exp.clone())), + AnyJsExpression::JsCallExpression(make_at_method( + object, + at_number_exp.clone().trim_trivia()?, + )), ); Some(JsRuleAction::new( diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap index 33075c836ddd..d33dac4d10ab 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap @@ -118,7 +118,7 @@ charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━ i Unsafe fix: Replace index references with .at(). - string.charAt(string.length·-·((·1·))); - + string.at(-1·); + + string.at(-1); ``` @@ -142,7 +142,7 @@ charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━ i Unsafe fix: Replace index references with .at(). - string.charAt(((·string.length·-·1·))); - + string.at(-1·); + + string.at(-1); ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json index c83de934a80d..6ea76e5c3e1e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json @@ -1,8 +1,16 @@ [ - "_.last(array)", - "lodash.last(array)", - "underscore.last(array)", - "_.last(new Array)", - "if (foo) _.last([bar])", - "function foo() {return _.last(arguments)}" + "array[0]", + "array[1]", + "array[5 + 9]", + "const offset = 5;array[offset + 9]", + "array[array.length - 1]", + "string.charAt(9)", + "string.charAt(5 + 9)", + "const offset = 5;string.charAt(offset + 9)", + "string.charAt(unknown)", + "string.charAt(-1)", + "string.charAt(1.5)", + "string.charAt(1n)", + "string.charAt(string.length - 1)", + "foo.charAt(bar.length - 1)" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap index c9f1ae8296eb..cbda2b5bb361 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap @@ -5,7 +5,7 @@ expression: checkAllIndexAccessInvalid.json --- # Input ```cjs -_.last(array) +array[0] ``` # Diagnostics @@ -14,22 +14,22 @@ checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━ ! Replace index references with .at(). - > 1 │ _.last(array) - │ ^^^^^^^^^^^^^ + > 1 │ array[0] + │ ^^^^^^^^ - i Prefer X.at(-1) over _.last(X). + i Prefer X.at(Y) over X[Y]. i Unsafe fix: Replace index references with .at(). - - _.last(array) - + array.at(-1) + - array[0] + + array.at(0) ``` # Input ```cjs -lodash.last(array) +array[1] ``` # Diagnostics @@ -38,14 +38,86 @@ checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━ ! Replace index references with .at(). - > 1 │ lodash.last(array) - │ ^^^^^^^^^^^^^^^^^^ + > 1 │ array[1] + │ ^^^^^^^^ + + i Prefer X.at(Y) over X[Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[1] + + array.at(1) + + +``` + +# Input +```cjs +array[5 + 9] +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). - i Prefer X.at(-1) over _.last(X). + > 1 │ array[5 + 9] + │ ^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. i Unsafe fix: Replace index references with .at(). - - lodash.last(array) + - array[5·+·9] + + array.at(5·+·9) + + +``` + +# Input +```cjs +const offset = 5;array[offset + 9] +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const offset = 5;array[offset + 9] + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - const·offset·=·5;array[offset·+·9] + + const·offset·=·5;array.at(offset·+·9) + + +``` + +# Input +```cjs +array[array.length - 1] +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1] + array.at(-1) @@ -53,7 +125,7 @@ checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━ # Input ```cjs -underscore.last(array) +string.charAt(9) ``` # Diagnostics @@ -62,22 +134,118 @@ checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━ ! Replace index references with .at(). - > 1 │ underscore.last(array) + > 1 │ string.charAt(9) + │ ^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.charAt(Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(9) + + string.at(9) + + +``` + +# Input +```cjs +string.charAt(5 + 9) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(5 + 9) + │ ^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.charAt(Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(5·+·9) + + string.at(5·+·9) + + +``` + +# Input +```cjs +const offset = 5;string.charAt(offset + 9) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const offset = 5;string.charAt(offset + 9) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.charAt(Y). + + i Unsafe fix: Replace index references with .at(). + + - const·offset·=·5;string.charAt(offset·+·9) + + const·offset·=·5;string.at(offset·+·9) + + +``` + +# Input +```cjs +string.charAt(unknown) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(unknown) │ ^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over _.last(X). + i Prefer X.at(Y) over X.charAt(Y). i Unsafe fix: Replace index references with .at(). - - underscore.last(array) - + array.at(-1) + - string.charAt(unknown) + + string.at(unknown) + + +``` + +# Input +```cjs +string.charAt(-1) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(-1) + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.charAt(Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(-1) + + string.at(-1) ``` # Input ```cjs -_.last(new Array) +string.charAt(1.5) ``` # Diagnostics @@ -86,63 +254,87 @@ checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━ ! Replace index references with .at(). - > 1 │ _.last(new Array) + > 1 │ string.charAt(1.5) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.charAt(Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(1.5) + + string.at(1.5) + + +``` + +# Input +```cjs +string.charAt(1n) +``` + +# Diagnostics +``` +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(1n) │ ^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over _.last(X). + i Prefer X.at(Y) over X.charAt(Y). i Unsafe fix: Replace index references with .at(). - - _.last(new·Array) - + (new·Array).at(-1) + - string.charAt(1n) + + string.at(1n) ``` # Input ```cjs -if (foo) _.last([bar]) +string.charAt(string.length - 1) ``` # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). - > 1 │ if (foo) _.last([bar]) - │ ^^^^^^^^^^^^^ + > 1 │ string.charAt(string.length - 1) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over _.last(X). + i Prefer X.at(-Y) over X.charAt(X.length - Y). i Unsafe fix: Replace index references with .at(). - - if·(foo)·_.last([bar]) - + if·(foo)·[bar].at(-1) + - string.charAt(string.length·-·1) + + string.at(-1) ``` # Input ```cjs -function foo() {return _.last(arguments)} +foo.charAt(bar.length - 1) ``` # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). - > 1 │ function foo() {return _.last(arguments)} - │ ^^^^^^^^^^^^^^^^^ + > 1 │ foo.charAt(bar.length - 1) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over _.last(X). + i Prefer X.at(Y) over X.charAt(Y). i Unsafe fix: Replace index references with .at(). - - function·foo()·{return·_.last(arguments)} - + function·foo()·{return·arguments.at(-1)} + - foo.charAt(bar.length·-·1) + + foo.at(bar.length·-·1) ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json index 2e173476eb5f..7597447d2f15 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json @@ -1,5 +1,8 @@ [ - "new _.last(array)", - "_.last(array, 2)", - "_.last(...array)" + "++array[1]", + "const offset = 5;const extraArgument = 6;string.charAt(offset + 9, extraArgument)", + "array[unknown]", + "array[-1]", + "array[1.5]", + "array[1n]" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap index abeff66bb442..e74a0ca06bfe 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap @@ -5,15 +5,30 @@ expression: checkAllIndexAccessValid.json --- # Input ```cjs -new _.last(array) +++array[1] ``` # Input ```cjs -_.last(array, 2) +const offset = 5;const extraArgument = 6;string.charAt(offset + 9, extraArgument) ``` # Input ```cjs -_.last(...array) +array[unknown] +``` + +# Input +```cjs +array[-1] +``` + +# Input +```cjs +array[1.5] +``` + +# Input +```cjs +array[1n] ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap index 42dc0b2fdba0..56ae4b2bb48f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap @@ -214,7 +214,7 @@ indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ i Unsafe fix: Replace index references with .at(). - array[array.length·-·((·1·))]; - + array.at(-1·); + + array.at(-1); ``` @@ -238,7 +238,7 @@ indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ i Unsafe fix: Replace index references with .at(). - array[((·array.length·-·1·))]; - + array.at(-1·); + + array.at(-1); ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json new file mode 100644 index 000000000000..c83de934a80d --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json @@ -0,0 +1,8 @@ +[ + "_.last(array)", + "lodash.last(array)", + "underscore.last(array)", + "_.last(new Array)", + "if (foo) _.last([bar])", + "function foo() {return _.last(arguments)}" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap new file mode 100644 index 000000000000..83f3cc510a52 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap @@ -0,0 +1,148 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: lodashInvalid.json +--- +# Input +```cjs +_.last(array) +``` + +# Diagnostics +``` +lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(array) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +lodash.last(array) +``` + +# Diagnostics +``` +lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ lodash.last(array) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - lodash.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +underscore.last(array) +``` + +# Diagnostics +``` +lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ underscore.last(array) + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - underscore.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +_.last(new Array) +``` + +# Diagnostics +``` +lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(new Array) + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(new·Array) + + (new·Array).at(-1) + + +``` + +# Input +```cjs +if (foo) _.last([bar]) +``` + +# Diagnostics +``` +lodashInvalid.json:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ if (foo) _.last([bar]) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - if·(foo)·_.last([bar]) + + if·(foo)·[bar].at(-1) + + +``` + +# Input +```cjs +function foo() {return _.last(arguments)} +``` + +# Diagnostics +``` +lodashInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ function foo() {return _.last(arguments)} + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - function·foo()·{return·_.last(arguments)} + + function·foo()·{return·arguments.at(-1)} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json new file mode 100644 index 000000000000..2e173476eb5f --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json @@ -0,0 +1,5 @@ +[ + "new _.last(array)", + "_.last(array, 2)", + "_.last(...array)" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap new file mode 100644 index 000000000000..ce00863f3d59 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap @@ -0,0 +1,19 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: lodashValid.json +--- +# Input +```cjs +new _.last(array) +``` + +# Input +```cjs +_.last(array, 2) +``` + +# Input +```cjs +_.last(...array) +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json index 3ad787784f90..f40bf2e82c35 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json @@ -24,5 +24,6 @@ "array.slice((( -9 )), (( -8 )), ).shift()", "(( array.slice(-9, -8).shift ))()", "array.slice(-0o11, -7)[0]", - "array.slice(-9, 0)[0]" + "array.slice(-9, 0)[0]", + "array.slice(-4).pop()" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap index fae514020f5c..bd3d740c77b2 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap @@ -89,7 +89,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-1).pop() │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(-1) over X.slice(-a).pop(). i Unsafe fix: Replace index references with .at(). @@ -113,7 +113,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-1.0).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y).shift(). i Unsafe fix: Replace index references with .at(). @@ -185,7 +185,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y).shift(). i Unsafe fix: Replace index references with .at(). @@ -353,7 +353,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ (( array.slice(-1) )).pop(); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(-1) over X.slice(-a).pop(). i Unsafe fix: Replace index references with .at(). @@ -377,7 +377,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ (( array.slice(-1).pop ))(); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(-1) over X.slice(-a).pop(). i Unsafe fix: Replace index references with .at(). @@ -401,7 +401,7 @@ sliceInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ (( array.slice(-1).pop() )); │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(-1) over X.slice(-a).pop(). i Unsafe fix: Replace index references with .at(). @@ -449,7 +449,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9, -8)[0] │ ^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a)[0]. i Unsafe fix: Replace index references with .at(). @@ -473,7 +473,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9, -0o10)[0] │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a)[0]. i Unsafe fix: Replace index references with .at(). @@ -497,7 +497,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9, -8).pop() │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y - 1) over X.slice(a, Y).pop(). i Unsafe fix: Replace index references with .at(). @@ -521,7 +521,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9, -8).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a).shift(). i Unsafe fix: Replace index references with .at(). @@ -545,7 +545,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice((( -9 )), (( -8 )), ).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a).shift(). i Unsafe fix: Replace index references with .at(). @@ -569,7 +569,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ (( array.slice(-9, -8).shift ))() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a).shift(). i Unsafe fix: Replace index references with .at(). @@ -593,7 +593,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-0o11, -7)[0] │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a)[0]. i Unsafe fix: Replace index references with .at(). @@ -617,7 +617,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ > 1 │ array.slice(-9, 0)[0] │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Prefer X.at(Y) over X.slice(Y, a)[0]. i Unsafe fix: Replace index references with .at(). @@ -626,3 +626,27 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ ``` + +# Input +```cjs +array.slice(-4).pop() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-4).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-4).pop() + + array.at(-1) + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new new file mode 100644 index 000000000000..bca6b7e29689 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new @@ -0,0 +1,168 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: sliceValid.json +--- +# Input +```cjs +array.slice(-1) +``` + +# Input +```cjs +new array.slice(-1) +``` + +# Input +```cjs +array.slice(-9).pop() +``` + +# Diagnostics +``` +sliceValid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).pop() + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1.1)[0] +``` + +# Input +```cjs +array.slice(-1)?.[0] +``` + +# Input +```cjs +array.slice?.(-1)[0] +``` + +# Input +```cjs +array?.slice(-1)[0] +``` + +# Input +```cjs +array.notSlice(-1)[0] +``` + +# Input +```cjs +array.slice()[0] +``` + +# Input +```cjs +array.slice(...[-1])[0] +``` + +# Input +```cjs +array.slice(-1).shift?.() +``` + +# Input +```cjs +array.slice(-1)?.shift() +``` + +# Input +```cjs +array.slice(-1).shift(...[]) +``` + +# Input +```cjs +new array.slice(-1).shift() +``` + +# Input +```cjs +array.slice(-1)[0] += 1 +``` + +# Input +```cjs +++ array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-1)[0] -- +``` + +# Input +```cjs +delete array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-9.1, -8.1)[0] +``` + +# Input +```cjs +array.slice(-unknown, -unknown2)[0] +``` + +# Input +```cjs +array.slice(-9.1, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).pop() +``` + +# Input +```cjs +array.slice(-9, ...unknown)[0] +``` + +# Input +```cjs +array.slice(...[-9], unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).shift() +``` + +# Input +```cjs +const KNOWN = -8; array.slice(-9, KNOWN).shift() +``` + +# Input +```cjs +(( (( array.slice( ((-9)), ((unknown)), ).shift ))() )); +``` + +# Input +```cjs +array.slice(-9, (a, really, _really, complicated, second) => argument)[0] +``` From 102397d0815ab07639fc4648ac5c5e18bf0b4266 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 04:19:22 +0900 Subject: [PATCH 04/48] fix: replace snapshot --- .../nursery/useAtIndex/sliceValid.json.snap | 19 ++ .../useAtIndex/sliceValid.json.snap.new | 168 ------------------ 2 files changed, 19 insertions(+), 168 deletions(-) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap index 961047c348e0..bca6b7e29689 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap @@ -18,6 +18,25 @@ new array.slice(-1) array.slice(-9).pop() ``` +# Diagnostics +``` +sliceValid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).pop() + + array.at(-1) + + +``` + # Input ```cjs array.slice(-1.1)[0] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new deleted file mode 100644 index bca6b7e29689..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap.new +++ /dev/null @@ -1,168 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: sliceValid.json ---- -# Input -```cjs -array.slice(-1) -``` - -# Input -```cjs -new array.slice(-1) -``` - -# Input -```cjs -array.slice(-9).pop() -``` - -# Diagnostics -``` -sliceValid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9).pop() - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9).pop() - + array.at(-1) - - -``` - -# Input -```cjs -array.slice(-1.1)[0] -``` - -# Input -```cjs -array.slice(-1)?.[0] -``` - -# Input -```cjs -array.slice?.(-1)[0] -``` - -# Input -```cjs -array?.slice(-1)[0] -``` - -# Input -```cjs -array.notSlice(-1)[0] -``` - -# Input -```cjs -array.slice()[0] -``` - -# Input -```cjs -array.slice(...[-1])[0] -``` - -# Input -```cjs -array.slice(-1).shift?.() -``` - -# Input -```cjs -array.slice(-1)?.shift() -``` - -# Input -```cjs -array.slice(-1).shift(...[]) -``` - -# Input -```cjs -new array.slice(-1).shift() -``` - -# Input -```cjs -array.slice(-1)[0] += 1 -``` - -# Input -```cjs -++ array.slice(-1)[0] -``` - -# Input -```cjs -array.slice(-1)[0] -- -``` - -# Input -```cjs -delete array.slice(-1)[0] -``` - -# Input -```cjs -array.slice(-9.1, -8.1)[0] -``` - -# Input -```cjs -array.slice(-unknown, -unknown2)[0] -``` - -# Input -```cjs -array.slice(-9.1, unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown).pop() -``` - -# Input -```cjs -array.slice(-9, ...unknown)[0] -``` - -# Input -```cjs -array.slice(...[-9], unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown).shift() -``` - -# Input -```cjs -const KNOWN = -8; array.slice(-9, KNOWN).shift() -``` - -# Input -```cjs -(( (( array.slice( ((-9)), ((unknown)), ).shift ))() )); -``` - -# Input -```cjs -array.slice(-9, (a, really, _really, complicated, second) => argument)[0] -``` From af07b0f04adc6da1c7a68eca9f1ad93652de0bf5 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 17:42:43 +0900 Subject: [PATCH 05/48] add: fix [hoge.length - unknown - 1] --- .../src/lint/nursery/use_at_index.rs | 131 ++++++++++++------ .../nursery/useAtIndex/charAtInvalid.json | 4 +- .../useAtIndex/charAtInvalid.json.snap | 50 ++++++- .../specs/nursery/useAtIndex/charAtValid.json | 4 +- .../nursery/useAtIndex/charAtValid.json.snap | 10 ++ .../nursery/useAtIndex/indexInvalid.json | 4 +- .../nursery/useAtIndex/indexInvalid.json.snap | 50 ++++++- 7 files changed, 209 insertions(+), 44 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 569f6523fdd9..7c52c426d8b3 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -202,6 +202,37 @@ fn is_same_reference(left: &AnyJsExpression, right: &AnyJsExpression) -> Option< } } +/// When using this expression in other operations, enclose it in parentheses as needed. +fn overwrap_parentheses_expression(node: &AnyJsExpression) -> Option { + match node { + AnyJsExpression::JsArrayExpression(exp) => { + Some(AnyJsExpression::JsArrayExpression(exp.clone())) + } + AnyJsExpression::JsCallExpression(exp) => { + Some(AnyJsExpression::JsCallExpression(exp.clone())) + } + AnyJsExpression::JsComputedMemberExpression(exp) => { + Some(AnyJsExpression::JsComputedMemberExpression(exp.clone())) + } + AnyJsExpression::JsIdentifierExpression(exp) => Some( + AnyJsExpression::JsIdentifierExpression(exp.clone().trim_trivia()?), + ), + AnyJsExpression::JsParenthesizedExpression(exp) => { + Some(AnyJsExpression::JsParenthesizedExpression(exp.clone())) + } + AnyJsExpression::JsStaticMemberExpression(exp) => { + Some(AnyJsExpression::JsStaticMemberExpression(exp.clone())) + } + _ => Some(AnyJsExpression::JsParenthesizedExpression( + make::js_parenthesized_expression( + make::token(T!['(']), + node.clone(), + make::token(T![')']), + ), + )), + } +} + /// If the node is a length method, it returns the object of interest. fn get_length_node(node: &AnyJsExpression) -> Option { let AnyJsExpression::JsStaticMemberExpression(node) = node else { @@ -243,6 +274,40 @@ fn get_integer_from_literal(node: &AnyJsExpression) -> Option { } } +/// Retrieve the value subtracted from the subtraction expression. +/// # Examples +/// ```js +/// a - b // => Some((a, [b])) +/// a - b - c // => Some((a, [b, c])) +/// ``` +fn get_left_node_from_minus_binary_expressions( + mut expression: AnyJsExpression, +) -> Option<(AnyJsExpression, Vec)> { + let mut right_list = vec![]; + + while let AnyJsExpression::JsBinaryExpression(binary) = expression { + let token = binary.operator_token().ok()?; + if token.kind() != T![-] { + return Some((AnyJsExpression::JsBinaryExpression(binary), right_list)); + } + + right_list.push(binary.right().ok()?); + expression = binary.left().ok()?; + } + Some((expression, right_list)) +} + +/// Combine the expressions in the list with the addition operator. +fn make_plus_binary_expression(list: Vec) -> Option { + list.into_iter().rev().reduce(|left, right| { + AnyJsExpression::JsBinaryExpression(make::js_binary_expression( + left, + make::token(T![+]), + right, + )) + }) +} + /// If the node is a negative index, it returns the negative index. /// # Examples /// ```js @@ -254,29 +319,41 @@ fn get_negative_index( member: &AnyJsExpression, object: &AnyJsExpression, ) -> Option { - let AnyJsExpression::JsBinaryExpression(member) = member else { - return None; - }; - let token = member.operator_token().ok()?; - if token.kind() != T![-] { + let (left, right_list) = get_left_node_from_minus_binary_expressions(member.clone())?; + if right_list.is_empty() { return None; } + // left expression should be hoge.length - let left = solve_parenthesized_expression(&member.left().ok()?)?; + let left = solve_parenthesized_expression(&left)?; let length_parent = get_length_node(&left)?; // left expression should be the same as the object if !is_same_reference(object, &length_parent)? { return None; } - let number_exp = solve_parenthesized_expression(&member.right().ok()?)?; - // right expression should be integer - let number = get_integer_from_literal(&number_exp)?; - if number > 0 { - Some(AnyJsExpression::JsUnaryExpression( - make::js_unary_expression(make::token(T![-]), number_exp), - )) + + if right_list.len() == 1 { + // right expression should be integer + if let Some(number) = + get_integer_from_literal(&solve_parenthesized_expression(&right_list[0])?) + { + if number > 0 { + Some(AnyJsExpression::JsUnaryExpression( + make::js_unary_expression(make::token(T![-]), right_list[0].clone()), + )) + } else { + None + } + } else { + Some(AnyJsExpression::JsUnaryExpression( + make::js_unary_expression( + make::token(T![-]), + overwrap_parentheses_expression(&right_list[0])?, + ), + )) + } } else { - None + make_plus_binary_expression(right_list) } } @@ -743,31 +820,7 @@ impl Rule for UseAtIndex { error_type: _, object, } = state; - let object = match object { - AnyJsExpression::JsArrayExpression(exp) => { - AnyJsExpression::JsArrayExpression(exp.clone()) - } - AnyJsExpression::JsCallExpression(exp) => { - AnyJsExpression::JsCallExpression(exp.clone()) - } - AnyJsExpression::JsComputedMemberExpression(exp) => { - AnyJsExpression::JsComputedMemberExpression(exp.clone()) - } - AnyJsExpression::JsIdentifierExpression(exp) => { - AnyJsExpression::JsIdentifierExpression(exp.clone().trim_trivia()?) - } - AnyJsExpression::JsParenthesizedExpression(exp) => { - AnyJsExpression::JsParenthesizedExpression(exp.clone()) - } - AnyJsExpression::JsStaticMemberExpression(exp) => { - AnyJsExpression::JsStaticMemberExpression(exp.clone()) - } - _ => AnyJsExpression::JsParenthesizedExpression(make::js_parenthesized_expression( - make::token(T!['(']), - object.clone(), - make::token(T![')']), - )), - }; + let object = overwrap_parentheses_expression(object)?; mutation.replace_node( prev_node, diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json index bd7b3e789a7a..1a96a3e4d493 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json @@ -7,5 +7,7 @@ "string.charAt((( string.length - 1 )));", "(( string )).charAt(string.length - 1);", "(( string.charAt ))(string.length - 1);", - "(( string.charAt(string.length - 1) ));" + "(( string.charAt(string.length - 1) ));", + "string.charAt(string.length - unknown - 1 );", + "string.charAt(string.length - (unknown + 1));" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap index d33dac4d10ab..77218193c17f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap @@ -118,7 +118,7 @@ charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━ i Unsafe fix: Replace index references with .at(). - string.charAt(string.length·-·((·1·))); - + string.at(-1); + + string.at(-((·1·))); ``` @@ -218,3 +218,51 @@ charAtInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━ ``` + +# Input +```cjs +string.charAt(string.length - unknown - 1 ); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - unknown - 1 ); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·unknown·-·1·); + + string.at(unknown·+1); + + +``` + +# Input +```cjs +string.charAt(string.length - (unknown + 1)); +``` + +# Diagnostics +``` +charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - (unknown + 1)); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·(unknown·+·1)); + + string.at(-(unknown·+·1)); + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json index c3bcee6b8eb2..32abba2e8626 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json @@ -7,5 +7,7 @@ "string?.charAt(string.length - 1);", "string.charAt(9);", "string1.charAt(string2.length - 1);", - "string.charAt(hoge.string.length - 1)" + "string.charAt(hoge.string.length - 1)", + "string.charAt(string.length - 1 + 1)", + "string.charAt(string.length + 1 - 1)" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap index 412b0f83e54a..5c8018909647 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap @@ -47,3 +47,13 @@ string1.charAt(string2.length - 1); ```cjs string.charAt(hoge.string.length - 1) ``` + +# Input +```cjs +string.charAt(string.length - 1 + 1) +``` + +# Input +```cjs +string.charAt(string.length + 1 - 1) +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json index 8155b7dfba80..61d7e0f364b2 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json @@ -18,5 +18,7 @@ "typeof array[array.length - 1]", "function foo() {return arguments[arguments.length - 1]}", "class Foo {bar; baz() {return this.bar[this.bar.length - 1]}}", - "class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}}" + "class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}}", + "array[array.length - unknown - 1]", + "array[array.length - (unknown + 1)]" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap index 56ae4b2bb48f..61f85f5a1d8f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap @@ -214,7 +214,7 @@ indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ i Unsafe fix: Replace index references with .at(). - array[array.length·-·((·1·))]; - + array.at(-1); + + array.at(-((·1·))); ``` @@ -482,3 +482,51 @@ indexInvalid.json:1:32 lint/nursery/useAtIndex FIXABLE ━━━━━━━ ``` + +# Input +```cjs +array[array.length - unknown - 1] +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - unknown - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·unknown·-·1] + + array.at(unknown·+1) + + +``` + +# Input +```cjs +array[array.length - (unknown + 1)] +``` + +# Diagnostics +``` +indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - (unknown + 1)] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·(unknown·+·1)] + + array.at(-(unknown·+·1)) + + +``` From 03cb8ef00fb97f8318e8a11b0dd8ed7d9f3f9db5 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 18:02:02 +0900 Subject: [PATCH 06/48] add: fix slice(hoge)[0] --- .../src/lint/nursery/use_at_index.rs | 4 +- .../nursery/useAtIndex/sliceInvalid.json | 5 +- .../nursery/useAtIndex/sliceInvalid.json.snap | 84 +++++++++++++++++-- .../specs/nursery/useAtIndex/sliceValid.json | 2 - .../nursery/useAtIndex/sliceValid.json.snap | 29 ------- 5 files changed, 84 insertions(+), 40 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 7c52c426d8b3..42c58127f8d8 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -466,8 +466,6 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option return None; }; let start_exp = solve_parenthesized_expression(arg0)?; - let start_index = get_integer_from_literal(&start_exp)?; - let sliced_exp = member.object().ok()?; if args.len() == 1 { @@ -478,6 +476,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option object: sliced_exp, }); } + let start_index = get_integer_from_literal(&start_exp)?; if start_index < 0 && at_value == -1 { return Some(UseAtIndexState { at_number_exp: make_number_literal(-1), @@ -487,6 +486,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option } return None; } + let start_index = get_integer_from_literal(&start_exp)?; let AnyJsCallArgument::AnyJsExpression(arg1) = &args[1] else { return None; }; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json index f40bf2e82c35..efab21ca205f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json @@ -5,6 +5,8 @@ "array.slice(-1).pop()", "array.slice(-1.0).shift()", "array.slice(-9)[0]", + "array.slice(-9).pop()", + "array.slice(-1.1)[0]", "array.slice(-0xA)[0b000]", "array.slice(-9).shift()", "array.slice(-1)[(( 0 ))];", @@ -25,5 +27,6 @@ "(( array.slice(-9, -8).shift ))()", "array.slice(-0o11, -7)[0]", "array.slice(-9, 0)[0]", - "array.slice(-4).pop()" + "array.slice(hoge)[0]", + "array.slice(hoge).shift()" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap index bd3d740c77b2..f39fe347fa96 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap @@ -145,6 +145,54 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ + array.at(-9) +``` + +# Input +```cjs +array.slice(-9).pop() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).pop() + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1.1)[0] +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1.1)[0] + │ ^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1.1)[0] + + array.at(-1.1) + + ``` # Input @@ -629,7 +677,7 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ # Input ```cjs -array.slice(-4).pop() +array.slice(hoge)[0] ``` # Diagnostics @@ -638,15 +686,39 @@ sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━ ! Replace index references with .at(). - > 1 │ array.slice(-4).pop() - │ ^^^^^^^^^^^^^^^^^^^^^ + > 1 │ array.slice(hoge)[0] + │ ^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Prefer X.at(Y) over X.slice(Y)[0]. i Unsafe fix: Replace index references with .at(). - - array.slice(-4).pop() - + array.at(-1) + - array.slice(hoge)[0] + + array.at(hoge) + + +``` + +# Input +```cjs +array.slice(hoge).shift() +``` + +# Diagnostics +``` +sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(hoge).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(hoge).shift() + + array.at(hoge) ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json index 9626bcb85a60..eafdcc5c9c87 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json @@ -1,8 +1,6 @@ [ "array.slice(-1)", "new array.slice(-1)", - "array.slice(-9).pop()", - "array.slice(-1.1)[0]", "array.slice(-1)?.[0]", "array.slice?.(-1)[0]", "array?.slice(-1)[0]", diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap index bca6b7e29689..2311f9934e09 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap @@ -13,35 +13,6 @@ array.slice(-1) new array.slice(-1) ``` -# Input -```cjs -array.slice(-9).pop() -``` - -# Diagnostics -``` -sliceValid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9).pop() - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9).pop() - + array.at(-1) - - -``` - -# Input -```cjs -array.slice(-1.1)[0] -``` - # Input ```cjs array.slice(-1)?.[0] From 2c0e29023f49612e8a01fe7571191e321d349e15 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 18:13:08 +0900 Subject: [PATCH 07/48] refactor: Use Cow to remove unnecessary clones of error messages --- .../biome_js_analyze/src/lint/nursery/use_at_index.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 42c58127f8d8..7138c9dd8952 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use ::serde::{Deserialize, Serialize}; use biome_analyze::{ context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, @@ -115,9 +117,9 @@ pub enum ErrorType { IdIndex, StringCharAtNegativeIndex, StringCharAt, - Slice(String), + Slice(Cow<'static, str>), SlicePop, - Slice2(String), + Slice2(Cow<'static, str>), Slice2Pop, GetLastFunction, } @@ -472,7 +474,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option if at_value == 0 { return Some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice(taker.to_string()), + error_type: ErrorType::Slice(Cow::Borrowed(taker)), object: sliced_exp, }); } @@ -497,7 +499,7 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option if at_value == 0 { Some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice2(taker.to_string()), + error_type: ErrorType::Slice2(Cow::Borrowed(taker)), object: sliced_exp, }) } else { From 927444e046e31ffc485c50e4085e2909d532af9d Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 18:24:55 +0900 Subject: [PATCH 08/48] refactor: use math(at_vaue, arg.len()) --- .../src/lint/nursery/use_at_index.rs | 57 ++++++++----------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 7138c9dd8952..384fa8bf2767 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -470,47 +470,40 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option let start_exp = solve_parenthesized_expression(arg0)?; let sliced_exp = member.object().ok()?; - if args.len() == 1 { - if at_value == 0 { - return Some(UseAtIndexState { - at_number_exp: start_exp, - error_type: ErrorType::Slice(Cow::Borrowed(taker)), - object: sliced_exp, - }); - } - let start_index = get_integer_from_literal(&start_exp)?; - if start_index < 0 && at_value == -1 { - return Some(UseAtIndexState { - at_number_exp: make_number_literal(-1), - error_type: ErrorType::SlicePop, - object: sliced_exp, - }); - } - return None; - } - let start_index = get_integer_from_literal(&start_exp)?; - let AnyJsCallArgument::AnyJsExpression(arg1) = &args[1] else { - return None; - }; - let end_exp = solve_parenthesized_expression(arg1)?; - let end_index = get_integer_from_literal(&end_exp)?; - // enable only x.slice(2, 4) - if start_index * end_index >= 0 && start_index < end_index { - if at_value == 0 { - Some(UseAtIndexState { + match (at_value, args.len()) { + (0, 1) => Some(UseAtIndexState { + at_number_exp: start_exp, + error_type: ErrorType::Slice(Cow::Borrowed(taker)), + object: sliced_exp, + }), + (-1, 1) if get_integer_from_literal(&start_exp)? < 0 => Some(UseAtIndexState { + at_number_exp: make_number_literal(-1), + error_type: ErrorType::SlicePop, + object: sliced_exp, + }), + (0, 2) => { + let start_index = get_integer_from_literal(&start_exp)?; + let end_index = get_integer_from_literal(&solve_parenthesized_expression( + &args[1].as_any_js_expression()?.clone(), + )?)?; + (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, error_type: ErrorType::Slice2(Cow::Borrowed(taker)), object: sliced_exp, }) - } else { - Some(UseAtIndexState { + } + (-1, 2) => { + let start_index = get_integer_from_literal(&start_exp)?; + let end_index = get_integer_from_literal(&solve_parenthesized_expression( + &args[1].as_any_js_expression()?.clone(), + )?)?; + (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), error_type: ErrorType::Slice2Pop, object: sliced_exp, }) } - } else { - None + _ => None, } } From 53a51cacadec5b67b9a9d845aca8a8a17b4814bb Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 20:49:17 +0900 Subject: [PATCH 09/48] refactor: split the function --- .../src/lint/nursery/use_at_index.rs | 451 ++++++++++-------- 1 file changed, 245 insertions(+), 206 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 384fa8bf2767..dee408098ab5 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -8,8 +8,9 @@ use biome_analyze::{ use biome_console::markup; use biome_js_factory::make::{self}; use biome_js_syntax::{ - AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, JsCallExpression, - JsComputedMemberExpression, JsParenthesizedExpression, JsUnaryExpression, T, + AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, JsBinaryExpression, + JsCallExpression, JsComputedMemberExpression, JsParenthesizedExpression, + JsStaticMemberExpression, JsUnaryExpression, T, }; use biome_rowan::{declare_node_union, AstNode, BatchMutationExt}; @@ -317,7 +318,7 @@ fn make_plus_binary_expression(list: Vec) -> Option Some(-1) /// hoge[fuga.length - 2] // => None /// ``` -fn get_negative_index( +fn extract_negative_index_expression( member: &AnyJsExpression, object: &AnyJsExpression, ) -> Option { @@ -360,7 +361,7 @@ fn get_negative_index( } /// Is the node a child node of `delete`? -fn is_delete_child(node: &AnyJsExpression) -> Option { +fn is_within_delete_expression(node: &AnyJsExpression) -> Option { node.syntax().parent()?.ancestors().find_map(|ancestor| { if let Some(unary) = JsUnaryExpression::cast(ancestor.clone()) { unary @@ -386,8 +387,8 @@ fn make_number_literal(value: i64) -> AnyJsExpression { /// .slice(0)[0] /// .slice(0, 1).pop(0) /// ``` -fn check_get_element_by_slice(node: &AnyJsExpression) -> Option { - if is_delete_child(node).unwrap_or(false) { +fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { + if is_within_delete_expression(node).unwrap_or(false) { return None; } // selector @@ -507,6 +508,242 @@ fn check_get_element_by_slice(node: &AnyJsExpression) -> Option } } +fn check_binary_expression_member( + member: JsBinaryExpression, + object: AnyJsExpression, + option: &UseAtIndexOptions, +) -> Option { + let member = AnyJsExpression::JsBinaryExpression(member); + let negative_index_exp = + extract_negative_index_expression(&member, &solve_parenthesized_expression(&object)?); + if let Some(negative_index) = negative_index_exp { + return Some(UseAtIndexState { + at_number_exp: negative_index, + error_type: ErrorType::NegativeIndex, + object, + }); + } + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::NegativeIndex, + object, + }) +} + +fn check_literal_expression_member( + member: AnyJsLiteralExpression, + object: AnyJsExpression, + option: &UseAtIndexOptions, +) -> Option { + let AnyJsLiteralExpression::JsNumberLiteralExpression(member) = member else { + return None; + }; + let value_token = member.value_token().ok()?; + let number = value_token.text_trimmed().parse::().ok()?; + (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { + at_number_exp: make_number_literal(number), + error_type: ErrorType::IdIndex, + object, + }) +} + +fn check_unary_expression_member( + member: JsUnaryExpression, + object: AnyJsExpression, + option: &UseAtIndexOptions, +) -> Option { + if !option.check_all_index_access { + return None; + } + // ignore -5 + let token = member.operator_token().ok()?; + if token.kind() == T![-] { + if let Some(arg) = + get_integer_from_literal(&solve_parenthesized_expression(&member.argument().ok()?)?) + { + if arg >= 0 { + return None; + } + } + } + Some(UseAtIndexState { + at_number_exp: AnyJsExpression::JsUnaryExpression(member), + error_type: ErrorType::IdIndex, + object, + }) +} + +/// check hoge[0] +fn check_computed_member_expression( + exp: &JsComputedMemberExpression, + option: &UseAtIndexOptions, +) -> Option { + // check slice + if let Some(slice_err) = + analyze_slice_element_access(&AnyJsExpression::JsComputedMemberExpression(exp.clone())) + { + return Some(slice_err); + } + // invalid optional chain, mutable case + if exp.is_optional_chain() + || is_within_delete_expression(&AnyJsExpression::JsComputedMemberExpression(exp.clone())) + .unwrap_or(false) + { + return None; + } + // check member + let member = solve_parenthesized_expression(&exp.member().ok()?)?; + let object = exp.object().ok()?; + match member.clone() { + // hoge[hoge.length - 1] + AnyJsExpression::JsBinaryExpression(binary) => { + check_binary_expression_member(binary, object, option) + } + // hoge[1] + AnyJsExpression::AnyJsLiteralExpression(literal) => { + check_literal_expression_member(literal, object, option) + } + // hoge[-x] + AnyJsExpression::JsUnaryExpression(unary) => { + check_unary_expression_member(unary, object, option) + } + AnyJsExpression::JsIdentifierExpression(_) => None, + _ => option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::IdIndex, + object, + }), + } +} + +fn check_call_expression_last( + call_exp: &JsCallExpression, + member: &JsStaticMemberExpression, +) -> Option { + let args: Vec<_> = call_exp + .arguments() + .ok()? + .args() + .into_iter() + .flatten() + .collect(); + if args.len() != 1 { + return None; + } + let object = member.object().ok()?; + let AnyJsExpression::JsIdentifierExpression(object) = object else { + return None; + }; + let lodash_function = ["_", "lodash", "underscore"]; + let object_name = object.syntax().text().to_string(); + if lodash_function.contains(&object_name.as_str()) { + let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + return None; + }; + Some(UseAtIndexState { + at_number_exp: make_number_literal(-1), + error_type: ErrorType::GetLastFunction, + object: solve_parenthesized_expression(arg0)?, + }) + } else { + None + } +} + +fn check_call_expression_char_at( + call_exp: &JsCallExpression, + member: &JsStaticMemberExpression, + option: &UseAtIndexOptions, +) -> Option { + let args: Vec<_> = call_exp + .arguments() + .ok()? + .args() + .into_iter() + .flatten() + .collect(); + if args.len() != 1 { + return None; + } + let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + return None; + }; + let core_arg0 = solve_parenthesized_expression(arg0)?; + let char_at_parent = &solve_parenthesized_expression(&member.object().ok()?)?; + match core_arg0.clone() { + // hoge.charAt(hoge.length - 1) + AnyJsExpression::JsBinaryExpression(_) => { + let at_number_exp = extract_negative_index_expression(&core_arg0, char_at_parent); + if let Some(at_number_exp) = at_number_exp { + Some(UseAtIndexState { + at_number_exp, + error_type: ErrorType::StringCharAtNegativeIndex, + object: char_at_parent.clone(), + }) + } else { + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }) + } + } + // hoge.charAt(1) + AnyJsExpression::AnyJsLiteralExpression(_member) => { + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }) + } + _ => option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt, + object: char_at_parent.clone(), + }), + } +} + +/// check hoge.fuga() +fn check_call_expression( + call_exp: &JsCallExpression, + option: &UseAtIndexOptions, +) -> Option { + // check slice + if let Some(slice_err) = + analyze_slice_element_access(&AnyJsExpression::JsCallExpression(call_exp.clone())) + { + return Some(slice_err); + } + + if call_exp.is_optional_chain() { + return None; + } + + let member = solve_parenthesized_expression(&call_exp.callee().ok()?)?; + match member { + AnyJsExpression::JsStaticMemberExpression(member) => { + if member.is_optional_chain() { + return None; + } + let member_name = member + .member() + .ok()? + .as_js_name()? + .value_token() + .ok()? + .token_text_trimmed(); + match member_name.text() { + "last" => check_call_expression_last(call_exp, &member), + "charAt" => check_call_expression_char_at(call_exp, &member, option), + //"lastIndexOf" => Some(ErrorType::GetLastFunction), + _ => None, + } + } + _ => None, + } +} + /// make `object.at(arg)` fn make_at_method(object: AnyJsExpression, arg: AnyJsExpression) -> JsCallExpression { let at_member = make::js_static_member_expression( @@ -552,208 +789,10 @@ impl Rule for UseAtIndex { let result: Option = match exp { // hoge[a] AnyJsArrayAccess::JsComputedMemberExpression(exp) => { - // check slice - if let Some(slice_err) = check_get_element_by_slice( - &AnyJsExpression::JsComputedMemberExpression(exp.clone()), - ) { - return Some(slice_err); - } - // invalid optional chain - if exp.is_optional_chain() { - return None; - } - // invalid mutable case - if is_delete_child(&AnyJsExpression::JsComputedMemberExpression(exp.clone())) - .unwrap_or(false) - { - return None; - } - // check member - let member = solve_parenthesized_expression(&exp.member().ok()?)?; - match member.clone() { - // hoge[hoge.length - 1] - AnyJsExpression::JsBinaryExpression(_binary) => { - let negative_index_exp = get_negative_index( - &member, - &solve_parenthesized_expression(&exp.object().ok()?)?, - ); - if let Some(negative_index) = negative_index_exp { - return Some(UseAtIndexState { - at_number_exp: negative_index, - error_type: ErrorType::NegativeIndex, - object: exp.object().ok()?, - }); - } - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::NegativeIndex, - object: exp.object().ok()?, - }) - } - // hoge[1] - AnyJsExpression::AnyJsLiteralExpression(member) => { - let AnyJsLiteralExpression::JsNumberLiteralExpression(member) = member - else { - return None; - }; - let value_token = member.value_token().ok()?; - let number = value_token.text_trimmed().parse::().ok()?; - if number >= 0 { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: make_number_literal(number), - error_type: ErrorType::IdIndex, - object: exp.object().ok()?, - }) - } else { - None - } - } - AnyJsExpression::JsUnaryExpression(unary) => { - if !option.check_all_index_access { - return None; - } - // ignore -5 - let token = unary.operator_token().ok()?; - if token.kind() == T![-] { - if let Some(arg) = get_integer_from_literal( - &solve_parenthesized_expression(&unary.argument().ok()?)?, - ) { - if arg >= 0 { - return None; - } - } - } - Some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::IdIndex, - object: exp.object().ok()?, - }) - } - AnyJsExpression::JsIdentifierExpression(_) => None, - _ => option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::IdIndex, - object: exp.object().ok()?, - }), - } + check_computed_member_expression(exp, option) } // hoge.fuga() - AnyJsArrayAccess::JsCallExpression(call_exp) => { - // check slice - if let Some(slice_err) = - check_get_element_by_slice(&AnyJsExpression::JsCallExpression(call_exp.clone())) - { - return Some(slice_err); - } - - if call_exp.is_optional_chain() { - return None; - } - - let member = solve_parenthesized_expression(&call_exp.callee().ok()?)?; - match member { - AnyJsExpression::JsStaticMemberExpression(member) => { - if member.is_optional_chain() { - return None; - } - let member_name = member - .member() - .ok()? - .as_js_name()? - .value_token() - .ok()? - .token_text_trimmed(); - match member_name.text() { - "last" => { - let args: Vec<_> = call_exp - .arguments() - .ok()? - .args() - .into_iter() - .flatten() - .collect(); - if args.len() != 1 { - return None; - } - let object = member.object().ok()?; - let AnyJsExpression::JsIdentifierExpression(object) = object else { - return None; - }; - let lodash_function = ["_", "lodash", "underscore"]; - let object_name = object.syntax().text().to_string(); - if lodash_function.contains(&object_name.as_str()) { - let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { - return None; - }; - Some(UseAtIndexState { - at_number_exp: make_number_literal(-1), - error_type: ErrorType::GetLastFunction, - object: solve_parenthesized_expression(arg0)?, - }) - } else { - None - } - } - "charAt" => { - let args: Vec<_> = call_exp - .arguments() - .ok()? - .args() - .into_iter() - .flatten() - .collect(); - if args.len() != 1 { - return None; - } - let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { - return None; - }; - let core_arg0 = solve_parenthesized_expression(arg0)?; - let char_at_parent = - &solve_parenthesized_expression(&member.object().ok()?)?; - match core_arg0.clone() { - // hoge.charAt(hoge.length - 1) - AnyJsExpression::JsBinaryExpression(_) => { - let at_number_exp = - get_negative_index(&core_arg0, char_at_parent); - if let Some(at_number_exp) = at_number_exp { - Some(UseAtIndexState { - at_number_exp, - error_type: ErrorType::StringCharAtNegativeIndex, - object: char_at_parent.clone(), - }) - } else { - option.check_all_index_access.then_some( - UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, - object: char_at_parent.clone(), - }, - ) - } - } - // hoge.charAt(1) - AnyJsExpression::AnyJsLiteralExpression(_member) => { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, - object: char_at_parent.clone(), - }) - } - _ => option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, - object: char_at_parent.clone(), - }), - } - } - //"lastIndexOf" => Some(ErrorType::GetLastFunction), - _ => None, - } - } - _ => return None, - } - } + AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp, option), }; result } From 46fd9052fe35063322a06de13252b7be00bc4bfd Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 21:02:34 +0900 Subject: [PATCH 10/48] refactor: fix solve_parenthesized_expression ownership --- .../src/lint/nursery/use_at_index.rs | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index dee408098ab5..e3ae8b42add8 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -132,13 +132,12 @@ pub enum ErrorType { /// (a) // Some(a) /// (a + b) // Some(a + b) /// ``` -fn solve_parenthesized_expression(node: &AnyJsExpression) -> Option { - if let AnyJsExpression::JsParenthesizedExpression(parenthesized_exp) = node { +fn solve_parenthesized_expression(mut node: AnyJsExpression) -> Option { + while let AnyJsExpression::JsParenthesizedExpression(parenthesized_exp) = node { let exp = parenthesized_exp.expression().ok()?; - solve_parenthesized_expression(&exp) - } else { - Some(node.clone()) + node = exp; } + Some(node) } /// Check if two expressions reference the same value. @@ -151,7 +150,7 @@ fn solve_parenthesized_expression(node: &AnyJsExpression) -> Option Option { +fn is_same_reference(left: AnyJsExpression, right: AnyJsExpression) -> Option { // solve JsParenthesizedExpression let left = solve_parenthesized_expression(left)?; let right = solve_parenthesized_expression(right)?; @@ -160,19 +159,19 @@ fn is_same_reference(left: &AnyJsExpression, right: &AnyJsExpression) -> Option< AnyJsExpression::JsComputedMemberExpression(left) => match right { AnyJsExpression::JsComputedMemberExpression(right) => { let AnyJsExpression::AnyJsLiteralExpression(left_member) = - solve_parenthesized_expression(&left.member().ok()?)? + solve_parenthesized_expression(left.member().ok()?)? else { return Some(false); }; let AnyJsExpression::AnyJsLiteralExpression(right_member) = - solve_parenthesized_expression(&right.member().ok()?)? + solve_parenthesized_expression(right.member().ok()?)? else { return Some(false); }; if left_member.text() != right_member.text() { return Some(false); } - is_same_reference(&left.object().ok()?, &right.object().ok()?) + is_same_reference(left.object().ok()?, right.object().ok()?) } _ => Some(false), }, @@ -184,7 +183,7 @@ fn is_same_reference(left: &AnyJsExpression, right: &AnyJsExpression) -> Option< if left_member.text() != right_member.text() { Some(false) } else { - is_same_reference(&left.object().ok()?, &right.object().ok()?) + is_same_reference(left.object().ok()?, right.object().ok()?) } } _ => Some(false), @@ -260,7 +259,7 @@ fn get_integer_from_literal(node: &AnyJsExpression) -> Option { if token.kind() != T![-] { return None; } - return get_integer_from_literal(&solve_parenthesized_expression(&unary.argument().ok()?)?) + return get_integer_from_literal(&solve_parenthesized_expression(unary.argument().ok()?)?) .map(|num| -num); } let AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::JsNumberLiteralExpression( @@ -320,7 +319,7 @@ fn make_plus_binary_expression(list: Vec) -> Option Option { let (left, right_list) = get_left_node_from_minus_binary_expressions(member.clone())?; if right_list.is_empty() { @@ -328,17 +327,17 @@ fn extract_negative_index_expression( } // left expression should be hoge.length - let left = solve_parenthesized_expression(&left)?; + let left = solve_parenthesized_expression(left)?; let length_parent = get_length_node(&left)?; // left expression should be the same as the object - if !is_same_reference(object, &length_parent)? { + if !is_same_reference(object, length_parent)? { return None; } if right_list.len() == 1 { // right expression should be integer if let Some(number) = - get_integer_from_literal(&solve_parenthesized_expression(&right_list[0])?) + get_integer_from_literal(&solve_parenthesized_expression(right_list[0].clone())?) { if number > 0 { Some(AnyJsExpression::JsUnaryExpression( @@ -399,7 +398,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option Option { - let object = solve_parenthesized_expression(&member.object().ok()?)?; + let object = solve_parenthesized_expression(member.object().ok()?)?; if member.is_optional_chain() { return None; } let value = - get_integer_from_literal(&solve_parenthesized_expression(&member.member().ok()?)?)?; + get_integer_from_literal(&solve_parenthesized_expression(member.member().ok()?)?)?; // enable only x[0] if value != 0 { return None; @@ -465,7 +464,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option 2 { return None; } - let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; let start_exp = solve_parenthesized_expression(arg0)?; @@ -485,7 +484,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - &args[1].as_any_js_expression()?.clone(), + args[1].as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, @@ -496,7 +495,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - &args[1].as_any_js_expression()?.clone(), + args[1].as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), @@ -515,7 +514,7 @@ fn check_binary_expression_member( ) -> Option { let member = AnyJsExpression::JsBinaryExpression(member); let negative_index_exp = - extract_negative_index_expression(&member, &solve_parenthesized_expression(&object)?); + extract_negative_index_expression(&member, solve_parenthesized_expression(object.clone())?); if let Some(negative_index) = negative_index_exp { return Some(UseAtIndexState { at_number_exp: negative_index, @@ -559,7 +558,7 @@ fn check_unary_expression_member( let token = member.operator_token().ok()?; if token.kind() == T![-] { if let Some(arg) = - get_integer_from_literal(&solve_parenthesized_expression(&member.argument().ok()?)?) + get_integer_from_literal(&solve_parenthesized_expression(member.argument().ok()?)?) { if arg >= 0 { return None; @@ -592,7 +591,7 @@ fn check_computed_member_expression( return None; } // check member - let member = solve_parenthesized_expression(&exp.member().ok()?)?; + let member = solve_parenthesized_expression(exp.member().ok()?)?; let object = exp.object().ok()?; match member.clone() { // hoge[hoge.length - 1] @@ -637,7 +636,7 @@ fn check_call_expression_last( let lodash_function = ["_", "lodash", "underscore"]; let object_name = object.syntax().text().to_string(); if lodash_function.contains(&object_name.as_str()) { - let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; Some(UseAtIndexState { @@ -665,26 +664,27 @@ fn check_call_expression_char_at( if args.len() != 1 { return None; } - let AnyJsCallArgument::AnyJsExpression(arg0) = &args[0] else { + let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; let core_arg0 = solve_parenthesized_expression(arg0)?; - let char_at_parent = &solve_parenthesized_expression(&member.object().ok()?)?; + let char_at_parent = solve_parenthesized_expression(member.object().ok()?)?; match core_arg0.clone() { // hoge.charAt(hoge.length - 1) AnyJsExpression::JsBinaryExpression(_) => { - let at_number_exp = extract_negative_index_expression(&core_arg0, char_at_parent); + let at_number_exp = + extract_negative_index_expression(&core_arg0, char_at_parent.clone()); if let Some(at_number_exp) = at_number_exp { Some(UseAtIndexState { at_number_exp, error_type: ErrorType::StringCharAtNegativeIndex, - object: char_at_parent.clone(), + object: char_at_parent, }) } else { option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, error_type: ErrorType::StringCharAt, - object: char_at_parent.clone(), + object: char_at_parent, }) } } @@ -720,7 +720,7 @@ fn check_call_expression( return None; } - let member = solve_parenthesized_expression(&call_exp.callee().ok()?)?; + let member = solve_parenthesized_expression(call_exp.callee().ok()?)?; match member { AnyJsExpression::JsStaticMemberExpression(member) => { if member.is_optional_chain() { From 94f103ef6f7348ef7357035bc6110b6fdd9e6754 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 21:07:41 +0900 Subject: [PATCH 11/48] refactor: use match touple --- .../src/lint/nursery/use_at_index.rs | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e3ae8b42add8..f6453e2bfde1 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -154,52 +154,47 @@ fn is_same_reference(left: AnyJsExpression, right: AnyJsExpression) -> Option match right { - AnyJsExpression::JsComputedMemberExpression(right) => { - let AnyJsExpression::AnyJsLiteralExpression(left_member) = - solve_parenthesized_expression(left.member().ok()?)? - else { - return Some(false); - }; - let AnyJsExpression::AnyJsLiteralExpression(right_member) = - solve_parenthesized_expression(right.member().ok()?)? - else { - return Some(false); - }; - if left_member.text() != right_member.text() { - return Some(false); - } - is_same_reference(left.object().ok()?, right.object().ok()?) + ( + AnyJsExpression::JsComputedMemberExpression(left), + AnyJsExpression::JsComputedMemberExpression(right), + ) => { + let AnyJsExpression::AnyJsLiteralExpression(left_member) = + solve_parenthesized_expression(left.member().ok()?)? + else { + return Some(false); + }; + let AnyJsExpression::AnyJsLiteralExpression(right_member) = + solve_parenthesized_expression(right.member().ok()?)? + else { + return Some(false); + }; + if left_member.text() != right_member.text() { + return Some(false); } - _ => Some(false), - }, + is_same_reference(left.object().ok()?, right.object().ok()?) + } // x.y - AnyJsExpression::JsStaticMemberExpression(left) => match right { - AnyJsExpression::JsStaticMemberExpression(right) => { - let left_member = left.member().ok()?; - let right_member = right.member().ok()?; - if left_member.text() != right_member.text() { - Some(false) - } else { - is_same_reference(left.object().ok()?, right.object().ok()?) - } + ( + AnyJsExpression::JsStaticMemberExpression(left), + AnyJsExpression::JsStaticMemberExpression(right), + ) => { + let left_member = left.member().ok()?; + let right_member = right.member().ok()?; + if left_member.text() != right_member.text() { + Some(false) + } else { + is_same_reference(left.object().ok()?, right.object().ok()?) } - _ => Some(false), - }, + } // x - AnyJsExpression::JsIdentifierExpression(left) => match right { - AnyJsExpression::JsIdentifierExpression(right) => { - Some(left.name().ok()?.text() == right.name().ok()?.text()) - } - _ => Some(false), - }, + ( + AnyJsExpression::JsIdentifierExpression(left), + AnyJsExpression::JsIdentifierExpression(right), + ) => Some(left.name().ok()?.text() == right.name().ok()?.text()), // this - AnyJsExpression::JsThisExpression(_) => match right { - AnyJsExpression::JsThisExpression(_) => Some(true), - _ => Some(false), - }, + (AnyJsExpression::JsThisExpression(_), AnyJsExpression::JsThisExpression(_)) => Some(true), _ => Some(false), } } From bf4c03cf64b05fca17383d09267b5bba621f36c4 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 22:02:04 +0900 Subject: [PATCH 12/48] refactor: Organize the error types for slice --- .../src/lint/nursery/use_at_index.rs | 218 +++++++++++------- 1 file changed, 132 insertions(+), 86 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index f6453e2bfde1..045ec76e7255 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -1,11 +1,9 @@ -use std::borrow::Cow; - use ::serde::{Deserialize, Serialize}; use biome_analyze::{ context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, }; -use biome_console::markup; +use biome_console::{markup, MarkupBuf}; use biome_js_factory::make::{self}; use biome_js_syntax::{ AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, JsBinaryExpression, @@ -103,28 +101,6 @@ declare_lint_rule! { } } -declare_node_union! { - pub AnyJsArrayAccess = JsComputedMemberExpression | JsCallExpression -} - -pub struct UseAtIndexState { - at_number_exp: AnyJsExpression, - error_type: ErrorType, - object: AnyJsExpression, -} - -pub enum ErrorType { - NegativeIndex, - IdIndex, - StringCharAtNegativeIndex, - StringCharAt, - Slice(Cow<'static, str>), - SlicePop, - Slice2(Cow<'static, str>), - Slice2Pop, - GetLastFunction, -} - /// If the node is a parenthized expression, it returns the expression inside. /// # Examples /// ```js @@ -386,7 +362,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let arg_length = call_exp.arguments().ok()?.args().into_iter().count(); @@ -409,9 +385,9 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option return None, }; @@ -465,36 +441,38 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Some(UseAtIndexState { + match (extract.clone(), args.len()) { + (SliceExtractType::ZeroMember | SliceExtractType::Shift, 1) => Some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice(Cow::Borrowed(taker)), + error_type: ErrorType::Slice(SliceArgType::OneArg, extract), object: sliced_exp, }), - (-1, 1) if get_integer_from_literal(&start_exp)? < 0 => Some(UseAtIndexState { - at_number_exp: make_number_literal(-1), - error_type: ErrorType::SlicePop, - object: sliced_exp, - }), - (0, 2) => { + (SliceExtractType::Pop, 1) if get_integer_from_literal(&start_exp)? < 0 => { + Some(UseAtIndexState { + at_number_exp: make_number_literal(-1), + error_type: ErrorType::Slice(SliceArgType::OneArg, SliceExtractType::Pop), + object: sliced_exp, + }) + } + (SliceExtractType::ZeroMember | SliceExtractType::Shift, 2) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args[1].as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice2(Cow::Borrowed(taker)), + error_type: ErrorType::Slice(SliceArgType::TwoArg, extract), object: sliced_exp, }) } - (-1, 2) => { + (SliceExtractType::Pop, 2) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args[1].as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), - error_type: ErrorType::Slice2Pop, + error_type: ErrorType::Slice(SliceArgType::TwoArg, SliceExtractType::Pop), object: sliced_exp, }) } @@ -513,13 +491,13 @@ fn check_binary_expression_member( if let Some(negative_index) = negative_index_exp { return Some(UseAtIndexState { at_number_exp: negative_index, - error_type: ErrorType::NegativeIndex, + error_type: ErrorType::Index(IndexType::Negative), object, }); } option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: member, - error_type: ErrorType::NegativeIndex, + error_type: ErrorType::Index(IndexType::Negative), object, }) } @@ -536,7 +514,7 @@ fn check_literal_expression_member( let number = value_token.text_trimmed().parse::().ok()?; (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { at_number_exp: make_number_literal(number), - error_type: ErrorType::IdIndex, + error_type: ErrorType::Index(IndexType::Positive), object, }) } @@ -562,7 +540,7 @@ fn check_unary_expression_member( } Some(UseAtIndexState { at_number_exp: AnyJsExpression::JsUnaryExpression(member), - error_type: ErrorType::IdIndex, + error_type: ErrorType::Index(IndexType::Positive), object, }) } @@ -604,7 +582,7 @@ fn check_computed_member_expression( AnyJsExpression::JsIdentifierExpression(_) => None, _ => option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: member, - error_type: ErrorType::IdIndex, + error_type: ErrorType::Index(IndexType::Positive), object, }), } @@ -672,13 +650,13 @@ fn check_call_expression_char_at( if let Some(at_number_exp) = at_number_exp { Some(UseAtIndexState { at_number_exp, - error_type: ErrorType::StringCharAtNegativeIndex, + error_type: ErrorType::StringCharAt(IndexType::Negative), object: char_at_parent, }) } else { option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, + error_type: ErrorType::StringCharAt(IndexType::Positive), object: char_at_parent, }) } @@ -687,13 +665,13 @@ fn check_call_expression_char_at( AnyJsExpression::AnyJsLiteralExpression(_member) => { option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, + error_type: ErrorType::StringCharAt(IndexType::Positive), object: char_at_parent.clone(), }) } _ => option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt, + error_type: ErrorType::StringCharAt(IndexType::Positive), object: char_at_parent.clone(), }), } @@ -754,6 +732,100 @@ fn make_at_method(object: AnyJsExpression, arg: AnyJsExpression) -> JsCallExpres make::js_call_expression(at_member.into(), args).build() } +/// Method of specifying the index +pub enum IndexType { + Negative, + Positive, +} + +/// The method to retrieve values from `.slice()` +#[derive(Clone)] +pub enum SliceExtractType { + Pop, + Shift, + ZeroMember, +} + +/// The number of arguments for `.slice()` +pub enum SliceArgType { + OneArg, + TwoArg, +} + +/// Type of Code to Fix +pub enum ErrorType { + Index(IndexType), + StringCharAt(IndexType), + Slice(SliceArgType, SliceExtractType), + GetLastFunction, +} + +/// Return the error message corresponding to the ErrorType. +fn get_error_message(error_type: &ErrorType) -> MarkupBuf { + match error_type { + ErrorType::Index(index) => { + match index { + IndexType::Negative => { + markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." }.to_owned() + } + IndexType::Positive => { + markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." }.to_owned() + } + } + } + ErrorType::StringCharAt(index) => { + match index { + IndexType::Negative => { + markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." }.to_owned() + } + IndexType::Positive => { + markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." }.to_owned() + } + } + } + ErrorType::Slice(arg, extract) => { + match extract { + SliceExtractType::Pop => match arg { + SliceArgType::TwoArg => { + markup! { "Prefer ""X.at(Y - 1)"" over ""X.slice(a, Y).pop()""." }.to_owned() + } + SliceArgType::OneArg =>{ + markup! { "Prefer ""X.at(-1)"" over ""X.slice(-a).pop()""." }.to_owned() + } + } + _ => { + let extract_string = match extract { + SliceExtractType::Pop => ".pop()", + SliceExtractType::Shift => ".shift()", + SliceExtractType::ZeroMember => "[0]", + }; + match arg { + SliceArgType::OneArg => { + markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)"{extract_string}"." }.to_owned() + } + SliceArgType::TwoArg => { + markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y, a)"{extract_string}"." }.to_owned() + } + } + } + } + } + ErrorType::GetLastFunction => { + markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() + } + } +} + +declare_node_union! { + pub AnyJsArrayAccess = JsComputedMemberExpression | JsCallExpression +} + +pub struct UseAtIndexState { + at_number_exp: AnyJsExpression, + error_type: ErrorType, + object: AnyJsExpression, +} + #[derive( Clone, Debug, @@ -794,43 +866,17 @@ impl Rule for UseAtIndex { fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); - Some(RuleDiagnostic::new( - rule_category!(), - node.range(), - markup! { - "Replace index references with "".at()""." - }.to_owned(), - ).note( - match &state.error_type { - ErrorType::NegativeIndex => { - markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." }.to_owned() - } - ErrorType::IdIndex => { - markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." }.to_owned() - } - ErrorType::StringCharAtNegativeIndex => { - markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." }.to_owned() + Some( + RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "Replace index references with "".at()""." } - ErrorType::StringCharAt => { - markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." }.to_owned() - } - ErrorType::Slice(taker) => { - markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)"{taker}"." }.to_owned() - } - ErrorType::SlicePop => { - markup! { "Prefer ""X.at(-1)"" over ""X.slice(-a).pop()""." }.to_owned() - } - ErrorType::Slice2(taker) => { - markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y, a)"{taker}"." }.to_owned() - } - ErrorType::Slice2Pop => { - markup! { "Prefer ""X.at(Y - 1)"" over ""X.slice(a, Y).pop()""." }.to_owned() - } - ErrorType::GetLastFunction => { - markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() - } - } - )) + .to_owned(), + ) + .note(get_error_message(&state.error_type)), + ) } fn action(ctx: &RuleContext, state: &Self::State) -> Option { From 7a388cc4f3298f74f3951693f70ffa8c0f6322c2 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 22:28:45 +0900 Subject: [PATCH 13/48] refactor: convert SliceType to struct --- .../src/lint/nursery/use_at_index.rs | 130 ++++++++---------- 1 file changed, 59 insertions(+), 71 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 045ec76e7255..fa08825a535b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -362,7 +362,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let arg_length = call_exp.arguments().ok()?.args().into_iter().count(); @@ -441,16 +441,22 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice(SliceArgType::OneArg, extract), + error_type: ErrorType::Slice { + arg_type: SliceArgType::OneArg, + extract_type, + }, object: sliced_exp, }), (SliceExtractType::Pop, 1) if get_integer_from_literal(&start_exp)? < 0 => { Some(UseAtIndexState { at_number_exp: make_number_literal(-1), - error_type: ErrorType::Slice(SliceArgType::OneArg, SliceExtractType::Pop), + error_type: ErrorType::Slice { + arg_type: SliceArgType::OneArg, + extract_type: SliceExtractType::Pop, + }, object: sliced_exp, }) } @@ -461,7 +467,10 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, - error_type: ErrorType::Slice(SliceArgType::TwoArg, extract), + error_type: ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type, + }, object: sliced_exp, }) } @@ -472,7 +481,10 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), - error_type: ErrorType::Slice(SliceArgType::TwoArg, SliceExtractType::Pop), + error_type: ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type: SliceExtractType::Pop, + }, object: sliced_exp, }) } @@ -491,13 +503,13 @@ fn check_binary_expression_member( if let Some(negative_index) = negative_index_exp { return Some(UseAtIndexState { at_number_exp: negative_index, - error_type: ErrorType::Index(IndexType::Negative), + error_type: ErrorType::Index { is_negative: true }, object, }); } option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: member, - error_type: ErrorType::Index(IndexType::Negative), + error_type: ErrorType::Index { is_negative: true }, object, }) } @@ -514,7 +526,7 @@ fn check_literal_expression_member( let number = value_token.text_trimmed().parse::().ok()?; (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { at_number_exp: make_number_literal(number), - error_type: ErrorType::Index(IndexType::Positive), + error_type: ErrorType::Index { is_negative: false }, object, }) } @@ -540,7 +552,7 @@ fn check_unary_expression_member( } Some(UseAtIndexState { at_number_exp: AnyJsExpression::JsUnaryExpression(member), - error_type: ErrorType::Index(IndexType::Positive), + error_type: ErrorType::Index { is_negative: false }, object, }) } @@ -582,7 +594,7 @@ fn check_computed_member_expression( AnyJsExpression::JsIdentifierExpression(_) => None, _ => option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: member, - error_type: ErrorType::Index(IndexType::Positive), + error_type: ErrorType::Index { is_negative: false }, object, }), } @@ -650,13 +662,13 @@ fn check_call_expression_char_at( if let Some(at_number_exp) = at_number_exp { Some(UseAtIndexState { at_number_exp, - error_type: ErrorType::StringCharAt(IndexType::Negative), + error_type: ErrorType::StringCharAt { is_negative: true }, object: char_at_parent, }) } else { option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt(IndexType::Positive), + error_type: ErrorType::StringCharAt { is_negative: false }, object: char_at_parent, }) } @@ -665,13 +677,13 @@ fn check_call_expression_char_at( AnyJsExpression::AnyJsLiteralExpression(_member) => { option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt(IndexType::Positive), + error_type: ErrorType::StringCharAt { is_negative: false }, object: char_at_parent.clone(), }) } _ => option.check_all_index_access.then_some(UseAtIndexState { at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt(IndexType::Positive), + error_type: ErrorType::StringCharAt { is_negative: false }, object: char_at_parent.clone(), }), } @@ -732,12 +744,6 @@ fn make_at_method(object: AnyJsExpression, arg: AnyJsExpression) -> JsCallExpres make::js_call_expression(at_member.into(), args).build() } -/// Method of specifying the index -pub enum IndexType { - Negative, - Positive, -} - /// The method to retrieve values from `.slice()` #[derive(Clone)] pub enum SliceExtractType { @@ -754,62 +760,44 @@ pub enum SliceArgType { /// Type of Code to Fix pub enum ErrorType { - Index(IndexType), - StringCharAt(IndexType), - Slice(SliceArgType, SliceExtractType), + Index { + is_negative: bool, + }, + StringCharAt { + is_negative: bool, + }, + Slice { + arg_type: SliceArgType, + extract_type: SliceExtractType, + }, GetLastFunction, } /// Return the error message corresponding to the ErrorType. fn get_error_message(error_type: &ErrorType) -> MarkupBuf { match error_type { - ErrorType::Index(index) => { - match index { - IndexType::Negative => { - markup! { "Prefer ""X.at(-Y)"" over ""X[X.length - Y]""." }.to_owned() - } - IndexType::Positive => { - markup! { "Prefer ""X.at(Y)"" over ""X[Y]""." }.to_owned() - } - } - } - ErrorType::StringCharAt(index) => { - match index { - IndexType::Negative => { - markup! { "Prefer ""X.at(-Y)"" over ""X.charAt(X.length - Y)""." }.to_owned() - } - IndexType::Positive => { - markup! { "Prefer ""X.at(Y)"" over ""X.charAt(Y)""." }.to_owned() - } - } - } - ErrorType::Slice(arg, extract) => { - match extract { - SliceExtractType::Pop => match arg { - SliceArgType::TwoArg => { - markup! { "Prefer ""X.at(Y - 1)"" over ""X.slice(a, Y).pop()""." }.to_owned() - } - SliceArgType::OneArg =>{ - markup! { "Prefer ""X.at(-1)"" over ""X.slice(-a).pop()""." }.to_owned() - } - } - _ => { - let extract_string = match extract { - SliceExtractType::Pop => ".pop()", - SliceExtractType::Shift => ".shift()", - SliceExtractType::ZeroMember => "[0]", - }; - match arg { - SliceArgType::OneArg => { - markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y)"{extract_string}"." }.to_owned() - } - SliceArgType::TwoArg => { - markup! { "Prefer ""X.at(Y)"" over ""X.slice(Y, a)"{extract_string}"." }.to_owned() - } - } - } - } - } + ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { + let (method, old_method) = if *is_negative { + ("X.at(-Y)", if matches!(error_type, ErrorType::StringCharAt { .. }) { "X.charAt(X.length - Y)" } else { "X[X.length - Y]" }) + } else { + ("X.at(Y)", if matches!(error_type, ErrorType::StringCharAt { .. }) { "X.charAt(Y)" } else { "X[Y]" }) + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + }, + ErrorType::Slice { arg_type, extract_type } => { + let extract_string = match extract_type { + SliceExtractType::Pop => ".pop()", + SliceExtractType::Shift => ".shift()", + SliceExtractType::ZeroMember => "[0]", + }; + let (method, old_method) = match (arg_type, extract_type) { + (SliceArgType::OneArg, SliceExtractType::Pop) => ("X.at(-1)", format!("X.slice(-a){}", extract_string)), + (SliceArgType::TwoArg, SliceExtractType::Pop) => ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)), + (SliceArgType::OneArg, _) => ("X.at(Y)", format!("X.slice(Y){}", extract_string)), + (SliceArgType::TwoArg, _) => ("X.at(Y)", format!("X.slice(Y, a){}", extract_string)), + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + }, ErrorType::GetLastFunction => { markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() } From 9a3d9a6a4b8caac348e665cec9db6621e1364fbe Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 22:43:24 +0900 Subject: [PATCH 14/48] refactor: impl struct function --- .../src/lint/nursery/use_at_index.rs | 230 ++++++++++-------- 1 file changed, 128 insertions(+), 102 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index fa08825a535b..005e491392ab 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -442,51 +442,55 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Some(UseAtIndexState { - at_number_exp: start_exp, - error_type: ErrorType::Slice { + (SliceExtractType::ZeroMember | SliceExtractType::Shift, 1) => Some(UseAtIndexState::new( + start_exp, + ErrorType::Slice { arg_type: SliceArgType::OneArg, extract_type, }, - object: sliced_exp, - }), + sliced_exp, + )), (SliceExtractType::Pop, 1) if get_integer_from_literal(&start_exp)? < 0 => { - Some(UseAtIndexState { - at_number_exp: make_number_literal(-1), - error_type: ErrorType::Slice { + Some(UseAtIndexState::new( + make_number_literal(-1), + ErrorType::Slice { arg_type: SliceArgType::OneArg, extract_type: SliceExtractType::Pop, }, - object: sliced_exp, - }) + sliced_exp, + )) } (SliceExtractType::ZeroMember | SliceExtractType::Shift, 2) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args[1].as_any_js_expression()?.clone(), )?)?; - (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { - at_number_exp: start_exp, - error_type: ErrorType::Slice { - arg_type: SliceArgType::TwoArg, - extract_type, - }, - object: sliced_exp, - }) + (start_index * end_index >= 0 && start_index < end_index).then_some( + UseAtIndexState::new( + start_exp, + ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type, + }, + sliced_exp, + ), + ) } (SliceExtractType::Pop, 2) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args[1].as_any_js_expression()?.clone(), )?)?; - (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { - at_number_exp: make_number_literal(end_index - 1), - error_type: ErrorType::Slice { - arg_type: SliceArgType::TwoArg, - extract_type: SliceExtractType::Pop, - }, - object: sliced_exp, - }) + (start_index * end_index >= 0 && start_index < end_index).then_some( + UseAtIndexState::new( + make_number_literal(end_index - 1), + ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type: SliceExtractType::Pop, + }, + sliced_exp, + ), + ) } _ => None, } @@ -501,17 +505,19 @@ fn check_binary_expression_member( let negative_index_exp = extract_negative_index_expression(&member, solve_parenthesized_expression(object.clone())?); if let Some(negative_index) = negative_index_exp { - return Some(UseAtIndexState { - at_number_exp: negative_index, - error_type: ErrorType::Index { is_negative: true }, + return Some(UseAtIndexState::new( + negative_index, + ErrorType::Index { is_negative: true }, object, - }); + )); } - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::Index { is_negative: true }, - object, - }) + option + .check_all_index_access + .then_some(UseAtIndexState::new( + member, + ErrorType::Index { is_negative: true }, + object, + )) } fn check_literal_expression_member( @@ -524,11 +530,11 @@ fn check_literal_expression_member( }; let value_token = member.value_token().ok()?; let number = value_token.text_trimmed().parse::().ok()?; - (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { - at_number_exp: make_number_literal(number), - error_type: ErrorType::Index { is_negative: false }, + (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState::new( + make_number_literal(number), + ErrorType::Index { is_negative: false }, object, - }) + )) } fn check_unary_expression_member( @@ -550,11 +556,11 @@ fn check_unary_expression_member( } } } - Some(UseAtIndexState { - at_number_exp: AnyJsExpression::JsUnaryExpression(member), - error_type: ErrorType::Index { is_negative: false }, + Some(UseAtIndexState::new( + AnyJsExpression::JsUnaryExpression(member), + ErrorType::Index { is_negative: false }, object, - }) + )) } /// check hoge[0] @@ -592,11 +598,13 @@ fn check_computed_member_expression( check_unary_expression_member(unary, object, option) } AnyJsExpression::JsIdentifierExpression(_) => None, - _ => option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::Index { is_negative: false }, - object, - }), + _ => option + .check_all_index_access + .then_some(UseAtIndexState::new( + member, + ErrorType::Index { is_negative: false }, + object, + )), } } @@ -624,11 +632,11 @@ fn check_call_expression_last( let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; - Some(UseAtIndexState { - at_number_exp: make_number_literal(-1), - error_type: ErrorType::GetLastFunction, - object: solve_parenthesized_expression(arg0)?, - }) + Some(UseAtIndexState::new( + make_number_literal(-1), + ErrorType::GetLastFunction, + solve_parenthesized_expression(arg0)?, + )) } else { None } @@ -660,32 +668,36 @@ fn check_call_expression_char_at( let at_number_exp = extract_negative_index_expression(&core_arg0, char_at_parent.clone()); if let Some(at_number_exp) = at_number_exp { - Some(UseAtIndexState { + Some(UseAtIndexState::new( at_number_exp, - error_type: ErrorType::StringCharAt { is_negative: true }, - object: char_at_parent, - }) + ErrorType::StringCharAt { is_negative: true }, + char_at_parent, + )) } else { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent, - }) + option + .check_all_index_access + .then_some(UseAtIndexState::new( + core_arg0, + ErrorType::StringCharAt { is_negative: false }, + char_at_parent, + )) } } // hoge.charAt(1) - AnyJsExpression::AnyJsLiteralExpression(_member) => { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent.clone(), - }) - } - _ => option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent.clone(), - }), + AnyJsExpression::AnyJsLiteralExpression(_member) => option + .check_all_index_access + .then_some(UseAtIndexState::new( + core_arg0, + ErrorType::StringCharAt { is_negative: false }, + char_at_parent.clone(), + )), + _ => option + .check_all_index_access + .then_some(UseAtIndexState::new( + core_arg0, + ErrorType::StringCharAt { is_negative: false }, + char_at_parent.clone(), + )), } } @@ -753,12 +765,14 @@ pub enum SliceExtractType { } /// The number of arguments for `.slice()` +#[derive(Clone)] pub enum SliceArgType { OneArg, TwoArg, } /// Type of Code to Fix +#[derive(Clone)] pub enum ErrorType { Index { is_negative: bool, @@ -773,33 +787,35 @@ pub enum ErrorType { GetLastFunction, } -/// Return the error message corresponding to the ErrorType. -fn get_error_message(error_type: &ErrorType) -> MarkupBuf { - match error_type { - ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { - let (method, old_method) = if *is_negative { - ("X.at(-Y)", if matches!(error_type, ErrorType::StringCharAt { .. }) { "X.charAt(X.length - Y)" } else { "X[X.length - Y]" }) - } else { - ("X.at(Y)", if matches!(error_type, ErrorType::StringCharAt { .. }) { "X.charAt(Y)" } else { "X[Y]" }) - }; - markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - }, - ErrorType::Slice { arg_type, extract_type } => { - let extract_string = match extract_type { - SliceExtractType::Pop => ".pop()", - SliceExtractType::Shift => ".shift()", - SliceExtractType::ZeroMember => "[0]", - }; - let (method, old_method) = match (arg_type, extract_type) { - (SliceArgType::OneArg, SliceExtractType::Pop) => ("X.at(-1)", format!("X.slice(-a){}", extract_string)), - (SliceArgType::TwoArg, SliceExtractType::Pop) => ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)), - (SliceArgType::OneArg, _) => ("X.at(Y)", format!("X.slice(Y){}", extract_string)), - (SliceArgType::TwoArg, _) => ("X.at(Y)", format!("X.slice(Y, a){}", extract_string)), - }; - markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - }, - ErrorType::GetLastFunction => { - markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() +impl ErrorType { + /// Return the error message corresponding to the ErrorType. + fn get_error_message(self: &ErrorType) -> MarkupBuf { + match self { + ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { + let (method, old_method) = if *is_negative { + ("X.at(-Y)", if matches!(self, ErrorType::StringCharAt { .. }) { "X.charAt(X.length - Y)" } else { "X[X.length - Y]" }) + } else { + ("X.at(Y)", if matches!(self, ErrorType::StringCharAt { .. }) { "X.charAt(Y)" } else { "X[Y]" }) + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + }, + ErrorType::Slice { arg_type, extract_type } => { + let extract_string = match extract_type { + SliceExtractType::Pop => ".pop()", + SliceExtractType::Shift => ".shift()", + SliceExtractType::ZeroMember => "[0]", + }; + let (method, old_method) = match (arg_type, extract_type) { + (SliceArgType::OneArg, SliceExtractType::Pop) => ("X.at(-1)", format!("X.slice(-a){}", extract_string)), + (SliceArgType::TwoArg, SliceExtractType::Pop) => ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)), + (SliceArgType::OneArg, _) => ("X.at(Y)", format!("X.slice(Y){}", extract_string)), + (SliceArgType::TwoArg, _) => ("X.at(Y)", format!("X.slice(Y, a){}", extract_string)), + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + }, + ErrorType::GetLastFunction => { + markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() + } } } } @@ -814,6 +830,16 @@ pub struct UseAtIndexState { object: AnyJsExpression, } +impl UseAtIndexState { + fn new(at_number_exp: AnyJsExpression, error_type: ErrorType, object: AnyJsExpression) -> Self { + Self { + at_number_exp, + error_type, + object, + } + } +} + #[derive( Clone, Debug, @@ -863,7 +889,7 @@ impl Rule for UseAtIndex { } .to_owned(), ) - .note(get_error_message(&state.error_type)), + .note(state.error_type.get_error_message()), ) } From fe32c96209b63bdb4464e32136ade57baa20cf46 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 22:48:46 +0900 Subject: [PATCH 15/48] refactor: use match {_ if hoge => ... } --- .../src/lint/nursery/use_at_index.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 005e491392ab..8994d7b8f004 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -598,13 +598,12 @@ fn check_computed_member_expression( check_unary_expression_member(unary, object, option) } AnyJsExpression::JsIdentifierExpression(_) => None, - _ => option - .check_all_index_access - .then_some(UseAtIndexState::new( - member, - ErrorType::Index { is_negative: false }, - object, - )), + _ if option.check_all_index_access => Some(UseAtIndexState::new( + member, + ErrorType::Index { is_negative: false }, + object, + )), + _ => None, } } From 9e1731283d138bf64de911d8b7776ec6ff3a6aed Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 22:50:06 +0900 Subject: [PATCH 16/48] refactor: integrate the branches in ErrorType::Slice --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 8994d7b8f004..30c0ee813b55 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -807,8 +807,7 @@ impl ErrorType { let (method, old_method) = match (arg_type, extract_type) { (SliceArgType::OneArg, SliceExtractType::Pop) => ("X.at(-1)", format!("X.slice(-a){}", extract_string)), (SliceArgType::TwoArg, SliceExtractType::Pop) => ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)), - (SliceArgType::OneArg, _) => ("X.at(Y)", format!("X.slice(Y){}", extract_string)), - (SliceArgType::TwoArg, _) => ("X.at(Y)", format!("X.slice(Y, a){}", extract_string)), + _ => ("X.at(Y)", format!("X.slice({}){}", if matches!(arg_type, SliceArgType::OneArg) { "Y" } else { "Y, a" }, extract_string)), }; markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() }, From df06a4901b97b4cbd4c102e1f9f7c820b5769fdf Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Sun, 29 Sep 2024 23:43:08 +0900 Subject: [PATCH 17/48] test: use .jsonc --- .../nursery/useAtIndex/charAtInvalid.json | 13 - .../useAtIndex/charAtInvalid.json.snap | 268 --- .../specs/nursery/useAtIndex/charAtValid.json | 13 - .../nursery/useAtIndex/charAtValid.json.snap | 59 - ....json => checkAllIndexAccessInvalid.jsonc} | 1 + ... => checkAllIndexAccessInvalid.jsonc.snap} | 30 +- ...id.json => checkAllIndexAccessValid.jsonc} | 0 ...ap => checkAllIndexAccessValid.jsonc.snap} | 0 .../nursery/useAtIndex/indexInvalid.json | 24 - .../nursery/useAtIndex/indexInvalid.json.snap | 532 ----- .../specs/nursery/useAtIndex/indexValid.json | 16 - .../nursery/useAtIndex/indexValid.json.snap | 74 - .../specs/nursery/useAtIndex/invalid.jsonc | 90 + .../nursery/useAtIndex/invalid.jsonc.snap | 1732 +++++++++++++++++ .../nursery/useAtIndex/lodashInvalid.json | 8 - .../useAtIndex/lodashInvalid.json.snap | 148 -- .../specs/nursery/useAtIndex/lodashValid.json | 5 - .../nursery/useAtIndex/lodashValid.json.snap | 19 - .../nursery/useAtIndex/sliceInvalid.json | 32 - .../nursery/useAtIndex/sliceInvalid.json.snap | 724 ------- .../specs/nursery/useAtIndex/sliceValid.json | 29 - .../nursery/useAtIndex/sliceValid.json.snap | 139 -- .../specs/nursery/useAtIndex/valid.jsonc | 75 + .../specs/nursery/useAtIndex/valid.jsonc.snap | 279 +++ 24 files changed, 2192 insertions(+), 2118 deletions(-) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap rename crates/biome_js_analyze/tests/specs/nursery/useAtIndex/{checkAllIndexAccessInvalid.json => checkAllIndexAccessInvalid.jsonc} (91%) rename crates/biome_js_analyze/tests/specs/nursery/useAtIndex/{checkAllIndexAccessInvalid.json.snap => checkAllIndexAccessInvalid.jsonc.snap} (69%) rename crates/biome_js_analyze/tests/specs/nursery/useAtIndex/{checkAllIndexAccessValid.json => checkAllIndexAccessValid.jsonc} (100%) rename crates/biome_js_analyze/tests/specs/nursery/useAtIndex/{checkAllIndexAccessValid.json.snap => checkAllIndexAccessValid.jsonc.snap} (100%) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json deleted file mode 100644 index 1a96a3e4d493..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - "string.charAt(string.length - 1);", - "string.charAt(string.length - 0o11);", - "some.string.charAt(some.string.length - 1);", - "string.charAt((( string.length )) - 0xFF);", - "string.charAt(string.length - (( 1 )));", - "string.charAt((( string.length - 1 )));", - "(( string )).charAt(string.length - 1);", - "(( string.charAt ))(string.length - 1);", - "(( string.charAt(string.length - 1) ));", - "string.charAt(string.length - unknown - 1 );", - "string.charAt(string.length - (unknown + 1));" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap deleted file mode 100644 index 77218193c17f..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtInvalid.json.snap +++ /dev/null @@ -1,268 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: charAtInvalid.json ---- -# Input -```cjs -string.charAt(string.length - 1); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - 1); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·1); - + string.at(-1); - - -``` - -# Input -```cjs -string.charAt(string.length - 0o11); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - 0o11); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·0o11); - + string.at(-0o11); - - -``` - -# Input -```cjs -some.string.charAt(some.string.length - 1); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ some.string.charAt(some.string.length - 1); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - some.string.charAt(some.string.length·-·1); - + some.string.at(-1); - - -``` - -# Input -```cjs -string.charAt((( string.length )) - 0xFF); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt((( string.length )) - 0xFF); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(((·string.length·))·-·0xFF); - + string.at(-0xFF); - - -``` - -# Input -```cjs -string.charAt(string.length - (( 1 ))); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - (( 1 ))); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·((·1·))); - + string.at(-((·1·))); - - -``` - -# Input -```cjs -string.charAt((( string.length - 1 ))); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt((( string.length - 1 ))); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(((·string.length·-·1·))); - + string.at(-1); - - -``` - -# Input -```cjs -(( string )).charAt(string.length - 1); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( string )).charAt(string.length - 1); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - ((·string·)).charAt(string.length·-·1); - + string.at(-1); - - -``` - -# Input -```cjs -(( string.charAt ))(string.length - 1); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( string.charAt ))(string.length - 1); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - ((·string.charAt·))(string.length·-·1); - + string.at(-1); - - -``` - -# Input -```cjs -(( string.charAt(string.length - 1) )); -``` - -# Diagnostics -``` -charAtInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( string.charAt(string.length - 1) )); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - ((·string.charAt(string.length·-·1)·)); - + ((·string.at(-1)·)); - - -``` - -# Input -```cjs -string.charAt(string.length - unknown - 1 ); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - unknown - 1 ); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·unknown·-·1·); - + string.at(unknown·+1); - - -``` - -# Input -```cjs -string.charAt(string.length - (unknown + 1)); -``` - -# Diagnostics -``` -charAtInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - (unknown + 1)); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·(unknown·+·1)); - + string.at(-(unknown·+·1)); - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json deleted file mode 100644 index 32abba2e8626..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - "string.charAt(string.length - 0);", - "string.charAt(string.length + 1)", - "string.charAt(string.length + -1)", - "foo.charAt(bar.length - 1)", - "string?.charAt?.(string.length - 1);", - "string?.charAt(string.length - 1);", - "string.charAt(9);", - "string1.charAt(string2.length - 1);", - "string.charAt(hoge.string.length - 1)", - "string.charAt(string.length - 1 + 1)", - "string.charAt(string.length + 1 - 1)" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap deleted file mode 100644 index 5c8018909647..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/charAtValid.json.snap +++ /dev/null @@ -1,59 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: charAtValid.json ---- -# Input -```cjs -string.charAt(string.length - 0); -``` - -# Input -```cjs -string.charAt(string.length + 1) -``` - -# Input -```cjs -string.charAt(string.length + -1) -``` - -# Input -```cjs -foo.charAt(bar.length - 1) -``` - -# Input -```cjs -string?.charAt?.(string.length - 1); -``` - -# Input -```cjs -string?.charAt(string.length - 1); -``` - -# Input -```cjs -string.charAt(9); -``` - -# Input -```cjs -string1.charAt(string2.length - 1); -``` - -# Input -```cjs -string.charAt(hoge.string.length - 1) -``` - -# Input -```cjs -string.charAt(string.length - 1 + 1) -``` - -# Input -```cjs -string.charAt(string.length + 1 - 1) -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc similarity index 91% rename from crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json rename to crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc index 6ea76e5c3e1e..51394550bbe1 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc @@ -4,6 +4,7 @@ "array[5 + 9]", "const offset = 5;array[offset + 9]", "array[array.length - 1]", + // `charAt` don't care about value "string.charAt(9)", "string.charAt(5 + 9)", "const offset = 5;string.charAt(offset + 9)", diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap similarity index 69% rename from crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap rename to crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap index cbda2b5bb361..b0ffb8dc9cb0 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.json.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap @@ -1,7 +1,7 @@ --- source: crates/biome_js_analyze/tests/spec_tests.rs assertion_line: 86 -expression: checkAllIndexAccessInvalid.json +expression: checkAllIndexAccessInvalid.jsonc --- # Input ```cjs @@ -10,7 +10,7 @@ array[0] # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -34,7 +34,7 @@ array[1] # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -58,7 +58,7 @@ array[5 + 9] # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -82,7 +82,7 @@ const offset = 5;array[offset + 9] # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -106,7 +106,7 @@ array[array.length - 1] # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -130,7 +130,7 @@ string.charAt(9) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -154,7 +154,7 @@ string.charAt(5 + 9) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -178,7 +178,7 @@ const offset = 5;string.charAt(offset + 9) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -202,7 +202,7 @@ string.charAt(unknown) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -226,7 +226,7 @@ string.charAt(-1) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -250,7 +250,7 @@ string.charAt(1.5) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -274,7 +274,7 @@ string.charAt(1n) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -298,7 +298,7 @@ string.charAt(string.length - 1) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). @@ -322,7 +322,7 @@ foo.charAt(bar.length - 1) # Diagnostics ``` -checkAllIndexAccessInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Replace index references with .at(). diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json rename to crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.json.snap rename to crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json deleted file mode 100644 index 61d7e0f364b2..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - "array[array.length - 1];", - "array[array.length -1];", - "array[array.length - /* comment */ 1];", - "array[array.length - 1.];", - "array[array.length - 0b1];", - "array[array.length - 9];", - "array[0][array[0].length - 1];", - "array[(( array.length )) - 1];", - "array[array.length - (( 1 ))];", - "array[(( array.length - 1 ))];", - "(( array ))[array.length - 1];", - "(( array[array.length - 1] ));", - "array[array.length - 1].pop().shift()[0];", - "a = array[array.length - 1]", - "const a = array[array.length - 1]", - "const {a = array[array.length - 1]} = {}", - "typeof array[array.length - 1]", - "function foo() {return arguments[arguments.length - 1]}", - "class Foo {bar; baz() {return this.bar[this.bar.length - 1]}}", - "class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}}", - "array[array.length - unknown - 1]", - "array[array.length - (unknown + 1)]" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap deleted file mode 100644 index 61f85f5a1d8f..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexInvalid.json.snap +++ /dev/null @@ -1,532 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: indexInvalid.json ---- -# Input -```cjs -array[array.length - 1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·1]; - + array.at(-1); - - -``` - -# Input -```cjs -array[array.length -1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length -1]; - │ ^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-1]; - + array.at(-1); - - -``` - -# Input -```cjs -array[array.length - /* comment */ 1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - /* comment */ 1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·/*·comment·*/·1]; - + array.at(-1); - - -``` - -# Input -```cjs -array[array.length - 1.]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 1.]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·1.]; - + array.at(-1.); - - -``` - -# Input -```cjs -array[array.length - 0b1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 0b1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·0b1]; - + array.at(-0b1); - - -``` - -# Input -```cjs -array[array.length - 9]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 9]; - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·9]; - + array.at(-9); - - -``` - -# Input -```cjs -array[0][array[0].length - 1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[0][array[0].length - 1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[0][array[0].length·-·1]; - + array[0].at(-1); - - -``` - -# Input -```cjs -array[(( array.length )) - 1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[(( array.length )) - 1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[((·array.length·))·-·1]; - + array.at(-1); - - -``` - -# Input -```cjs -array[array.length - (( 1 ))]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - (( 1 ))]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·((·1·))]; - + array.at(-((·1·))); - - -``` - -# Input -```cjs -array[(( array.length - 1 ))]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[(( array.length - 1 ))]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[((·array.length·-·1·))]; - + array.at(-1); - - -``` - -# Input -```cjs -(( array ))[array.length - 1]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array ))[array.length - 1]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - ((·array·))[array.length·-·1]; - + ((·array·)).at(-1); - - -``` - -# Input -```cjs -(( array[array.length - 1] )); -``` - -# Diagnostics -``` -indexInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array[array.length - 1] )); - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - ((·array[array.length·-·1]·)); - + ((·array.at(-1)·)); - - -``` - -# Input -```cjs -array[array.length - 1].pop().shift()[0]; -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 1].pop().shift()[0]; - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·1].pop().shift()[0]; - + array.at(-1).pop().shift()[0]; - - -``` - -# Input -```cjs -a = array[array.length - 1] -``` - -# Diagnostics -``` -indexInvalid.json:1:5 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ a = array[array.length - 1] - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - a·=·array[array.length·-·1] - + a·=·array.at(-1) - - -``` - -# Input -```cjs -const a = array[array.length - 1] -``` - -# Diagnostics -``` -indexInvalid.json:1:11 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const a = array[array.length - 1] - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - const·a·=·array[array.length·-·1] - + const·a·=·array.at(-1) - - -``` - -# Input -```cjs -const {a = array[array.length - 1]} = {} -``` - -# Diagnostics -``` -indexInvalid.json:1:12 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const {a = array[array.length - 1]} = {} - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - const·{a·=·array[array.length·-·1]}·=·{} - + const·{a·=·array.at(-1)}·=·{} - - -``` - -# Input -```cjs -typeof array[array.length - 1] -``` - -# Diagnostics -``` -indexInvalid.json:1:8 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ typeof array[array.length - 1] - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - typeof·array[array.length·-·1] - + typeof·array.at(-1) - - -``` - -# Input -```cjs -function foo() {return arguments[arguments.length - 1]} -``` - -# Diagnostics -``` -indexInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ function foo() {return arguments[arguments.length - 1]} - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - function·foo()·{return·arguments[arguments.length·-·1]} - + function·foo()·{return·arguments.at(-1)} - - -``` - -# Input -```cjs -class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} -``` - -# Diagnostics -``` -indexInvalid.json:1:31 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - class·Foo·{bar;·baz()·{return·this.bar[this.bar.length·-·1]}} - + class·Foo·{bar;·baz()·{return·this.bar.at(-1)}} - - -``` - -# Input -```cjs -class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} -``` - -# Diagnostics -``` -indexInvalid.json:1:32 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - class·Foo·{#bar;·baz()·{return·this.#bar[this.#bar.length·-·1]}} - + class·Foo·{#bar;·baz()·{return·this.#bar.at(-1)}} - - -``` - -# Input -```cjs -array[array.length - unknown - 1] -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - unknown - 1] - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·unknown·-·1] - + array.at(unknown·+1) - - -``` - -# Input -```cjs -array[array.length - (unknown + 1)] -``` - -# Diagnostics -``` -indexInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - (unknown + 1)] - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·(unknown·+·1)] - + array.at(-(unknown·+·1)) - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json deleted file mode 100644 index 9f352b6d5586..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - "array.at(-1)", - "array[array.length - 0];", - "array[array.length + 1]", - "array[array.length + -1]", - "foo[bar.length - 1]", - "array?.[array.length - 1];", - "array[array.length - 1] = 1", - "array[array.length - 1] %= 1", - "++ array[array.length - 1]", - "array[array.length - 1] --", - "delete array[array.length - 1]", - "class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}}", - "([array[array.length - 1]] = [])", - "({foo: array[array.length - 1] = 9} = {})" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap deleted file mode 100644 index 43b532c1b1ea..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/indexValid.json.snap +++ /dev/null @@ -1,74 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: valid.json ---- -# Input -```cjs -array.at(-1) -``` - -# Input -```cjs -array[array.length - 0]; -``` - -# Input -```cjs -array[array.length + 1] -``` - -# Input -```cjs -array[array.length + -1] -``` - -# Input -```cjs -foo[bar.length - 1] -``` - -# Input -```cjs -array?.[array.length - 1]; -``` - -# Input -```cjs -array[array.length - 1] = 1 -``` - -# Input -```cjs -array[array.length - 1] %= 1 -``` - -# Input -```cjs -++ array[array.length - 1] -``` - -# Input -```cjs -array[array.length - 1] -- -``` - -# Input -```cjs -delete array[array.length - 1] -``` - -# Input -```cjs -class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}} -``` - -# Input -```cjs -([array[array.length - 1]] = []) -``` - -# Input -```cjs -({foo: array[array.length - 1] = 9} = {}) -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc new file mode 100644 index 000000000000..752e74c2b64e --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc @@ -0,0 +1,90 @@ +[ + // ======================================================================== + // Index access + // ======================================================================== + "array[array.length - 1];", + "array[array.length -1];", + "array[array.length - /* comment */ 1];", + "array[array.length - 1.];", + "array[array.length - 0b1];", + "array[array.length - 9];", + "array[0][array[0].length - 1];", + "array[(( array.length )) - 1];", + "array[array.length - (( 1 ))];", + "array[(( array.length - 1 ))];", + "(( array ))[array.length - 1];", + "(( array[array.length - 1] ));", + "array[array.length - 1].pop().shift()[0];", + "a = array[array.length - 1]", + "const a = array[array.length - 1]", + "const {a = array[array.length - 1]} = {}", + "typeof array[array.length - 1]", + "function foo() {return arguments[arguments.length - 1]}", + "class Foo {bar; baz() {return this.bar[this.bar.length - 1]}}", + "class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}}", + // Support some polynomials as well. + "array[array.length - unknown - 1]", + "array[array.length - (unknown + 1)]", + // ======================================================================== + // `String#charAt` + // ======================================================================== + "string.charAt(string.length - 1);", + "string.charAt(string.length - 0o11);", + "some.string.charAt(some.string.length - 1);", + "string.charAt((( string.length )) - 0xFF);", + "string.charAt(string.length - (( 1 )));", + "string.charAt((( string.length - 1 )));", + "(( string )).charAt(string.length - 1);", + "(( string.charAt ))(string.length - 1);", + "(( string.charAt(string.length - 1) ));", + "string.charAt(string.length - unknown - 1 );", + "string.charAt(string.length - (unknown + 1));", + // ======================================================================== + // `.slice(x)` + // ======================================================================== + "array.slice(0)[0]", + "array.slice(-0)[0]", + "array.slice(-1)[0]", + "array.slice(-1).pop()", + "array.slice(-1.0).shift()", + "array.slice(-9)[0]", + "array.slice(-9).pop()", + "array.slice(-1.1)[0]", + "array.slice(-0xA)[0b000]", + "array.slice(-9).shift()", + "array.slice(-1)[(( 0 ))];", + "array.slice(-(( 1 )))[0];", + "array.slice((( -1 )))[0];", + "(( array.slice(-1) ))[0];", + "(( array )).slice(-1)[0];", + "(( array.slice(-1)[0] ));", + "(( array.slice(-1) )).pop();", + "(( array.slice(-1).pop ))();", + "(( array.slice(-1).pop() ));", + "array.slice(-1)[0].pop().shift().slice(-1)", + // ======================================================================== + // `.slice(x, y)` + // ======================================================================== + "array.slice(-9, -8)[0]", + "array.slice(-9, -0o10)[0]", + "array.slice(-9, -8).pop()", + "array.slice(-9, -8).shift()", + "array.slice((( -9 )), (( -8 )), ).shift()", + "(( array.slice(-9, -8).shift ))()", + "array.slice(-0o11, -7)[0]", + "array.slice(-9, 0)[0]", + "array.slice(hoge)[0]", + "array.slice(hoge).shift()", + // ======================================================================== + // `lodash.last()` + // ======================================================================== + "_.last(array)", + "lodash.last(array)", + "underscore.last(array)", + "_.last(new Array)", + "const foo = []; _.last([bar])", + "const foo = []; _.last(new Array)", + "const foo = []; _.last(((new Array)))", + "if (foo) _.last([bar])", + "function foo() {return _.last(arguments)}" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap new file mode 100644 index 000000000000..5e4dfd341880 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap @@ -0,0 +1,1732 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: invalid.jsonc +--- +# Input +```cjs +array[array.length - 1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length -1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length -1]; + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - /* comment */ 1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - /* comment */ 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·/*·comment·*/·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - 1.]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1.]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1.]; + + array.at(-1.); + + +``` + +# Input +```cjs +array[array.length - 0b1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 0b1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·0b1]; + + array.at(-0b1); + + +``` + +# Input +```cjs +array[array.length - 9]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 9]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·9]; + + array.at(-9); + + +``` + +# Input +```cjs +array[0][array[0].length - 1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[0][array[0].length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[0][array[0].length·-·1]; + + array[0].at(-1); + + +``` + +# Input +```cjs +array[(( array.length )) - 1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[(( array.length )) - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[((·array.length·))·-·1]; + + array.at(-1); + + +``` + +# Input +```cjs +array[array.length - (( 1 ))]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - (( 1 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·((·1·))]; + + array.at(-((·1·))); + + +``` + +# Input +```cjs +array[(( array.length - 1 ))]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[(( array.length - 1 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[((·array.length·-·1·))]; + + array.at(-1); + + +``` + +# Input +```cjs +(( array ))[array.length - 1]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array ))[array.length - 1]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array·))[array.length·-·1]; + + ((·array·)).at(-1); + + +``` + +# Input +```cjs +(( array[array.length - 1] )); +``` + +# Diagnostics +``` +invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array[array.length - 1] )); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array[array.length·-·1]·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +array[array.length - 1].pop().shift()[0]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - 1].pop().shift()[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·1].pop().shift()[0]; + + array.at(-1).pop().shift()[0]; + + +``` + +# Input +```cjs +a = array[array.length - 1] +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ a = array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - a·=·array[array.length·-·1] + + a·=·array.at(-1) + + +``` + +# Input +```cjs +const a = array[array.length - 1] +``` + +# Diagnostics +``` +invalid.jsonc:1:11 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const a = array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - const·a·=·array[array.length·-·1] + + const·a·=·array.at(-1) + + +``` + +# Input +```cjs +const {a = array[array.length - 1]} = {} +``` + +# Diagnostics +``` +invalid.jsonc:1:12 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const {a = array[array.length - 1]} = {} + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - const·{a·=·array[array.length·-·1]}·=·{} + + const·{a·=·array.at(-1)}·=·{} + + +``` + +# Input +```cjs +typeof array[array.length - 1] +``` + +# Diagnostics +``` +invalid.jsonc:1:8 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ typeof array[array.length - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - typeof·array[array.length·-·1] + + typeof·array.at(-1) + + +``` + +# Input +```cjs +function foo() {return arguments[arguments.length - 1]} +``` + +# Diagnostics +``` +invalid.jsonc:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ function foo() {return arguments[arguments.length - 1]} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - function·foo()·{return·arguments[arguments.length·-·1]} + + function·foo()·{return·arguments.at(-1)} + + +``` + +# Input +```cjs +class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} +``` + +# Diagnostics +``` +invalid.jsonc:1:31 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - class·Foo·{bar;·baz()·{return·this.bar[this.bar.length·-·1]}} + + class·Foo·{bar;·baz()·{return·this.bar.at(-1)}} + + +``` + +# Input +```cjs +class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} +``` + +# Diagnostics +``` +invalid.jsonc:1:32 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - class·Foo·{#bar;·baz()·{return·this.#bar[this.#bar.length·-·1]}} + + class·Foo·{#bar;·baz()·{return·this.#bar.at(-1)}} + + +``` + +# Input +```cjs +array[array.length - unknown - 1] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - unknown - 1] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·unknown·-·1] + + array.at(unknown·+1) + + +``` + +# Input +```cjs +array[array.length - (unknown + 1)] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array[array.length - (unknown + 1)] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X[X.length - Y]. + + i Unsafe fix: Replace index references with .at(). + + - array[array.length·-·(unknown·+·1)] + + array.at(-(unknown·+·1)) + + +``` + +# Input +```cjs +string.charAt(string.length - 1); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +string.charAt(string.length - 0o11); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - 0o11); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·0o11); + + string.at(-0o11); + + +``` + +# Input +```cjs +some.string.charAt(some.string.length - 1); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ some.string.charAt(some.string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - some.string.charAt(some.string.length·-·1); + + some.string.at(-1); + + +``` + +# Input +```cjs +string.charAt((( string.length )) - 0xFF); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt((( string.length )) - 0xFF); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(((·string.length·))·-·0xFF); + + string.at(-0xFF); + + +``` + +# Input +```cjs +string.charAt(string.length - (( 1 ))); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - (( 1 ))); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·((·1·))); + + string.at(-((·1·))); + + +``` + +# Input +```cjs +string.charAt((( string.length - 1 ))); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt((( string.length - 1 ))); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(((·string.length·-·1·))); + + string.at(-1); + + +``` + +# Input +```cjs +(( string )).charAt(string.length - 1); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string )).charAt(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string·)).charAt(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +(( string.charAt ))(string.length - 1); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string.charAt ))(string.length - 1); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string.charAt·))(string.length·-·1); + + string.at(-1); + + +``` + +# Input +```cjs +(( string.charAt(string.length - 1) )); +``` + +# Diagnostics +``` +invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( string.charAt(string.length - 1) )); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - ((·string.charAt(string.length·-·1)·)); + + ((·string.at(-1)·)); + + +``` + +# Input +```cjs +string.charAt(string.length - unknown - 1 ); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - unknown - 1 ); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·unknown·-·1·); + + string.at(unknown·+1); + + +``` + +# Input +```cjs +string.charAt(string.length - (unknown + 1)); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ string.charAt(string.length - (unknown + 1)); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-Y) over X.charAt(X.length - Y). + + i Unsafe fix: Replace index references with .at(). + + - string.charAt(string.length·-·(unknown·+·1)); + + string.at(-(unknown·+·1)); + + +``` + +# Input +```cjs +array.slice(0)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(0)[0] + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(0)[0] + + array.at(0) + + +``` + +# Input +```cjs +array.slice(-0)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0)[0] + + array.at(-0) + + +``` + +# Input +```cjs +array.slice(-1)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[0] + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1).pop() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1).pop() + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1.0).shift() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1.0).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1.0).shift() + + array.at(-1.0) + + +``` + +# Input +```cjs +array.slice(-9)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9)[0] + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9).pop() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).pop() + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).pop() + + array.at(-1) + + +``` + +# Input +```cjs +array.slice(-1.1)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1.1)[0] + │ ^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1.1)[0] + + array.at(-1.1) + + +``` + +# Input +```cjs +array.slice(-0xA)[0b000] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0xA)[0b000] + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0xA)[0b000] + + array.at(-0xA) + + +``` + +# Input +```cjs +array.slice(-9).shift() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9).shift() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-1)[(( 0 ))]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[(( 0 ))]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[((·0·))]; + + array.at(-1); + + +``` + +# Input +```cjs +array.slice(-(( 1 )))[0]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-(( 1 )))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-((·1·)))[0]; + + array.at(-((·1·))); + + +``` + +# Input +```cjs +array.slice((( -1 )))[0]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice((( -1 )))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(((·-1·)))[0]; + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1) ))[0]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1) ))[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)·))[0]; + + array.at(-1); + + +``` + +# Input +```cjs +(( array )).slice(-1)[0]; +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array )).slice(-1)[0]; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array·)).slice(-1)[0]; + + ((·array·)).at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1)[0] )); +``` + +# Diagnostics +``` +invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1)[0] )); + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)[0]·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +(( array.slice(-1) )).pop(); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1) )).pop(); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1)·)).pop(); + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1).pop ))(); +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1).pop ))(); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1).pop·))(); + + array.at(-1); + + +``` + +# Input +```cjs +(( array.slice(-1).pop() )); +``` + +# Diagnostics +``` +invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-1).pop() )); + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over X.slice(-a).pop(). + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-1).pop()·)); + + ((·array.at(-1)·)); + + +``` + +# Input +```cjs +array.slice(-1)[0].pop().shift().slice(-1) +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-1)[0].pop().shift().slice(-1) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-1)[0].pop().shift().slice(-1) + + array.at(-1).pop().shift().slice(-1) + + +``` + +# Input +```cjs +array.slice(-9, -8)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -0o10)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -0o10)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-0o10)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -8).pop() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8).pop() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y - 1) over X.slice(a, Y).pop(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8).pop() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-9, -8).shift() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, -8).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·-8).shift() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice((( -9 )), (( -8 )), ).shift() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice((( -9 )), (( -8 )), ).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(((·-9·)),·((·-8·)),·).shift() + + array.at(-9) + + +``` + +# Input +```cjs +(( array.slice(-9, -8).shift ))() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ (( array.slice(-9, -8).shift ))() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a).shift(). + + i Unsafe fix: Replace index references with .at(). + + - ((·array.slice(-9,·-8).shift·))() + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(-0o11, -7)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-0o11, -7)[0] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-0o11,·-7)[0] + + array.at(-0o11) + + +``` + +# Input +```cjs +array.slice(-9, 0)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(-9, 0)[0] + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y, a)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(-9,·0)[0] + + array.at(-9) + + +``` + +# Input +```cjs +array.slice(hoge)[0] +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(hoge)[0] + │ ^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y)[0]. + + i Unsafe fix: Replace index references with .at(). + + - array.slice(hoge)[0] + + array.at(hoge) + + +``` + +# Input +```cjs +array.slice(hoge).shift() +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ array.slice(hoge).shift() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(Y) over X.slice(Y).shift(). + + i Unsafe fix: Replace index references with .at(). + + - array.slice(hoge).shift() + + array.at(hoge) + + +``` + +# Input +```cjs +_.last(array) +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(array) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +lodash.last(array) +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ lodash.last(array) + │ ^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - lodash.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +underscore.last(array) +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ underscore.last(array) + │ ^^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - underscore.last(array) + + array.at(-1) + + +``` + +# Input +```cjs +_.last(new Array) +``` + +# Diagnostics +``` +invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ _.last(new Array) + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - _.last(new·Array) + + (new·Array).at(-1) + + +``` + +# Input +```cjs +const foo = []; _.last([bar]) +``` + +# Diagnostics +``` +invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const foo = []; _.last([bar]) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - const·foo·=·[];·_.last([bar]) + + const·foo·=·[];·[bar].at(-1) + + +``` + +# Input +```cjs +const foo = []; _.last(new Array) +``` + +# Diagnostics +``` +invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const foo = []; _.last(new Array) + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - const·foo·=·[];·_.last(new·Array) + + const·foo·=·[];·(new·Array).at(-1) + + +``` + +# Input +```cjs +const foo = []; _.last(((new Array))) +``` + +# Diagnostics +``` +invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ const foo = []; _.last(((new Array))) + │ ^^^^^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - const·foo·=·[];·_.last(((new·Array))) + + const·foo·=·[];·(new·Array).at(-1) + + +``` + +# Input +```cjs +if (foo) _.last([bar]) +``` + +# Diagnostics +``` +invalid.jsonc:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ if (foo) _.last([bar]) + │ ^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - if·(foo)·_.last([bar]) + + if·(foo)·[bar].at(-1) + + +``` + +# Input +```cjs +function foo() {return _.last(arguments)} +``` + +# Diagnostics +``` +invalid.jsonc:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Replace index references with .at(). + + > 1 │ function foo() {return _.last(arguments)} + │ ^^^^^^^^^^^^^^^^^ + + i Prefer X.at(-1) over _.last(X). + + i Unsafe fix: Replace index references with .at(). + + - function·foo()·{return·_.last(arguments)} + + function·foo()·{return·arguments.at(-1)} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json deleted file mode 100644 index c83de934a80d..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "_.last(array)", - "lodash.last(array)", - "underscore.last(array)", - "_.last(new Array)", - "if (foo) _.last([bar])", - "function foo() {return _.last(arguments)}" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap deleted file mode 100644 index 83f3cc510a52..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashInvalid.json.snap +++ /dev/null @@ -1,148 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: lodashInvalid.json ---- -# Input -```cjs -_.last(array) -``` - -# Diagnostics -``` -lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ _.last(array) - │ ^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - _.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -lodash.last(array) -``` - -# Diagnostics -``` -lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ lodash.last(array) - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - lodash.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -underscore.last(array) -``` - -# Diagnostics -``` -lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ underscore.last(array) - │ ^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - underscore.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -_.last(new Array) -``` - -# Diagnostics -``` -lodashInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ _.last(new Array) - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - _.last(new·Array) - + (new·Array).at(-1) - - -``` - -# Input -```cjs -if (foo) _.last([bar]) -``` - -# Diagnostics -``` -lodashInvalid.json:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ if (foo) _.last([bar]) - │ ^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - if·(foo)·_.last([bar]) - + if·(foo)·[bar].at(-1) - - -``` - -# Input -```cjs -function foo() {return _.last(arguments)} -``` - -# Diagnostics -``` -lodashInvalid.json:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ function foo() {return _.last(arguments)} - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - function·foo()·{return·_.last(arguments)} - + function·foo()·{return·arguments.at(-1)} - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json deleted file mode 100644 index 2e173476eb5f..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "new _.last(array)", - "_.last(array, 2)", - "_.last(...array)" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap deleted file mode 100644 index ce00863f3d59..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/lodashValid.json.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: lodashValid.json ---- -# Input -```cjs -new _.last(array) -``` - -# Input -```cjs -_.last(array, 2) -``` - -# Input -```cjs -_.last(...array) -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json deleted file mode 100644 index efab21ca205f..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - "array.slice(0)[0]", - "array.slice(-0)[0]", - "array.slice(-1)[0]", - "array.slice(-1).pop()", - "array.slice(-1.0).shift()", - "array.slice(-9)[0]", - "array.slice(-9).pop()", - "array.slice(-1.1)[0]", - "array.slice(-0xA)[0b000]", - "array.slice(-9).shift()", - "array.slice(-1)[(( 0 ))];", - "array.slice(-(( 1 )))[0];", - "array.slice((( -1 )))[0];", - "(( array.slice(-1) ))[0];", - "(( array )).slice(-1)[0];", - "(( array.slice(-1)[0] ));", - "(( array.slice(-1) )).pop();", - "(( array.slice(-1).pop ))();", - "(( array.slice(-1).pop() ));", - "array.slice(-1)[0].pop().shift().slice(-1)", - "array.slice(-9, -8)[0]", - "array.slice(-9, -0o10)[0]", - "array.slice(-9, -8).pop()", - "array.slice(-9, -8).shift()", - "array.slice((( -9 )), (( -8 )), ).shift()", - "(( array.slice(-9, -8).shift ))()", - "array.slice(-0o11, -7)[0]", - "array.slice(-9, 0)[0]", - "array.slice(hoge)[0]", - "array.slice(hoge).shift()" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap deleted file mode 100644 index f39fe347fa96..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceInvalid.json.snap +++ /dev/null @@ -1,724 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: sliceInvalid.json ---- -# Input -```cjs -array.slice(0)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(0)[0] - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(0)[0] - + array.at(0) - - -``` - -# Input -```cjs -array.slice(-0)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-0)[0] - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-0)[0] - + array.at(-0) - - -``` - -# Input -```cjs -array.slice(-1)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1)[0] - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1)[0] - + array.at(-1) - - -``` - -# Input -```cjs -array.slice(-1).pop() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1).pop() - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1).pop() - + array.at(-1) - - -``` - -# Input -```cjs -array.slice(-1.0).shift() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1.0).shift() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y).shift(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1.0).shift() - + array.at(-1.0) - - -``` - -# Input -```cjs -array.slice(-9)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9)[0] - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9)[0] - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-9).pop() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9).pop() - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9).pop() - + array.at(-1) - - -``` - -# Input -```cjs -array.slice(-1.1)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1.1)[0] - │ ^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1.1)[0] - + array.at(-1.1) - - -``` - -# Input -```cjs -array.slice(-0xA)[0b000] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-0xA)[0b000] - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-0xA)[0b000] - + array.at(-0xA) - - -``` - -# Input -```cjs -array.slice(-9).shift() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9).shift() - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y).shift(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9).shift() - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-1)[(( 0 ))]; -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1)[(( 0 ))]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1)[((·0·))]; - + array.at(-1); - - -``` - -# Input -```cjs -array.slice(-(( 1 )))[0]; -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-(( 1 )))[0]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-((·1·)))[0]; - + array.at(-((·1·))); - - -``` - -# Input -```cjs -array.slice((( -1 )))[0]; -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice((( -1 )))[0]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(((·-1·)))[0]; - + array.at(-1); - - -``` - -# Input -```cjs -(( array.slice(-1) ))[0]; -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-1) ))[0]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-1)·))[0]; - + array.at(-1); - - -``` - -# Input -```cjs -(( array )).slice(-1)[0]; -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array )).slice(-1)[0]; - │ ^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - ((·array·)).slice(-1)[0]; - + ((·array·)).at(-1); - - -``` - -# Input -```cjs -(( array.slice(-1)[0] )); -``` - -# Diagnostics -``` -sliceInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-1)[0] )); - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-1)[0]·)); - + ((·array.at(-1)·)); - - -``` - -# Input -```cjs -(( array.slice(-1) )).pop(); -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-1) )).pop(); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-1)·)).pop(); - + array.at(-1); - - -``` - -# Input -```cjs -(( array.slice(-1).pop ))(); -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-1).pop ))(); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-1).pop·))(); - + array.at(-1); - - -``` - -# Input -```cjs -(( array.slice(-1).pop() )); -``` - -# Diagnostics -``` -sliceInvalid.json:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-1).pop() )); - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over X.slice(-a).pop(). - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-1).pop()·)); - + ((·array.at(-1)·)); - - -``` - -# Input -```cjs -array.slice(-1)[0].pop().shift().slice(-1) -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-1)[0].pop().shift().slice(-1) - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-1)[0].pop().shift().slice(-1) - + array.at(-1).pop().shift().slice(-1) - - -``` - -# Input -```cjs -array.slice(-9, -8)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9, -8)[0] - │ ^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9,·-8)[0] - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-9, -0o10)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9, -0o10)[0] - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9,·-0o10)[0] - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-9, -8).pop() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9, -8).pop() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y - 1) over X.slice(a, Y).pop(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9,·-8).pop() - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-9, -8).shift() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9, -8).shift() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a).shift(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9,·-8).shift() - + array.at(-9) - - -``` - -# Input -```cjs -array.slice((( -9 )), (( -8 )), ).shift() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice((( -9 )), (( -8 )), ).shift() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a).shift(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(((·-9·)),·((·-8·)),·).shift() - + array.at(-9) - - -``` - -# Input -```cjs -(( array.slice(-9, -8).shift ))() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ (( array.slice(-9, -8).shift ))() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a).shift(). - - i Unsafe fix: Replace index references with .at(). - - - ((·array.slice(-9,·-8).shift·))() - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(-0o11, -7)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-0o11, -7)[0] - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-0o11,·-7)[0] - + array.at(-0o11) - - -``` - -# Input -```cjs -array.slice(-9, 0)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(-9, 0)[0] - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y, a)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(-9,·0)[0] - + array.at(-9) - - -``` - -# Input -```cjs -array.slice(hoge)[0] -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(hoge)[0] - │ ^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y)[0]. - - i Unsafe fix: Replace index references with .at(). - - - array.slice(hoge)[0] - + array.at(hoge) - - -``` - -# Input -```cjs -array.slice(hoge).shift() -``` - -# Diagnostics -``` -sliceInvalid.json:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array.slice(hoge).shift() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.slice(Y).shift(). - - i Unsafe fix: Replace index references with .at(). - - - array.slice(hoge).shift() - + array.at(hoge) - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json deleted file mode 100644 index eafdcc5c9c87..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - "array.slice(-1)", - "new array.slice(-1)", - "array.slice(-1)?.[0]", - "array.slice?.(-1)[0]", - "array?.slice(-1)[0]", - "array.notSlice(-1)[0]", - "array.slice()[0]", - "array.slice(...[-1])[0]", - "array.slice(-1).shift?.()", - "array.slice(-1)?.shift()", - "array.slice(-1).shift(...[])", - "new array.slice(-1).shift()", - "array.slice(-1)[0] += 1", - "++ array.slice(-1)[0]", - "array.slice(-1)[0] --", - "delete array.slice(-1)[0]", - "array.slice(-9.1, -8.1)[0]", - "array.slice(-unknown, -unknown2)[0]", - "array.slice(-9.1, unknown)[0]", - "array.slice(-9, unknown).pop()", - "array.slice(-9, ...unknown)[0]", - "array.slice(...[-9], unknown)[0]", - "array.slice(-9, unknown)[0]", - "array.slice(-9, unknown).shift()", - "const KNOWN = -8; array.slice(-9, KNOWN).shift()", - "(( (( array.slice( ((-9)), ((unknown)), ).shift ))() ));", - "array.slice(-9, (a, really, _really, complicated, second) => argument)[0]" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap deleted file mode 100644 index 2311f9934e09..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/sliceValid.json.snap +++ /dev/null @@ -1,139 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: sliceValid.json ---- -# Input -```cjs -array.slice(-1) -``` - -# Input -```cjs -new array.slice(-1) -``` - -# Input -```cjs -array.slice(-1)?.[0] -``` - -# Input -```cjs -array.slice?.(-1)[0] -``` - -# Input -```cjs -array?.slice(-1)[0] -``` - -# Input -```cjs -array.notSlice(-1)[0] -``` - -# Input -```cjs -array.slice()[0] -``` - -# Input -```cjs -array.slice(...[-1])[0] -``` - -# Input -```cjs -array.slice(-1).shift?.() -``` - -# Input -```cjs -array.slice(-1)?.shift() -``` - -# Input -```cjs -array.slice(-1).shift(...[]) -``` - -# Input -```cjs -new array.slice(-1).shift() -``` - -# Input -```cjs -array.slice(-1)[0] += 1 -``` - -# Input -```cjs -++ array.slice(-1)[0] -``` - -# Input -```cjs -array.slice(-1)[0] -- -``` - -# Input -```cjs -delete array.slice(-1)[0] -``` - -# Input -```cjs -array.slice(-9.1, -8.1)[0] -``` - -# Input -```cjs -array.slice(-unknown, -unknown2)[0] -``` - -# Input -```cjs -array.slice(-9.1, unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown).pop() -``` - -# Input -```cjs -array.slice(-9, ...unknown)[0] -``` - -# Input -```cjs -array.slice(...[-9], unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown)[0] -``` - -# Input -```cjs -array.slice(-9, unknown).shift() -``` - -# Input -```cjs -const KNOWN = -8; array.slice(-9, KNOWN).shift() -``` - -# Input -```cjs -(( (( array.slice( ((-9)), ((unknown)), ).shift ))() )); -``` - -# Input -```cjs -array.slice(-9, (a, really, _really, complicated, second) => argument)[0] -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc new file mode 100644 index 000000000000..bfcd2f543276 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc @@ -0,0 +1,75 @@ +[ + // ======================================================================== + // Index access + // ======================================================================== + "array.at(-1)", + "array[array.length - 0];", + "array[array.length + 1]", + "array[array.length + -1]", + "foo[bar.length - 1]", + "array?.[array.length - 1];", + // LHS + "array[array.length - 1] = 1", + "array[array.length - 1] %= 1", + "++ array[array.length - 1]", + "array[array.length - 1] --", + "delete array[array.length - 1]", + "class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}}", + "([array[array.length - 1]] = [])", + "({foo: array[array.length - 1] = 9} = {})", + // ======================================================================== + // `String#charAt` + // ======================================================================== + "string.charAt(string.length - 0);", + "string.charAt(string.length + 1)", + "string.charAt(string.length + -1)", + "foo.charAt(bar.length - 1)", + "string?.charAt?.(string.length - 1);", + "string?.charAt(string.length - 1);", + "string.charAt(9);", + "string1.charAt(string2.length - 1);", + "string.charAt(hoge.string.length - 1)", + "string.charAt(string.length - 1 + 1)", + "string.charAt(string.length + 1 - 1)", + // ======================================================================== + // `.slice(x)` + // ======================================================================== + "array.slice(-1)", + "new array.slice(-1)", + "array.slice(-1)?.[0]", + "array.slice?.(-1)[0]", + "array?.slice(-1)[0]", + "array.notSlice(-1)[0]", + "array.slice()[0]", + "array.slice(...[-1])[0]", + "array.slice(-1).shift?.()", + "array.slice(-1)?.shift()", + "array.slice(-1).shift(...[])", + "new array.slice(-1).shift()", + // LHS + "array.slice(-1)[0] += 1", + "++ array.slice(-1)[0]", + "array.slice(-1)[0] --", + "delete array.slice(-1)[0]", + // ======================================================================== + // `.slice(x, y)` + // ======================================================================== + "array.slice(-9.1, -8.1)[0]", + "array.slice(-unknown, -unknown2)[0]", + "array.slice(-9.1, unknown)[0]", + "array.slice(-9, unknown).pop()", + "array.slice(-9, ...unknown)[0]", + "array.slice(...[-9], unknown)[0]", + // Since the second argument is explicitly specified and its value can change the output if it is not a number, ignore all cases. + "array.slice(-9, unknown)[0]", + "array.slice(-9, unknown).shift()", + "const KNOWN = -8; array.slice(-9, KNOWN).shift()", + "(( (( array.slice( ((-9)), ((unknown)), ).shift ))() ));", + "array.slice(-9, (a, really, _really, complicated, second) => argument)[0]", + // ======================================================================== + // `lodash.last()` + // ======================================================================== + "new _.last(array)", + "_.last(array, 2)", + "_.last(...array)" +] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap new file mode 100644 index 000000000000..1f220a0616fb --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap @@ -0,0 +1,279 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 86 +expression: valid.jsonc +--- +# Input +```cjs +array.at(-1) +``` + +# Input +```cjs +array[array.length - 0]; +``` + +# Input +```cjs +array[array.length + 1] +``` + +# Input +```cjs +array[array.length + -1] +``` + +# Input +```cjs +foo[bar.length - 1] +``` + +# Input +```cjs +array?.[array.length - 1]; +``` + +# Input +```cjs +array[array.length - 1] = 1 +``` + +# Input +```cjs +array[array.length - 1] %= 1 +``` + +# Input +```cjs +++ array[array.length - 1] +``` + +# Input +```cjs +array[array.length - 1] -- +``` + +# Input +```cjs +delete array[array.length - 1] +``` + +# Input +```cjs +class Foo {bar; #bar; baz() {return this.#bar[this.bar.length - 1]}} +``` + +# Input +```cjs +([array[array.length - 1]] = []) +``` + +# Input +```cjs +({foo: array[array.length - 1] = 9} = {}) +``` + +# Input +```cjs +string.charAt(string.length - 0); +``` + +# Input +```cjs +string.charAt(string.length + 1) +``` + +# Input +```cjs +string.charAt(string.length + -1) +``` + +# Input +```cjs +foo.charAt(bar.length - 1) +``` + +# Input +```cjs +string?.charAt?.(string.length - 1); +``` + +# Input +```cjs +string?.charAt(string.length - 1); +``` + +# Input +```cjs +string.charAt(9); +``` + +# Input +```cjs +string1.charAt(string2.length - 1); +``` + +# Input +```cjs +string.charAt(hoge.string.length - 1) +``` + +# Input +```cjs +string.charAt(string.length - 1 + 1) +``` + +# Input +```cjs +string.charAt(string.length + 1 - 1) +``` + +# Input +```cjs +array.slice(-1) +``` + +# Input +```cjs +new array.slice(-1) +``` + +# Input +```cjs +array.slice(-1)?.[0] +``` + +# Input +```cjs +array.slice?.(-1)[0] +``` + +# Input +```cjs +array?.slice(-1)[0] +``` + +# Input +```cjs +array.notSlice(-1)[0] +``` + +# Input +```cjs +array.slice()[0] +``` + +# Input +```cjs +array.slice(...[-1])[0] +``` + +# Input +```cjs +array.slice(-1).shift?.() +``` + +# Input +```cjs +array.slice(-1)?.shift() +``` + +# Input +```cjs +array.slice(-1).shift(...[]) +``` + +# Input +```cjs +new array.slice(-1).shift() +``` + +# Input +```cjs +array.slice(-1)[0] += 1 +``` + +# Input +```cjs +++ array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-1)[0] -- +``` + +# Input +```cjs +delete array.slice(-1)[0] +``` + +# Input +```cjs +array.slice(-9.1, -8.1)[0] +``` + +# Input +```cjs +array.slice(-unknown, -unknown2)[0] +``` + +# Input +```cjs +array.slice(-9.1, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).pop() +``` + +# Input +```cjs +array.slice(-9, ...unknown)[0] +``` + +# Input +```cjs +array.slice(...[-9], unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown)[0] +``` + +# Input +```cjs +array.slice(-9, unknown).shift() +``` + +# Input +```cjs +const KNOWN = -8; array.slice(-9, KNOWN).shift() +``` + +# Input +```cjs +(( (( array.slice( ((-9)), ((unknown)), ).shift ))() )); +``` + +# Input +```cjs +array.slice(-9, (a, really, _really, complicated, second) => argument)[0] +``` + +# Input +```cjs +new _.last(array) +``` + +# Input +```cjs +_.last(array, 2) +``` + +# Input +```cjs +_.last(...array) +``` From 2ff8b95f9c67301c923179a7deb3bb9c7b570096 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Mon, 30 Sep 2024 00:11:57 +0900 Subject: [PATCH 18/48] test: add no lodash testcase --- .../specs/nursery/useAtIndex/invalid.jsonc | 2 +- .../specs/nursery/useAtIndex/valid.jsonc | 9 +++++++-- .../specs/nursery/useAtIndex/valid.jsonc.snap | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc index 752e74c2b64e..527c4293052f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc @@ -76,7 +76,7 @@ "array.slice(hoge)[0]", "array.slice(hoge).shift()", // ======================================================================== - // `lodash.last()` + // `lodash.last(array)` // ======================================================================== "_.last(array)", "lodash.last(array)", diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc index bfcd2f543276..403f95ea6061 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc @@ -67,9 +67,14 @@ "(( (( array.slice( ((-9)), ((unknown)), ).shift ))() ));", "array.slice(-9, (a, really, _really, complicated, second) => argument)[0]", // ======================================================================== - // `lodash.last()` + // `lodash.last(array)` // ======================================================================== "new _.last(array)", "_.last(array, 2)", - "_.last(...array)" + "_.last(...array)", + // not lodash + "_.last()", + "other._.last(array)", + "other.underscore.last(array)", + "other.lodash.last(array)" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap index 1f220a0616fb..6c9d73deeb52 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap @@ -277,3 +277,23 @@ _.last(array, 2) ```cjs _.last(...array) ``` + +# Input +```cjs +_.last() +``` + +# Input +```cjs +other._.last(array) +``` + +# Input +```cjs +other.underscore.last(array) +``` + +# Input +```cjs +other.lodash.last(array) +``` From e14e34ade59fb8ac4c7b73b79ffde862f11f09fa Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:14:45 +0900 Subject: [PATCH 19/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 30c0ee813b55..e5cb82cee563 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -18,7 +18,7 @@ use crate::JsRuleAction; use schemars::JsonSchema; declare_lint_rule! { - /// Enforce using .at to retrieve elements. + /// Use `at()` instead of integer index access. /// /// When extracting elements from an array, especially when retrieving from the end, `.at` is convenient. Replace the previously used syntax with `.at()`. /// From d4f6da4f7a732bec224d51a3dea020e7fe54f4ec Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:15:47 +0900 Subject: [PATCH 20/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e5cb82cee563..6747fe2f1fa6 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -732,7 +732,6 @@ fn check_call_expression( match member_name.text() { "last" => check_call_expression_last(call_exp, &member), "charAt" => check_call_expression_char_at(call_exp, &member, option), - //"lastIndexOf" => Some(ErrorType::GetLastFunction), _ => None, } } From fef17d35e0a5f7b920931aae15b452c911857aed Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:18:06 +0900 Subject: [PATCH 21/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 6747fe2f1fa6..117bffa6b57b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -355,7 +355,7 @@ fn make_number_literal(value: i64) -> AnyJsExpression { /// # Examples /// ```js /// .slice(0)[0] -/// .slice(0, 1).pop(0) +/// .slice(0, 1).pop() /// ``` fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { if is_within_delete_expression(node).unwrap_or(false) { From c5986d8e32d2b55a53db24212c1c1170d20ff8ea Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:19:17 +0900 Subject: [PATCH 22/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 117bffa6b57b..bf686b7dd2dd 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -730,7 +730,6 @@ fn check_call_expression( .ok()? .token_text_trimmed(); match member_name.text() { - "last" => check_call_expression_last(call_exp, &member), "charAt" => check_call_expression_char_at(call_exp, &member, option), _ => None, } From 0b83a267b34a0ed0a2ee258c54eafa927eac43dd Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:22:48 +0900 Subject: [PATCH 23/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- .../src/lint/nursery/use_at_index.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index bf686b7dd2dd..0b9f3cb19b07 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -381,15 +381,14 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option (object, SliceExtractType::Pop), + "shift" => (object, SliceExtractType::Shift), + _ => { + return None; + } } } AnyJsExpression::JsComputedMemberExpression(member) => { From 3f470f17670ed95702bbb50c516e0d007e7517d9 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:23:24 +0900 Subject: [PATCH 24/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 0b9f3cb19b07..43f040afba35 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -726,9 +726,8 @@ fn check_call_expression( .ok()? .as_js_name()? .value_token() - .ok()? - .token_text_trimmed(); - match member_name.text() { + .ok()?; + match member_name.text_trimmed() { "charAt" => check_call_expression_char_at(call_exp, &member, option), _ => None, } From 794b3a4a9cf01ff9c6edf84ffa631ee3895d7640 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:24:08 +0900 Subject: [PATCH 25/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 43f040afba35..75d993b72f70 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -418,9 +418,8 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Date: Thu, 3 Oct 2024 15:27:00 +0900 Subject: [PATCH 26/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 75d993b72f70..29d573c52e9a 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -365,8 +365,8 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { - let arg_length = call_exp.arguments().ok()?.args().into_iter().count(); - if arg_length != 0 { + let has_args = !call_exp.arguments().ok()?.args().is_empty(); + if has_args { return None; } let member = solve_parenthesized_expression(call_exp.callee().ok()?)?; From 58cea0dc613a889795fe44160963b4f0375556c9 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:27:18 +0900 Subject: [PATCH 27/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- .../biome_js_analyze/src/lint/nursery/use_at_index.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 29d573c52e9a..b3b55b31d038 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -423,16 +423,12 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option = call_exp + let [Some(arg0), optional_arg1, None] = call_exp .arguments() .ok()? - .args() - .into_iter() - .flatten() - .collect(); - if args.is_empty() || args.len() > 2 { + .get_arguments_by_index([0, 1, 2])) else { return None; - } + }; let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; From 80c8eaa4437723ca08fa2586706c3d44f18fb9f5 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:27:27 +0900 Subject: [PATCH 28/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- .../src/lint/nursery/use_at_index.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index b3b55b31d038..82c1dcb7ecef 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -435,8 +435,8 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Some(UseAtIndexState::new( + match (extract_type.clone(), optional_arg1) { + (SliceExtractType::ZeroMember | SliceExtractType::Shift, None) => Some(UseAtIndexState::new( start_exp, ErrorType::Slice { arg_type: SliceArgType::OneArg, @@ -444,7 +444,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { + (SliceExtractType::Pop, None) if get_integer_from_literal(&start_exp)? < 0 => { Some(UseAtIndexState::new( make_number_literal(-1), ErrorType::Slice { @@ -454,10 +454,10 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { + (SliceExtractType::ZeroMember | SliceExtractType::Shift, Some(arg1)) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - args[1].as_any_js_expression()?.clone(), + args1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some( UseAtIndexState::new( @@ -470,10 +470,10 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { + (SliceExtractType::Pop, Some(arg1)) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - args[1].as_any_js_expression()?.clone(), + args1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some( UseAtIndexState::new( From 8057c5385ae07d17ecaa805736b150941290770d Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:28:04 +0900 Subject: [PATCH 29/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 82c1dcb7ecef..daee552b5283 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -711,7 +711,7 @@ fn check_call_expression( } let member = solve_parenthesized_expression(call_exp.callee().ok()?)?; - match member { + match call_exp.callee().ok()?.omit_parentheses() { AnyJsExpression::JsStaticMemberExpression(member) => { if member.is_optional_chain() { return None; From 6c7d450952b59f193efc59577546e5f838efd8ab Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 15:29:28 +0900 Subject: [PATCH 30/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index daee552b5283..f11b8dcc462b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -819,15 +819,6 @@ pub struct UseAtIndexState { object: AnyJsExpression, } -impl UseAtIndexState { - fn new(at_number_exp: AnyJsExpression, error_type: ErrorType, object: AnyJsExpression) -> Self { - Self { - at_number_exp, - error_type, - object, - } - } -} #[derive( Clone, From 8f309f7ad9cbed77c9e3cd127583c71ff1c9cac8 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 16:06:52 +0900 Subject: [PATCH 31/48] fix: use initialize the struct --- .../src/lint/nursery/use_at_index.rs | 146 ++++++++---------- 1 file changed, 68 insertions(+), 78 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index f11b8dcc462b..607044b7cba6 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -436,55 +436,51 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Some(UseAtIndexState::new( - start_exp, - ErrorType::Slice { + (SliceExtractType::ZeroMember | SliceExtractType::Shift, None) => Some(UseAtIndexState { + at_number_exp: start_exp, + error_type: ErrorType::Slice { arg_type: SliceArgType::OneArg, extract_type, }, - sliced_exp, - )), + object: sliced_exp, + }), (SliceExtractType::Pop, None) if get_integer_from_literal(&start_exp)? < 0 => { - Some(UseAtIndexState::new( - make_number_literal(-1), - ErrorType::Slice { + Some(UseAtIndexState { + at_number_exp: make_number_literal(-1), + error_type: ErrorType::Slice { arg_type: SliceArgType::OneArg, extract_type: SliceExtractType::Pop, }, - sliced_exp, - )) + object: sliced_exp, + }) } (SliceExtractType::ZeroMember | SliceExtractType::Shift, Some(arg1)) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args1.as_any_js_expression()?.clone(), )?)?; - (start_index * end_index >= 0 && start_index < end_index).then_some( - UseAtIndexState::new( - start_exp, - ErrorType::Slice { - arg_type: SliceArgType::TwoArg, - extract_type, - }, - sliced_exp, - ), - ) + (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { + at_number_exp: start_exp, + error_type: ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type, + }, + object: sliced_exp, + }) } (SliceExtractType::Pop, Some(arg1)) => { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( args1.as_any_js_expression()?.clone(), )?)?; - (start_index * end_index >= 0 && start_index < end_index).then_some( - UseAtIndexState::new( - make_number_literal(end_index - 1), - ErrorType::Slice { - arg_type: SliceArgType::TwoArg, - extract_type: SliceExtractType::Pop, - }, - sliced_exp, - ), - ) + (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { + at_number_exp: make_number_literal(end_index - 1), + error_type: ErrorType::Slice { + arg_type: SliceArgType::TwoArg, + extract_type: SliceExtractType::Pop, + }, + object: sliced_exp, + }) } _ => None, } @@ -499,19 +495,17 @@ fn check_binary_expression_member( let negative_index_exp = extract_negative_index_expression(&member, solve_parenthesized_expression(object.clone())?); if let Some(negative_index) = negative_index_exp { - return Some(UseAtIndexState::new( - negative_index, - ErrorType::Index { is_negative: true }, + return Some(UseAtIndexState { + at_number_exp: negative_index, + error_type: ErrorType::Index { is_negative: true }, object, - )); + }); } - option - .check_all_index_access - .then_some(UseAtIndexState::new( - member, - ErrorType::Index { is_negative: true }, - object, - )) + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::Index { is_negative: true }, + object, + }) } fn check_literal_expression_member( @@ -524,11 +518,11 @@ fn check_literal_expression_member( }; let value_token = member.value_token().ok()?; let number = value_token.text_trimmed().parse::().ok()?; - (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState::new( - make_number_literal(number), - ErrorType::Index { is_negative: false }, + (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { + at_number_exp: make_number_literal(number), + error_type: ErrorType::Index { is_negative: false }, object, - )) + }) } fn check_unary_expression_member( @@ -550,11 +544,11 @@ fn check_unary_expression_member( } } } - Some(UseAtIndexState::new( - AnyJsExpression::JsUnaryExpression(member), - ErrorType::Index { is_negative: false }, + Some(UseAtIndexState { + at_number_exp: AnyJsExpression::JsUnaryExpression(member), + error_type: ErrorType::Index { is_negative: false }, object, - )) + }) } /// check hoge[0] @@ -592,11 +586,11 @@ fn check_computed_member_expression( check_unary_expression_member(unary, object, option) } AnyJsExpression::JsIdentifierExpression(_) => None, - _ if option.check_all_index_access => Some(UseAtIndexState::new( - member, - ErrorType::Index { is_negative: false }, + _ if option.check_all_index_access => Some(UseAtIndexState { + at_number_exp: member, + error_type: ErrorType::Index { is_negative: false }, object, - )), + }), _ => None, } } @@ -661,36 +655,32 @@ fn check_call_expression_char_at( let at_number_exp = extract_negative_index_expression(&core_arg0, char_at_parent.clone()); if let Some(at_number_exp) = at_number_exp { - Some(UseAtIndexState::new( + Some(UseAtIndexState { at_number_exp, - ErrorType::StringCharAt { is_negative: true }, - char_at_parent, - )) + error_type: ErrorType::StringCharAt { is_negative: true }, + object: char_at_parent, + }) } else { - option - .check_all_index_access - .then_some(UseAtIndexState::new( - core_arg0, - ErrorType::StringCharAt { is_negative: false }, - char_at_parent, - )) + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt { is_negative: false }, + object: char_at_parent, + }) } } // hoge.charAt(1) - AnyJsExpression::AnyJsLiteralExpression(_member) => option - .check_all_index_access - .then_some(UseAtIndexState::new( - core_arg0, - ErrorType::StringCharAt { is_negative: false }, - char_at_parent.clone(), - )), - _ => option - .check_all_index_access - .then_some(UseAtIndexState::new( - core_arg0, - ErrorType::StringCharAt { is_negative: false }, - char_at_parent.clone(), - )), + AnyJsExpression::AnyJsLiteralExpression(_member) => { + option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt { is_negative: false }, + object: char_at_parent.clone(), + }) + } + _ => option.check_all_index_access.then_some(UseAtIndexState { + at_number_exp: core_arg0, + error_type: ErrorType::StringCharAt { is_negative: false }, + object: char_at_parent.clone(), + }), } } From 637c44d2d111f5676c74c6a25aad8bd716b60cd7 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 16:08:44 +0900 Subject: [PATCH 32/48] fix: remove fix rule for lodash --- .../src/lint/nursery/use_at_index.rs | 38 --- .../specs/nursery/useAtIndex/invalid.jsonc | 14 +- .../nursery/useAtIndex/invalid.jsonc.snap | 216 ------------------ .../specs/nursery/useAtIndex/valid.jsonc | 11 + .../specs/nursery/useAtIndex/valid.jsonc.snap | 45 ++++ 5 files changed, 57 insertions(+), 267 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 607044b7cba6..f5fdc8837bf2 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -595,40 +595,6 @@ fn check_computed_member_expression( } } -fn check_call_expression_last( - call_exp: &JsCallExpression, - member: &JsStaticMemberExpression, -) -> Option { - let args: Vec<_> = call_exp - .arguments() - .ok()? - .args() - .into_iter() - .flatten() - .collect(); - if args.len() != 1 { - return None; - } - let object = member.object().ok()?; - let AnyJsExpression::JsIdentifierExpression(object) = object else { - return None; - }; - let lodash_function = ["_", "lodash", "underscore"]; - let object_name = object.syntax().text().to_string(); - if lodash_function.contains(&object_name.as_str()) { - let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { - return None; - }; - Some(UseAtIndexState::new( - make_number_literal(-1), - ErrorType::GetLastFunction, - solve_parenthesized_expression(arg0)?, - )) - } else { - None - } -} - fn check_call_expression_char_at( call_exp: &JsCallExpression, member: &JsStaticMemberExpression, @@ -764,7 +730,6 @@ pub enum ErrorType { arg_type: SliceArgType, extract_type: SliceExtractType, }, - GetLastFunction, } impl ErrorType { @@ -791,9 +756,6 @@ impl ErrorType { _ => ("X.at(Y)", format!("X.slice({}){}", if matches!(arg_type, SliceArgType::OneArg) { "Y" } else { "Y, a" }, extract_string)), }; markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - }, - ErrorType::GetLastFunction => { - markup! { "Prefer ""X.at(-1)"" over ""_.last(X)""." }.to_owned() } } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc index 527c4293052f..dec9b46d727d 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc @@ -74,17 +74,5 @@ "array.slice(-0o11, -7)[0]", "array.slice(-9, 0)[0]", "array.slice(hoge)[0]", - "array.slice(hoge).shift()", - // ======================================================================== - // `lodash.last(array)` - // ======================================================================== - "_.last(array)", - "lodash.last(array)", - "underscore.last(array)", - "_.last(new Array)", - "const foo = []; _.last([bar])", - "const foo = []; _.last(new Array)", - "const foo = []; _.last(((new Array)))", - "if (foo) _.last([bar])", - "function foo() {return _.last(arguments)}" + "array.slice(hoge).shift()" ] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap index 5e4dfd341880..442185164898 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap @@ -1514,219 +1514,3 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` - -# Input -```cjs -_.last(array) -``` - -# Diagnostics -``` -invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ _.last(array) - │ ^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - _.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -lodash.last(array) -``` - -# Diagnostics -``` -invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ lodash.last(array) - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - lodash.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -underscore.last(array) -``` - -# Diagnostics -``` -invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ underscore.last(array) - │ ^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - underscore.last(array) - + array.at(-1) - - -``` - -# Input -```cjs -_.last(new Array) -``` - -# Diagnostics -``` -invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ _.last(new Array) - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - _.last(new·Array) - + (new·Array).at(-1) - - -``` - -# Input -```cjs -const foo = []; _.last([bar]) -``` - -# Diagnostics -``` -invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const foo = []; _.last([bar]) - │ ^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - const·foo·=·[];·_.last([bar]) - + const·foo·=·[];·[bar].at(-1) - - -``` - -# Input -```cjs -const foo = []; _.last(new Array) -``` - -# Diagnostics -``` -invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const foo = []; _.last(new Array) - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - const·foo·=·[];·_.last(new·Array) - + const·foo·=·[];·(new·Array).at(-1) - - -``` - -# Input -```cjs -const foo = []; _.last(((new Array))) -``` - -# Diagnostics -``` -invalid.jsonc:1:17 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const foo = []; _.last(((new Array))) - │ ^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - const·foo·=·[];·_.last(((new·Array))) - + const·foo·=·[];·(new·Array).at(-1) - - -``` - -# Input -```cjs -if (foo) _.last([bar]) -``` - -# Diagnostics -``` -invalid.jsonc:1:10 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ if (foo) _.last([bar]) - │ ^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - if·(foo)·_.last([bar]) - + if·(foo)·[bar].at(-1) - - -``` - -# Input -```cjs -function foo() {return _.last(arguments)} -``` - -# Diagnostics -``` -invalid.jsonc:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ function foo() {return _.last(arguments)} - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-1) over _.last(X). - - i Unsafe fix: Replace index references with .at(). - - - function·foo()·{return·_.last(arguments)} - + function·foo()·{return·arguments.at(-1)} - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc index 403f95ea6061..f1d4cc4ef24c 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc @@ -69,6 +69,17 @@ // ======================================================================== // `lodash.last(array)` // ======================================================================== + // Under the original rules, it was considered an error, but due to concerns about false positives, it is currently allowed. + "_.last(array)", + "lodash.last(array)", + "underscore.last(array)", + "_.last(new Array)", + "const foo = []; _.last([bar])", + "const foo = []; _.last(new Array)", + "const foo = []; _.last(((new Array)))", + "if (foo) _.last([bar])", + "function foo() {return _.last(arguments)}", + // valid "new _.last(array)", "_.last(array, 2)", "_.last(...array)", diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap index 6c9d73deeb52..29185ee65b7d 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/valid.jsonc.snap @@ -263,6 +263,51 @@ const KNOWN = -8; array.slice(-9, KNOWN).shift() array.slice(-9, (a, really, _really, complicated, second) => argument)[0] ``` +# Input +```cjs +_.last(array) +``` + +# Input +```cjs +lodash.last(array) +``` + +# Input +```cjs +underscore.last(array) +``` + +# Input +```cjs +_.last(new Array) +``` + +# Input +```cjs +const foo = []; _.last([bar]) +``` + +# Input +```cjs +const foo = []; _.last(new Array) +``` + +# Input +```cjs +const foo = []; _.last(((new Array))) +``` + +# Input +```cjs +if (foo) _.last([bar]) +``` + +# Input +```cjs +function foo() {return _.last(arguments)} +``` + # Input ```cjs new _.last(array) From ded38aa684ad3379193c23d7d8c2e566ae7349a7 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 16:13:08 +0900 Subject: [PATCH 33/48] fix: Omission of modifications in the proposed corrections. --- .../src/lint/nursery/use_at_index.rs | 91 ++++++++++--------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index f5fdc8837bf2..a05fa62fb8a5 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -1,3 +1,4 @@ +use crate::JsRuleAction; use ::serde::{Deserialize, Serialize}; use biome_analyze::{ context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, @@ -10,9 +11,7 @@ use biome_js_syntax::{ JsCallExpression, JsComputedMemberExpression, JsParenthesizedExpression, JsStaticMemberExpression, JsUnaryExpression, T, }; -use biome_rowan::{declare_node_union, AstNode, BatchMutationExt}; - -use crate::JsRuleAction; +use biome_rowan::{declare_node_union, AstNode, AstSeparatedList, BatchMutationExt}; #[cfg(feature = "schemars")] use schemars::JsonSchema; @@ -65,10 +64,6 @@ declare_lint_rule! { /// const foo = string.charAt(string.length - 5); /// ``` /// - /// ```js,expect_diagnostic - /// const foo = lodash.last(array); - /// ``` - /// /// ### Valid /// /// ```js @@ -376,12 +371,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option (object, SliceExtractType::Pop), @@ -413,23 +403,17 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - args1.as_any_js_expression()?.clone(), + arg1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, @@ -471,7 +455,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let start_index = get_integer_from_literal(&start_exp)?; let end_index = get_integer_from_literal(&solve_parenthesized_expression( - args1.as_any_js_expression()?.clone(), + arg1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), @@ -666,18 +650,12 @@ fn check_call_expression( return None; } - let member = solve_parenthesized_expression(call_exp.callee().ok()?)?; match call_exp.callee().ok()?.omit_parentheses() { AnyJsExpression::JsStaticMemberExpression(member) => { if member.is_optional_chain() { return None; } - let member_name = member - .member() - .ok()? - .as_js_name()? - .value_token() - .ok()?; + let member_name = member.member().ok()?.as_js_name()?.value_token().ok()?; match member_name.text_trimmed() { "charAt" => check_call_expression_char_at(call_exp, &member, option), _ => None, @@ -738,22 +716,54 @@ impl ErrorType { match self { ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { let (method, old_method) = if *is_negative { - ("X.at(-Y)", if matches!(self, ErrorType::StringCharAt { .. }) { "X.charAt(X.length - Y)" } else { "X[X.length - Y]" }) + ( + "X.at(-Y)", + if matches!(self, ErrorType::StringCharAt { .. }) { + "X.charAt(X.length - Y)" + } else { + "X[X.length - Y]" + }, + ) } else { - ("X.at(Y)", if matches!(self, ErrorType::StringCharAt { .. }) { "X.charAt(Y)" } else { "X[Y]" }) + ( + "X.at(Y)", + if matches!(self, ErrorType::StringCharAt { .. }) { + "X.charAt(Y)" + } else { + "X[Y]" + }, + ) }; markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - }, - ErrorType::Slice { arg_type, extract_type } => { + } + ErrorType::Slice { + arg_type, + extract_type, + } => { let extract_string = match extract_type { SliceExtractType::Pop => ".pop()", SliceExtractType::Shift => ".shift()", SliceExtractType::ZeroMember => "[0]", }; let (method, old_method) = match (arg_type, extract_type) { - (SliceArgType::OneArg, SliceExtractType::Pop) => ("X.at(-1)", format!("X.slice(-a){}", extract_string)), - (SliceArgType::TwoArg, SliceExtractType::Pop) => ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)), - _ => ("X.at(Y)", format!("X.slice({}){}", if matches!(arg_type, SliceArgType::OneArg) { "Y" } else { "Y, a" }, extract_string)), + (SliceArgType::OneArg, SliceExtractType::Pop) => { + ("X.at(-1)", format!("X.slice(-a){}", extract_string)) + } + (SliceArgType::TwoArg, SliceExtractType::Pop) => { + ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)) + } + _ => ( + "X.at(Y)", + format!( + "X.slice({}){}", + if matches!(arg_type, SliceArgType::OneArg) { + "Y" + } else { + "Y, a" + }, + extract_string + ), + ), }; markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() } @@ -771,7 +781,6 @@ pub struct UseAtIndexState { object: AnyJsExpression, } - #[derive( Clone, Debug, From cc56e7fc73bb8ab7e32467ef4e27a43c6c7b7d31 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 16:31:04 +0900 Subject: [PATCH 34/48] refactor: rename function --- .../src/lint/nursery/use_at_index.rs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index a05fa62fb8a5..d47bc9bd7282 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -103,7 +103,7 @@ declare_lint_rule! { /// (a) // Some(a) /// (a + b) // Some(a + b) /// ``` -fn solve_parenthesized_expression(mut node: AnyJsExpression) -> Option { +fn unwrap_parenthesized_expression(mut node: AnyJsExpression) -> Option { while let AnyJsExpression::JsParenthesizedExpression(parenthesized_exp) = node { let exp = parenthesized_exp.expression().ok()?; node = exp; @@ -123,8 +123,8 @@ fn solve_parenthesized_expression(mut node: AnyJsExpression) -> Option Option { // solve JsParenthesizedExpression - let left = solve_parenthesized_expression(left)?; - let right = solve_parenthesized_expression(right)?; + let left = unwrap_parenthesized_expression(left)?; + let right = unwrap_parenthesized_expression(right)?; match (left, right) { // x[0] ( @@ -132,12 +132,12 @@ fn is_same_reference(left: AnyJsExpression, right: AnyJsExpression) -> Option { let AnyJsExpression::AnyJsLiteralExpression(left_member) = - solve_parenthesized_expression(left.member().ok()?)? + unwrap_parenthesized_expression(left.member().ok()?)? else { return Some(false); }; let AnyJsExpression::AnyJsLiteralExpression(right_member) = - solve_parenthesized_expression(right.member().ok()?)? + unwrap_parenthesized_expression(right.member().ok()?)? else { return Some(false); }; @@ -225,7 +225,7 @@ fn get_integer_from_literal(node: &AnyJsExpression) -> Option { if token.kind() != T![-] { return None; } - return get_integer_from_literal(&solve_parenthesized_expression(unary.argument().ok()?)?) + return get_integer_from_literal(&unwrap_parenthesized_expression(unary.argument().ok()?)?) .map(|num| -num); } let AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::JsNumberLiteralExpression( @@ -248,7 +248,7 @@ fn get_integer_from_literal(node: &AnyJsExpression) -> Option { /// a - b // => Some((a, [b])) /// a - b - c // => Some((a, [b, c])) /// ``` -fn get_left_node_from_minus_binary_expressions( +fn split_minus_binary_expressions( mut expression: AnyJsExpression, ) -> Option<(AnyJsExpression, Vec)> { let mut right_list = vec![]; @@ -287,13 +287,13 @@ fn extract_negative_index_expression( member: &AnyJsExpression, object: AnyJsExpression, ) -> Option { - let (left, right_list) = get_left_node_from_minus_binary_expressions(member.clone())?; + let (left, right_list) = split_minus_binary_expressions(member.clone())?; if right_list.is_empty() { return None; } // left expression should be hoge.length - let left = solve_parenthesized_expression(left)?; + let left = unwrap_parenthesized_expression(left)?; let length_parent = get_length_node(&left)?; // left expression should be the same as the object if !is_same_reference(object, length_parent)? { @@ -303,7 +303,7 @@ fn extract_negative_index_expression( if right_list.len() == 1 { // right expression should be integer if let Some(number) = - get_integer_from_literal(&solve_parenthesized_expression(right_list[0].clone())?) + get_integer_from_literal(&unwrap_parenthesized_expression(right_list[0].clone())?) { if number > 0 { Some(AnyJsExpression::JsUnaryExpression( @@ -364,7 +364,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { - let object = solve_parenthesized_expression(member.object().ok()?)?; + let object = unwrap_parenthesized_expression(member.object().ok()?)?; if member.is_optional_chain() { return None; } let value = - get_integer_from_literal(&solve_parenthesized_expression(member.member().ok()?)?)?; + get_integer_from_literal(&unwrap_parenthesized_expression(member.member().ok()?)?)?; // enable only x[0] if value != 0 { return None; @@ -416,7 +416,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { let start_index = get_integer_from_literal(&start_exp)?; - let end_index = get_integer_from_literal(&solve_parenthesized_expression( + let end_index = get_integer_from_literal(&unwrap_parenthesized_expression( arg1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { @@ -454,7 +454,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let start_index = get_integer_from_literal(&start_exp)?; - let end_index = get_integer_from_literal(&solve_parenthesized_expression( + let end_index = get_integer_from_literal(&unwrap_parenthesized_expression( arg1.as_any_js_expression()?.clone(), )?)?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { @@ -476,8 +476,10 @@ fn check_binary_expression_member( option: &UseAtIndexOptions, ) -> Option { let member = AnyJsExpression::JsBinaryExpression(member); - let negative_index_exp = - extract_negative_index_expression(&member, solve_parenthesized_expression(object.clone())?); + let negative_index_exp = extract_negative_index_expression( + &member, + unwrap_parenthesized_expression(object.clone())?, + ); if let Some(negative_index) = negative_index_exp { return Some(UseAtIndexState { at_number_exp: negative_index, @@ -521,7 +523,7 @@ fn check_unary_expression_member( let token = member.operator_token().ok()?; if token.kind() == T![-] { if let Some(arg) = - get_integer_from_literal(&solve_parenthesized_expression(member.argument().ok()?)?) + get_integer_from_literal(&unwrap_parenthesized_expression(member.argument().ok()?)?) { if arg >= 0 { return None; @@ -554,7 +556,7 @@ fn check_computed_member_expression( return None; } // check member - let member = solve_parenthesized_expression(exp.member().ok()?)?; + let member = unwrap_parenthesized_expression(exp.member().ok()?)?; let object = exp.object().ok()?; match member.clone() { // hoge[hoge.length - 1] @@ -597,8 +599,8 @@ fn check_call_expression_char_at( let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; - let core_arg0 = solve_parenthesized_expression(arg0)?; - let char_at_parent = solve_parenthesized_expression(member.object().ok()?)?; + let core_arg0 = unwrap_parenthesized_expression(arg0)?; + let char_at_parent = unwrap_parenthesized_expression(member.object().ok()?)?; match core_arg0.clone() { // hoge.charAt(hoge.length - 1) AnyJsExpression::JsBinaryExpression(_) => { From 8c45f9b028263378b780487f8f227e0f310e8eed Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 17:51:59 +0900 Subject: [PATCH 35/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- .../src/lint/nursery/use_at_index.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index d47bc9bd7282..525dfd2b4990 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -21,20 +21,6 @@ declare_lint_rule! { /// /// When extracting elements from an array, especially when retrieving from the end, `.at` is convenient. Replace the previously used syntax with `.at()`. /// - /// ## Options - /// - /// ### `checkAllIndexAccess` - /// - /// By default, only negative element accesses will use errors, but I will also generate errors for positive accesses. - /// - /// ```json,ignore - /// { - /// "//": "...", - /// "options": { - /// "checkAllIndexAccess": true - /// } - /// } - /// ``` /// /// ## Examples /// From 5bb55e89b49ffe12fdd1edd2a98ac5066f7e180b Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 18:31:34 +0900 Subject: [PATCH 36/48] fix: remove check_all_index_access option --- .../src/lint/nursery/use_at_index.rs | 142 +------- .../checkAllIndexAccessInvalid.jsonc | 17 - .../checkAllIndexAccessInvalid.jsonc.snap | 340 ------------------ .../checkAllIndexAccessInvalid.options.json | 16 - .../useAtIndex/checkAllIndexAccessValid.jsonc | 8 - .../checkAllIndexAccessValid.jsonc.snap | 34 -- .../checkAllIndexAccessValid.options.json | 16 - 7 files changed, 15 insertions(+), 558 deletions(-) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 525dfd2b4990..b28d15d65d45 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -1,5 +1,4 @@ use crate::JsRuleAction; -use ::serde::{Deserialize, Serialize}; use biome_analyze::{ context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, @@ -13,9 +12,6 @@ use biome_js_syntax::{ }; use biome_rowan::{declare_node_union, AstNode, AstSeparatedList, BatchMutationExt}; -#[cfg(feature = "schemars")] -use schemars::JsonSchema; - declare_lint_rule! { /// Use `at()` instead of integer index access. /// @@ -459,75 +455,23 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { let member = AnyJsExpression::JsBinaryExpression(member); let negative_index_exp = extract_negative_index_expression( &member, unwrap_parenthesized_expression(object.clone())?, ); - if let Some(negative_index) = negative_index_exp { - return Some(UseAtIndexState { - at_number_exp: negative_index, - error_type: ErrorType::Index { is_negative: true }, - object, - }); - } - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::Index { is_negative: true }, - object, - }) -} - -fn check_literal_expression_member( - member: AnyJsLiteralExpression, - object: AnyJsExpression, - option: &UseAtIndexOptions, -) -> Option { - let AnyJsLiteralExpression::JsNumberLiteralExpression(member) = member else { - return None; - }; - let value_token = member.value_token().ok()?; - let number = value_token.text_trimmed().parse::().ok()?; - (number >= 0 && option.check_all_index_access).then_some(UseAtIndexState { - at_number_exp: make_number_literal(number), - error_type: ErrorType::Index { is_negative: false }, - object, - }) -} + let negative_index = negative_index_exp?; -fn check_unary_expression_member( - member: JsUnaryExpression, - object: AnyJsExpression, - option: &UseAtIndexOptions, -) -> Option { - if !option.check_all_index_access { - return None; - } - // ignore -5 - let token = member.operator_token().ok()?; - if token.kind() == T![-] { - if let Some(arg) = - get_integer_from_literal(&unwrap_parenthesized_expression(member.argument().ok()?)?) - { - if arg >= 0 { - return None; - } - } - } Some(UseAtIndexState { - at_number_exp: AnyJsExpression::JsUnaryExpression(member), - error_type: ErrorType::Index { is_negative: false }, + at_number_exp: negative_index, + error_type: ErrorType::Index { is_negative: true }, object, }) } /// check hoge[0] -fn check_computed_member_expression( - exp: &JsComputedMemberExpression, - option: &UseAtIndexOptions, -) -> Option { +fn check_computed_member_expression(exp: &JsComputedMemberExpression) -> Option { // check slice if let Some(slice_err) = analyze_slice_element_access(&AnyJsExpression::JsComputedMemberExpression(exp.clone())) @@ -547,22 +491,8 @@ fn check_computed_member_expression( match member.clone() { // hoge[hoge.length - 1] AnyJsExpression::JsBinaryExpression(binary) => { - check_binary_expression_member(binary, object, option) - } - // hoge[1] - AnyJsExpression::AnyJsLiteralExpression(literal) => { - check_literal_expression_member(literal, object, option) - } - // hoge[-x] - AnyJsExpression::JsUnaryExpression(unary) => { - check_unary_expression_member(unary, object, option) + check_binary_expression_member(binary, object) } - AnyJsExpression::JsIdentifierExpression(_) => None, - _ if option.check_all_index_access => Some(UseAtIndexState { - at_number_exp: member, - error_type: ErrorType::Index { is_negative: false }, - object, - }), _ => None, } } @@ -570,7 +500,6 @@ fn check_computed_member_expression( fn check_call_expression_char_at( call_exp: &JsCallExpression, member: &JsStaticMemberExpression, - option: &UseAtIndexOptions, ) -> Option { let args: Vec<_> = call_exp .arguments() @@ -592,41 +521,18 @@ fn check_call_expression_char_at( AnyJsExpression::JsBinaryExpression(_) => { let at_number_exp = extract_negative_index_expression(&core_arg0, char_at_parent.clone()); - if let Some(at_number_exp) = at_number_exp { - Some(UseAtIndexState { - at_number_exp, - error_type: ErrorType::StringCharAt { is_negative: true }, - object: char_at_parent, - }) - } else { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent, - }) - } - } - // hoge.charAt(1) - AnyJsExpression::AnyJsLiteralExpression(_member) => { - option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent.clone(), + at_number_exp.map(|at_number_exp| UseAtIndexState { + at_number_exp, + error_type: ErrorType::StringCharAt { is_negative: true }, + object: char_at_parent, }) } - _ => option.check_all_index_access.then_some(UseAtIndexState { - at_number_exp: core_arg0, - error_type: ErrorType::StringCharAt { is_negative: false }, - object: char_at_parent.clone(), - }), + _ => None, } } /// check hoge.fuga() -fn check_call_expression( - call_exp: &JsCallExpression, - option: &UseAtIndexOptions, -) -> Option { +fn check_call_expression(call_exp: &JsCallExpression) -> Option { // check slice if let Some(slice_err) = analyze_slice_element_access(&AnyJsExpression::JsCallExpression(call_exp.clone())) @@ -645,7 +551,7 @@ fn check_call_expression( } let member_name = member.member().ok()?.as_js_name()?.value_token().ok()?; match member_name.text_trimmed() { - "charAt" => check_call_expression_char_at(call_exp, &member, option), + "charAt" => check_call_expression_char_at(call_exp, &member), _ => None, } } @@ -769,40 +675,22 @@ pub struct UseAtIndexState { object: AnyJsExpression, } -#[derive( - Clone, - Debug, - Default, - biome_deserialize_macros::Deserializable, - Deserialize, - Serialize, - Eq, - PartialEq, -)] -#[cfg_attr(feature = "schemars", derive(JsonSchema))] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct UseAtIndexOptions { - // Force the use of the `.at()` method in cases other than positive integers. - pub check_all_index_access: bool, -} - impl Rule for UseAtIndex { type Query = Ast; type State = UseAtIndexState; type Signals = Option; - type Options = UseAtIndexOptions; + type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { let exp = ctx.query(); - let option = ctx.options(); let result: Option = match exp { // hoge[a] AnyJsArrayAccess::JsComputedMemberExpression(exp) => { - check_computed_member_expression(exp, option) + check_computed_member_expression(exp) } // hoge.fuga() - AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp, option), + AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp), }; result } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc deleted file mode 100644 index 51394550bbe1..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc +++ /dev/null @@ -1,17 +0,0 @@ -[ - "array[0]", - "array[1]", - "array[5 + 9]", - "const offset = 5;array[offset + 9]", - "array[array.length - 1]", - // `charAt` don't care about value - "string.charAt(9)", - "string.charAt(5 + 9)", - "const offset = 5;string.charAt(offset + 9)", - "string.charAt(unknown)", - "string.charAt(-1)", - "string.charAt(1.5)", - "string.charAt(1n)", - "string.charAt(string.length - 1)", - "foo.charAt(bar.length - 1)" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap deleted file mode 100644 index b0ffb8dc9cb0..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.jsonc.snap +++ /dev/null @@ -1,340 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: checkAllIndexAccessInvalid.jsonc ---- -# Input -```cjs -array[0] -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[0] - │ ^^^^^^^^ - - i Prefer X.at(Y) over X[Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[0] - + array.at(0) - - -``` - -# Input -```cjs -array[1] -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[1] - │ ^^^^^^^^ - - i Prefer X.at(Y) over X[Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[1] - + array.at(1) - - -``` - -# Input -```cjs -array[5 + 9] -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[5 + 9] - │ ^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[5·+·9] - + array.at(5·+·9) - - -``` - -# Input -```cjs -const offset = 5;array[offset + 9] -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const offset = 5;array[offset + 9] - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - const·offset·=·5;array[offset·+·9] - + const·offset·=·5;array.at(offset·+·9) - - -``` - -# Input -```cjs -array[array.length - 1] -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ array[array.length - 1] - │ ^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X[X.length - Y]. - - i Unsafe fix: Replace index references with .at(). - - - array[array.length·-·1] - + array.at(-1) - - -``` - -# Input -```cjs -string.charAt(9) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(9) - │ ^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(9) - + string.at(9) - - -``` - -# Input -```cjs -string.charAt(5 + 9) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(5 + 9) - │ ^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(5·+·9) - + string.at(5·+·9) - - -``` - -# Input -```cjs -const offset = 5;string.charAt(offset + 9) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:18 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ const offset = 5;string.charAt(offset + 9) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - const·offset·=·5;string.charAt(offset·+·9) - + const·offset·=·5;string.at(offset·+·9) - - -``` - -# Input -```cjs -string.charAt(unknown) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(unknown) - │ ^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(unknown) - + string.at(unknown) - - -``` - -# Input -```cjs -string.charAt(-1) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(-1) - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(-1) - + string.at(-1) - - -``` - -# Input -```cjs -string.charAt(1.5) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(1.5) - │ ^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(1.5) - + string.at(1.5) - - -``` - -# Input -```cjs -string.charAt(1n) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(1n) - │ ^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(1n) - + string.at(1n) - - -``` - -# Input -```cjs -string.charAt(string.length - 1) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ string.charAt(string.length - 1) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(-Y) over X.charAt(X.length - Y). - - i Unsafe fix: Replace index references with .at(). - - - string.charAt(string.length·-·1) - + string.at(-1) - - -``` - -# Input -```cjs -foo.charAt(bar.length - 1) -``` - -# Diagnostics -``` -checkAllIndexAccessInvalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Replace index references with .at(). - - > 1 │ foo.charAt(bar.length - 1) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ - - i Prefer X.at(Y) over X.charAt(Y). - - i Unsafe fix: Replace index references with .at(). - - - foo.charAt(bar.length·-·1) - + foo.at(bar.length·-·1) - - -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json deleted file mode 100644 index f1c0c4e50343..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessInvalid.options.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", - "linter": { - "enabled": true, - "rules": { - "nursery": { - "useAtIndex": { - "level": "error", - "options": { - "checkAllIndexAccess": true - } - } - } - } - } -} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc deleted file mode 100644 index 7597447d2f15..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc +++ /dev/null @@ -1,8 +0,0 @@ -[ - "++array[1]", - "const offset = 5;const extraArgument = 6;string.charAt(offset + 9, extraArgument)", - "array[unknown]", - "array[-1]", - "array[1.5]", - "array[1n]" -] diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap deleted file mode 100644 index e74a0ca06bfe..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.jsonc.snap +++ /dev/null @@ -1,34 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -assertion_line: 86 -expression: checkAllIndexAccessValid.json ---- -# Input -```cjs -++array[1] -``` - -# Input -```cjs -const offset = 5;const extraArgument = 6;string.charAt(offset + 9, extraArgument) -``` - -# Input -```cjs -array[unknown] -``` - -# Input -```cjs -array[-1] -``` - -# Input -```cjs -array[1.5] -``` - -# Input -```cjs -array[1n] -``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json deleted file mode 100644 index f1c0c4e50343..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/checkAllIndexAccessValid.options.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", - "linter": { - "enabled": true, - "rules": { - "nursery": { - "useAtIndex": { - "level": "error", - "options": { - "checkAllIndexAccess": true - } - } - } - } - } -} From bc1d20c819a460807793f892c77390677d4b10ff Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Thu, 3 Oct 2024 18:43:12 +0900 Subject: [PATCH 37/48] fix: use omit_parentheses() --- .../src/lint/nursery/use_at_index.rs | 62 ++++++------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index b28d15d65d45..e0111411fc28 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -78,21 +78,6 @@ declare_lint_rule! { } } -/// If the node is a parenthized expression, it returns the expression inside. -/// # Examples -/// ```js -/// a // Some(a) -/// (a) // Some(a) -/// (a + b) // Some(a + b) -/// ``` -fn unwrap_parenthesized_expression(mut node: AnyJsExpression) -> Option { - while let AnyJsExpression::JsParenthesizedExpression(parenthesized_exp) = node { - let exp = parenthesized_exp.expression().ok()?; - node = exp; - } - Some(node) -} - /// Check if two expressions reference the same value. /// Only literals are allowed for members. /// # Examples @@ -105,8 +90,8 @@ fn unwrap_parenthesized_expression(mut node: AnyJsExpression) -> Option Option { // solve JsParenthesizedExpression - let left = unwrap_parenthesized_expression(left)?; - let right = unwrap_parenthesized_expression(right)?; + let left = left.omit_parentheses(); + let right = right.omit_parentheses(); match (left, right) { // x[0] ( @@ -114,12 +99,12 @@ fn is_same_reference(left: AnyJsExpression, right: AnyJsExpression) -> Option { let AnyJsExpression::AnyJsLiteralExpression(left_member) = - unwrap_parenthesized_expression(left.member().ok()?)? + left.member().ok()?.omit_parentheses() else { return Some(false); }; let AnyJsExpression::AnyJsLiteralExpression(right_member) = - unwrap_parenthesized_expression(right.member().ok()?)? + right.member().ok()?.omit_parentheses() else { return Some(false); }; @@ -207,7 +192,7 @@ fn get_integer_from_literal(node: &AnyJsExpression) -> Option { if token.kind() != T![-] { return None; } - return get_integer_from_literal(&unwrap_parenthesized_expression(unary.argument().ok()?)?) + return get_integer_from_literal(&unary.argument().ok()?.omit_parentheses()) .map(|num| -num); } let AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::JsNumberLiteralExpression( @@ -275,7 +260,7 @@ fn extract_negative_index_expression( } // left expression should be hoge.length - let left = unwrap_parenthesized_expression(left)?; + let left = left.omit_parentheses(); let length_parent = get_length_node(&left)?; // left expression should be the same as the object if !is_same_reference(object, length_parent)? { @@ -284,9 +269,7 @@ fn extract_negative_index_expression( if right_list.len() == 1 { // right expression should be integer - if let Some(number) = - get_integer_from_literal(&unwrap_parenthesized_expression(right_list[0].clone())?) - { + if let Some(number) = get_integer_from_literal(&right_list[0].clone().omit_parentheses()) { if number > 0 { Some(AnyJsExpression::JsUnaryExpression( make::js_unary_expression(make::token(T![-]), right_list[0].clone()), @@ -346,7 +329,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { - let object = unwrap_parenthesized_expression(member.object().ok()?)?; + let object = member.object().ok()?.omit_parentheses(); if member.is_optional_chain() { return None; } - let value = - get_integer_from_literal(&unwrap_parenthesized_expression(member.member().ok()?)?)?; + let value = get_integer_from_literal(&member.member().ok()?.omit_parentheses())?; // enable only x[0] if value != 0 { return None; @@ -398,7 +380,7 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option Option { let start_index = get_integer_from_literal(&start_exp)?; - let end_index = get_integer_from_literal(&unwrap_parenthesized_expression( - arg1.as_any_js_expression()?.clone(), - )?)?; + let end_index = + get_integer_from_literal(&arg1.as_any_js_expression()?.clone().omit_parentheses())?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: start_exp, error_type: ErrorType::Slice { @@ -436,9 +417,8 @@ fn analyze_slice_element_access(node: &AnyJsExpression) -> Option { let start_index = get_integer_from_literal(&start_exp)?; - let end_index = get_integer_from_literal(&unwrap_parenthesized_expression( - arg1.as_any_js_expression()?.clone(), - )?)?; + let end_index = + get_integer_from_literal(&arg1.as_any_js_expression()?.clone().omit_parentheses())?; (start_index * end_index >= 0 && start_index < end_index).then_some(UseAtIndexState { at_number_exp: make_number_literal(end_index - 1), error_type: ErrorType::Slice { @@ -457,10 +437,8 @@ fn check_binary_expression_member( object: AnyJsExpression, ) -> Option { let member = AnyJsExpression::JsBinaryExpression(member); - let negative_index_exp = extract_negative_index_expression( - &member, - unwrap_parenthesized_expression(object.clone())?, - ); + let negative_index_exp = + extract_negative_index_expression(&member, object.clone().omit_parentheses()); let negative_index = negative_index_exp?; Some(UseAtIndexState { @@ -486,7 +464,7 @@ fn check_computed_member_expression(exp: &JsComputedMemberExpression) -> Option< return None; } // check member - let member = unwrap_parenthesized_expression(exp.member().ok()?)?; + let member = exp.member().ok()?.omit_parentheses(); let object = exp.object().ok()?; match member.clone() { // hoge[hoge.length - 1] @@ -514,8 +492,8 @@ fn check_call_expression_char_at( let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { return None; }; - let core_arg0 = unwrap_parenthesized_expression(arg0)?; - let char_at_parent = unwrap_parenthesized_expression(member.object().ok()?)?; + let core_arg0 = arg0.omit_parentheses(); + let char_at_parent = member.object().ok()?.omit_parentheses(); match core_arg0.clone() { // hoge.charAt(hoge.length - 1) AnyJsExpression::JsBinaryExpression(_) => { From 2305e65d5c1bd518da4ad7ea59d5beaa70507acd Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Fri, 4 Oct 2024 01:34:09 +0900 Subject: [PATCH 38/48] fix: use .get_arguments_by_index([]) --- .../src/lint/nursery/use_at_index.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e0111411fc28..e6c2dde7d408 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -479,26 +479,18 @@ fn check_call_expression_char_at( call_exp: &JsCallExpression, member: &JsStaticMemberExpression, ) -> Option { - let args: Vec<_> = call_exp - .arguments() - .ok()? - .args() - .into_iter() - .flatten() - .collect(); - if args.len() != 1 { + let [Some(arg0), None] = call_exp.arguments().ok()?.get_arguments_by_index([0, 1]) else { return None; - } - let AnyJsCallArgument::AnyJsExpression(arg0) = args[0].clone() else { + }; + let AnyJsCallArgument::AnyJsExpression(arg0) = arg0.clone() else { return None; }; - let core_arg0 = arg0.omit_parentheses(); + let arg0 = arg0.omit_parentheses(); let char_at_parent = member.object().ok()?.omit_parentheses(); - match core_arg0.clone() { + match arg0.clone() { // hoge.charAt(hoge.length - 1) AnyJsExpression::JsBinaryExpression(_) => { - let at_number_exp = - extract_negative_index_expression(&core_arg0, char_at_parent.clone()); + let at_number_exp = extract_negative_index_expression(&arg0, char_at_parent.clone()); at_number_exp.map(|at_number_exp| UseAtIndexState { at_number_exp, error_type: ErrorType::StringCharAt { is_negative: true }, From 827d4a80dc06bd6a9edb2451a51dd09018a4bdf2 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Fri, 4 Oct 2024 01:37:21 +0900 Subject: [PATCH 39/48] doc: replace "hoge" => "foo" --- .../src/lint/nursery/use_at_index.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e6c2dde7d408..252d8b60c827 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -246,9 +246,9 @@ fn make_plus_binary_expression(list: Vec) -> Option None -/// hoge[hoge.length - 1] // => Some(-1) -/// hoge[fuga.length - 2] // => None +/// foo[bar.length - 0] // => None +/// foo[bar.length - 1] // => Some(-1) +/// foo[bar.length - 2] // => None /// ``` fn extract_negative_index_expression( member: &AnyJsExpression, @@ -259,7 +259,7 @@ fn extract_negative_index_expression( return None; } - // left expression should be hoge.length + // left expression should be foo.length let left = left.omit_parentheses(); let length_parent = get_length_node(&left)?; // left expression should be the same as the object @@ -448,7 +448,7 @@ fn check_binary_expression_member( }) } -/// check hoge[0] +/// check foo[foo.length - 1] fn check_computed_member_expression(exp: &JsComputedMemberExpression) -> Option { // check slice if let Some(slice_err) = @@ -467,7 +467,7 @@ fn check_computed_member_expression(exp: &JsComputedMemberExpression) -> Option< let member = exp.member().ok()?.omit_parentheses(); let object = exp.object().ok()?; match member.clone() { - // hoge[hoge.length - 1] + // foo[foo.length - 1] AnyJsExpression::JsBinaryExpression(binary) => { check_binary_expression_member(binary, object) } @@ -475,6 +475,7 @@ fn check_computed_member_expression(exp: &JsComputedMemberExpression) -> Option< } } +/// check foo.charAt(foo.length - 1) fn check_call_expression_char_at( call_exp: &JsCallExpression, member: &JsStaticMemberExpression, @@ -488,7 +489,7 @@ fn check_call_expression_char_at( let arg0 = arg0.omit_parentheses(); let char_at_parent = member.object().ok()?.omit_parentheses(); match arg0.clone() { - // hoge.charAt(hoge.length - 1) + // foo.charAt(foo.length - 1) AnyJsExpression::JsBinaryExpression(_) => { let at_number_exp = extract_negative_index_expression(&arg0, char_at_parent.clone()); at_number_exp.map(|at_number_exp| UseAtIndexState { @@ -501,7 +502,7 @@ fn check_call_expression_char_at( } } -/// check hoge.fuga() +/// check foo.bar() fn check_call_expression(call_exp: &JsCallExpression) -> Option { // check slice if let Some(slice_err) = @@ -655,11 +656,11 @@ impl Rule for UseAtIndex { let exp = ctx.query(); let result: Option = match exp { - // hoge[a] + // foo[a] AnyJsArrayAccess::JsComputedMemberExpression(exp) => { check_computed_member_expression(exp) } - // hoge.fuga() + // foo.bar() AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp), }; result From 29324d778191b86d3694da12741d152fc4cf4fd4 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Fri, 4 Oct 2024 01:45:33 +0900 Subject: [PATCH 40/48] refactor: move impl Rule --- .../src/lint/nursery/use_at_index.rs | 342 +++++++++--------- 1 file changed, 171 insertions(+), 171 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 252d8b60c827..68e3fe6f23df 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -78,6 +78,177 @@ declare_lint_rule! { } } +/// The method to retrieve values from `.slice()` +#[derive(Clone)] +pub enum SliceExtractType { + Pop, + Shift, + ZeroMember, +} + +/// The number of arguments for `.slice()` +#[derive(Clone)] +pub enum SliceArgType { + OneArg, + TwoArg, +} + +/// Type of Code to Fix +#[derive(Clone)] +pub enum ErrorType { + Index { + is_negative: bool, + }, + StringCharAt { + is_negative: bool, + }, + Slice { + arg_type: SliceArgType, + extract_type: SliceExtractType, + }, +} + +declare_node_union! { + pub AnyJsArrayAccess = JsComputedMemberExpression | JsCallExpression +} + +pub struct UseAtIndexState { + at_number_exp: AnyJsExpression, + error_type: ErrorType, + object: AnyJsExpression, +} + +impl Rule for UseAtIndex { + type Query = Ast; + type State = UseAtIndexState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let exp = ctx.query(); + + let result: Option = match exp { + // foo[a] + AnyJsArrayAccess::JsComputedMemberExpression(exp) => { + check_computed_member_expression(exp) + } + // foo.bar() + AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp), + }; + result + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + Some( + RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "Replace index references with "".at()""." + } + .to_owned(), + ) + .note(state.error_type.get_error_message()), + ) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + let mut mutation = ctx.root().begin(); + let prev_node = match node { + AnyJsArrayAccess::JsComputedMemberExpression(node) => { + AnyJsExpression::JsComputedMemberExpression(node.clone()) + } + AnyJsArrayAccess::JsCallExpression(node) => { + AnyJsExpression::JsCallExpression(node.clone()) + } + }; + let UseAtIndexState { + at_number_exp, + error_type: _, + object, + } = state; + let object = overwrap_parentheses_expression(object)?; + + mutation.replace_node( + prev_node, + AnyJsExpression::JsCallExpression(make_at_method( + object, + at_number_exp.clone().trim_trivia()?, + )), + ); + + Some(JsRuleAction::new( + ActionCategory::QuickFix, + ctx.metadata().applicability(), + markup! { "Replace index references with "".at()""." }.to_owned(), + mutation, + )) + } +} + +impl ErrorType { + /// Return the error message corresponding to the ErrorType. + fn get_error_message(self: &ErrorType) -> MarkupBuf { + match self { + ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { + let (method, old_method) = if *is_negative { + ( + "X.at(-Y)", + if matches!(self, ErrorType::StringCharAt { .. }) { + "X.charAt(X.length - Y)" + } else { + "X[X.length - Y]" + }, + ) + } else { + ( + "X.at(Y)", + if matches!(self, ErrorType::StringCharAt { .. }) { + "X.charAt(Y)" + } else { + "X[Y]" + }, + ) + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + } + ErrorType::Slice { + arg_type, + extract_type, + } => { + let extract_string = match extract_type { + SliceExtractType::Pop => ".pop()", + SliceExtractType::Shift => ".shift()", + SliceExtractType::ZeroMember => "[0]", + }; + let (method, old_method) = match (arg_type, extract_type) { + (SliceArgType::OneArg, SliceExtractType::Pop) => { + ("X.at(-1)", format!("X.slice(-a){}", extract_string)) + } + (SliceArgType::TwoArg, SliceExtractType::Pop) => { + ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)) + } + _ => ( + "X.at(Y)", + format!( + "X.slice({}){}", + if matches!(arg_type, SliceArgType::OneArg) { + "Y" + } else { + "Y, a" + }, + extract_string + ), + ), + }; + markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() + } + } + } +} + /// Check if two expressions reference the same value. /// Only literals are allowed for members. /// # Examples @@ -544,174 +715,3 @@ fn make_at_method(object: AnyJsExpression, arg: AnyJsExpression) -> JsCallExpres ); make::js_call_expression(at_member.into(), args).build() } - -/// The method to retrieve values from `.slice()` -#[derive(Clone)] -pub enum SliceExtractType { - Pop, - Shift, - ZeroMember, -} - -/// The number of arguments for `.slice()` -#[derive(Clone)] -pub enum SliceArgType { - OneArg, - TwoArg, -} - -/// Type of Code to Fix -#[derive(Clone)] -pub enum ErrorType { - Index { - is_negative: bool, - }, - StringCharAt { - is_negative: bool, - }, - Slice { - arg_type: SliceArgType, - extract_type: SliceExtractType, - }, -} - -impl ErrorType { - /// Return the error message corresponding to the ErrorType. - fn get_error_message(self: &ErrorType) -> MarkupBuf { - match self { - ErrorType::Index { is_negative } | ErrorType::StringCharAt { is_negative } => { - let (method, old_method) = if *is_negative { - ( - "X.at(-Y)", - if matches!(self, ErrorType::StringCharAt { .. }) { - "X.charAt(X.length - Y)" - } else { - "X[X.length - Y]" - }, - ) - } else { - ( - "X.at(Y)", - if matches!(self, ErrorType::StringCharAt { .. }) { - "X.charAt(Y)" - } else { - "X[Y]" - }, - ) - }; - markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - } - ErrorType::Slice { - arg_type, - extract_type, - } => { - let extract_string = match extract_type { - SliceExtractType::Pop => ".pop()", - SliceExtractType::Shift => ".shift()", - SliceExtractType::ZeroMember => "[0]", - }; - let (method, old_method) = match (arg_type, extract_type) { - (SliceArgType::OneArg, SliceExtractType::Pop) => { - ("X.at(-1)", format!("X.slice(-a){}", extract_string)) - } - (SliceArgType::TwoArg, SliceExtractType::Pop) => { - ("X.at(Y - 1)", format!("X.slice(a, Y){}", extract_string)) - } - _ => ( - "X.at(Y)", - format!( - "X.slice({}){}", - if matches!(arg_type, SliceArgType::OneArg) { - "Y" - } else { - "Y, a" - }, - extract_string - ), - ), - }; - markup! { "Prefer "{method}" over "{old_method}"." }.to_owned() - } - } - } -} - -declare_node_union! { - pub AnyJsArrayAccess = JsComputedMemberExpression | JsCallExpression -} - -pub struct UseAtIndexState { - at_number_exp: AnyJsExpression, - error_type: ErrorType, - object: AnyJsExpression, -} - -impl Rule for UseAtIndex { - type Query = Ast; - type State = UseAtIndexState; - type Signals = Option; - type Options = (); - - fn run(ctx: &RuleContext) -> Self::Signals { - let exp = ctx.query(); - - let result: Option = match exp { - // foo[a] - AnyJsArrayAccess::JsComputedMemberExpression(exp) => { - check_computed_member_expression(exp) - } - // foo.bar() - AnyJsArrayAccess::JsCallExpression(call_exp) => check_call_expression(call_exp), - }; - result - } - - fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { - let node = ctx.query(); - Some( - RuleDiagnostic::new( - rule_category!(), - node.range(), - markup! { - "Replace index references with "".at()""." - } - .to_owned(), - ) - .note(state.error_type.get_error_message()), - ) - } - - fn action(ctx: &RuleContext, state: &Self::State) -> Option { - let node = ctx.query(); - let mut mutation = ctx.root().begin(); - let prev_node = match node { - AnyJsArrayAccess::JsComputedMemberExpression(node) => { - AnyJsExpression::JsComputedMemberExpression(node.clone()) - } - AnyJsArrayAccess::JsCallExpression(node) => { - AnyJsExpression::JsCallExpression(node.clone()) - } - }; - let UseAtIndexState { - at_number_exp, - error_type: _, - object, - } = state; - let object = overwrap_parentheses_expression(object)?; - - mutation.replace_node( - prev_node, - AnyJsExpression::JsCallExpression(make_at_method( - object, - at_number_exp.clone().trim_trivia()?, - )), - ); - - Some(JsRuleAction::new( - ActionCategory::QuickFix, - ctx.metadata().applicability(), - markup! { "Replace index references with "".at()""." }.to_owned(), - mutation, - )) - } -} From 45160982628fa4fb10900a7e11361888e1575f66 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:11:39 +0900 Subject: [PATCH 41/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 68e3fe6f23df..d29b1ccf8672 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -348,9 +348,8 @@ fn get_length_node(node: &AnyJsExpression) -> Option { let member_name = member_name .as_js_name()? .value_token() - .ok()? - .token_text_trimmed(); - if member_name.text() != "length" { + .ok()?; + if member_name.text_trimmed() != "length" { return None; } node.object().ok() From ab9d2147ca4780c75ed79e104b92331d37e47717 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:13:21 +0900 Subject: [PATCH 42/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index d29b1ccf8672..6d507b8e823c 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -15,7 +15,12 @@ use biome_rowan::{declare_node_union, AstNode, AstSeparatedList, BatchMutationEx declare_lint_rule! { /// Use `at()` instead of integer index access. /// - /// When extracting elements from an array, especially when retrieving from the end, `.at` is convenient. Replace the previously used syntax with `.at()`. + /// Accessing an element at the end of an array or a string is inconvenient because you have to subtract the length of the array or the string from the backward 1-based index of the element to access. + /// For example, to access the last element of an array or a string, you would have to write `array[array.length - 1]`. + /// A more convenient way to achieve the same thing is to use the `at()` method with a negative index. + /// To access the last element of an array or a string just write `array.at(-1)`. + /// + /// This rule enforces the usage of `at()` over index access, `chatAt()`, and `slice()[0]` when `at()` is more convenient. /// /// /// ## Examples From 90d2c9717656ebf8aa56e8608e46ddc1872fbcee Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:18:06 +0900 Subject: [PATCH 43/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 6d507b8e823c..e37b63d1336c 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -187,7 +187,7 @@ impl Rule for UseAtIndex { Some(JsRuleAction::new( ActionCategory::QuickFix, ctx.metadata().applicability(), - markup! { "Replace index references with "".at()""." }.to_owned(), + markup! { "Use "".at()""." }.to_owned(), mutation, )) } From 4528b4e1171cb263169a498e547e51222e1011a8 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:21:51 +0900 Subject: [PATCH 44/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e37b63d1336c..e9d49c88ab19 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -149,12 +149,12 @@ impl Rule for UseAtIndex { RuleDiagnostic::new( rule_category!(), node.range(), - markup! { - "Replace index references with "".at()""." - } + state.error_type.get_error_message() .to_owned(), ) - .note(state.error_type.get_error_message()), + .note(markup! { + "Using "".at()"" is more convenient and is easier to read." + }), ) } From 44109dc598a157db5fd163ed3f419a997b2886d5 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:22:46 +0900 Subject: [PATCH 45/48] Update crates/biome_js_analyze/src/lint/nursery/use_at_index.rs Co-authored-by: Victorien Elvinger --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index e9d49c88ab19..2b1a1a0730a3 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -426,7 +426,7 @@ fn make_plus_binary_expression(list: Vec) -> Option None /// ``` fn extract_negative_index_expression( - member: &AnyJsExpression, + member: AnyJsExpression, object: AnyJsExpression, ) -> Option { let (left, right_list) = split_minus_binary_expressions(member.clone())?; From 456efb024d6d970d48bf0105d7aef734044baf1a Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:29:21 +0900 Subject: [PATCH 46/48] refactor: format & lint --- .../src/lint/nursery/use_at_index.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 2b1a1a0730a3..1b27885b48bc 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -149,12 +149,11 @@ impl Rule for UseAtIndex { RuleDiagnostic::new( rule_category!(), node.range(), - state.error_type.get_error_message() - .to_owned(), + state.error_type.get_error_message(), ) .note(markup! { - "Using "".at()"" is more convenient and is easier to read." - }), + "Using "".at()"" is more convenient and is easier to read." + }), ) } @@ -350,10 +349,7 @@ fn get_length_node(node: &AnyJsExpression) -> Option { return None; }; let member_name = node.member().ok()?; - let member_name = member_name - .as_js_name()? - .value_token() - .ok()?; + let member_name = member_name.as_js_name()?.value_token().ok()?; if member_name.text_trimmed() != "length" { return None; } @@ -429,7 +425,7 @@ fn extract_negative_index_expression( member: AnyJsExpression, object: AnyJsExpression, ) -> Option { - let (left, right_list) = split_minus_binary_expressions(member.clone())?; + let (left, right_list) = split_minus_binary_expressions(member)?; if right_list.is_empty() { return None; } @@ -613,7 +609,7 @@ fn check_binary_expression_member( ) -> Option { let member = AnyJsExpression::JsBinaryExpression(member); let negative_index_exp = - extract_negative_index_expression(&member, object.clone().omit_parentheses()); + extract_negative_index_expression(member, object.clone().omit_parentheses()); let negative_index = negative_index_exp?; Some(UseAtIndexState { @@ -666,7 +662,7 @@ fn check_call_expression_char_at( match arg0.clone() { // foo.charAt(foo.length - 1) AnyJsExpression::JsBinaryExpression(_) => { - let at_number_exp = extract_negative_index_expression(&arg0, char_at_parent.clone()); + let at_number_exp = extract_negative_index_expression(arg0, char_at_parent.clone()); at_number_exp.map(|at_number_exp| UseAtIndexState { at_number_exp, error_type: ErrorType::StringCharAt { is_negative: true }, From 5fbbe9094d2e345e3ba935100b7ee6e84e1b6af9 Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:35:33 +0900 Subject: [PATCH 47/48] doc: fix comment typo --- crates/biome_js_analyze/src/lint/nursery/use_at_index.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs index 1b27885b48bc..f2299963c7e9 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_at_index.rs @@ -417,8 +417,8 @@ fn make_plus_binary_expression(list: Vec) -> Option None -/// foo[bar.length - 1] // => Some(-1) +/// foo[foo.length - 0] // => None +/// foo[foo.length - 1] // => Some(-1) /// foo[bar.length - 2] // => None /// ``` fn extract_negative_index_expression( From 4a9833677b0b4837c603facbd16cc88f36906bcb Mon Sep 17 00:00:00 2001 From: GunseiKPaseri Date: Wed, 9 Oct 2024 23:43:19 +0900 Subject: [PATCH 48/48] test: fix test --- .../nursery/useAtIndex/invalid.jsonc.snap | 378 +++++++++--------- 1 file changed, 189 insertions(+), 189 deletions(-) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap index 442185164898..cd3a4ea89fd8 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useAtIndex/invalid.jsonc.snap @@ -12,14 +12,14 @@ array[array.length - 1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - 1]; │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·1]; + array.at(-1); @@ -36,14 +36,14 @@ array[array.length -1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length -1]; │ ^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-1]; + array.at(-1); @@ -60,14 +60,14 @@ array[array.length - /* comment */ 1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - /* comment */ 1]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·/*·comment·*/·1]; + array.at(-1); @@ -84,14 +84,14 @@ array[array.length - 1.]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - 1.]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·1.]; + array.at(-1.); @@ -108,14 +108,14 @@ array[array.length - 0b1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - 0b1]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·0b1]; + array.at(-0b1); @@ -132,14 +132,14 @@ array[array.length - 9]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - 9]; │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·9]; + array.at(-9); @@ -156,14 +156,14 @@ array[0][array[0].length - 1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[0][array[0].length - 1]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[0][array[0].length·-·1]; + array[0].at(-1); @@ -180,14 +180,14 @@ array[(( array.length )) - 1]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[(( array.length )) - 1]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[((·array.length·))·-·1]; + array.at(-1); @@ -204,14 +204,14 @@ array[array.length - (( 1 ))]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - (( 1 ))]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·((·1·))]; + array.at(-((·1·))); @@ -228,14 +228,14 @@ array[(( array.length - 1 ))]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[(( array.length - 1 ))]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[((·array.length·-·1·))]; + array.at(-1); @@ -252,14 +252,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ (( array ))[array.length - 1]; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array·))[array.length·-·1]; + ((·array·)).at(-1); @@ -276,14 +276,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ (( array[array.length - 1] )); │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array[array.length·-·1]·)); + ((·array.at(-1)·)); @@ -300,14 +300,14 @@ array[array.length - 1].pop().shift()[0]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - 1].pop().shift()[0]; │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·1].pop().shift()[0]; + array.at(-1).pop().shift()[0]; @@ -324,14 +324,14 @@ a = array[array.length - 1] ``` invalid.jsonc:1:5 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ a = array[array.length - 1] │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - a·=·array[array.length·-·1] + a·=·array.at(-1) @@ -348,14 +348,14 @@ const a = array[array.length - 1] ``` invalid.jsonc:1:11 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ const a = array[array.length - 1] │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - const·a·=·array[array.length·-·1] + const·a·=·array.at(-1) @@ -372,14 +372,14 @@ const {a = array[array.length - 1]} = {} ``` invalid.jsonc:1:12 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ const {a = array[array.length - 1]} = {} │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - const·{a·=·array[array.length·-·1]}·=·{} + const·{a·=·array.at(-1)}·=·{} @@ -396,14 +396,14 @@ typeof array[array.length - 1] ``` invalid.jsonc:1:8 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ typeof array[array.length - 1] │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - typeof·array[array.length·-·1] + typeof·array.at(-1) @@ -420,14 +420,14 @@ function foo() {return arguments[arguments.length - 1]} ``` invalid.jsonc:1:24 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ function foo() {return arguments[arguments.length - 1]} │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - function·foo()·{return·arguments[arguments.length·-·1]} + function·foo()·{return·arguments.at(-1)} @@ -444,14 +444,14 @@ class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} ``` invalid.jsonc:1:31 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ class Foo {bar; baz() {return this.bar[this.bar.length - 1]}} │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - class·Foo·{bar;·baz()·{return·this.bar[this.bar.length·-·1]}} + class·Foo·{bar;·baz()·{return·this.bar.at(-1)}} @@ -468,14 +468,14 @@ class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} ``` invalid.jsonc:1:32 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ class Foo {#bar; baz() {return this.#bar[this.#bar.length - 1]}} │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - class·Foo·{#bar;·baz()·{return·this.#bar[this.#bar.length·-·1]}} + class·Foo·{#bar;·baz()·{return·this.#bar.at(-1)}} @@ -492,14 +492,14 @@ array[array.length - unknown - 1] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - unknown - 1] │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·unknown·-·1] + array.at(unknown·+1) @@ -516,14 +516,14 @@ array[array.length - (unknown + 1)] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X[X.length - Y]. > 1 │ array[array.length - (unknown + 1)] │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X[X.length - Y]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array[array.length·-·(unknown·+·1)] + array.at(-(unknown·+·1)) @@ -540,14 +540,14 @@ string.charAt(string.length - 1); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt(string.length - 1); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(string.length·-·1); + string.at(-1); @@ -564,14 +564,14 @@ string.charAt(string.length - 0o11); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt(string.length - 0o11); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(string.length·-·0o11); + string.at(-0o11); @@ -588,14 +588,14 @@ some.string.charAt(some.string.length - 1); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ some.string.charAt(some.string.length - 1); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - some.string.charAt(some.string.length·-·1); + some.string.at(-1); @@ -612,14 +612,14 @@ string.charAt((( string.length )) - 0xFF); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt((( string.length )) - 0xFF); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(((·string.length·))·-·0xFF); + string.at(-0xFF); @@ -636,14 +636,14 @@ string.charAt(string.length - (( 1 ))); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt(string.length - (( 1 ))); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(string.length·-·((·1·))); + string.at(-((·1·))); @@ -660,14 +660,14 @@ string.charAt((( string.length - 1 ))); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt((( string.length - 1 ))); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(((·string.length·-·1·))); + string.at(-1); @@ -684,14 +684,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ (( string )).charAt(string.length - 1); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·string·)).charAt(string.length·-·1); + string.at(-1); @@ -708,14 +708,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ (( string.charAt ))(string.length - 1); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·string.charAt·))(string.length·-·1); + string.at(-1); @@ -732,14 +732,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ (( string.charAt(string.length - 1) )); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·string.charAt(string.length·-·1)·)); + ((·string.at(-1)·)); @@ -756,14 +756,14 @@ string.charAt(string.length - unknown - 1 ); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt(string.length - unknown - 1 ); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(string.length·-·unknown·-·1·); + string.at(unknown·+1); @@ -780,14 +780,14 @@ string.charAt(string.length - (unknown + 1)); ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-Y) over X.charAt(X.length - Y). > 1 │ string.charAt(string.length - (unknown + 1)); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-Y) over X.charAt(X.length - Y). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - string.charAt(string.length·-·(unknown·+·1)); + string.at(-(unknown·+·1)); @@ -804,14 +804,14 @@ array.slice(0)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(0)[0] │ ^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(0)[0] + array.at(0) @@ -828,14 +828,14 @@ array.slice(-0)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-0)[0] │ ^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-0)[0] + array.at(-0) @@ -852,14 +852,14 @@ array.slice(-1)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-1)[0] │ ^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1)[0] + array.at(-1) @@ -876,14 +876,14 @@ array.slice(-1).pop() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-1) over X.slice(-a).pop(). > 1 │ array.slice(-1).pop() │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1).pop() + array.at(-1) @@ -900,14 +900,14 @@ array.slice(-1.0).shift() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y).shift(). > 1 │ array.slice(-1.0).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1.0).shift() + array.at(-1.0) @@ -924,14 +924,14 @@ array.slice(-9)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-9)[0] │ ^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9)[0] + array.at(-9) @@ -948,14 +948,14 @@ array.slice(-9).pop() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-1) over X.slice(-a).pop(). > 1 │ array.slice(-9).pop() │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9).pop() + array.at(-1) @@ -972,14 +972,14 @@ array.slice(-1.1)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-1.1)[0] │ ^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1.1)[0] + array.at(-1.1) @@ -996,14 +996,14 @@ array.slice(-0xA)[0b000] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-0xA)[0b000] │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-0xA)[0b000] + array.at(-0xA) @@ -1020,14 +1020,14 @@ array.slice(-9).shift() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y).shift(). > 1 │ array.slice(-9).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9).shift() + array.at(-9) @@ -1044,14 +1044,14 @@ array.slice(-1)[(( 0 ))]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-1)[(( 0 ))]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1)[((·0·))]; + array.at(-1); @@ -1068,14 +1068,14 @@ array.slice(-(( 1 )))[0]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-(( 1 )))[0]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-((·1·)))[0]; + array.at(-((·1·))); @@ -1092,14 +1092,14 @@ array.slice((( -1 )))[0]; ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice((( -1 )))[0]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(((·-1·)))[0]; + array.at(-1); @@ -1116,14 +1116,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ (( array.slice(-1) ))[0]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-1)·))[0]; + array.at(-1); @@ -1140,14 +1140,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ (( array )).slice(-1)[0]; │ ^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array·)).slice(-1)[0]; + ((·array·)).at(-1); @@ -1164,14 +1164,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ (( array.slice(-1)[0] )); │ ^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-1)[0]·)); + ((·array.at(-1)·)); @@ -1188,14 +1188,14 @@ invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-1) over X.slice(-a).pop(). > 1 │ (( array.slice(-1) )).pop(); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-1)·)).pop(); + array.at(-1); @@ -1212,14 +1212,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-1) over X.slice(-a).pop(). > 1 │ (( array.slice(-1).pop ))(); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-1).pop·))(); + array.at(-1); @@ -1236,14 +1236,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:4 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(-1) over X.slice(-a).pop(). > 1 │ (( array.slice(-1).pop() )); │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(-1) over X.slice(-a).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-1).pop()·)); + ((·array.at(-1)·)); @@ -1260,14 +1260,14 @@ array.slice(-1)[0].pop().shift().slice(-1) ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(-1)[0].pop().shift().slice(-1) │ ^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-1)[0].pop().shift().slice(-1) + array.at(-1).pop().shift().slice(-1) @@ -1284,14 +1284,14 @@ array.slice(-9, -8)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a)[0]. > 1 │ array.slice(-9, -8)[0] │ ^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9,·-8)[0] + array.at(-9) @@ -1308,14 +1308,14 @@ array.slice(-9, -0o10)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a)[0]. > 1 │ array.slice(-9, -0o10)[0] │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9,·-0o10)[0] + array.at(-9) @@ -1332,14 +1332,14 @@ array.slice(-9, -8).pop() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y - 1) over X.slice(a, Y).pop(). > 1 │ array.slice(-9, -8).pop() │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y - 1) over X.slice(a, Y).pop(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9,·-8).pop() + array.at(-9) @@ -1356,14 +1356,14 @@ array.slice(-9, -8).shift() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a).shift(). > 1 │ array.slice(-9, -8).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9,·-8).shift() + array.at(-9) @@ -1380,14 +1380,14 @@ array.slice((( -9 )), (( -8 )), ).shift() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a).shift(). > 1 │ array.slice((( -9 )), (( -8 )), ).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(((·-9·)),·((·-8·)),·).shift() + array.at(-9) @@ -1404,14 +1404,14 @@ invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━ ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a).shift(). > 1 │ (( array.slice(-9, -8).shift ))() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - ((·array.slice(-9,·-8).shift·))() + array.at(-9) @@ -1428,14 +1428,14 @@ array.slice(-0o11, -7)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a)[0]. > 1 │ array.slice(-0o11, -7)[0] │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-0o11,·-7)[0] + array.at(-0o11) @@ -1452,14 +1452,14 @@ array.slice(-9, 0)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y, a)[0]. > 1 │ array.slice(-9, 0)[0] │ ^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y, a)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(-9,·0)[0] + array.at(-9) @@ -1476,14 +1476,14 @@ array.slice(hoge)[0] ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y)[0]. > 1 │ array.slice(hoge)[0] │ ^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y)[0]. + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(hoge)[0] + array.at(hoge) @@ -1500,14 +1500,14 @@ array.slice(hoge).shift() ``` invalid.jsonc:1:1 lint/nursery/useAtIndex FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Replace index references with .at(). + ! Prefer X.at(Y) over X.slice(Y).shift(). > 1 │ array.slice(hoge).shift() │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - i Prefer X.at(Y) over X.slice(Y).shift(). + i Using .at() is more convenient and is easier to read. - i Unsafe fix: Replace index references with .at(). + i Unsafe fix: Use .at(). - array.slice(hoge).shift() + array.at(hoge)