Skip to content

[Bug]: variable declarator remove bug #16583

@nerodesu017

Description

@nerodesu017

💻

  • Would you like to work on a fix?

How are you using Babel?

Programmatic API (babel.transform, babel.parse)

Input code

// WORKING_DIR_ROOT/test/custom_tests/remove.js

const traverse = require("../../packages/babel-traverse/lib").default;
const t = require("../../packages/babel-types/lib");
const generate = require("../../packages/babel-generator/lib");
const { parse } = require("../../packages/babel-parser");


const script = `
{
  let a = 33;
}
let a = 42;
`

const AST = parse(script);

let found = false;
traverse(AST, {
  /**
   * 
   * @param {NodePath<t.VariableDeclarator>} path 
   * @returns 
   */
  VariableDeclarator(path){
          
    if(!found && path.toString() === "a = 33"){
      found = true;
       path.remove();
      return;
    } else if(path.toString() === "a = 42") {
      const binding = path.scope.getBinding("a");  
      // binding is undefined
      console.log(binding);
      // can't access path of undefined
      console.log(binding.path.toString());
    }
    
    
  }
});

Configuration file name

No response

Configuration

No response

Current and expected behavior

The test script is comprised of a BlockStatement containing one VariableDeclaration with a VariableDeclarator named a having any type of value. Outside of the BlockStatement we have another VariableDeclaration, just as the last one, with a VariableDeclarator having the same name.

What babel is doing is that when we remove the VariableDeclarator (NOT VariableDeclaration) from inside the BlockStatement makes it so that both Bindings of a (the one from the scope of the BlockStatement, and also the one from the parent scope) are deleted.

The expected behavior would be that only the Binding of a in the BlockStatement's scope to be deleted, but that doesn't happen

Environment

System:
OS: Windows 10 10.0.19045
Binaries:
Node: 20.14.0 - C:\Program Files\nodejs\node.EXE
Yarn: 4.2.2 - C:\Program Files\nodejs\yarn.CMD
npm: 10.7.0 - C:\Program Files\nodejs\npm.CMD

Possible solution

The problem arises in the packages/babel-traverse/src/path/removal.ts > remove function, more specifically, when calling the this._callRemovalHooks() function. By the point the _callRemovalHooks function is called, the Binding of a is already removed, keep this in mind.

The first hook:

function (self: NodePath, parent: NodePath) {
    const removeParent =
      // while (NODE);
      // removing the test of a while/switch, we can either just remove it entirely *or* turn the
      // `test` into `true` unlikely that the latter will ever be what's wanted so we just remove
      // the loop to avoid infinite recursion
      (self.key === "test" && (parent.isWhile() || parent.isSwitchCase())) ||
      // export NODE;
      // just remove a declaration for an export as this is no longer valid
      (self.key === "declaration" && parent.isExportDeclaration()) ||
      // label: NODE
      // stray labeled statement with no body
      (self.key === "body" && parent.isLabeledStatement()) ||
      // let NODE;
      // remove an entire declaration if there are no declarators left
      (self.listKey === "declarations" &&
        parent.isVariableDeclaration() &&
        parent.node.declarations.length === 1) ||
      // NODE;
      // remove the entire expression statement if there's no expression
      (self.key === "expression" && parent.isExpressionStatement());

    if (removeParent) {
      parent.remove();
      return true;
    }
  }

is the one we are going down on, which in turn calls parent.remove(), which is the same remove function that was previously mentioned.

Now, the problem lies when we call this._removeFromScope(), that is because the call to getBindingIdentifiers returns the bindings that we already removed.

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    i: needs triageoutdatedA closed issue/PR that is archived due to age. Recommended to make a new issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions