Documenting and clarifying duplicate ESLint rules

After upgrading to eslint-plugin-sonarjs@2, I found a ton of new rules that duplicate existing ESLint rules, either from the base ESLint or popular plugins like typescript-eslint, eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-regexp, and eslint-plugin-unicorn.

Without knowing how exactly they differ, and not wanting to suddenly cause a bunch or lint rule name changes, invalidating many configurations and “disable comments”, I simply tested them using their documented examples to find where they overlap, and disabled them.

I would like for each of sonar’s rule duplicating an existing rule to:

  1. Be flagged as such, so a user can appropriately update configurations
  2. Explain what it changes / improves on, so that I can feel more confident that switching over to sonar’s re-implemented rules is worth over the original.
  3. Consider providing improvements upstream ?

If it’s just some special code to connect to SonarQube, maybe provide them as a separate preset? (such a preset could also disable base rules it replaces)

Here’s a non-exhaustive list (I noted 172 rules that I haven’t manually validated yet, because I haven’t seen a specific keyword in their description or haven’t hit them in code):

    /*
     * eslint-plugin-sonarjs@2 added rules that duplicate base ESLint rules or specific plugins we prefer
     */
    // Duplicates no-warning-comments
    "sonarjs/todo-tag": "off",
    // Duplicates unicorn/prefer-string-starts-ends-with
    "sonarjs/prefer-string-starts-ends-with": "off",
    // Duplicates no-nested-ternary and unicorn/no-nested-ternary
    "sonarjs/no-nested-conditional": "off",

    // Base ESLint rules duplications (same names)

    "sonarjs/default-param-last": "off",
    "sonarjs/no-delete-var": "off",
    "sonarjs/no-empty-function": "off",
    "sonarjs/no-extend-native": "off",
    "sonarjs/no-redeclare": "off",
    "sonarjs/no-unreachable": "off",
    "sonarjs/no-unused-expressions": "off",
    "sonarjs/no-unused-private-class-members": "off",
    "sonarjs/sonar-block-scoped-var": "off",
    "sonarjs/sonar-max-params": "off",
    "sonarjs/sonar-no-control-regex": "off",
    "sonarjs/sonar-no-dupe-keys": "off",
    "sonarjs/sonar-no-empty-character-class": "off", // Also regexp/no-empty-character-class
    "sonarjs/sonar-no-fallthrough": "off",
    "sonarjs/sonar-no-invalid-regexp": "off", // Also regexp/no-invalid-regexp
    "sonarjs/sonar-no-misleading-character-class": "off",
    "sonarjs/use-isnan": "off",

    // eslint-plugin-autofix rules duplications (same names)

    "sonarjs/no-lonely-if": "off", // Also unicorn/no-lonely-if
    "sonarjs/no-throw-literal": "off",
    "sonarjs/no-useless-catch": "off",
    "sonarjs/no-var": "off",
    "sonarjs/prefer-object-spread": "off",
    "sonarjs/prefer-spread": "off", // Also unicorn/prefer-spread
    "sonarjs/sonar-no-regex-spaces": "off", // Also regexp/prefer-quantifier
    "sonarjs/sonar-no-unused-vars": "off",

    // Other regex-related duplications

    // Duplicates regexp/prefer-d, regexp/no-obscure-range and regexp/no-dupe-characters-character-class
    "sonarjs/duplicates-in-character-class": "off",
    // Duplicates regexp/prefer-w, regexp/prefer-d, regexp/match-any
    "sonarjs/concise-regex": "off",
    // Duplicates regexp/no-empty-alternative, regexp/no-trivially-nested-quantifier, regexp/no-dupe-disjunctions and regexp/no-trivially-nested-quantifier
    "sonarjs/empty-string-repetition": "off",
    // Duplicates regexp/no-useless-character-class
    "sonarjs/single-char-in-character-classes": "off",
    // Duplicates unicorn/better-regex and regexp/prefer-character-class
    "sonarjs/single-character-alternation": "off",
    // Duplicates regexp/no-super-linear-move
    "sonarjs/slow-regex": "off",
    // Duplciates regexp/prefer-regexp-exec
    "sonarjs/sonar-prefer-regexp-exec": "off",
    // Duplicates regexp/no-empty-alternative
    "sonarjs/no-empty-alternatives": "off",
    // Duplicates regexp/no-useless-dollar-replacements + regexp/no-unused-capturing-group
    "sonarjs/existing-groups": "off",
    // Duplicates regexp/no-empty-capturing-group and regexp/no-empty-group
    "sonarjs/no-empty-group": "off",

    // React/JSX-related duplications

    // Duplicates react/hook-use-state
    "sonarjs/hook-use-state": "off",
    // Duplicates react/jsx-key
    "sonarjs/jsx-key": "off",
    // Duplicated react/jsx-no-constructed-context-values
    "sonarjs/jsx-no-constructed-context-values": "off",
    // Duplicates react/jsx-no-useless-fragment
    "sonarjs/jsx-no-useless-fragment": "off",
    // Duplicates react/no-array-index-key
    "sonarjs/no-array-index-key": "off",
    // Duplicates react/no-deprecated
    "sonarjs/no-deprecated-react": "off",
    // Duplicates react/no-find-dom-node
    "sonarjs/no-find-dom-node": "off",
    // Duplicates react/no-unknown-property
    "sonarjs/no-unknown-property": "off",
    // Duplicates react/no-unsafe
    "sonarjs/no-unsafe": "off",
    // Duplicates react/no-unstable-nested-components
    "sonarjs/no-unstable-nested-components": "off",
    // Duplicates react-hooks/rules-of-hooks
    "sonarjs/rules-of-hooks": "off",
    // Duplicates react/jsx-no-leaked-render
    "sonarjs/sonar-jsx-no-leaked-render": "off",
    // Duplicates react/no-unused-class-component-methods
    "sonarjs/sonar-no-unused-class-component-methods": "off",
    // Duplicates react/prefer-read-only-props
    "sonarjs/sonar-prefer-read-only-props": "off",

        /*
         * SonarJS rules obsoleted by TS rules of the same name
         */
        "sonarjs/no-misused-promises": "off",
        "sonarjs/no-redundant-type-constituents": "off",
        "sonarjs/prefer-enum-initializers": "off",
        "sonarjs/prefer-nullish-coalescing": "off",
        "sonarjs/sonar-prefer-optional-chain": "off",

        // Redundant in TypeScript
        "sonarjs/function-return-type": "off",

        // [...]nobsoleted by @typescript-eslint/no-deprecated
        "sonarjs/deprecation": "off",
1 Like

Hi Samuel,

eslint-plugin-sonarjs is meant to be used without the need for those other plugins. In the future, it might disable the redundant rules as you suggest.

To answer your question, when we adopt rules from ESLint plugins, we generally improve them to reduce false positives and edge cases and provide a more thorough description and guidance. If you have been testing basic examples, you might not see this.

In your case, my recommendation is to use Sonar’s versions of the rules unless you encounter problems. If you see any false positives or there’s some rule you particularly care about that might be missing, please feel free to share them here.

To avoid dealing with ESLint plugins in the first place, consider using SonarQube in your CI/CD and IDE. Both are free and open source and support 30+ languages. There is also an enterprise version, which includes taint analysis and dataflow bug detection.

1 Like