diff --git a/packages/jsts/src/linter/quickfixes/rules.ts b/packages/jsts/src/linter/quickfixes/rules.ts index e4f5a253178..0bffa86f872 100644 --- a/packages/jsts/src/linter/quickfixes/rules.ts +++ b/packages/jsts/src/linter/quickfixes/rules.ts @@ -233,6 +233,7 @@ export const quickFixRules = new Set([ 'S1128', // eslint-plugin-import - 'S6859', 'S3863', + 'S6859', + 'S7060', ]); diff --git a/packages/jsts/src/rules/README.md b/packages/jsts/src/rules/README.md index c25032a5104..2fc80482564 100644 --- a/packages/jsts/src/rules/README.md +++ b/packages/jsts/src/rules/README.md @@ -215,6 +215,7 @@ SonarJS rules for ESLint to help developers produce [Clean Code](https://www.son | [no-same-argument-assert](https://sonarsource.github.io/rspec/#/rspec/S5863/javascript) | Assertions should not be given twice the same argument | ✅ | | | | | | | [no-same-line-conditional](https://sonarsource.github.io/rspec/#/rspec/S3972/javascript) | Conditionals should start on new lines | ✅ | | 🔧 | 💡 | | | | [no-self-compare](https://sonarsource.github.io/rspec/#/rspec/S6679/javascript) | "Number.isNaN()" should be used to check for "NaN" value | ✅ | | 🔧 | 💡 | | | +| [no-self-import](https://sonarsource.github.io/rspec/#/rspec/S7060/javascript) | Module should not import itself | ✅ | | | 💡 | | | | [no-small-switch](https://sonarsource.github.io/rspec/#/rspec/S1301/javascript) | "switch" statements should have at least 3 "case" clauses | ✅ | | | | | | | [no-tab](https://sonarsource.github.io/rspec/#/rspec/S105/javascript) | Tabulation characters should not be used | | ✅ | | | | ❌ | | [no-table-as-layout](https://sonarsource.github.io/rspec/#/rspec/S5257/javascript) | HTML "<table>" should not be used for layout purposes | ✅ | | | | | | diff --git a/packages/jsts/src/rules/S7060/cb.fixture.js b/packages/jsts/src/rules/S7060/cb.fixture.js new file mode 100644 index 00000000000..40c27ee173a --- /dev/null +++ b/packages/jsts/src/rules/S7060/cb.fixture.js @@ -0,0 +1,12 @@ + +import f from './cb.fixture.js'; // Noncompliant [[qf1]] +// fix@qf1 {{Remove this import}} +// edit@qf1 [[sc=0;ec=32]] {{}} + +import f from './cb.fixture'; // Noncompliant + +const f = require('./cb.fixture.js'); // Noncompliant [[qf2]] +// fix@qf2 {{Remove this require}} +// edit@qf2 [[sc=0;ec=37]] {{}} + + diff --git a/packages/jsts/src/rules/S7060/cb.test.ts b/packages/jsts/src/rules/S7060/cb.test.ts new file mode 100644 index 00000000000..61df06da91a --- /dev/null +++ b/packages/jsts/src/rules/S7060/cb.test.ts @@ -0,0 +1,28 @@ +/* + * 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 { check } from '../../../tests/tools'; +import { rule } from './'; +import path from 'path'; + +const sonarId = path.basename(__dirname); + +describe('Rule S7060', () => { + check(sonarId, rule, __dirname); +}); diff --git a/packages/jsts/src/rules/S7060/decorator.ts b/packages/jsts/src/rules/S7060/decorator.ts new file mode 100644 index 00000000000..448f7c631fc --- /dev/null +++ b/packages/jsts/src/rules/S7060/decorator.ts @@ -0,0 +1,77 @@ +/* + * 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/S7060/javascript + +import { Rule } from 'eslint'; +import { generateMeta, interceptReport } from '../helpers'; +import { meta } from './meta'; +import * as estree from 'estree'; + +export function decorate(rule: Rule.RuleModule): Rule.RuleModule { + return interceptReport( + { + ...rule, + meta: generateMeta(meta as Rule.RuleMetaData, { + ...rule.meta!, + hasSuggestions: true, + }), + }, + reportWithQuickFix, + ); +} + +function reportWithQuickFix(context: Rule.RuleContext, reportDescriptor: Rule.ReportDescriptor) { + if (!('node' in reportDescriptor)) { + return; + } + const { node } = reportDescriptor; + const suggest: Rule.SuggestionReportDescriptor[] = []; + if (node.type === 'ImportDeclaration') { + suggest.push({ + desc: 'Remove this import', + fix: fixer => fixer.remove(node), + }); + } else if (node.type === 'CallExpression') { + const variableDecl = findRequireVariableDeclaration(node); + if (variableDecl) { + suggest.push({ + desc: 'Remove this require', + fix: fixer => fixer.remove(variableDecl), + }); + } + } + context.report({ + ...reportDescriptor, + suggest, + }); +} + +function findRequireVariableDeclaration(node: estree.Node) { + const parent = getParent(getParent(node)); + if (parent.type === 'VariableDeclaration') { + return parent; + } + return undefined; +} + +function getParent(node: estree.Node): estree.Node { + // @ts-ignore + return node.parent as estree.Node; +} diff --git a/packages/jsts/src/rules/S7060/index.ts b/packages/jsts/src/rules/S7060/index.ts new file mode 100644 index 00000000000..48f5cc9f384 --- /dev/null +++ b/packages/jsts/src/rules/S7060/index.ts @@ -0,0 +1,24 @@ +/* + * 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 { rules as importRules } from 'eslint-plugin-import'; +import { decorate } from './decorator'; + +export const rule = decorate(importRules['no-self-import']); diff --git a/packages/jsts/src/rules/S7060/meta.ts b/packages/jsts/src/rules/S7060/meta.ts new file mode 100644 index 00000000000..5732e80f83e --- /dev/null +++ b/packages/jsts/src/rules/S7060/meta.ts @@ -0,0 +1,34 @@ +/* + * 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. + */ + +// DO NOT EDIT! This file is autogenerated by "npm run generate-meta" + +export const meta = { + type: 'suggestion', + docs: { + description: 'Module should not import itself', + recommended: true, + url: 'https://sonarsource.github.io/rspec/#/rspec/S7060/javascript', + requiresTypeChecking: false, + }, + fixable: 'code', +}; + +export const sonarKey = 'S7060'; diff --git a/packages/jsts/src/rules/index.ts b/packages/jsts/src/rules/index.ts index 4746338d2ba..7d0ea8605e6 100644 --- a/packages/jsts/src/rules/index.ts +++ b/packages/jsts/src/rules/index.ts @@ -220,6 +220,7 @@ import { rule as S4324 } from './S4324'; // no-return-type-any import { rule as S5863 } from './S5863'; // no-same-argument-assert import { rule as S3972 } from './S3972'; // no-same-line-conditional import { rule as S6679 } from './S6679'; // no-self-compare +import { rule as S7060 } from './S7060'; // no-self-import import { rule as S1607 } from './S1607'; // no-skipped-tests import { rule as S1301 } from './S1301'; // no-small-switch import { rule as S105 } from './S105'; // no-tab @@ -785,6 +786,7 @@ const bridgeRules: { [key: string]: Rule.RuleModule } = { S6958, S6959, S7059, + S7060, S878: eslintRules['no-sequences'], S881, S888, diff --git a/packages/jsts/src/rules/plugin.ts b/packages/jsts/src/rules/plugin.ts index b203597295f..e5865e0d2f1 100644 --- a/packages/jsts/src/rules/plugin.ts +++ b/packages/jsts/src/rules/plugin.ts @@ -350,6 +350,7 @@ import { rule as S2755 } from './S2755'; import { rule as S4817 } from './S4817'; import { rule as S6627 } from './S6627'; import { rule as S1607 } from './S1607'; +import { rule as S7060 } from './S7060'; import type { Rule, Linter } from 'eslint'; import { rule as S7059 } from './S7059'; @@ -555,6 +556,7 @@ export const rules: Record = { 'no-same-argument-assert': S5863, 'no-same-line-conditional': S3972, 'no-self-compare': S6679, + 'no-self-import': S7060, 'no-skipped-test': S1607, 'no-small-switch': S1301, 'no-tab': S105, diff --git a/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/CheckList.java b/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/CheckList.java index 429d530a234..5f992c37cb9 100644 --- a/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/CheckList.java +++ b/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/CheckList.java @@ -326,6 +326,7 @@ public static List> getAllChecks() { NoReturnTypeAnyCheck.class, NoSameArgumentAssertCheck.class, NoSelfCompareCheck.class, + NoSelfImportCheck.class, NoSkippedTestsCheck.class, NoSparseArraysCheck.class, NoStaticElementInteractionsCheck.class, diff --git a/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/NoSelfImportCheck.java b/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/NoSelfImportCheck.java new file mode 100644 index 00000000000..c0e22c0acf7 --- /dev/null +++ b/sonar-plugin/javascript-checks/src/main/java/org/sonar/javascript/checks/NoSelfImportCheck.java @@ -0,0 +1,32 @@ +/* + * 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. + */ +package org.sonar.javascript.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.javascript.api.Check; +import org.sonar.plugins.javascript.api.JavaScriptRule; +import org.sonar.plugins.javascript.api.TypeScriptRule; + +@TypeScriptRule +@JavaScriptRule +@Rule(key = "S7060") +public class NoSelfImportCheck extends Check { + +} diff --git a/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.html b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.html new file mode 100644 index 00000000000..a5ca21369a4 --- /dev/null +++ b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.html @@ -0,0 +1,24 @@ +

Why is this an issue?

+

When a module imports itself it has no effect. This means that the import statement does nothing useful and serves no purpose. This can happen +during refactoring or when a developer mistakenly imports the module itself.

+

To fix the problem remove the self-import statement.

+
+// file: foo.js
+import foo from './foo'; // Noncompliant
+
+const foo = require('./foo'); // Noncompliant
+
+
+// file: index.js
+import index from '.'; // Noncompliant
+
+const index = require('.'); // Noncompliant
+
+

Resources

+

Documentation

+ + diff --git a/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.json b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.json new file mode 100644 index 00000000000..3c5ca88b12b --- /dev/null +++ b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/S7060.json @@ -0,0 +1,26 @@ +{ + "title": "Module should not import itself", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-7060", + "sqKey": "S7060", + "scope": "All", + "quickfix": "covered", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM", + "RELIABILITY": "MEDIUM" + }, + "attribute": "MODULAR" + }, + "compatibleLanguages": [ + "JAVASCRIPT", + "TYPESCRIPT" + ] +} diff --git a/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/Sonar_way_profile.json b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/Sonar_way_profile.json index d945f07e474..804c32a319f 100644 --- a/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/Sonar_way_profile.json +++ b/sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/Sonar_way_profile.json @@ -338,6 +338,7 @@ "S6957", "S6958", "S6959", - "S7059" + "S7059", + "S7060" ] }