From 45cb63e16546e37d76ac4778510233b9c9e6d266 Mon Sep 17 00:00:00 2001 From: Eric MORAND Date: Mon, 5 Feb 2024 15:39:16 +0100 Subject: [PATCH 1/4] Fix FP S4679 (`no-array-index-key`) - Allow keys combining index and other values --- packages/jsts/src/rules/S6479/index.ts | 20 ++++ packages/jsts/src/rules/S6479/rule.ts | 48 +++++++++ packages/jsts/src/rules/S6479/unit.test.ts | 109 +++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 packages/jsts/src/rules/S6479/index.ts create mode 100644 packages/jsts/src/rules/S6479/rule.ts create mode 100644 packages/jsts/src/rules/S6479/unit.test.ts diff --git a/packages/jsts/src/rules/S6479/index.ts b/packages/jsts/src/rules/S6479/index.ts new file mode 100644 index 00000000000..da2121d0940 --- /dev/null +++ b/packages/jsts/src/rules/S6479/index.ts @@ -0,0 +1,20 @@ +/* + * SonarQube JavaScript Plugin + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +export { rule } from './rule'; diff --git a/packages/jsts/src/rules/S6479/rule.ts b/packages/jsts/src/rules/S6479/rule.ts new file mode 100644 index 00000000000..b34bb42e05a --- /dev/null +++ b/packages/jsts/src/rules/S6479/rule.ts @@ -0,0 +1,48 @@ +/* + * SonarQube JavaScript Plugin + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// https://sonarsource.github.io/rspec/#/rspec/S6486/javascript + +// inspired from `no-array-index` from `eslint-plugin-react`: +// https://github.com/jsx-eslint/eslint-plugin-react/blob/0a2f6b7e9df32215fcd4e3061ec69ea3f2eef793/lib/rules/no-array-index-key.js#L16 + +import { rules } from 'eslint-plugin-react'; +import { interceptReportForReact } from '../helpers'; +import { Rule } from 'eslint'; + +export const rule = interceptReportForReact( + rules['no-array-index-key'], + (context, reportDescriptor) => { + const { node } = reportDescriptor as Rule.ReportDescriptor & { + node: Rule.Node; + }; + + if (node.type === 'BinaryExpression') { + return; + } + + // we got a report from ESLint, hence one of the expressions included in the literal _is_ the array index + // we can then safely bail if there is another expression in the literal + if (node.type === 'TemplateLiteral' && node.expressions.length > 1) { + return; + } + + context.report(reportDescriptor); + }, +); diff --git a/packages/jsts/src/rules/S6479/unit.test.ts b/packages/jsts/src/rules/S6479/unit.test.ts new file mode 100644 index 00000000000..3566ae2d67d --- /dev/null +++ b/packages/jsts/src/rules/S6479/unit.test.ts @@ -0,0 +1,109 @@ +/* + * SonarQube JavaScript Plugin + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { JavaScriptRuleTester } from '../tools'; +import { rule } from './rule'; + +const ruleTester = new JavaScriptRuleTester(); + +ruleTester.run('', rule, { + valid: [ + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return
; + })}; +} +`, + }, + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return
; + })}; +} +`, + }, + { + code: ` +export const MyComponent = ({items}) => { + const renderItems = () => { + let i = 0; + + return items.map(() => { + return
; + }); + } + + return <>{renderItems()}; +} +`, + }, + { + code: ` +export const MyComponent = ({items}) => { + const computeKey = (item, index) => { + return item.id + '' + index; + } + + return <>{items.map((item, index) => { + return
; + })}; +} +`, + }, + { + code: ` +export const MyComponent = ({items}) => { + const computeKey = (index) => { + return index; + } + + return <>{items.map((_item, index) => { + return
; + })}; +} // this test should trigger the rule but it seems ESLint is missing it +`, + }, + ], + invalid: [ + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return
{item.id}
; + })}; +} +`, + errors: 1, + }, + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return
{item.id}
; + })}; +} +`, + errors: 1, + }, + ], +}); From 945f14ad7a0a3cffd915f0073e9214b17efb22ea Mon Sep 17 00:00:00 2001 From: Eric Morand <156682586+ericmorand-sonarsource@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:58:03 +0100 Subject: [PATCH 2/4] Update packages/jsts/src/rules/S6479/rule.ts Co-authored-by: Yassin Kammoun <52890329+yassin-kammoun-sonarsource@users.noreply.github.com> --- packages/jsts/src/rules/S6479/rule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jsts/src/rules/S6479/rule.ts b/packages/jsts/src/rules/S6479/rule.ts index b34bb42e05a..f74015ac641 100644 --- a/packages/jsts/src/rules/S6479/rule.ts +++ b/packages/jsts/src/rules/S6479/rule.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// https://sonarsource.github.io/rspec/#/rspec/S6486/javascript +// https://sonarsource.github.io/rspec/#/rspec/S4679/javascript // inspired from `no-array-index` from `eslint-plugin-react`: // https://github.com/jsx-eslint/eslint-plugin-react/blob/0a2f6b7e9df32215fcd4e3061ec69ea3f2eef793/lib/rules/no-array-index-key.js#L16 From 9b1eeb742f0ad8b778e6b4c477eaa35830f0e944 Mon Sep 17 00:00:00 2001 From: Eric MORAND Date: Mon, 5 Feb 2024 17:03:28 +0100 Subject: [PATCH 3/4] Add S6479 to the list of exported rules --- packages/jsts/src/rules/S6479/rule.ts | 2 +- packages/jsts/src/rules/S6479/unit.test.ts | 2 +- packages/jsts/src/rules/index.ts | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/jsts/src/rules/S6479/rule.ts b/packages/jsts/src/rules/S6479/rule.ts index f74015ac641..506760cc4db 100644 --- a/packages/jsts/src/rules/S6479/rule.ts +++ b/packages/jsts/src/rules/S6479/rule.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// https://sonarsource.github.io/rspec/#/rspec/S4679/javascript +// https://sonarsource.github.io/rspec/#/rspec/S6479/javascript // inspired from `no-array-index` from `eslint-plugin-react`: // https://github.com/jsx-eslint/eslint-plugin-react/blob/0a2f6b7e9df32215fcd4e3061ec69ea3f2eef793/lib/rules/no-array-index-key.js#L16 diff --git a/packages/jsts/src/rules/S6479/unit.test.ts b/packages/jsts/src/rules/S6479/unit.test.ts index 3566ae2d67d..f4e5db65f07 100644 --- a/packages/jsts/src/rules/S6479/unit.test.ts +++ b/packages/jsts/src/rules/S6479/unit.test.ts @@ -22,7 +22,7 @@ import { rule } from './rule'; const ruleTester = new JavaScriptRuleTester(); -ruleTester.run('', rule, { +ruleTester.run('Rule S6479 - no-array-index-key', rule, { valid: [ { code: ` diff --git a/packages/jsts/src/rules/index.ts b/packages/jsts/src/rules/index.ts index c7e9eaf9f2c..b3bfc98745f 100644 --- a/packages/jsts/src/rules/index.ts +++ b/packages/jsts/src/rules/index.ts @@ -300,6 +300,7 @@ import { rule as S2817 } from './S2817'; // web-sql-database import { rule as S5689 } from './S5689'; // x-powered-by import { rule as S2755 } from './S2755'; // xml-parser-xxe import { rule as S4817 } from './S4817'; // xpath +import { rule as S6479 } from './S6479'; // no-array-index-key /** * Maps ESLint rule keys declared in the JavaScript checks to rule implementations @@ -587,5 +588,6 @@ rules['web-sql-database'] = S2817; rules['x-powered-by'] = S5689; rules['xml-parser-xxe'] = S2755; rules['xpath'] = S4817; +rules['no-array-index-key'] = S6479; export { rules }; From fbd148783bbfeb6e6538450852ab83721dc3bc9b Mon Sep 17 00:00:00 2001 From: Eric MORAND Date: Tue, 6 Feb 2024 12:05:09 +0100 Subject: [PATCH 4/4] Update integration tests expectations --- .../test/expected/jsts/Joust/typescript-S6479.json | 3 --- .../expected/jsts/ant-design/typescript-S6479.json | 6 ------ .../expected/jsts/desktop/typescript-S6479.json | 3 --- .../test/expected/jsts/eigen/typescript-S6479.json | 6 ------ .../jsts/react-cloud-music/javascript-S6479.json | 13 +------------ .../expected/jsts/vuetify/typescript-S6479.json | 3 --- 6 files changed, 1 insertion(+), 33 deletions(-) diff --git a/its/ruling/src/test/expected/jsts/Joust/typescript-S6479.json b/its/ruling/src/test/expected/jsts/Joust/typescript-S6479.json index 06c83b50e83..61341ca7b45 100644 --- a/its/ruling/src/test/expected/jsts/Joust/typescript-S6479.json +++ b/its/ruling/src/test/expected/jsts/Joust/typescript-S6479.json @@ -1,7 +1,4 @@ { -"Joust:ts/components/EventLog.tsx": [ -108 -], "Joust:ts/components/Timeline.tsx": [ 144 ] diff --git a/its/ruling/src/test/expected/jsts/ant-design/typescript-S6479.json b/its/ruling/src/test/expected/jsts/ant-design/typescript-S6479.json index f554783cc97..d51ab41792e 100644 --- a/its/ruling/src/test/expected/jsts/ant-design/typescript-S6479.json +++ b/its/ruling/src/test/expected/jsts/ant-design/typescript-S6479.json @@ -1,7 +1,4 @@ { -"ant-design:components/badge/ScrollNumber.tsx": [ -59 -], "ant-design:components/card/Card.tsx": [ 48 ], @@ -14,9 +11,6 @@ "ant-design:components/descriptions/index.tsx": [ 182 ], -"ant-design:components/list/Item.tsx": [ -99 -], "ant-design:components/skeleton/Paragraph.tsx": [ 29 ], diff --git a/its/ruling/src/test/expected/jsts/desktop/typescript-S6479.json b/its/ruling/src/test/expected/jsts/desktop/typescript-S6479.json index de545de4b9c..416521f230c 100644 --- a/its/ruling/src/test/expected/jsts/desktop/typescript-S6479.json +++ b/its/ruling/src/test/expected/jsts/desktop/typescript-S6479.json @@ -1,7 +1,4 @@ { -"desktop:app/src/ui/changes/no-changes.tsx": [ -216 -], "desktop:app/src/ui/check-runs/ci-check-run-actions-job-step-list.tsx": [ 43 ], diff --git a/its/ruling/src/test/expected/jsts/eigen/typescript-S6479.json b/its/ruling/src/test/expected/jsts/eigen/typescript-S6479.json index 27a1bc2fdc6..1ae5a8a95ef 100644 --- a/its/ruling/src/test/expected/jsts/eigen/typescript-S6479.json +++ b/its/ruling/src/test/expected/jsts/eigen/typescript-S6479.json @@ -114,12 +114,6 @@ "eigen:src/palette/elements/Tabs/ContentTabs.tsx": [ 17 ], -"eigen:src/palette/elements/Tabs/NavigationalTabs.tsx": [ -30 -], -"eigen:src/palette/elements/Tabs/StepTabs.tsx": [ -27 -], "eigen:src/palette/elements/Tabs/Tabs.tests.tsx": [ 51 ] diff --git a/its/ruling/src/test/expected/jsts/react-cloud-music/javascript-S6479.json b/its/ruling/src/test/expected/jsts/react-cloud-music/javascript-S6479.json index d9ce9a0e61b..111a2b7b93b 100644 --- a/its/ruling/src/test/expected/jsts/react-cloud-music/javascript-S6479.json +++ b/its/ruling/src/test/expected/jsts/react-cloud-music/javascript-S6479.json @@ -1,17 +1,6 @@ { -"react-cloud-music:src/application/Player/normal-player/index.jsx": [ -236 -], "react-cloud-music:src/application/Rank/index.jsx": [ -37, -49 -], -"react-cloud-music:src/application/Search/index.jsx": [ -98, -122 -], -"react-cloud-music:src/application/Singers/index.jsx": [ -64 +37 ], "react-cloud-music:src/application/User/Login/PhoneForm/step-two/index.jsx": [ 76 diff --git a/its/ruling/src/test/expected/jsts/vuetify/typescript-S6479.json b/its/ruling/src/test/expected/jsts/vuetify/typescript-S6479.json index 94524c1a16b..cd9c22ed970 100644 --- a/its/ruling/src/test/expected/jsts/vuetify/typescript-S6479.json +++ b/its/ruling/src/test/expected/jsts/vuetify/typescript-S6479.json @@ -8,9 +8,6 @@ "vuetify:packages/vuetify/src/components/VCombobox/VCombobox.tsx": [ 408 ], -"vuetify:packages/vuetify/src/components/VMessages/VMessages.tsx": [ -66 -], "vuetify:packages/vuetify/src/components/VSelect/VSelect.tsx": [ 286 ],