Fix crash for private identifier in expando assignments (#37764)

* Fix crash for private identifier in expando assignments

It does this by disallowing private identifiers from expando assignments
entirely. I haven't thought of a scenario where they make sense, but I
haven't thought about it exhaustively either.

Fixes #37356

* Update baselines

I think the new error is probably better. It's certainly different!
This commit is contained in:
Nathan Shively-Sanders
2020-04-03 10:29:22 -07:00
committed by GitHub
parent d68295e74e
commit 7cf4b12d88
8 changed files with 48 additions and 10 deletions
+2 -2
View File
@@ -2155,7 +2155,7 @@ namespace ts {
/** Any series of property and element accesses. */
export function isBindableStaticAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticAccessExpression {
return isPropertyAccessExpression(node) && (!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword || isBindableStaticNameExpression(node.expression, /*excludeThisKeyword*/ true))
return isPropertyAccessExpression(node) && (!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword || isIdentifier(node.name) && isBindableStaticNameExpression(node.expression, /*excludeThisKeyword*/ true))
|| isBindableStaticElementAccessExpression(node, excludeThisKeyword);
}
@@ -4432,7 +4432,7 @@ namespace ts {
}
export function isPropertyAccessEntityNameExpression(node: Node): node is PropertyAccessEntityNameExpression {
return isPropertyAccessExpression(node) && isEntityNameExpression(node.expression);
return isPropertyAccessExpression(node) && isIdentifier(node.name) && isEntityNameExpression(node.expression);
}
export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined {
@@ -0,0 +1,9 @@
//// [privateIdentifierExpando.js]
const x = {};
x.#bar.baz = 20;
//// [privateIdentifierExpando.d.ts]
declare const x: {};
@@ -0,0 +1,7 @@
=== tests/cases/conformance/salsa/privateIdentifierExpando.js ===
const x = {};
>x : Symbol(x, Decl(privateIdentifierExpando.js, 0, 5))
x.#bar.baz = 20;
>x : Symbol(x, Decl(privateIdentifierExpando.js, 0, 5))
@@ -0,0 +1,13 @@
=== tests/cases/conformance/salsa/privateIdentifierExpando.js ===
const x = {};
>x : {}
>{} : {}
x.#bar.baz = 20;
>x.#bar.baz = 20 : 20
>x.#bar.baz : any
>x.#bar : any
>x : {}
>baz : any
>20 : 20
@@ -1,13 +1,16 @@
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(1,9): error TS2339: Property '#nope' does not exist on type 'typeof import("tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment")'.
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(1,1): error TS2304: Cannot find name 'exports'.
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(1,9): error TS18016: Private identifiers are not allowed outside class bodies.
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(3,13): error TS18016: Private identifiers are not allowed outside class bodies.
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(6,3): error TS2339: Property '#foo' does not exist on type 'typeof B'.
tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js(11,14): error TS2339: Property '#foo' does not exist on type 'C'.
==== tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js (4 errors) ====
==== tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js (5 errors) ====
exports.#nope = 1; // Error (outside class body)
~~~~~~~
!!! error TS2304: Cannot find name 'exports'.
~~~~~
!!! error TS2339: Property '#nope' does not exist on type 'typeof import("tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment")'.
!!! error TS18016: Private identifiers are not allowed outside class bodies.
function A() { }
A.prototype.#no = 2; // Error (outside class body)
~~~
@@ -1,7 +1,5 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment.js ===
exports.#nope = 1; // Error (outside class body)
>exports : Symbol(#nope, Decl(privateNameJsBadAssignment.js, 0, 0))
function A() { }
>A : Symbol(A, Decl(privateNameJsBadAssignment.js, 0, 18))
@@ -11,10 +9,10 @@ A.prototype.#no = 2; // Error (outside class body)
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
class B {}
>B : Symbol(B, Decl(privateNameJsBadAssignment.js, 2, 20), Decl(privateNameJsBadAssignment.js, 4, 10))
>B : Symbol(B, Decl(privateNameJsBadAssignment.js, 2, 20))
B.#foo = 3; // Error (outside class body)
>B : Symbol(B, Decl(privateNameJsBadAssignment.js, 2, 20), Decl(privateNameJsBadAssignment.js, 4, 10))
>B : Symbol(B, Decl(privateNameJsBadAssignment.js, 2, 20))
class C {
>C : Symbol(C, Decl(privateNameJsBadAssignment.js, 5, 11))
@@ -2,7 +2,7 @@
exports.#nope = 1; // Error (outside class body)
>exports.#nope = 1 : 1
>exports.#nope : any
>exports : typeof import("tests/cases/conformance/classes/members/privateNames/privateNameJsBadAssignment")
>exports : any
>1 : 1
function A() { }
@@ -0,0 +1,8 @@
// @allowJs: true
// @checkJs: true
// @declaration: true
// @emitDeclarationOnly: true
// @Filename: privateIdentifierExpando.js
const x = {};
x.#bar.baz = 20;