From e39e1b1a3a4ab12406566672c4e2615be3f1e862 Mon Sep 17 00:00:00 2001 From: Vasu Singh Date: Thu, 18 Jan 2024 03:33:59 +0530 Subject: [PATCH] fix(lint/noArrayIndexKey): false negative in template literals (#1586) --- CHANGELOG.md | 2 + .../suspicious/no_array_index_key.rs | 104 +++- .../suspicious/noArrayIndexKey/invalid.jsx | 108 ++-- .../noArrayIndexKey/invalid.jsx.snap | 483 +++++++++++------- .../suspicious/noArrayIndexKey/valid.jsx | 26 +- .../suspicious/noArrayIndexKey/valid.jsx.snap | 26 +- .../src/content/docs/internals/changelog.mdx | 2 + .../docs/linter/rules/no-array-index-key.md | 72 +++ 8 files changed, 553 insertions(+), 270 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36f69c4b874f..81c8aa0a0a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom #### Bug fixes +- Fix [#1575](https://github.com/biomejs/biome/issues/1575). [noArrayIndexKey](https://biomejs.dev/linter/rules/no-array-index-key/) now captures array index value inside template literals and with string concatination. Contributed by @vasucp1207 + ### Parser diff --git a/crates/biome_js_analyze/src/semantic_analyzers/suspicious/no_array_index_key.rs b/crates/biome_js_analyze/src/semantic_analyzers/suspicious/no_array_index_key.rs index e351ac233a09..94f1efa43636 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/suspicious/no_array_index_key.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/suspicious/no_array_index_key.rs @@ -4,9 +4,10 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; use biome_js_syntax::{ - AnyJsFunction, AnyJsMemberExpression, JsCallArgumentList, JsCallArguments, JsCallExpression, - JsFormalParameter, JsObjectExpression, JsObjectMemberList, JsParameterList, JsParameters, - JsPropertyObjectMember, JsReferenceIdentifier, JsxAttribute, + AnyJsExpression, AnyJsFunction, AnyJsMemberExpression, AnyJsTemplateElement, + JsBinaryExpression, JsCallArgumentList, JsCallArguments, JsCallExpression, JsFormalParameter, + JsObjectExpression, JsObjectMemberList, JsParameterList, JsParameters, JsPropertyObjectMember, + JsReferenceIdentifier, JsxAttribute, }; use biome_rowan::{declare_node_union, AstNode, TextRange}; @@ -36,6 +37,32 @@ declare_rule! { /// React.cloneElement(child, { key: index }) /// )) /// ``` + /// + /// ```jsx,expect_diagnostic + /// something.forEach((Element, index) => { + /// foo + /// }); + /// ``` + /// + /// ```jsx,expect_diagnostic + /// something.forEach((Element, index) => { + /// foo + /// }); + /// ``` + /// + /// ### Valid + /// ```jsx + /// something.forEach((item) => { + /// foo + /// }); + /// ``` + /// + /// ```jsx + /// something.forEach((item) => { + /// foo + /// }); + /// ``` + /// pub(crate) NoArrayIndexKey { version: "1.0.0", name: "noArrayIndexKey", @@ -71,7 +98,7 @@ impl NoArrayIndexKeyQuery { } /// Extracts the reference from the possible invalid prop - fn as_js_reference_identifier(&self) -> Option { + fn as_js_expression(&self) -> Option { match self { NoArrayIndexKeyQuery::JsxAttribute(attribute) => attribute .initializer()? @@ -79,16 +106,10 @@ impl NoArrayIndexKeyQuery { .ok()? .as_jsx_expression_attribute_value()? .expression() - .ok()? - .as_js_identifier_expression()? - .name() - .ok(), - NoArrayIndexKeyQuery::JsPropertyObjectMember(object_member) => object_member - .value() - .ok()? - .as_js_identifier_expression()? - .name() .ok(), + NoArrayIndexKeyQuery::JsPropertyObjectMember(object_member) => { + object_member.value().ok() + } } } } @@ -114,7 +135,35 @@ impl Rule for NoArrayIndexKey { } let model = ctx.model(); - let reference = node.as_js_reference_identifier()?; + let reference = node.as_js_expression()?; + + let mut capture_array_index = None; + + match reference { + AnyJsExpression::JsIdentifierExpression(identifier_expression) => { + capture_array_index = Some(identifier_expression.name().ok()?); + } + AnyJsExpression::JsTemplateExpression(template_expression) => { + let template_elements = template_expression.elements(); + for element in template_elements { + if let AnyJsTemplateElement::JsTemplateElement(template_element) = element { + let cap_index_value = template_element + .expression() + .ok()? + .as_js_identifier_expression()? + .name() + .ok(); + capture_array_index = cap_index_value; + } + } + } + AnyJsExpression::JsBinaryExpression(binary_expression) => { + let _ = cap_array_index_value(&binary_expression, &mut capture_array_index); + } + _ => {} + }; + + let reference = capture_array_index?; // Given the reference identifier retrieved from the key property, // find the declaration and ensure it resolves to the parameter of a function, @@ -227,3 +276,30 @@ fn is_array_method_index( None } } + +fn cap_array_index_value( + binary_expression: &JsBinaryExpression, + capture_array_index: &mut Option, +) -> Option<()> { + let left = binary_expression.left().ok()?; + let right = binary_expression.right().ok()?; + + // recursive call if left or right again are binary_expressions + if let Some(left_binary) = left.as_js_binary_expression() { + cap_array_index_value(left_binary, capture_array_index); + }; + + if let Some(right_binary) = right.as_js_binary_expression() { + cap_array_index_value(right_binary, capture_array_index); + }; + + if let Some(left_expression) = left.as_js_identifier_expression() { + *capture_array_index = left_expression.name().ok(); + }; + + if let Some(right_expression) = right.as_js_identifier_expression() { + *capture_array_index = right_expression.name().ok(); + }; + + Some(()) +} diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx index 242d3458dd09..96d22b559a9b 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx +++ b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx @@ -1,72 +1,72 @@ import { Children, cloneElement } from "react"; something.forEach((Element, index) => { - foo; + foo; }); something.forEach((element, index, array) => { - foo; + foo; }); things.filter((thing, index) => { - otherThings.push(foo); + otherThings.push(foo); }); something.forEach((Element, index) => { - ; + ; }); something.forEach((element, index, array) => { - ; + ; }); things.filter((thing, index) => { - otherThings.push(); + otherThings.push(); }); things.reduce( - (collection, thing, index) => collection.concat(), - [] + (collection, thing, index) => collection.concat(), + [] ); React.Children.map(this.props.children, (child, index) => - React.cloneElement(child, { key: index }) + React.cloneElement(child, { key: index }) ); React.Children.forEach(this.props.children, function (child, index) { - return React.cloneElement(child, { key: index }); + return React.cloneElement(child, { key: index }); }); Children.map(this.props.children, (child, index) => - cloneElement(child, { key: index }) + cloneElement(child, { key: index }) ); Children.forEach(this.props.children, function (child, index) { - return cloneElement(child, { key: index }); + return cloneElement(child, { key: index }); }); Children.forEach(this.props.children, function (child, index) { - const foo = cloneElement(child, { key: index }); - return foo; + const foo = cloneElement(child, { key: index }); + return foo; }); function Test(props) { - return Children.map(props.children, function (child, index) { - return cloneElement(child, { key: index }); - }); + return Children.map(props.children, function (child, index) { + return cloneElement(child, { key: index }); + }); } things.map((thing, index) => React.cloneElement(thing, { key: index })); things.flatMap((thing, index) => { - return ; + return ; }); Array.from(things, (thing, index) => { - return ; + return ; }); const mapping = { - foo: () => things.map((_, index) => ), + foo: () => things.map((_, index) => ), }; class A extends React.Component { - renderThings = () => things.map((_, index) => ); + renderThings = () => things.map((_, index) => ); } const Component1 = () => things.map((_, index) => ); @@ -74,37 +74,61 @@ const Component1 = () => things.map((_, index) => ); const Component2 = () => things.map((_, index) => ); function Component3() { - return things.map((_, index) => ); + return things.map((_, index) => ); } function Component4() { - let elements = things.map((_, index) => ); - if (condition) { - elements = others.map((_, index) => ); - } - return elements; + let elements = things.map((_, index) => ); + if (condition) { + elements = others.map((_, index) => ); + } + return elements; } function Component5({ things }) { - const elements = useMemo( - () => things.map((_, index) => ), - [things] - ); - return elements; + const elements = useMemo( + () => things.map((_, index) => ), + [things] + ); + return elements; } function Component6({ things }) { - const elements = useMemo( - () => things.map((_, index) => ), - [things] - ); - return elements; + const elements = useMemo( + () => things.map((_, index) => ), + [things] + ); + return elements; } function Component7() { - return ( - - {({ things }) => things.map((_, index) => )} - - ); + return ( + + {({ things }) => things.map((_, index) => )} + + ); } + +function Component8() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} + +function Component9() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} + +function Component10() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx.snap index bb6f6a5b83c8..721b849d542d 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx.snap @@ -7,72 +7,72 @@ expression: invalid.jsx import { Children, cloneElement } from "react"; something.forEach((Element, index) => { - foo; + foo; }); something.forEach((element, index, array) => { - foo; + foo; }); things.filter((thing, index) => { - otherThings.push(foo); + otherThings.push(foo); }); something.forEach((Element, index) => { - ; + ; }); something.forEach((element, index, array) => { - ; + ; }); things.filter((thing, index) => { - otherThings.push(); + otherThings.push(); }); things.reduce( - (collection, thing, index) => collection.concat(), - [] + (collection, thing, index) => collection.concat(), + [] ); React.Children.map(this.props.children, (child, index) => - React.cloneElement(child, { key: index }) + React.cloneElement(child, { key: index }) ); React.Children.forEach(this.props.children, function (child, index) { - return React.cloneElement(child, { key: index }); + return React.cloneElement(child, { key: index }); }); Children.map(this.props.children, (child, index) => - cloneElement(child, { key: index }) + cloneElement(child, { key: index }) ); Children.forEach(this.props.children, function (child, index) { - return cloneElement(child, { key: index }); + return cloneElement(child, { key: index }); }); Children.forEach(this.props.children, function (child, index) { - const foo = cloneElement(child, { key: index }); - return foo; + const foo = cloneElement(child, { key: index }); + return foo; }); function Test(props) { - return Children.map(props.children, function (child, index) { - return cloneElement(child, { key: index }); - }); + return Children.map(props.children, function (child, index) { + return cloneElement(child, { key: index }); + }); } things.map((thing, index) => React.cloneElement(thing, { key: index })); things.flatMap((thing, index) => { - return ; + return ; }); Array.from(things, (thing, index) => { - return ; + return ; }); const mapping = { - foo: () => things.map((_, index) => ), + foo: () => things.map((_, index) => ), }; class A extends React.Component { - renderThings = () => things.map((_, index) => ); + renderThings = () => things.map((_, index) => ); } const Component1 = () => things.map((_, index) => ); @@ -80,52 +80,75 @@ const Component1 = () => things.map((_, index) => ); const Component2 = () => things.map((_, index) => ); function Component3() { - return things.map((_, index) => ); + return things.map((_, index) => ); } function Component4() { - let elements = things.map((_, index) => ); - if (condition) { - elements = others.map((_, index) => ); - } - return elements; + let elements = things.map((_, index) => ); + if (condition) { + elements = others.map((_, index) => ); + } + return elements; } function Component5({ things }) { - const elements = useMemo( - () => things.map((_, index) => ), - [things] - ); - return elements; + const elements = useMemo( + () => things.map((_, index) => ), + [things] + ); + return elements; } function Component6({ things }) { - const elements = useMemo( - () => things.map((_, index) => ), - [things] - ); - return elements; + const elements = useMemo( + () => things.map((_, index) => ), + [things] + ); + return elements; } function Component7() { - return ( - - {({ things }) => things.map((_, index) => )} - - ); + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} + +function Component8() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} + +function Component9() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); } +function Component10() { + return ( + + {({ things }) => things.map((_, index) => )} + + ); +} ``` # Diagnostics ``` -invalid.jsx:4:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:4:21 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 3 │ something.forEach((Element, index) => { - > 4 │ foo; - │ ^^^^^ + > 4 │ foo; + │ ^^^^^ 5 │ }); 6 │ something.forEach((element, index, array) => { @@ -135,7 +158,7 @@ invalid.jsx:4:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 2 │ > 3 │ something.forEach((Element, index) => { │ ^^^^^ - 4 │ foo; + 4 │ foo; 5 │ }); i The order of the items may change, and this also affects performances and component state. @@ -146,24 +169,24 @@ invalid.jsx:4:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:7:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:7:21 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 5 │ }); 6 │ something.forEach((element, index, array) => { - > 7 │ foo; - │ ^^^^^ + > 7 │ foo; + │ ^^^^^ 8 │ }); 9 │ things.filter((thing, index) => { i This is the source of the key value. - 4 │ foo; + 4 │ foo; 5 │ }); > 6 │ something.forEach((element, index, array) => { │ ^^^^^ - 7 │ foo; + 7 │ foo; 8 │ }); i The order of the items may change, and this also affects performances and component state. @@ -174,24 +197,24 @@ invalid.jsx:7:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:10:31 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:10:34 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 8 │ }); 9 │ things.filter((thing, index) => { - > 10 │ otherThings.push(foo); - │ ^^^^^ + > 10 │ otherThings.push(foo); + │ ^^^^^ 11 │ }); 12 │ i This is the source of the key value. - 7 │ foo; + 7 │ foo; 8 │ }); > 9 │ things.filter((thing, index) => { │ ^^^^^ - 10 │ otherThings.push(foo); + 10 │ otherThings.push(foo); 11 │ }); i The order of the items may change, and this also affects performances and component state. @@ -202,13 +225,13 @@ invalid.jsx:10:31 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:14:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:14:21 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 13 │ something.forEach((Element, index) => { - > 14 │ ; - │ ^^^^^ + > 14 │ ; + │ ^^^^^ 15 │ }); 16 │ something.forEach((element, index, array) => { @@ -218,7 +241,7 @@ invalid.jsx:14:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 12 │ > 13 │ something.forEach((Element, index) => { │ ^^^^^ - 14 │ ; + 14 │ ; 15 │ }); i The order of the items may change, and this also affects performances and component state. @@ -229,24 +252,24 @@ invalid.jsx:14:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:17:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:17:21 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 15 │ }); 16 │ something.forEach((element, index, array) => { - > 17 │ ; - │ ^^^^^ + > 17 │ ; + │ ^^^^^ 18 │ }); 19 │ things.filter((thing, index) => { i This is the source of the key value. - 14 │ ; + 14 │ ; 15 │ }); > 16 │ something.forEach((element, index, array) => { │ ^^^^^ - 17 │ ; + 17 │ ; 18 │ }); i The order of the items may change, and this also affects performances and component state. @@ -257,24 +280,24 @@ invalid.jsx:17:18 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:20:31 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:20:34 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 18 │ }); 19 │ things.filter((thing, index) => { - > 20 │ otherThings.push(); - │ ^^^^^ + > 20 │ otherThings.push(); + │ ^^^^^ 21 │ }); 22 │ things.reduce( i This is the source of the key value. - 17 │ ; + 17 │ ; 18 │ }); > 19 │ things.filter((thing, index) => { │ ^^^^^ - 20 │ otherThings.push(); + 20 │ otherThings.push(); 21 │ }); i The order of the items may change, and this also affects performances and component state. @@ -285,24 +308,24 @@ invalid.jsx:20:31 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:23:62 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:23:65 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 21 │ }); 22 │ things.reduce( - > 23 │ (collection, thing, index) => collection.concat(), - │ ^^^^^ - 24 │ [] + > 23 │ (collection, thing, index) => collection.concat(), + │ ^^^^^ + 24 │ [] 25 │ ); i This is the source of the key value. 21 │ }); 22 │ things.reduce( - > 23 │ (collection, thing, index) => collection.concat(), - │ ^^^^^ - 24 │ [] + > 23 │ (collection, thing, index) => collection.concat(), + │ ^^^^^ + 24 │ [] 25 │ ); i The order of the items may change, and this also affects performances and component state. @@ -313,13 +336,13 @@ invalid.jsx:23:62 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:28:35 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:28:38 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 27 │ React.Children.map(this.props.children, (child, index) => - > 28 │ React.cloneElement(child, { key: index }) - │ ^^^^^ + > 28 │ React.cloneElement(child, { key: index }) + │ ^^^^^ 29 │ ); 30 │ @@ -329,7 +352,7 @@ invalid.jsx:28:35 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 26 │ > 27 │ React.Children.map(this.props.children, (child, index) => │ ^^^^^ - 28 │ React.cloneElement(child, { key: index }) + 28 │ React.cloneElement(child, { key: index }) 29 │ ); i The order of the items may change, and this also affects performances and component state. @@ -340,13 +363,13 @@ invalid.jsx:28:35 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:32:42 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:32:45 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 31 │ React.Children.forEach(this.props.children, function (child, index) { - > 32 │ return React.cloneElement(child, { key: index }); - │ ^^^^^ + > 32 │ return React.cloneElement(child, { key: index }); + │ ^^^^^ 33 │ }); 34 │ @@ -356,7 +379,7 @@ invalid.jsx:32:42 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 30 │ > 31 │ React.Children.forEach(this.props.children, function (child, index) { │ ^^^^^ - 32 │ return React.cloneElement(child, { key: index }); + 32 │ return React.cloneElement(child, { key: index }); 33 │ }); i The order of the items may change, and this also affects performances and component state. @@ -367,13 +390,13 @@ invalid.jsx:32:42 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:36:29 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:36:32 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 35 │ Children.map(this.props.children, (child, index) => - > 36 │ cloneElement(child, { key: index }) - │ ^^^^^ + > 36 │ cloneElement(child, { key: index }) + │ ^^^^^ 37 │ ); 38 │ @@ -383,7 +406,7 @@ invalid.jsx:36:29 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 34 │ > 35 │ Children.map(this.props.children, (child, index) => │ ^^^^^ - 36 │ cloneElement(child, { key: index }) + 36 │ cloneElement(child, { key: index }) 37 │ ); i The order of the items may change, and this also affects performances and component state. @@ -394,13 +417,13 @@ invalid.jsx:36:29 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:40:36 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:40:39 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 39 │ Children.forEach(this.props.children, function (child, index) { - > 40 │ return cloneElement(child, { key: index }); - │ ^^^^^ + > 40 │ return cloneElement(child, { key: index }); + │ ^^^^^ 41 │ }); 42 │ @@ -410,7 +433,7 @@ invalid.jsx:40:36 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 38 │ > 39 │ Children.forEach(this.props.children, function (child, index) { │ ^^^^^ - 40 │ return cloneElement(child, { key: index }); + 40 │ return cloneElement(child, { key: index }); 41 │ }); i The order of the items may change, and this also affects performances and component state. @@ -421,14 +444,14 @@ invalid.jsx:40:36 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:44:41 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:44:44 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 43 │ Children.forEach(this.props.children, function (child, index) { - > 44 │ const foo = cloneElement(child, { key: index }); - │ ^^^^^ - 45 │ return foo; + > 44 │ const foo = cloneElement(child, { key: index }); + │ ^^^^^ + 45 │ return foo; 46 │ }); i This is the source of the key value. @@ -437,8 +460,8 @@ invalid.jsx:44:41 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 42 │ > 43 │ Children.forEach(this.props.children, function (child, index) { │ ^^^^^ - 44 │ const foo = cloneElement(child, { key: index }); - 45 │ return foo; + 44 │ const foo = cloneElement(child, { key: index }); + 45 │ return foo; i The order of the items may change, and this also affects performances and component state. @@ -448,24 +471,24 @@ invalid.jsx:44:41 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:50:37 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:50:43 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 48 │ function Test(props) { - 49 │ return Children.map(props.children, function (child, index) { - > 50 │ return cloneElement(child, { key: index }); - │ ^^^^^ - 51 │ }); + 49 │ return Children.map(props.children, function (child, index) { + > 50 │ return cloneElement(child, { key: index }); + │ ^^^^^ + 51 │ }); 52 │ } i This is the source of the key value. 48 │ function Test(props) { - > 49 │ return Children.map(props.children, function (child, index) { - │ ^^^^^ - 50 │ return cloneElement(child, { key: index }); - 51 │ }); + > 49 │ return Children.map(props.children, function (child, index) { + │ ^^^^^ + 50 │ return cloneElement(child, { key: index }); + 51 │ }); i The order of the items may change, and this also affects performances and component state. @@ -503,13 +526,13 @@ invalid.jsx:54:63 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:57:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:57:28 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 56 │ things.flatMap((thing, index) => { - > 57 │ return ; - │ ^^^^^ + > 57 │ return ; + │ ^^^^^ 58 │ }); 59 │ @@ -519,7 +542,7 @@ invalid.jsx:57:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 55 │ > 56 │ things.flatMap((thing, index) => { │ ^^^^^ - 57 │ return ; + 57 │ return ; 58 │ }); i The order of the items may change, and this also affects performances and component state. @@ -530,13 +553,13 @@ invalid.jsx:57:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:61:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:61:28 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 60 │ Array.from(things, (thing, index) => { - > 61 │ return ; - │ ^^^^^ + > 61 │ return ; + │ ^^^^^ 62 │ }); 63 │ @@ -546,7 +569,7 @@ invalid.jsx:61:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ 59 │ > 60 │ Array.from(things, (thing, index) => { │ ^^^^^ - 61 │ return ; + 61 │ return ; 62 │ }); i The order of the items may change, and this also affects performances and component state. @@ -557,21 +580,21 @@ invalid.jsx:61:25 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:65:54 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:65:57 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 64 │ const mapping = { - > 65 │ foo: () => things.map((_, index) => ), - │ ^^^^^ + > 65 │ foo: () => things.map((_, index) => ), + │ ^^^^^ 66 │ }; 67 │ i This is the source of the key value. 64 │ const mapping = { - > 65 │ foo: () => things.map((_, index) => ), - │ ^^^^^ + > 65 │ foo: () => things.map((_, index) => ), + │ ^^^^^ 66 │ }; 67 │ @@ -583,21 +606,21 @@ invalid.jsx:65:54 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:69:64 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:69:67 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 68 │ class A extends React.Component { - > 69 │ renderThings = () => things.map((_, index) => ); - │ ^^^^^ + > 69 │ renderThings = () => things.map((_, index) => ); + │ ^^^^^ 70 │ } 71 │ i This is the source of the key value. 68 │ class A extends React.Component { - > 69 │ renderThings = () => things.map((_, index) => ); - │ ^^^^^ + > 69 │ renderThings = () => things.map((_, index) => ); + │ ^^^^^ 70 │ } 71 │ @@ -665,21 +688,21 @@ invalid.jsx:74:67 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:77:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:77:53 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 76 │ function Component3() { - > 77 │ return things.map((_, index) => ); - │ ^^^^^ + > 77 │ return things.map((_, index) => ); + │ ^^^^^ 78 │ } 79 │ i This is the source of the key value. 76 │ function Component3() { - > 77 │ return things.map((_, index) => ); - │ ^^^^^ + > 77 │ return things.map((_, index) => ); + │ ^^^^^ 78 │ } 79 │ @@ -691,23 +714,23 @@ invalid.jsx:77:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:81:58 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:81:61 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 80 │ function Component4() { - > 81 │ let elements = things.map((_, index) => ); - │ ^^^^^ - 82 │ if (condition) { - 83 │ elements = others.map((_, index) => ); + > 81 │ let elements = things.map((_, index) => ); + │ ^^^^^ + 82 │ if (condition) { + 83 │ elements = others.map((_, index) => ); i This is the source of the key value. 80 │ function Component4() { - > 81 │ let elements = things.map((_, index) => ); - │ ^^^^^ - 82 │ if (condition) { - 83 │ elements = others.map((_, index) => ); + > 81 │ let elements = things.map((_, index) => ); + │ ^^^^^ + 82 │ if (condition) { + 83 │ elements = others.map((_, index) => ); i The order of the items may change, and this also affects performances and component state. @@ -717,25 +740,25 @@ invalid.jsx:81:58 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:83:55 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:83:61 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 81 │ let elements = things.map((_, index) => ); - 82 │ if (condition) { - > 83 │ elements = others.map((_, index) => ); - │ ^^^^^ - 84 │ } - 85 │ return elements; + 81 │ let elements = things.map((_, index) => ); + 82 │ if (condition) { + > 83 │ elements = others.map((_, index) => ); + │ ^^^^^ + 84 │ } + 85 │ return elements; i This is the source of the key value. - 81 │ let elements = things.map((_, index) => ); - 82 │ if (condition) { - > 83 │ elements = others.map((_, index) => ); - │ ^^^^^ - 84 │ } - 85 │ return elements; + 81 │ let elements = things.map((_, index) => ); + 82 │ if (condition) { + > 83 │ elements = others.map((_, index) => ); + │ ^^^^^ + 84 │ } + 85 │ return elements; i The order of the items may change, and this also affects performances and component state. @@ -745,25 +768,25 @@ invalid.jsx:83:55 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:90:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:90:56 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 88 │ function Component5({ things }) { - 89 │ const elements = useMemo( - > 90 │ () => things.map((_, index) => ), - │ ^^^^^ - 91 │ [things] - 92 │ ); + 89 │ const elements = useMemo( + > 90 │ () => things.map((_, index) => ), + │ ^^^^^ + 91 │ [things] + 92 │ ); i This is the source of the key value. 88 │ function Component5({ things }) { - 89 │ const elements = useMemo( - > 90 │ () => things.map((_, index) => ), - │ ^^^^^ - 91 │ [things] - 92 │ ); + 89 │ const elements = useMemo( + > 90 │ () => things.map((_, index) => ), + │ ^^^^^ + 91 │ [things] + 92 │ ); i The order of the items may change, and this also affects performances and component state. @@ -773,25 +796,109 @@ invalid.jsx:90:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:98:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:98:56 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. 96 │ function Component6({ things }) { - 97 │ const elements = useMemo( - > 98 │ () => things.map((_, index) => ), - │ ^^^^^ - 99 │ [things] - 100 │ ); + 97 │ const elements = useMemo( + > 98 │ () => things.map((_, index) => ), + │ ^^^^^ + 99 │ [things] + 100 │ ); i This is the source of the key value. 96 │ function Component6({ things }) { - 97 │ const elements = useMemo( - > 98 │ () => things.map((_, index) => ), - │ ^^^^^ - 99 │ [things] - 100 │ ); + 97 │ const elements = useMemo( + > 98 │ () => things.map((_, index) => ), + │ ^^^^^ + 99 │ [things] + 100 │ ); + + i The order of the items may change, and this also affects performances and component state. + + i Check the React documentation. + + +``` + +``` +invalid.jsx:107:71 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Avoid using the index of an array as key property in an element. + + 105 │ return ( + 106 │ + > 107 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 108 │ + 109 │ ); + + i This is the source of the key value. + + 105 │ return ( + 106 │ + > 107 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 108 │ + 109 │ ); + + i The order of the items may change, and this also affects performances and component state. + + i Check the React documentation. + + +``` + +``` +invalid.jsx:115:83 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Avoid using the index of an array as key property in an element. + + 113 │ return ( + 114 │ + > 115 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 116 │ + 117 │ ); + + i This is the source of the key value. + + 113 │ return ( + 114 │ + > 115 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 116 │ + 117 │ ); + + i The order of the items may change, and this also affects performances and component state. + + i Check the React documentation. + + +``` + +``` +invalid.jsx:123:80 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Avoid using the index of an array as key property in an element. + + 121 │ return ( + 122 │ + > 123 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 124 │ + 125 │ ); + + i This is the source of the key value. + + 121 │ return ( + 122 │ + > 123 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 124 │ + 125 │ ); i The order of the items may change, and this also affects performances and component state. @@ -801,25 +908,25 @@ invalid.jsx:98:50 lint/suspicious/noArrayIndexKey ━━━━━━━━━━ ``` ``` -invalid.jsx:107:62 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:131:80 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 105 │ return ( - 106 │ - > 107 │ {({ things }) => things.map((_, index) => )} - │ ^^^^^ - 108 │ - 109 │ ); + 129 │ return ( + 130 │ + > 131 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 132 │ + 133 │ ); i This is the source of the key value. - 105 │ return ( - 106 │ - > 107 │ {({ things }) => things.map((_, index) => )} - │ ^^^^^ - 108 │ - 109 │ ); + 129 │ return ( + 130 │ + > 131 │ {({ things }) => things.map((_, index) => )} + │ ^^^^^ + 132 │ + 133 │ ); i The order of the items may change, and this also affects performances and component state. diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx index 25cec256947d..421a19befb3c 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx +++ b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx @@ -1,52 +1,52 @@ import { Children, cloneElement } from "react"; something.forEach((element, index) => { - foo + foo }); -something.forEach((element, index) => { - +something.forEach((children, index) => { + }); const mapping = { foo: () => ( - things.map((_, index) => ) + things.map((item) => ) ), } class A extends React.Component { renderThings = () => ( - things.map((_, index) => ) + things.map((item) => ) ) } -const Component8 = () => things.map((_, index) => ); +const Component8 = () => things.map((item, index) => ); const Component9 = () => ( - things.map((_, index) => ) + things.map((item, index) => ) ); function Component10() { - return things.map((_, index) => ); + return things.map((item, index) => ); } function Component11() { - let elements = things.map((_, index) => ); + let elements = things.map((item, index) => ); if (condition) { - elements = others.map((_, index) => ); + elements = others.map((item, index) => ); } return elements; } function Component12({things}) { - const elements = useMemo(() => things.map((_, index) => ), [things]); + const elements = useMemo(() => things.map((item, index) => ), [things]); return elements; } function Component13({things}) { const elements = useMemo(() => ( - things.map((_, index) => ) + things.map((item, index) => ) ), [things]); return elements; } @@ -55,7 +55,7 @@ function Component14() { return ( {({things}) => ( - things.map((_, index) => ) + things.map((item, index) => ) )} ) diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx.snap b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx.snap index f2499b371dc3..76a908abad4d 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/valid.jsx.snap @@ -7,52 +7,52 @@ expression: valid.jsx import { Children, cloneElement } from "react"; something.forEach((element, index) => { - foo + foo }); -something.forEach((element, index) => { - +something.forEach((children, index) => { + }); const mapping = { foo: () => ( - things.map((_, index) => ) + things.map((item) => ) ), } class A extends React.Component { renderThings = () => ( - things.map((_, index) => ) + things.map((item) => ) ) } -const Component8 = () => things.map((_, index) => ); +const Component8 = () => things.map((item, index) => ); const Component9 = () => ( - things.map((_, index) => ) + things.map((item, index) => ) ); function Component10() { - return things.map((_, index) => ); + return things.map((item, index) => ); } function Component11() { - let elements = things.map((_, index) => ); + let elements = things.map((item, index) => ); if (condition) { - elements = others.map((_, index) => ); + elements = others.map((item, index) => ); } return elements; } function Component12({things}) { - const elements = useMemo(() => things.map((_, index) => ), [things]); + const elements = useMemo(() => things.map((item, index) => ), [things]); return elements; } function Component13({things}) { const elements = useMemo(() => ( - things.map((_, index) => ) + things.map((item, index) => ) ), [things]); return elements; } @@ -61,7 +61,7 @@ function Component14() { return ( {({things}) => ( - things.map((_, index) => ) + things.map((item, index) => ) )} ) diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index d787f8e9baac..f894a5ad3fbb 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -40,6 +40,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom #### Bug fixes +- Fix [#1575](https://github.com/biomejs/biome/issues/1575). [noArrayIndexKey](https://biomejs.dev/linter/rules/no-array-index-key/) now captures array index value inside template literals and with string concatination. Contributed by @vasucp1207 + ### Parser diff --git a/website/src/content/docs/linter/rules/no-array-index-key.md b/website/src/content/docs/linter/rules/no-array-index-key.md index ae2954a416e2..001fc296570d 100644 --- a/website/src/content/docs/linter/rules/no-array-index-key.md +++ b/website/src/content/docs/linter/rules/no-array-index-key.md @@ -83,6 +83,78 @@ React.Children.map(this.props.children, (child, index) => ( +```jsx +something.forEach((Element, index) => { + foo +}); +``` + +
suspicious/noArrayIndexKey.js:2:33 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Avoid using the index of an array as key property in an element.
+  
+    1 │ something.forEach((Element, index) => {
+  > 2 │     <Component key={`test-key-${index}`} >foo</Component>
+                                   ^^^^^
+    3 │ });
+    4 │ 
+  
+   This is the source of the key value.
+  
+  > 1 │ something.forEach((Element, index) => {
+                               ^^^^^
+    2 │     <Component key={`test-key-${index}`} >foo</Component>
+    3 │ });
+  
+   The order of the items may change, and this also affects performances and component state.
+  
+   Check the React documentation. 
+  
+
+ +```jsx +something.forEach((Element, index) => { + foo +}); +``` + +
suspicious/noArrayIndexKey.js:2:30 lint/suspicious/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Avoid using the index of an array as key property in an element.
+  
+    1 │ something.forEach((Element, index) => {
+  > 2 │     <Component key={"test" + index} >foo</Component>
+                                ^^^^^
+    3 │ });
+    4 │ 
+  
+   This is the source of the key value.
+  
+  > 1 │ something.forEach((Element, index) => {
+                               ^^^^^
+    2 │     <Component key={"test" + index} >foo</Component>
+    3 │ });
+  
+   The order of the items may change, and this also affects performances and component state.
+  
+   Check the React documentation. 
+  
+
+ +### Valid + +```jsx +something.forEach((item) => { + foo +}); +``` + +```jsx +something.forEach((item) => { + foo +}); +``` + ## Related links - [Disable a rule](/linter/#disable-a-lint-rule)