Repository: eslint/eslint
File: lib/rules/no-useless-constructor.js (JavaScript)

Code before:
        function checkForConstructor(node) {
            if (node.kind !== "constructor") {
                return;
            }

            /*
             * Prevent crashing on parsers which do not require class constructor
             * to have a body, e.g. typescript and flow
             */
            if (!node.value.body) {
                return;
            }

            const body = node.value.body.body;
            const ctorParams = node.value.params;
            const superClass = node.parent.parent.superClass;

            if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
                context.report({
                    node,
                    messageId: "noUselessConstructor",
                    suggest: [
                        {
                            messageId: "removeConstructor",
                            fix(fixer) {
                                return fixer.remove(node);
                            }
                        }
                    ]
                });
            }
        }

        return {
            MethodDefinition: checkForConstructor
        };
    }
};


Review comment (bug):
In some edge cases, if the constructor triggers automatic semicolon insertion, then the semicolon needs to be inserted explicitly for the code to continue working when the constructor is removed. For example:

```js
class A {
  foo = "bar"
  constructor() { }
  [0]() { }
}
```

Simply removing the constructor from the above code would result in a syntax error. In this case, the fix needs to insert the semicolon:

```js
class A {
  foo = "bar"
  ;
  [0]() { }
}
```

The logic to conditionally insert a semicolon while replacing a node is already implemented in other rules, for example in `require-await`:

https://github.com/eslint/eslint/blob/3a4eaf921543b1cd5d1df4ea9dec02fab396af2a/lib/rules/require-await.js#L111-L114

For our specific case, calculating `addSemicolon` should be a bit easier because we can be sure that the node to be removed remove is a constructor, so probably:

```js
const nextToken = sourceCode.getTokenAfter(node);
const addSemiColon = nextToken.type === "Punctuator" && nextToken.value === "[" && astUtils.needsPrecedingSemicolon(sourceCode, node);                
return fixer.replaceTextRange(node.range, addSemiColon ? ";" : "");
```


Code after:
         * @returns {void}
         */
        function checkForConstructor(node) {
            if (node.kind !== "constructor") {
                return;
            }

            /*
             * Prevent crashing on parsers which do not require class constructor
             * to have a body, e.g. typescript and flow
             */
            if (!node.value.body) {
                return;
            }

            const body = node.value.body.body;
            const ctorParams = node.value.params;
            const superClass = node.parent.parent.superClass;

            if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
                context.report({
                    node,
                    messageId: "noUselessConstructor",
                    suggest: [
                        {
                            messageId: "removeConstructor",
                            *fix(fixer) {
                                const nextToken = context.sourceCode.getTokenAfter(node);
                                const addSemiColon = nextToken.type === "Punctuator" && nextToken.value === "[" && astUtils.needsPrecedingSemicolon(context.sourceCode, node);

                                yield fixer.replaceText(node, addSemiColon ? ";" : "");
                            }
                        }
                    ]
                });
            }
        }

        return {
            MethodDefinition: checkForConstructor
        };
    }
};