Skip to content

Reuse of Identifier when parsing ObjectPattern with AssignmentPattern #928

@bradzacher

Description

@bradzacher

Context: typescript-eslint/typescript-eslint#1686
cc @kaicataldo

When parsing an assignment within an object pattern, acorn reuses the identifier object for both the Property.key, and the AssignmentPattern.left. Best shown by the example below.

This is a problem because it means that any mutations applied to one node also apply to the other node.
For example, ESLint adds a parent property as it traverses the AST. This node reuse means that the parent will be wrong for one of the two locations (whichever one is traversed first).

It has exposed a subtle bug in ESLint's camelcase rule.
When the code is parsed by espree (and thus acorn), the rule does not fire an error, because it does not detect the correct lineage for the node.
However, when the code is parsed by a parser that does not reuse objects (like typescript-estree), the rule fires an error, because the parent pointers are correct.

Is this intentional? To me it seems like a bug in acorn, as I would assume that every AST node is a brand new object, no matter how similar to an existing node they might be. It seems like ESLint makes the same assumption as well.


const acorn = require('acorn');
const ast = acorn.parse("const {foo=1} = {};");

/*
  {
    "type": "Property",
    "start": 7,
    "end": 12,
    "method": false,
    "shorthand": true,
    "computed": false,
    "key": {
      "type": "Identifier",
      "start": 7,
      "end": 10,
      "name": "foo"
    },
    "kind": "init",
    "value": {
      "type": "AssignmentPattern",
      "start": 7,
      "end": 12,
      "left": {
        "type": "Identifier",
        "start": 7,
        "end": 10,
        "name": "foo"
      },
      "right": {
        "type": "Literal",
        "start": 11,
        "end": 12,
        "value": 1,
        "raw": "1"
      }
    }
  }
*/
const node = ast.body[0].declarations[0].id.properties[0];
console.log(node.key === node.value.left) // true

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions