Variables used in computed method key are not scoped correctly

Bug Report

  • I would like to work on a fix!

Current behavior

Where an object/class method has a computed key containing a variable, and the method includes a variable with same name in its body or parameters, the scope of the variable in method key is incorrectly identified.

Babel determines the scope of the var in the method key to be inside the method’s body, not outside as it actually is.

Input Code

https://codesandbox.io/s/babel-issue-12790-b9y6b

Input code:

const x = 'q';
const obj = {
  [x](x) {
    return x;
  }
};

Plugin:

() => ({
  visitor: {
    Program(path) {
      path.scope.rename('x');
    }
  }
})

Expected behavior

Expected output to be:

const _x = 'q';
const obj = {
  [_x](x) {
    return x;
  }
};

but instead get:

const _x = 'q';
const obj = {
  [x](x) { // <-- `[x]` refers to non-existent variable
    return x;
  }
};

Babel Configuration (babel.config.js, .babelrc, package.json#babel, cli command, .eslintrc)

None.

Environment

System:
  OS: macOS Mojave 10.14.6
Binaries:
  Node: 14.15.5 - ~/.nvm/versions/node/v14.15.5/bin/node
  npm: 6.14.11 - ~/.nvm/versions/node/v14.15.5/bin/npm
npmPackages:
  @babel/core: ^7.12.13 => 7.12.13

Additional context

I am very happy to work on a fix. But I’m unfamiliar with how/where Babel calculates scope of identifiers, so if someone could point me in the right direction, that would be a big help.

1 possible answer(s) on “Variables used in computed method key are not scoped correctly

  1. @overlookmotel It seems that there are two different bugs in Babel:

    1. When path refers to the x inside the computed key, path.scope is the method scope rather than the program scope. This also causes scoping problems such as in this example
    2. When renaming a variable, we stop traversing the AST when we enter a node which introduces a new scope (such as ObjectMethod).

    We can probably fix (1) by adding a list of “nodes whose keys should refer to the grandparent scope”, and start from path.parentPath in

    let path = this.parentPath;

    in those cases.

    We can fix (2) by modifying

    const renameVisitor: Visitor<Renamer> = {

    to do something similar to

    export const environmentVisitor = {

    : when there is a computed method, we can use skipAllButComputedKey to continue visiting the method key.