From 1dc3d06b7ad416ab6f2e4574c31381ec15ab5c55 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 4 Jun 2024 00:48:35 +0800 Subject: [PATCH 1/4] feat(typescript-estree): private identifiers can only appear on LHS of in expressions --- .../_error_/private-lhs-not-in/fixture.ts | 4 + .../snapshots/1-TSESTree-Error.shot | 11 ++ .../snapshots/2-Babel-Error.shot | 3 + .../snapshots/3-Alignment-Error.shot | 3 + .../fixtures/_error_/private-rhs/fixture.ts | 4 + .../snapshots/1-TSESTree-Error.shot | 11 ++ .../private-rhs/snapshots/2-Babel-Error.shot | 3 + .../snapshots/3-Alignment-Error.shot | 3 + .../fixtures/private-lhs-in/fixture.ts | 4 + .../snapshots/1-TSESTree-AST.shot | 142 +++++++++++++++++ .../snapshots/2-TSESTree-Tokens.shot | 126 +++++++++++++++ .../private-lhs-in/snapshots/3-Babel-AST.shot | 120 ++++++++++++++ .../snapshots/4-Babel-Tokens.shot | 126 +++++++++++++++ .../snapshots/5-AST-Alignment-AST.shot | 146 ++++++++++++++++++ .../snapshots/6-AST-Alignment-Tokens.shot | 136 ++++++++++++++++ .../src/expression/BinaryExpression/spec.ts | 13 +- .../_error_/private-id-lhs/fixture.ts | 4 + .../snapshots/1-TSESTree-Error.shot | 11 ++ .../snapshots/2-Babel-Error.shot | 3 + .../snapshots/3-Alignment-Error.shot | 3 + .../_error_/private-id-rhs/fixture.ts | 4 + .../snapshots/1-TSESTree-Error.shot | 11 ++ .../snapshots/2-Babel-Error.shot | 3 + .../snapshots/3-Alignment-Error.shot | 3 + .../tests/fixtures-with-differences-ast.shot | 1 + .../fixtures-with-differences-tokens.shot | 1 + packages/typescript-estree/src/convert.ts | 14 ++ 27 files changed, 911 insertions(+), 2 deletions(-) create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/fixture.ts create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/3-Alignment-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/fixture.ts create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/3-Alignment-Error.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/1-TSESTree-AST.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/2-TSESTree-Tokens.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/3-Babel-AST.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/4-Babel-Tokens.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/5-AST-Alignment-AST.shot create mode 100644 packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/6-AST-Alignment-Tokens.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/fixture.ts create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/3-Alignment-Error.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/fixture.ts create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/3-Alignment-Error.shot diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/fixture.ts b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/fixture.ts new file mode 100644 index 000000000000..daafa9ccb62f --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/fixture.ts @@ -0,0 +1,4 @@ +class A { + #a; + b = #a + 1; +} diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..d13505ccf2b0 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-lhs-not-in TSESTree - Error 1`] = ` +"TSError + 1 | class A { + 2 | #a; +> 3 | b = #a + 1; + | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + 4 | } + 5 |" +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..d22242e1bb87 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-lhs-not-in Babel - Error 1`] = `[SyntaxError: Private names are only allowed in property accesses (\`obj.#a\`) or in \`in\` expressions (\`#a in obj\`). (3:6)]`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..1805523cc015 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-lhs-not-in Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/fixture.ts b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/fixture.ts new file mode 100644 index 000000000000..ce0e507a49fd --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/fixture.ts @@ -0,0 +1,4 @@ +class A { + #a; + b = 1 in #a; +} diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..fec52de0c362 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-rhs TSESTree - Error 1`] = ` +"TSError + 1 | class A { + 2 | #a; +> 3 | b = 1 in #a; + | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + 4 | } + 5 |" +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..7b5133414a25 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-rhs Babel - Error 1`] = `[SyntaxError: Private names are only allowed in property accesses (\`obj.#a\`) or in \`in\` expressions (\`#a in obj\`). (3:11)]`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..c64927b583c1 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-rhs/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression _error_ private-rhs Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts new file mode 100644 index 000000000000..f17ec7e20d07 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts @@ -0,0 +1,4 @@ +class A { + #a; + b = #a in A; +} diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/1-TSESTree-AST.shot new file mode 100644 index 000000000000..c54d2d6421d8 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/1-TSESTree-AST.shot @@ -0,0 +1,142 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in TSESTree - AST 1`] = ` +Program { + type: "Program", + body: [ + ClassDeclaration { + type: "ClassDeclaration", + abstract: false, + body: ClassBody { + type: "ClassBody", + body: [ + PropertyDefinition { + type: "PropertyDefinition", + computed: false, + declare: false, + decorators: [], + definite: false, + key: PrivateIdentifier { + type: "PrivateIdentifier", + name: "a", + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, + optional: false, + override: false, + readonly: false, + static: false, + value: null, + + range: [12, 15], + loc: { + start: { column: 2, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + PropertyDefinition { + type: "PropertyDefinition", + computed: false, + declare: false, + decorators: [], + definite: false, + key: Identifier { + type: "Identifier", + decorators: [], + name: "b", + optional: false, + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, + optional: false, + override: false, + readonly: false, + static: false, + value: BinaryExpression { + type: "BinaryExpression", + left: PrivateIdentifier { + type: "PrivateIdentifier", + name: "a", + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + operator: "in", + right: Identifier { + type: "Identifier", + decorators: [], + name: "A", + optional: false, + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [22, 29], + loc: { + start: { column: 6, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [18, 30], + loc: { + start: { column: 2, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + ], + + range: [8, 32], + loc: { + start: { column: 8, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, + declare: false, + decorators: [], + id: Identifier { + type: "Identifier", + decorators: [], + name: "A", + optional: false, + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + implements: [], + superClass: null, + + range: [0, 32], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, + ], + sourceType: "script", + + range: [0, 33], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 5 }, + }, +} +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/2-TSESTree-Tokens.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/2-TSESTree-Tokens.shot new file mode 100644 index 000000000000..09fb221ebb60 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/2-TSESTree-Tokens.shot @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in TSESTree - Tokens 1`] = ` +[ + Keyword { + type: "Keyword", + value: "class", + + range: [0, 5], + loc: { + start: { column: 0, line: 1 }, + end: { column: 5, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "A", + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [8, 9], + loc: { + start: { column: 8, line: 1 }, + end: { column: 9, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "#a", + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [14, 15], + loc: { + start: { column: 4, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "b", + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "=", + + range: [20, 21], + loc: { + start: { column: 4, line: 3 }, + end: { column: 5, line: 3 }, + }, + }, + Identifier { + type: "Identifier", + value: "#a", + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + Keyword { + type: "Keyword", + value: "in", + + range: [25, 27], + loc: { + start: { column: 9, line: 3 }, + end: { column: 11, line: 3 }, + }, + }, + Identifier { + type: "Identifier", + value: "A", + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [29, 30], + loc: { + start: { column: 13, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [31, 32], + loc: { + start: { column: 0, line: 4 }, + end: { column: 1, line: 4 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/3-Babel-AST.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/3-Babel-AST.shot new file mode 100644 index 000000000000..8d90317fec8b --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/3-Babel-AST.shot @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in Babel - AST 1`] = ` +Program { + type: "Program", + body: [ + ClassDeclaration { + type: "ClassDeclaration", + body: ClassBody { + type: "ClassBody", + body: [ + PropertyDefinition { + type: "PropertyDefinition", + computed: false, + key: PrivateIdentifier { + type: "PrivateIdentifier", + name: "a", + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, + static: false, + value: null, + + range: [12, 15], + loc: { + start: { column: 2, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + PropertyDefinition { + type: "PropertyDefinition", + computed: false, + key: Identifier { + type: "Identifier", + name: "b", + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, + static: false, + value: BinaryExpression { + type: "BinaryExpression", + left: PrivateIdentifier { + type: "PrivateIdentifier", + name: "a", + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + operator: "in", + right: Identifier { + type: "Identifier", + name: "A", + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [22, 29], + loc: { + start: { column: 6, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [18, 30], + loc: { + start: { column: 2, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + ], + + range: [8, 32], + loc: { + start: { column: 8, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, + id: Identifier { + type: "Identifier", + name: "A", + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + superClass: null, + + range: [0, 32], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, + ], + sourceType: "script", + + range: [0, 33], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 5 }, + }, +} +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/4-Babel-Tokens.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/4-Babel-Tokens.shot new file mode 100644 index 000000000000..2ffda1aafbe5 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/4-Babel-Tokens.shot @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in Babel - Tokens 1`] = ` +[ + Keyword { + type: "Keyword", + value: "class", + + range: [0, 5], + loc: { + start: { column: 0, line: 1 }, + end: { column: 5, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "A", + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [8, 9], + loc: { + start: { column: 8, line: 1 }, + end: { column: 9, line: 1 }, + }, + }, + PrivateIdentifier { + type: "PrivateIdentifier", + value: "a", + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [14, 15], + loc: { + start: { column: 4, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "b", + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "=", + + range: [20, 21], + loc: { + start: { column: 4, line: 3 }, + end: { column: 5, line: 3 }, + }, + }, + PrivateIdentifier { + type: "PrivateIdentifier", + value: "a", + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + Keyword { + type: "Keyword", + value: "in", + + range: [25, 27], + loc: { + start: { column: 9, line: 3 }, + end: { column: 11, line: 3 }, + }, + }, + Identifier { + type: "Identifier", + value: "A", + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [29, 30], + loc: { + start: { column: 13, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [31, 32], + loc: { + start: { column: 0, line: 4 }, + end: { column: 1, line: 4 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/5-AST-Alignment-AST.shot new file mode 100644 index 000000000000..be95b85d8e44 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/5-AST-Alignment-AST.shot @@ -0,0 +1,146 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in AST Alignment - AST 1`] = ` +"Snapshot Diff: +- TSESTree ++ Babel + + Program { + type: 'Program', + body: Array [ + ClassDeclaration { + type: 'ClassDeclaration', +- abstract: false, + body: ClassBody { + type: 'ClassBody', + body: Array [ + PropertyDefinition { + type: 'PropertyDefinition', + computed: false, +- declare: false, +- decorators: Array [], +- definite: false, + key: PrivateIdentifier { + type: 'PrivateIdentifier', + name: 'a', + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, +- optional: false, +- override: false, +- readonly: false, + static: false, + value: null, + + range: [12, 15], + loc: { + start: { column: 2, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + PropertyDefinition { + type: 'PropertyDefinition', + computed: false, +- declare: false, +- decorators: Array [], +- definite: false, + key: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'b', +- optional: false, + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, +- optional: false, +- override: false, +- readonly: false, + static: false, + value: BinaryExpression { + type: 'BinaryExpression', + left: PrivateIdentifier { + type: 'PrivateIdentifier', + name: 'a', + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + operator: 'in', + right: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'A', +- optional: false, + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [22, 29], + loc: { + start: { column: 6, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + + range: [18, 30], + loc: { + start: { column: 2, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + ], + + range: [8, 32], + loc: { + start: { column: 8, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, +- declare: false, +- decorators: Array [], + id: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'A', +- optional: false, + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, +- implements: Array [], + superClass: null, + + range: [0, 32], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 4 }, + }, + }, + ], + sourceType: 'script', + + range: [0, 33], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 5 }, + }, + }" +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/6-AST-Alignment-Tokens.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/6-AST-Alignment-Tokens.shot new file mode 100644 index 000000000000..31764d700643 --- /dev/null +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/private-lhs-in/snapshots/6-AST-Alignment-Tokens.shot @@ -0,0 +1,136 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression BinaryExpression private-lhs-in AST Alignment - Token 1`] = ` +"Snapshot Diff: +- TSESTree ++ Babel + + Array [ + Keyword { + type: 'Keyword', + value: 'class', + + range: [0, 5], + loc: { + start: { column: 0, line: 1 }, + end: { column: 5, line: 1 }, + }, + }, + Identifier { + type: 'Identifier', + value: 'A', + + range: [6, 7], + loc: { + start: { column: 6, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + Punctuator { + type: 'Punctuator', + value: '{', + + range: [8, 9], + loc: { + start: { column: 8, line: 1 }, + end: { column: 9, line: 1 }, + }, + }, +- Identifier { +- type: 'Identifier', +- value: '#a', ++ PrivateIdentifier { ++ type: 'PrivateIdentifier', ++ value: 'a', + + range: [12, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 4, line: 2 }, + }, + }, + Punctuator { + type: 'Punctuator', + value: ';', + + range: [14, 15], + loc: { + start: { column: 4, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + Identifier { + type: 'Identifier', + value: 'b', + + range: [18, 19], + loc: { + start: { column: 2, line: 3 }, + end: { column: 3, line: 3 }, + }, + }, + Punctuator { + type: 'Punctuator', + value: '=', + + range: [20, 21], + loc: { + start: { column: 4, line: 3 }, + end: { column: 5, line: 3 }, + }, + }, +- Identifier { +- type: 'Identifier', +- value: '#a', ++ PrivateIdentifier { ++ type: 'PrivateIdentifier', ++ value: 'a', + + range: [22, 24], + loc: { + start: { column: 6, line: 3 }, + end: { column: 8, line: 3 }, + }, + }, + Keyword { + type: 'Keyword', + value: 'in', + + range: [25, 27], + loc: { + start: { column: 9, line: 3 }, + end: { column: 11, line: 3 }, + }, + }, + Identifier { + type: 'Identifier', + value: 'A', + + range: [28, 29], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, + }, + }, + Punctuator { + type: 'Punctuator', + value: ';', + + range: [29, 30], + loc: { + start: { column: 13, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + Punctuator { + type: 'Punctuator', + value: '}', + + range: [31, 32], + loc: { + start: { column: 0, line: 4 }, + end: { column: 1, line: 4 }, + }, + }, + ]" +`; diff --git a/packages/ast-spec/src/expression/BinaryExpression/spec.ts b/packages/ast-spec/src/expression/BinaryExpression/spec.ts index d42f1d4e77f4..61d95ec89701 100644 --- a/packages/ast-spec/src/expression/BinaryExpression/spec.ts +++ b/packages/ast-spec/src/expression/BinaryExpression/spec.ts @@ -7,9 +7,18 @@ import type { BinaryOperatorToText } from './BinaryOperatorToText'; export * from './BinaryOperatorToText'; -export interface BinaryExpression extends BaseNode { +export interface PrivateInExpression extends BaseNode { + type: AST_NODE_TYPES.BinaryExpression; + operator: 'in'; + left: PrivateIdentifier; + right: Expression; +} + +export interface SymmetricBinaryExpression extends BaseNode { type: AST_NODE_TYPES.BinaryExpression; operator: ValueOf; - left: Expression | PrivateIdentifier; + left: Expression; right: Expression; } + +export type BinaryExpression = SymmetricBinaryExpression | PrivateInExpression; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/fixture.ts b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/fixture.ts new file mode 100644 index 000000000000..402329cc285f --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/fixture.ts @@ -0,0 +1,4 @@ +class A { + #a; + c = (#a, 1); +} diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..2c546c104c8d --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-lhs TSESTree - Error 1`] = ` +"TSError + 1 | class A { + 2 | #a; +> 3 | c = (#a, 1); + | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + 4 | } + 5 |" +`; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..5f4c38cb67c5 --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-lhs Babel - Error 1`] = `[SyntaxError: Private names are only allowed in property accesses (\`obj.#a\`) or in \`in\` expressions (\`#a in obj\`). (3:7)]`; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..75446be600b8 --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-lhs Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/fixture.ts b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/fixture.ts new file mode 100644 index 000000000000..9f1df9d73fa4 --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/fixture.ts @@ -0,0 +1,4 @@ +class A { + #a; + c = (1, #a); +} diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..efc3f8222475 --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-rhs TSESTree - Error 1`] = ` +"TSError + 1 | class A { + 2 | #a; +> 3 | c = (1, #a); + | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + 4 | } + 5 |" +`; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..c0eaaa32329b --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-rhs Babel - Error 1`] = `[SyntaxError: Private names are only allowed in property accesses (\`obj.#a\`) or in \`in\` expressions (\`#a in obj\`). (3:10)]`; diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..87b15f36b42e --- /dev/null +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-rhs/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression SequenceExpression _error_ private-id-rhs Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/tests/fixtures-with-differences-ast.shot b/packages/ast-spec/tests/fixtures-with-differences-ast.shot index 409216445120..fea6fe808b2f 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-ast.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-ast.shot @@ -132,6 +132,7 @@ exports[`AST Fixtures List fixtures with AST differences 1`] = ` "element/AccessorProperty/fixtures/no-annotation-with-value/fixture.ts", "element/AccessorProperty/fixtures/with-annotation-no-value/fixture.ts", "element/AccessorProperty/fixtures/with-annotation-with-value/fixture.ts", + "expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts", "expression/TSSatisfiesExpression/fixtures/arrow-func-with-parentheses/fixture.ts", "expression/TSSatisfiesExpression/fixtures/chained-satisfies/fixture.ts", "expression/TSSatisfiesExpression/fixtures/conditional-no-parentheses/fixture.ts", diff --git a/packages/ast-spec/tests/fixtures-with-differences-tokens.shot b/packages/ast-spec/tests/fixtures-with-differences-tokens.shot index 43ef25b5170b..d64455335168 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-tokens.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-tokens.shot @@ -26,6 +26,7 @@ exports[`AST Fixtures List fixtures with Token differences 1`] = ` "element/AccessorProperty/fixtures/modifier-private/fixture.ts", "element/AccessorProperty/fixtures/modifier-protected/fixture.ts", "element/AccessorProperty/fixtures/modifier-public/fixture.ts", + "expression/BinaryExpression/fixtures/private-lhs-in/fixture.ts", "expression/UpdateExpression/fixtures/valid-assignment/fixture.ts", "jsx/JSXNamespacedName/fixtures/component-dashed/fixture.tsx", "jsx/JSXNamespacedName/fixtures/component/fixture.tsx", diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 9d068cbf2432..8d072b64e736 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2089,6 +2089,20 @@ export class Converter { // Binary Operations case SyntaxKind.BinaryExpression: { + if ( + node.operatorToken.kind !== SyntaxKind.InKeyword && + node.left.kind === SyntaxKind.PrivateIdentifier + ) { + this.#throwError( + node.left, + "Private identifiers are only allowed on the left-hand-side of an 'in' expression.", + ); + } else if (node.right.kind === SyntaxKind.PrivateIdentifier) { + this.#throwError( + node.right, + "Private identifiers are only allowed on the left-hand-side of an 'in' expression.", + ); + } // TypeScript uses BinaryExpression for sequences as well if (isComma(node.operatorToken)) { const result = this.createNode(node, { From 470e4f2dd3056db9c0c3a64dcef8808106f50916 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 29 Sep 2025 12:22:34 -0400 Subject: [PATCH 2/4] Fix lint issues --- packages/ast-spec/src/expression/BinaryExpression/spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ast-spec/src/expression/BinaryExpression/spec.ts b/packages/ast-spec/src/expression/BinaryExpression/spec.ts index 61d95ec89701..874df2c6d586 100644 --- a/packages/ast-spec/src/expression/BinaryExpression/spec.ts +++ b/packages/ast-spec/src/expression/BinaryExpression/spec.ts @@ -9,16 +9,16 @@ export * from './BinaryOperatorToText'; export interface PrivateInExpression extends BaseNode { type: AST_NODE_TYPES.BinaryExpression; - operator: 'in'; left: PrivateIdentifier; + operator: 'in'; right: Expression; } export interface SymmetricBinaryExpression extends BaseNode { type: AST_NODE_TYPES.BinaryExpression; - operator: ValueOf; left: Expression; + operator: ValueOf; right: Expression; } -export type BinaryExpression = SymmetricBinaryExpression | PrivateInExpression; +export type BinaryExpression = PrivateInExpression | SymmetricBinaryExpression; From ae6dcb5fa05345725ad3de91314df89e5249b79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 6 Oct 2025 08:25:37 -0400 Subject: [PATCH 3/4] Update packages/typescript-estree/src/convert.ts Co-authored-by: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> --- packages/typescript-estree/src/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 8cd8b66e65f7..9e540f6adc87 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2411,7 +2411,7 @@ export class Converter { ) { this.#throwError( node.left, - "Private identifiers are only allowed on the left-hand-side of an 'in' expression.", + "Private identifiers cannot appear on the right-hand-side of an 'in' expression.", ); } else if (node.right.kind === SyntaxKind.PrivateIdentifier) { this.#throwError( From 546a854223bac2ca8fb1f4789f1e677f8eac8203 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 6 Oct 2025 08:28:24 -0400 Subject: [PATCH 4/4] yarn test -u --- .../_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot | 2 +- .../_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot index 74514e850b19..ec75deb33cff 100644 --- a/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/expression/BinaryExpression/fixtures/_error_/private-lhs-not-in/snapshots/1-TSESTree-Error.shot @@ -5,6 +5,6 @@ TSError 1 | class A { 2 | #a; > 3 | b = #a + 1; - | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + | ^^ Private identifiers cannot appear on the right-hand-side of an 'in' expression. 4 | } 5 | diff --git a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot index b3135387d8cf..ba6e5a7a7e5f 100644 --- a/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/expression/SequenceExpression/fixtures/_error_/private-id-lhs/snapshots/1-TSESTree-Error.shot @@ -5,6 +5,6 @@ TSError 1 | class A { 2 | #a; > 3 | c = (#a, 1); - | ^^ Private identifiers are only allowed on the left-hand-side of an 'in' expression. + | ^^ Private identifiers cannot appear on the right-hand-side of an 'in' expression. 4 | } 5 |