Skip to content

Commit 8779d4c

Browse files
authored
Fix JS merge crashes from lovefield (#27989)
1. Merge enum with expando. 2. Merge enum member with property assignment. 3. Merge interface-declared method declaration with prototype-property-assignment method declaration. The reason that the enum merges crash is that getTypeOfSymbol assumes that symbol flags are (basically) mutually exclusive. This assumption is shredded, badly, for JS merges. One fix is to drop the assumption of exclusivity and instead order cases by least to most likely. This has the highest chance of working, but is also slow, since you would prefer to order cases by most likely *first*, not *last*. The other fix, which is what I did here, is to add a last-chance re-dispatch at the bottom of `getTypeOfVariableOrParameterOrPropertyWorker`. This dispatch uses the valueDeclaration instead of the symbol flags.
1 parent a3c2268 commit 8779d4c

9 files changed

+287
-0
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,9 @@ namespace ts {
22142214

22152215
function getDeclarationOfJSPrototypeContainer(symbol: Symbol) {
22162216
const decl = symbol.parent!.valueDeclaration;
2217+
if (!decl) {
2218+
return undefined;
2219+
}
22172220
const initializer = isAssignmentDeclaration(decl) ? getAssignedExpandoInitializer(decl) :
22182221
hasOnlyExpressionInitializer(decl) ? getDeclaredExpandoInitializer(decl) :
22192222
undefined;
@@ -5241,6 +5244,14 @@ namespace ts {
52415244
|| isBindingElement(declaration)) {
52425245
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
52435246
}
5247+
// getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive.
5248+
// Re-dispatch based on valueDeclaration.kind instead.
5249+
else if (isEnumDeclaration(declaration)) {
5250+
type = getTypeOfFuncClassEnumModule(symbol);
5251+
}
5252+
else if (isEnumMember(declaration)) {
5253+
type = getTypeOfEnumMember(symbol);
5254+
}
52445255
else {
52455256
return Debug.fail("Unhandled declaration kind! " + Debug.showSyntaxKind(declaration) + " for " + Debug.showSymbol(symbol));
52465257
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/conformance/salsa/enums.js(2,10): error TS2540: Cannot assign to 'DESC' because it is a constant or a read-only property.
2+
tests/cases/conformance/salsa/enums.js(3,10): error TS2540: Cannot assign to 'ASC' because it is a constant or a read-only property.
3+
4+
5+
==== tests/cases/conformance/salsa/lovefield-ts.d.ts (0 errors) ====
6+
// bug #27352, crashes from github.com/google/lovefield
7+
declare namespace lf {
8+
export enum Order { ASC, DESC }
9+
}
10+
11+
==== tests/cases/conformance/salsa/enums.js (2 errors) ====
12+
lf.Order = {}
13+
lf.Order.DESC = 0;
14+
~~~~
15+
!!! error TS2540: Cannot assign to 'DESC' because it is a constant or a read-only property.
16+
lf.Order.ASC = 1;
17+
~~~
18+
!!! error TS2540: Cannot assign to 'ASC' because it is a constant or a read-only property.
19+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/salsa/lovefield-ts.d.ts ===
2+
// bug #27352, crashes from github.com/google/lovefield
3+
declare namespace lf {
4+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(enums.js, 0, 0), Decl(enums.js, 0, 13))
5+
6+
export enum Order { ASC, DESC }
7+
>Order : Symbol(Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
8+
>ASC : Symbol(Order.ASC, Decl(lovefield-ts.d.ts, 2, 23), Decl(enums.js, 1, 18))
9+
>DESC : Symbol(Order.DESC, Decl(lovefield-ts.d.ts, 2, 28), Decl(enums.js, 0, 13))
10+
}
11+
12+
=== tests/cases/conformance/salsa/enums.js ===
13+
lf.Order = {}
14+
>lf.Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
15+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(enums.js, 0, 0), Decl(enums.js, 0, 13))
16+
>Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
17+
18+
lf.Order.DESC = 0;
19+
>lf.Order.DESC : Symbol(lf.Order.DESC, Decl(lovefield-ts.d.ts, 2, 28), Decl(enums.js, 0, 13))
20+
>lf.Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
21+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(enums.js, 0, 0), Decl(enums.js, 0, 13))
22+
>Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
23+
>DESC : Symbol(lf.Order.DESC, Decl(lovefield-ts.d.ts, 2, 28), Decl(enums.js, 0, 13))
24+
25+
lf.Order.ASC = 1;
26+
>lf.Order.ASC : Symbol(lf.Order.ASC, Decl(lovefield-ts.d.ts, 2, 23), Decl(enums.js, 1, 18))
27+
>lf.Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
28+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(enums.js, 0, 0), Decl(enums.js, 0, 13))
29+
>Order : Symbol(lf.Order, Decl(lovefield-ts.d.ts, 1, 22), Decl(enums.js, 0, 0), Decl(enums.js, 1, 3))
30+
>ASC : Symbol(lf.Order.ASC, Decl(lovefield-ts.d.ts, 2, 23), Decl(enums.js, 1, 18))
31+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/conformance/salsa/lovefield-ts.d.ts ===
2+
// bug #27352, crashes from github.com/google/lovefield
3+
declare namespace lf {
4+
>lf : typeof lf
5+
6+
export enum Order { ASC, DESC }
7+
>Order : Order
8+
>ASC : Order
9+
>DESC : Order
10+
}
11+
12+
=== tests/cases/conformance/salsa/enums.js ===
13+
lf.Order = {}
14+
>lf.Order = {} : typeof lf.Order
15+
>lf.Order : typeof lf.Order
16+
>lf : typeof lf
17+
>Order : typeof lf.Order
18+
>{} : {}
19+
20+
lf.Order.DESC = 0;
21+
>lf.Order.DESC = 0 : 0
22+
>lf.Order.DESC : any
23+
>lf.Order : typeof lf.Order
24+
>lf : typeof lf
25+
>Order : typeof lf.Order
26+
>DESC : any
27+
>0 : 0
28+
29+
lf.Order.ASC = 1;
30+
>lf.Order.ASC = 1 : 1
31+
>lf.Order.ASC : any
32+
>lf.Order : typeof lf.Order
33+
>lf : typeof lf
34+
>Order : typeof lf.Order
35+
>ASC : any
36+
>1 : 1
37+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tests/cases/conformance/salsa/lovefield-ts.d.ts(4,19): error TS2503: Cannot find namespace 'query'.
2+
tests/cases/conformance/salsa/lovefield-ts.d.ts(5,24): error TS2503: Cannot find namespace 'schema'.
3+
tests/cases/conformance/salsa/lovefield-ts.d.ts(7,25): error TS2503: Cannot find namespace 'query'.
4+
tests/cases/conformance/salsa/lovefield-ts.d.ts(9,14): error TS2304: Cannot find name 'TransactionStats'.
5+
tests/cases/conformance/salsa/lovefield.js(3,23): error TS2694: Namespace 'lf' has no exported member 'schema'.
6+
tests/cases/conformance/salsa/lovefield.js(4,14): error TS2304: Cannot find name 'IThenable'.
7+
8+
9+
==== tests/cases/conformance/salsa/lovefield-ts.d.ts (4 errors) ====
10+
// bug #27352, crashes from github.com/google/lovefield
11+
declare namespace lf {
12+
export interface Transaction {
13+
attach(query: query.Builder): Promise<Array<Object>>
14+
~~~~~
15+
!!! error TS2503: Cannot find namespace 'query'.
16+
begin(scope: Array<schema.Table>): Promise<void>
17+
~~~~~~
18+
!!! error TS2503: Cannot find namespace 'schema'.
19+
commit(): Promise<void>
20+
exec(queries: Array<query.Builder>): Promise<Array<Array<Object>>>
21+
~~~~~
22+
!!! error TS2503: Cannot find namespace 'query'.
23+
rollback(): Promise<void>
24+
stats(): TransactionStats
25+
~~~~~~~~~~~~~~~~
26+
!!! error TS2304: Cannot find name 'TransactionStats'.
27+
}
28+
}
29+
==== tests/cases/conformance/salsa/lovefield.js (2 errors) ====
30+
lf.Transaction = function() {};
31+
/**
32+
* @param {!Array<!lf.schema.Table>} scope
33+
~~~~~~
34+
!!! error TS2694: Namespace 'lf' has no exported member 'schema'.
35+
* @return {!IThenable}
36+
~~~~~~~~~
37+
!!! error TS2304: Cannot find name 'IThenable'.
38+
*/
39+
lf.Transaction.prototype.begin = function(scope) {};
40+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/conformance/salsa/lovefield-ts.d.ts ===
2+
// bug #27352, crashes from github.com/google/lovefield
3+
declare namespace lf {
4+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(lovefield.js, 0, 0))
5+
6+
export interface Transaction {
7+
>Transaction : Symbol(Transaction, Decl(lovefield-ts.d.ts, 1, 22), Decl(lovefield.js, 0, 0))
8+
9+
attach(query: query.Builder): Promise<Array<Object>>
10+
>attach : Symbol(Transaction.attach, Decl(lovefield-ts.d.ts, 2, 32))
11+
>query : Symbol(query, Decl(lovefield-ts.d.ts, 3, 11))
12+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
13+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
14+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
15+
16+
begin(scope: Array<schema.Table>): Promise<void>
17+
>begin : Symbol(Transaction.begin, Decl(lovefield-ts.d.ts, 3, 56), Decl(lovefield.js, 0, 31))
18+
>scope : Symbol(scope, Decl(lovefield-ts.d.ts, 4, 10))
19+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
20+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
21+
22+
commit(): Promise<void>
23+
>commit : Symbol(Transaction.commit, Decl(lovefield-ts.d.ts, 4, 52))
24+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
25+
26+
exec(queries: Array<query.Builder>): Promise<Array<Array<Object>>>
27+
>exec : Symbol(Transaction.exec, Decl(lovefield-ts.d.ts, 5, 27))
28+
>queries : Symbol(queries, Decl(lovefield-ts.d.ts, 6, 9))
29+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
30+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
31+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
32+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
33+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
34+
35+
rollback(): Promise<void>
36+
>rollback : Symbol(Transaction.rollback, Decl(lovefield-ts.d.ts, 6, 70))
37+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
38+
39+
stats(): TransactionStats
40+
>stats : Symbol(Transaction.stats, Decl(lovefield-ts.d.ts, 7, 29))
41+
}
42+
}
43+
=== tests/cases/conformance/salsa/lovefield.js ===
44+
lf.Transaction = function() {};
45+
>lf.Transaction : Symbol(lf.Transaction, Decl(lovefield-ts.d.ts, 1, 22), Decl(lovefield.js, 0, 0))
46+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(lovefield.js, 0, 0))
47+
>Transaction : Symbol(lf.Transaction, Decl(lovefield-ts.d.ts, 1, 22), Decl(lovefield.js, 0, 0))
48+
49+
/**
50+
* @param {!Array<!lf.schema.Table>} scope
51+
* @return {!IThenable}
52+
*/
53+
lf.Transaction.prototype.begin = function(scope) {};
54+
>lf.Transaction.prototype : Symbol(lf.Transaction.begin, Decl(lovefield-ts.d.ts, 3, 56), Decl(lovefield.js, 0, 31))
55+
>lf.Transaction : Symbol(lf.Transaction, Decl(lovefield-ts.d.ts, 1, 22), Decl(lovefield.js, 0, 0))
56+
>lf : Symbol(lf, Decl(lovefield-ts.d.ts, 0, 0), Decl(lovefield.js, 0, 0))
57+
>Transaction : Symbol(lf.Transaction, Decl(lovefield-ts.d.ts, 1, 22), Decl(lovefield.js, 0, 0))
58+
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
59+
>begin : Symbol(lf.Transaction.begin, Decl(lovefield-ts.d.ts, 3, 56), Decl(lovefield.js, 0, 31))
60+
>scope : Symbol(scope, Decl(lovefield.js, 5, 42))
61+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
=== tests/cases/conformance/salsa/lovefield-ts.d.ts ===
2+
// bug #27352, crashes from github.com/google/lovefield
3+
declare namespace lf {
4+
export interface Transaction {
5+
attach(query: query.Builder): Promise<Array<Object>>
6+
>attach : (query: any) => Promise<Object[]>
7+
>query : any
8+
>query : any
9+
10+
begin(scope: Array<schema.Table>): Promise<void>
11+
>begin : (scope: any[]) => Promise<void>
12+
>scope : any[]
13+
>schema : any
14+
15+
commit(): Promise<void>
16+
>commit : () => Promise<void>
17+
18+
exec(queries: Array<query.Builder>): Promise<Array<Array<Object>>>
19+
>exec : (queries: any[]) => Promise<Object[][]>
20+
>queries : any[]
21+
>query : any
22+
23+
rollback(): Promise<void>
24+
>rollback : () => Promise<void>
25+
26+
stats(): TransactionStats
27+
>stats : () => any
28+
}
29+
}
30+
=== tests/cases/conformance/salsa/lovefield.js ===
31+
lf.Transaction = function() {};
32+
>lf.Transaction = function() {} : typeof Transaction
33+
>lf.Transaction : typeof Transaction
34+
>lf : typeof lf
35+
>Transaction : typeof Transaction
36+
>function() {} : typeof Transaction
37+
38+
/**
39+
* @param {!Array<!lf.schema.Table>} scope
40+
* @return {!IThenable}
41+
*/
42+
lf.Transaction.prototype.begin = function(scope) {};
43+
>lf.Transaction.prototype.begin = function(scope) {} : (scope: any[]) => any
44+
>lf.Transaction.prototype.begin : any
45+
>lf.Transaction.prototype : any
46+
>lf.Transaction : typeof Transaction
47+
>lf : typeof lf
48+
>Transaction : typeof Transaction
49+
>prototype : any
50+
>begin : any
51+
>function(scope) {} : (scope: any[]) => any
52+
>scope : any[]
53+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @Filename: lovefield-ts.d.ts
5+
// bug #27352, crashes from github.com/google/lovefield
6+
declare namespace lf {
7+
export enum Order { ASC, DESC }
8+
}
9+
10+
// @Filename: enums.js
11+
lf.Order = {}
12+
lf.Order.DESC = 0;
13+
lf.Order.ASC = 1;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @Filename: lovefield-ts.d.ts
5+
// bug #27352, crashes from github.com/google/lovefield
6+
declare namespace lf {
7+
export interface Transaction {
8+
attach(query: query.Builder): Promise<Array<Object>>
9+
begin(scope: Array<schema.Table>): Promise<void>
10+
commit(): Promise<void>
11+
exec(queries: Array<query.Builder>): Promise<Array<Array<Object>>>
12+
rollback(): Promise<void>
13+
stats(): TransactionStats
14+
}
15+
}
16+
// @Filename: lovefield.js
17+
lf.Transaction = function() {};
18+
/**
19+
* @param {!Array<!lf.schema.Table>} scope
20+
* @return {!IThenable}
21+
*/
22+
lf.Transaction.prototype.begin = function(scope) {};

0 commit comments

Comments
 (0)