Repository: babel/babel (TypeScript)

Issue:
[Bug]: Super call in arrow function causing infinite recursion
### 💻

- [X] Would you like to work on a fix?

### How are you using Babel?

@babel/cli

### Input code

```js
class Bar extends Foo {
    constructor() {
        let f = (...args) => super(...args);
        f();
    }
}
```

[Example on Babel REPL](https://babeljs.io/repl/#?browsers=since%202017&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYGwhgzhAEBCYCdoFMAeAXZA7AJjAYgPaHQDeAUNFdMIVhOggK7DqEIAUAlGZdfyGTpoAM2gBeaBwB0sxAHMIPcQD5oEJgAdknWdIVKA3H35UR3Y_wC-5G0A&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env&prettier=false&targets=&version=7.20.2&externalPlugins=&assumptions=%7B%7D)

### Configuration file name

.babelrc

### Configuration

```json
{
    "presets": [["@babel/preset-env", { "bugfixes": true }]],
    "targets": ["since 2017"]
}
```

### Current and expected behavior

##### Current behavior (silently success and causing infinite recursion)
```js
"use strict";

class Bar extends Foo {
  constructor() {
    var _supercall = function _supercall() {
      return _supercall(...arguments);
    };
    let f = function f() {
      return _supercall(...arguments);
    };
    f();
  }
}
```

##### Expected behavior
- Give ~~warning or~~ error in this situation;
- ~~Or just keep the arrow function;~~
- ~~Or transpile the whole class into pre-ES6 style (just like the result of using [`since 2016`](https://babeljs.io/repl/#?browsers=since%202016&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYGwhgzhAEBCYCdoFMAeAXZA7AJjAYgPaHQDeAUNFdMIVhOggK7DqEIAUAlGZdfyGTpoAM2gBeaBwB0sxAHMIPcQD5oEJgAdknWdIVKA3H35UR3Y_wC-5G0A&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env&prettier=false&targets=&version=7.20.2&externalPlugins=&assumptions=%7B%7D)).~~

### Environment

- Babel version: v7.20.2
- Node: v16.17.0
- pnpm version: pnpm 7.14.2
- OS: Windows 10
- Monorepo: no

### Possible solution

_No response_

### Additional context

_No response_


Hints:
Hey @SuperSodaSea! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant [Slack community](https://babeljs.slack.com) that typically always has someone willing to help. You can sign-up [here](https://slack.babeljs.io/) for an invite.
Since it looks impossible to transpile this code into a ES6 class without arrow function, I wondered which one would be the best solution.
Seems that `@babel/plugin-transform-spread` will throw error in a similar situation, so I'd like to add a check and throw in `@babel/plugin-transform-parameters`. I will make a PR later.

https://github.com/babel/babel/blob/156b608d48900b9489bd31aff03189a6c18f9040/packages/babel-plugin-transform-spread/src/index.ts#L140-L146
Thanks for the investigation @SuperSodaSea; an error with a similar message looks fine to me!

Patch:
diff --git a/packages/babel-plugin-transform-parameters/src/index.ts b/packages/babel-plugin-transform-parameters/src/index.ts
--- a/packages/babel-plugin-transform-parameters/src/index.ts
+++ b/packages/babel-plugin-transform-parameters/src/index.ts
@@ -27,7 +27,10 @@ export default declare((api, options: Options) => {
             .some(param => param.isRestElement() || param.isAssignmentPattern())
         ) {
           // default/rest visitors require access to `arguments`, so it cannot be an arrow
-          path.arrowFunctionToExpression({ noNewArrows });
+          path.arrowFunctionToExpression({
+            allowInsertArrowWithRest: false,
+            noNewArrows,
+          });
 
           // In some cases arrowFunctionToExpression replaces the function with a wrapper.
           // Return early; the wrapped function will be visited later in the AST traversal.
diff --git a/packages/babel-traverse/src/path/conversion.ts b/packages/babel-traverse/src/path/conversion.ts
--- a/packages/babel-traverse/src/path/conversion.ts
+++ b/packages/babel-traverse/src/path/conversion.ts
@@ -149,12 +149,14 @@ export function arrowFunctionToExpression(
   this: NodePath<t.ArrowFunctionExpression>,
   {
     allowInsertArrow = true,
+    allowInsertArrowWithRest = allowInsertArrow,
     /** @deprecated Use `noNewArrows` instead */
     specCompliant = false,
     // TODO(Babel 8): Consider defaulting to `false` for spec compliancy
     noNewArrows = !specCompliant,
   }: {
     allowInsertArrow?: boolean | void;
+    allowInsertArrowWithRest?: boolean | void;
     specCompliant?: boolean | void;
     noNewArrows?: boolean;
   } = {},
@@ -169,6 +171,7 @@ export function arrowFunctionToExpression(
     this,
     noNewArrows,
     allowInsertArrow,
+    allowInsertArrowWithRest,
   );
 
   // @ts-expect-error TS requires explicit fn type annotation
@@ -240,6 +243,7 @@ function hoistFunctionEnvironment(
   // TODO(Babel 8): Consider defaulting to `false` for spec compliancy
   noNewArrows: boolean | void = true,
   allowInsertArrow: boolean | void = true,
+  allowInsertArrowWithRest: boolean | void = true,
 ): { thisBinding: string; fnPath: NodePath<t.Function> } {
   let arrowParent;
   let thisEnvFn: NodePath<t.Function> = fnPath.findParent(p => {
@@ -286,7 +290,17 @@ function hoistFunctionEnvironment(
   if (inConstructor && superCalls.length > 0) {
     if (!allowInsertArrow) {
       throw superCalls[0].buildCodeFrameError(
-        "Unable to handle nested super() usage in arrow",
+        "When using '@babel/plugin-transform-arrow-functions', " +
+          "it's not possible to compile `super()` in an arrow function without compiling classes.\n" +
+          "Please add '@babel/plugin-transform-classes' to your Babel configuration.",
+      );
+    }
+    if (!allowInsertArrowWithRest) {
+      // preset-env with target `since 2017` enables `transform-parameters` without `transform-classes`.
+      throw superCalls[0].buildCodeFrameError(
+        "When using '@babel/plugin-transform-parameters', " +
+          "it's not possible to compile `super()` in an arrow function with default or rest parameters without compiling classes.\n" +
+          "Please add '@babel/plugin-transform-classes' to your Babel configuration.",
       );
     }
     const allSuperCalls: NodePath<t.CallExpression>[] = [];
@@ -345,7 +359,9 @@ function hoistFunctionEnvironment(
   if (superProps.length > 0) {
     if (!allowInsertArrow) {
       throw superProps[0].buildCodeFrameError(
-        "Unable to handle nested super.prop usage",
+        "When using '@babel/plugin-transform-arrow-functions', " +
+          "it's not possible to compile `super.prop` in an arrow function without compiling classes.\n" +
+          "Please add '@babel/plugin-transform-classes' to your Babel configuration.",
       );
     }
 


Test patch:
diff --git a/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/input.js b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/input.js
@@ -0,0 +1,6 @@
+class Bar extends Foo {
+  constructor() {
+      let f = () => super();
+      f();
+  }
+}
diff --git a/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/options.json b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-call/options.json
@@ -0,0 +1,4 @@
+{
+  "plugins": ["transform-arrow-functions"],
+  "throws": "When using '@babel/plugin-transform-arrow-functions', it's not possible to compile `super()` in an arrow function without compiling classes.\nPlease add '@babel/plugin-transform-classes' to your Babel configuration."
+}
diff --git a/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/input.js b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/input.js
@@ -0,0 +1,6 @@
+class Bar extends Foo {
+  constructor() {
+      let f = () => super.x;
+      f();
+  }
+}
diff --git a/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/options.json b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-arrow-functions/test/fixtures/arrow-functions/super-prop/options.json
@@ -0,0 +1,4 @@
+{
+  "plugins": ["transform-arrow-functions"],
+  "throws": "When using '@babel/plugin-transform-arrow-functions', it's not possible to compile `super.prop` in an arrow function without compiling classes.\nPlease add '@babel/plugin-transform-classes' to your Babel configuration."
+}
diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/input.js
@@ -0,0 +1,6 @@
+class Bar extends Foo {
+    constructor() {
+        let f = (x = super()) => x;
+        f();
+    }
+}
diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/options.json b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-default-super-no-classes-plugin/options.json
@@ -0,0 +1,4 @@
+{
+    "plugins": ["transform-parameters"],
+    "throws": "When using '@babel/plugin-transform-parameters', it's not possible to compile `super()` in an arrow function with default or rest parameters without compiling classes.\nPlease add '@babel/plugin-transform-classes' to your Babel configuration."
+}
diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/input.js b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/input.js
@@ -0,0 +1,6 @@
+class Bar extends Foo {
+    constructor() {
+        let f = (...args) => super(...args);
+        f();
+    }
+}
diff --git a/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/options.json b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-parameters/test/fixtures/parameters/arrow-rest-super-no-classes-plugin/options.json
@@ -0,0 +1,4 @@
+{
+    "plugins": ["transform-parameters"],
+    "throws": "When using '@babel/plugin-transform-parameters', it's not possible to compile `super()` in an arrow function with default or rest parameters without compiling classes.\nPlease add '@babel/plugin-transform-classes' to your Babel configuration."
+}