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 ], 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..506760cc4db --- /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/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 + +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..f4e5db65f07 --- /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 S6479 - no-array-index-key', rule, { + valid: [ + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return
; + })}>; +} +`, + }, + { + code: ` +export const MyComponent = ({items}) => { + return <>{items.map((item, index) => { + return