Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linter: unicorn/switch-case-braces with --fix mangles code #8491

Open
aparajita opened this issue Jan 14, 2025 · 2 comments
Open

linter: unicorn/switch-case-braces with --fix mangles code #8491

aparajita opened this issue Jan 14, 2025 · 2 comments
Labels
A-linter Area - Linter C-bug Category - Bug good first issue Experience Level - Good for newcomers

Comments

@aparajita
Copy link

What version of Oxlint are you using?

0.15.6

What command did you run?

oxlint -c oxlint.config.jsonc --fix test.ts

What does your .oxlint.json config file look like?

// See https://oxc.rs/docs/guide/usage/linter/rules.html
// Only non-default rules are configured here.
{
  "$schema": "../../node_modules/oxlint/configuration_schema.json",
  "plugins": ["eslint", "import", "node", "promise", "typescript", "unicorn"],
  "env": {
    "browser": true,
    "es6": true,
    "node": true,
  },
  "rules": {
    // Correctness
    "import/default": "error",
    "import/namespace": "error",

    "promise/no-callback-in-promise": "error",
    "promise/no-new-statics": "error",
    "promise/valid-params": "error",

    // Perf
    "eslint/no-await-in-loop": "error",
    "oxc/no-accumulating-spread": "error",
    "unicorn/prefer-set-has": "off",

    // Restriction
    "eslint/default-case": "error",
    "eslint/no-alert": "error",
    "eslint/no-bitwise": "error",
    "eslint/no-console": "off",
    "eslint/no-div-regex": "error",
    "eslint/no-empty": "error",
    "eslint/no-empty-function": "error",
    "eslint/no-eq-null": "error",
    "eslint/no-eval": "error",
    "eslint/no-iterator": "error",
    "eslint/no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
    "eslint/no-proto": "error",
    "eslint/no-regex-spaces": "error",
    "eslint/no-restricted-globals": "off",
    "eslint/no-undefined": "off", // We prefer === undefined
    "eslint/no-unused-expressions": "error",
    "eslint/no-var": "error",
    // Sometimes we need void to indicate we don't care about awaiting a promise
    "eslint/no-void": "off",
    "eslint/unicode-bom": "error",

    "import/no-amd": "error",
    "import/no-commonjs": "off",
    "import/no-cycle": "error",
    "import/no-default-export": "off", // We like default exports
    "import/no-dynamic-require": "off",
    "import/no-webpack-loader-syntax": "error",
    "import/unambiguous": "off", // Too many false positives

    "node/no-new-require": "error",

    "oxc/bad-bitwise-operator": "error",
    "oxc/no-async-await": "off",
    "oxc/no-barrel-file": "error",
    "oxc/no-const-enum": "error",
    "oxc/no-optional-chaining": "off",
    "oxc/no-rest-spread-properties": "error",

    "promise/catch-or-return": "error",
    "promise/spec-only": "error",

    "@typescript-eslint/explicit-function-return-type": "error",
    "@typescript-eslint/no-dynamic-delete": "error",
    "@typescript-eslint/no-empty-object-type": "error",
    "@typescript-eslint/no-explicit-any": "off", // Sometimes we need any,
    "@typescript-eslint/no-import-type-side-effects": "error",
    "@typescript-eslint/no-namespace": "error",
    "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
    "@typescript-eslint/no-non-null-assertion": "error",
    "@typescript-eslint/no-require-imports": "error",
    "@typescript-eslint/no-var-requires": "error",
    "@typescript-eslint/prefer-literal-enum-member": "error",

    // unicorn
    "unicorn/no-abusive-eslint-disable": "error",
    "unicorn/no-anonymous-default-export": "off", // We like default exports,
    "unicorn/no-array-for-each": "error",
    "unicorn/no-array-reduce": "error",
    "unicorn/no-length-as-slice-end": "error",
    "unicorn/no-magic-array-flat-depth": "error",
    "unicorn/no-nested-ternary": "error",
    "unicorn/no-process-exit": "off", // No need to warn about this
    "unicorn/prefer-modern-math-apis": "error",
    "unicorn/prefer-node-protocol": "error",
    "unicorn/prefer-number-properties": "error",

    // Suspicious
    "eslint/no-extend-native": "error",
    "eslint/no-new": "error",
    "eslint/no-unexpected-multiline": "error",
    "eslint/no-useless-concat": "error",
    "eslint/no-useless-constructor": "error",

    "import/no-duplicates": "error",
    "import/no-named-as-default": "error",
    "import/no-named-as-default-member": "error",
    "import/no-self-import": "error",

    "oxc/no-approximate-constants": "error",
    "oxc/no-misrefactored-assign-op": "error",
    "oxc/no-async-endpoint-handlers": "error",

    "promise/no-promise-in-callback": "error",

    "@typescript-eslint/no-confusing-non-null-assertion": "error",
    "@typescript-eslint/no-extraneous-class": "error",
    "@typescript-eslint/no-unnecessary-type-constraint": "error",

    "unicorn/consistent-function-scoping": "error",
    "unicorn/prefer-add-event-listener": "error",

    // Pedantic
    "eslint/array-callback-return": "error",
    "eslint/eqeqeq": "error",
    "eslint/max-classes-per-file": "error",
    "eslint/max-lines": ["error", { "max": 300 }],
    "eslint/no-array-constructor": "error",
    "eslint/no-case-declarations": "error",
    "eslint/no-constructor-return": "error",
    "eslint/no-else-return": "error",
    "eslint/no-fallthrough": "error",
    "eslint/no-inner-declarations": "error",
    "eslint/no-negated-condition": "off", // Sometimes this is clearer
    "eslint/no-new-wrappers": "error",
    "eslint/no-object-constructor": "error",
    "eslint/no-prototype-builtins": "error",
    "eslint/no-redeclare": "error",
    "eslint/no-self-compare": "error",
    "eslint/no-throw-literal": "error",
    "eslint/radix": "error",

    // We choose to declare functions that return a Promise async,
    // even if they don't use await. Because oxlint doesn't use
    // TypeScript's type inference, it can't tell if a function
    // returns a Promise, so it can't properly enforce this rule
    // the way typescript-eslint does.
    "eslint/require-await": "off",
    "eslint/sort-vars": "error",
    "eslint/symbol-description": "error",

    "import/max-dependencies": "off", // We heavily factor our code

    "@typescript-eslint/ban-ts-comment": [
      "error",
      {
        "ts-expect-error": "allow-with-description",
        "ts-ignore": "allow-with-description",
        "ts-nocheck": false,
        "ts-check": false,
        "minimumDescriptionLength": 7,
      },
    ],
    "@typescript-eslint/ban-types": "off", // no-wrapper-object-types does this better
    "@typescript-eslint/no-unsafe-function-type": "error",

    // We're fine with enums that don't have initializers
    "@typescript-eslint/prefer-enum-initializers": "off",
    "@typescript-eslint/prefer-ts-expect-error": "error",

    "unicorn/consistent-empty-array-spread": "error",
    "unicorn/escape-case": "error",
    "unicorn/explicit-length-check": "error",
    "unicorn/new-for-builtins": "error",
    "unicorn/no-hex-escape": "error",
    "unicorn/no-instanceof-array": "error",
    "unicorn/no-lonely-if": "error",
    "unicorn/no-negation-in-equality-check": "error",
    "unicorn/no-new-buffer": "error",
    "unicorn/no-object-as-default-parameter": "error",
    "unicorn/no-static-only-class": "error",
    "unicorn/no-this-assignment": "error",
    "unicorn/no-typeof-undefined": "error",
    "unicorn/no-unreadable-iife": "error",
    "unicorn/no-useless-promise-resolve-reject": "error",
    "unicorn/no-useless-switch-case": "error",
    "unicorn/no-useless-undefined": "error",
    "unicorn/prefer-array-flat": "error",
    "unicorn/prefer-array-some": "error",
    "unicorn/prefer-blob-reading-methods": "error",
    "unicorn/prefer-code-point": "error",
    "unicorn/prefer-date-now": "error",
    "unicorn/prefer-dom-node-append": "error",
    "unicorn/prefer-dom-node-dataset": "error",
    "unicorn/prefer-dom-node-remove": "error",
    "unicorn/prefer-event-target": "error",
    "unicorn/prefer-math-min-max": "error",
    "unicorn/prefer-math-trunc": "error",
    "unicorn/prefer-native-coercion-functions": "error",
    "unicorn/prefer-prototype-methods": "error",
    "unicorn/prefer-query-selector": "error",
    "unicorn/prefer-regexp-test": "error",
    "unicorn/prefer-string-replace-all": "error",
    "unicorn/prefer-string-slice": "error",
    "unicorn/prefer-type-error": "error",
    "unicorn/require-number-to-fixed-digits-argument": "error",

    // style
    "eslint/default-case-last": "error",
    "eslint/default-param-last": "error",
    "eslint/func-names": ["error", "as-needed"],
    "eslint/guard-for-in": "error",
    "eslint/max-params": ["error", { "max": 4 }],
    "eslint/new-cap": "error",
    "eslint/no-continue": "off",
    "eslint/no-duplicate-imports": "off", // Handled by import/no-duplicates
    "eslint/no-extra-label": "error",
    "eslint/no-label-var": "error",
    "eslint/no-labels": "error",
    "eslint/no-lone-blocks": "error",
    "eslint/no-magic-numbers": "off", // Too many false positives
    "eslint/no-multi-assign": "error",
    "eslint/no-multi-str": "error",
    "eslint/no-new-func": "error",
    "eslint/no-return-assign": "error",
    "eslint/no-script-url": "error",
    "eslint/no-template-curly-in-string": "error",
    "eslint/no-ternary": "off", // Sometimes this is clearer
    "eslint/prefer-exponentiation-operator": "error",
    "eslint/prefer-numeric-literals": "error",
    "eslint/prefer-object-has-own": "off", // Make error if we use ES2022
    "eslint/prefer-rest-params": "error",
    "eslint/prefer-spread": "error",
    "eslint/sort-imports": "off", // Handled by import/order
    "eslint/sort-keys": "off", // Sometimes we want to group keys
    "eslint/vars-on-top": "error", // We don't allow vars, but this is good for consistency
    "eslint/yoda": "error",

    "import/first": "error",

    "promise/avoid-new": "off", // Sometimes we need new Promise
    "promise/param-names": "error",
    "promise/prefer-await-to-callbacks": "error",
    "promise/prefer-await-to-then": "error",

    "@typescript-eslint/adjacent-overload-signatures": "error",
    "@typescript-eslint/array-type": ["error", { "default": "array" }],
    "@typescript-eslint/ban-tslint-comment": "error",
    "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
    "@typescript-eslint/no-empty-interface": "error",

    "unicorn/consistent-existence-index-check": "error",
    "unicorn/empty-brace-spaces": "error",
    "unicorn/error-message": "error",
    "unicorn/filename-case": "error",
    "unicorn/no-await-expression-member": "error",
    "unicorn/no-console-spaces": "error",
    "unicorn/no-unreadable-array-destructuring": "error",
    "unicorn/no-zero-fractions": "error",
    "unicorn/number-literal-case": "error",
    "unicorn/numberic-separators-style": "error",
    "unicorn/prefer-array-flat-map": "error",
    "unicorn/prefer-dom-node-text-content": "error",
    "unicorn/prefer-includes": "error",
    "unicorn/prefer-logical-operator-over-ternary": "error",
    "unicorn/prefer-modern-dom-apis": "error",
    "unicorn/prefer-negative-index": "error",
    "unicorn/prefer-optional-catch-binding": "error",
    "unicorn/prefer-reflect-apply": "error",
    "unicorn/prefer-string-raw": "error",
    "unicorn/prefer-string-trim-start-end": "error",
    "unicorn/prefer-structured-clone": "error",
    "unicorn/require-array-join-separator": "error",
    "unicorn/switch-case-braces": "error",
    "unicorn/text-encoding-identifier-case": "error",
    "unicorn/throw-new-error": "error",
  },

  "overrides": [
    {
      // Disable some TypeScript rules for non-.ts files
      "files": ["**/*.{cjs,js}"],
      "rules": {
        "no-require-imports": "off",
      },
    },
  ],
}

What happened?

Given this source code:

Running oxlint with --fix produces this:

const alpha = 7
let beta = ''
let gamma = 0

switch (alpha) {
  case 1: {beta = 'one'gamma = 1break}
}

Which is of course disastrously wrong. unicorn has no problem fixing this same file.

@aparajita aparajita added A-linter Area - Linter C-bug Category - Bug labels Jan 14, 2025
@Boshen Boshen added the good first issue Experience Level - Good for newcomers label Jan 21, 2025
@taearls
Copy link
Contributor

taearls commented Jan 25, 2025

@aparajita hey! I'm interested in taking a look at this issue.

just to clarify the reproduction scenario, does the source code that produces this issue look something like this?

const alpha = 7
let beta = ''
let gamma = 0

switch (alpha) {
  case 1: 
    beta = 'one'
    gamma = 1
    break
}

@aparajita
Copy link
Author

Yes, didn't notice the original source code wasn't there.

Same issue if there are braces around the case statements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linter Area - Linter C-bug Category - Bug good first issue Experience Level - Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants