Skip to content

Fix eslint-parser AST definition of AccessorProperty - #18257

Merged
nicolo-ribaudo merged 2 commits into
babel:mainfrom
jpradelle:18230
Sep 30, 2026
Merged

nicolo-ribaudo merged 2 commits into
babel:mainfrom
jpradelle:18230

Conversation

@jpradelle

Copy link
Copy Markdown
Contributor
Q 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 聽 A
Fixed Issues? Fixes #18230
Patch: Bug Fix? Yes
Major: Breaking Change? Fixes AccessorProperty quasis ranges to be consistent with PropertyDefinition, might break stuff made based on wrong values ?
Minor: New Feature? No
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes? No
License MIT

Fixes template quasis parsing for AccessorProperty. For example this was wrongly parsed:

class Test {
  accessor foo = html`
    <foo-baz icon="${' '}"></foo-baz>
  `;
}

See ticket #18230 for more details and samples.

@github-actions

Copy link
Copy Markdown

Thanks for opening this pull request! 馃帀

We really appreciate you taking the time to contribute, @jpradelle.

A maintainer will take a look as soon as they can. In the meantime, please make sure that:

  • the description explains what changed and why
  • any related issues are linked
  • existing tests still pass

If anything needs adjusting we'll leave comments here. Thanks again!

@babel-bot

babel-bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/62196

// AST Types that are not presented in Babel AST
const newTypes = {
AccessorProperty: ["decorators", "typeAnnotation"].concat(
ESLINT_VISITOR_KEYS.PropertyDefinition,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ESLINT_VISITOR_KEYS.PropertyDefinition,
ESLINT_VISITOR_KEYS.AccessorProperty,

There is no guarantee that the shape of these two AST types will always be exactly the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing in ESLINT_VISITOR_KEYS that looks like to be something for AccessorProperty.
I can replace with (which is the computed values of PropertyDefinition)

AccessorProperty: ["decorators", "typeAnnotation", "value", "key"],

I kept typeAnnotation copied from PropertyDefinition, but I'm not sure about the use, and I don't get why this was added here https://github.com/babel/babel/pull/13752/changes#diff-76e55da3b712c3967c9951e46430e7c52680d3de9baf60f803c742233b181b2dR16

decorators I found it in node when parsing this code

class T { @foo() accessor a; }

For the fix of this issue specifically, only this is needed.

AccessorProperty: ["value"],

I'm not completely sure of the impact of the rest.

I think we should go with at least

AccessorProperty: ["decorators", "value", "key"],

@JLHwung JLHwung Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. The accessor is still stage 2.7 so espree does not support this syntax. In that case the AccessorProperty basically follows what Babel defines ClassAccessorProperty, in that case we can just pull the visitor keys from types.VISITOR_KEYS.ClassAccessorProperty.

Update: never mind, the ClassAccessorProperty will not have value anyway. At this point I agree with you and we can just put AccessorProperty: ["decorators", "typeAnnotation", "value", "key"], here.

Comment on lines +224 to +238
const ast = parseForESLint("class T { accessor a = `x${y}z`; }", {
eslintVisitorKeys: true,
eslintScopeManager: true,
babelOptions: BABEL_OPTIONS,
}).ast;

const accessor = ast.body[0].body.body[0];
expect(accessor.type).toBe("AccessorProperty");

const quasis = accessor.value.quasis;
expect(quasis.map(q => q.range)).toEqual([
[23, 27], // `x${
[28, 31], // }z`
]);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ast = parseForESLint("class T { accessor a = `x${y}z`; }", {
eslintVisitorKeys: true,
eslintScopeManager: true,
babelOptions: BABEL_OPTIONS,
}).ast;
const accessor = ast.body[0].body.body[0];
expect(accessor.type).toBe("AccessorProperty");
const quasis = accessor.value.quasis;
expect(quasis.map(q => q.range)).toEqual([
[23, 27], // `x${
[28, 31], // }z`
]);
});
parseAndAssertSame("class T { accessor a = `x${y}z`; }");

parseAndAssertSame would have caught the quasis mismatches.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing so throws an exception

Babel and Espree > templates > template element ranges inside accessor property #18230
    SyntaxError: Unexpected token a
        at Espree.raise (/home/jpradelle/workspace/babel/node_modules/espree/dist/espree.cjs:892:6)
        at Espree.unexpected (/home/jpradelle/workspace/babel/node_modules/espree/dist/espree.cjs:942:10)
        at Espree.pp$9.semicolon (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:776:68)
        at Espree.pp$8.parseClassField (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:1680:10)
        at Espree.pp$8.parseClassElement (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:1612:12)
        at Espree.pp$8.parseClass (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:1528:26)
        at Espree.pp$8.parseStatement (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:989:19)
        at Espree.pp$8.parseTopLevel (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:856:23)
        at Espree.parseTopLevel (/home/jpradelle/workspace/babel/node_modules/espree/dist/espree.cjs:879:18)
        at Espree.parse (/home/jpradelle/workspace/babel/node_modules/acorn/dist/acorn.js:611:17)

accessor keyword is not parsed properly by Espree

I didn't commit this test I firstly used, that's more in the spirit of comparing things like that, but I didn't find it relevant

    it("accessor property template ranges match property template ranges #18230", () => {
      const collectQuasiRanges = ast => {
        const ranges = [];
        const walk = node => {
          if (!node || typeof node !== "object") return;
          if (node.type === "TemplateElement") {
            ranges.push([node.range[0], node.range[1]]);
          }
          for (const key of Object.keys(node)) {
            const value = node[key];
            if (Array.isArray(value)) {
              value.forEach(walk);
            } else if (value && typeof value === "object" && value.type) {
              walk(value);
            }
          }
        };
        walk(ast);
        return ranges;
      };

      const options = {
        eslintVisitorKeys: true,
        eslintScopeManager: true,
        babelOptions: BABEL_OPTIONS,
      };

      const withAccessor = parseForESLint(
        `class Test {
          /**/accessor foo = \`\${' '}\`;
        }`,
        options,
      ).ast;
      const withoutAccessor = parseForESLint(
        `class Test {
          /*accessor*/ foo = \`\${' '}\`;
        }`,
        options,
      ).ast;

      expect(collectQuasiRanges(withAccessor)).toEqual(
        collectQuasiRanges(withoutAccessor),
      );
    });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am glad that you have not committed that test, which is too complex for merely AST comparision. Could you move the parseAndAssertSame test to the typescript-estree.test.js? I believe typescript-estree should support parsing accessor property very well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working as expected in typescript-estree.test.js, thanks 馃憤

@pkg-pr-new

pkg-pr-new Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

commit: e5a6e70

@JLHwung JLHwung left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@nicolo-ribaudo nicolo-ribaudo added the PR: Bug Fix 馃悰 A type of pull request used for our changelog categories label Sep 30, 2026

@nicolo-ribaudo nicolo-ribaudo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@nicolo-ribaudo nicolo-ribaudo changed the title Fix eslint-parser AccessorProperty template parsing (#18230) Fix eslint-parser AST definition of AccessorProperty Sep 30, 2026
@nicolo-ribaudo
nicolo-ribaudo merged commit b6bb564 into babel:main Sep 30, 2026
57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: Bug Fix 馃悰 A type of pull request used for our changelog categories

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@babel/eslint-parser: Inconsistent TemplateElement ranges for PropertyDefinition vs. AccessorProperty

4 participants