Skip to content

Commit 757e670

Browse files
authored
Fix getExpandoSymbol for already-expando symbols (#36457)
* Fix getExpandoSymbol for already-expando symbols getExpandoSymbol is intended to get the symbol of the expando in a declaration like ```js var C = class { } ``` However, when this occurs in a `module.exports` assignment, alias resolution already jumps to the exported symbol -- in this case the expando symbol: ``js // @filename: first.js module.exports = class { } // @filename: other.js /* @type {import("./first")} */ var C ``` `getExpandoSymbol` is then called on `class { }` instead of `module.exports = class { }`. Previously, `getExpandoSymbol` would incorrectly treat `class { }` the same as the export assignment and get the expando ... from the expando itself. This resulted in merging the expando with itself, which causes bogus errors and could cause other problems. Now `getExpandoSymbol` checks that the expando "initializer" is different from the declaration. * Better check for getExpandoSymbol of expando Check the declaration directly instead of the initialiser.
1 parent c239626 commit 757e670

7 files changed

+271
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2776,7 +2776,7 @@ namespace ts {
27762776
*/
27772777
function getExpandoSymbol(symbol: Symbol): Symbol | undefined {
27782778
const decl = symbol.valueDeclaration;
2779-
if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias) {
2779+
if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias || getExpandoInitializer(decl, /*isPrototypeAssignment*/ false)) {
27802780
return undefined;
27812781
}
27822782
const init = isVariableDeclaration(decl) ? getDeclaredExpandoInitializer(decl) : getAssignedExpandoInitializer(decl);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/conformance/jsdoc/MC.js ===
2+
const MW = require("./MW");
3+
>MW : Symbol(MW, Decl(MC.js, 0, 5))
4+
>require : Symbol(require)
5+
>"./MW" : Symbol("tests/cases/conformance/jsdoc/MW", Decl(MW.js, 0, 0))
6+
7+
/** @typedef {number} Cictema */
8+
9+
module.exports = class MC {
10+
>module.exports : Symbol("tests/cases/conformance/jsdoc/MC", Decl(MC.js, 0, 0))
11+
>module : Symbol(export=, Decl(MC.js, 0, 27))
12+
>exports : Symbol(export=, Decl(MC.js, 0, 27))
13+
>MC : Symbol(MC, Decl(MC.js, 4, 16))
14+
15+
watch() {
16+
>watch : Symbol(MC.watch, Decl(MC.js, 4, 27))
17+
18+
return new MW(this);
19+
>MW : Symbol(MW, Decl(MC.js, 0, 5))
20+
>this : Symbol(MC, Decl(MC.js, 4, 16))
21+
}
22+
};
23+
24+
=== tests/cases/conformance/jsdoc/MW.js ===
25+
/** @typedef {import("./MC")} MC */
26+
27+
class MW {
28+
>MW : Symbol(MW, Decl(MW.js, 0, 0))
29+
30+
/**
31+
* @param {MC} compiler the compiler
32+
*/
33+
constructor(compiler) {
34+
>compiler : Symbol(compiler, Decl(MW.js, 6, 14))
35+
36+
this.compiler = compiler;
37+
>this.compiler : Symbol(MW.compiler, Decl(MW.js, 6, 25))
38+
>this : Symbol(MW, Decl(MW.js, 0, 0))
39+
>compiler : Symbol(MW.compiler, Decl(MW.js, 6, 25))
40+
>compiler : Symbol(compiler, Decl(MW.js, 6, 14))
41+
}
42+
}
43+
44+
module.exports = MW;
45+
>module.exports : Symbol("tests/cases/conformance/jsdoc/MW", Decl(MW.js, 0, 0))
46+
>module : Symbol(export=, Decl(MW.js, 9, 1))
47+
>exports : Symbol(export=, Decl(MW.js, 9, 1))
48+
>MW : Symbol(MW, Decl(MW.js, 0, 0))
49+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/conformance/jsdoc/MC.js ===
2+
const MW = require("./MW");
3+
>MW : typeof import("tests/cases/conformance/jsdoc/MW")
4+
>require("./MW") : typeof import("tests/cases/conformance/jsdoc/MW")
5+
>require : any
6+
>"./MW" : "./MW"
7+
8+
/** @typedef {number} Cictema */
9+
10+
module.exports = class MC {
11+
>module.exports = class MC { watch() { return new MW(this); }} : typeof import("tests/cases/conformance/jsdoc/MC")
12+
>module.exports : typeof import("tests/cases/conformance/jsdoc/MC")
13+
>module : { "\"tests/cases/conformance/jsdoc/MC\"": typeof import("tests/cases/conformance/jsdoc/MC"); }
14+
>exports : typeof import("tests/cases/conformance/jsdoc/MC")
15+
>class MC { watch() { return new MW(this); }} : typeof import("tests/cases/conformance/jsdoc/MC")
16+
>MC : typeof import("tests/cases/conformance/jsdoc/MC")
17+
18+
watch() {
19+
>watch : () => import("tests/cases/conformance/jsdoc/MW")
20+
21+
return new MW(this);
22+
>new MW(this) : import("tests/cases/conformance/jsdoc/MW")
23+
>MW : typeof import("tests/cases/conformance/jsdoc/MW")
24+
>this : this
25+
}
26+
};
27+
28+
=== tests/cases/conformance/jsdoc/MW.js ===
29+
/** @typedef {import("./MC")} MC */
30+
31+
class MW {
32+
>MW : MW
33+
34+
/**
35+
* @param {MC} compiler the compiler
36+
*/
37+
constructor(compiler) {
38+
>compiler : import("tests/cases/conformance/jsdoc/MC")
39+
40+
this.compiler = compiler;
41+
>this.compiler = compiler : import("tests/cases/conformance/jsdoc/MC")
42+
>this.compiler : import("tests/cases/conformance/jsdoc/MC")
43+
>this : this
44+
>compiler : import("tests/cases/conformance/jsdoc/MC")
45+
>compiler : import("tests/cases/conformance/jsdoc/MC")
46+
}
47+
}
48+
49+
module.exports = MW;
50+
>module.exports = MW : typeof MW
51+
>module.exports : typeof MW
52+
>module : { "\"tests/cases/conformance/jsdoc/MW\"": typeof MW; }
53+
>exports : typeof MW
54+
>MW : typeof MW
55+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/conformance/jsdoc/MC.js ===
2+
const MW = require("./MW");
3+
>MW : Symbol(MW, Decl(MC.js, 0, 5))
4+
>require : Symbol(require)
5+
>"./MW" : Symbol("tests/cases/conformance/jsdoc/MW", Decl(MW.js, 0, 0))
6+
7+
/** @typedef {number} Meyerhauser */
8+
9+
/** @class */
10+
module.exports = function MC() {
11+
>module.exports : Symbol("tests/cases/conformance/jsdoc/MC", Decl(MC.js, 0, 0))
12+
>module : Symbol(export=, Decl(MC.js, 0, 27))
13+
>exports : Symbol(export=, Decl(MC.js, 0, 27))
14+
>MC : Symbol(MC, Decl(MC.js, 5, 16))
15+
16+
/** @type {any} */
17+
var x = {}
18+
>x : Symbol(x, Decl(MC.js, 7, 7))
19+
20+
return new MW(x);
21+
>MW : Symbol(MW, Decl(MC.js, 0, 5))
22+
>x : Symbol(x, Decl(MC.js, 7, 7))
23+
24+
};
25+
26+
=== tests/cases/conformance/jsdoc/MW.js ===
27+
/** @typedef {import("./MC")} MC */
28+
29+
class MW {
30+
>MW : Symbol(MW, Decl(MW.js, 0, 0))
31+
32+
/**
33+
* @param {MC} compiler the compiler
34+
*/
35+
constructor(compiler) {
36+
>compiler : Symbol(compiler, Decl(MW.js, 6, 14))
37+
38+
this.compiler = compiler;
39+
>this.compiler : Symbol(MW.compiler, Decl(MW.js, 6, 25))
40+
>this : Symbol(MW, Decl(MW.js, 0, 0))
41+
>compiler : Symbol(MW.compiler, Decl(MW.js, 6, 25))
42+
>compiler : Symbol(compiler, Decl(MW.js, 6, 14))
43+
}
44+
}
45+
46+
module.exports = MW;
47+
>module.exports : Symbol("tests/cases/conformance/jsdoc/MW", Decl(MW.js, 0, 0))
48+
>module : Symbol(export=, Decl(MW.js, 9, 1))
49+
>exports : Symbol(export=, Decl(MW.js, 9, 1))
50+
>MW : Symbol(MW, Decl(MW.js, 0, 0))
51+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
=== tests/cases/conformance/jsdoc/MC.js ===
2+
const MW = require("./MW");
3+
>MW : typeof import("tests/cases/conformance/jsdoc/MW")
4+
>require("./MW") : typeof import("tests/cases/conformance/jsdoc/MW")
5+
>require : any
6+
>"./MW" : "./MW"
7+
8+
/** @typedef {number} Meyerhauser */
9+
10+
/** @class */
11+
module.exports = function MC() {
12+
>module.exports = function MC() { /** @type {any} */ var x = {} return new MW(x);} : typeof MC
13+
>module.exports : typeof MC
14+
>module : { "\"tests/cases/conformance/jsdoc/MC\"": typeof MC; }
15+
>exports : typeof MC
16+
>function MC() { /** @type {any} */ var x = {} return new MW(x);} : typeof MC
17+
>MC : typeof MC
18+
19+
/** @type {any} */
20+
var x = {}
21+
>x : any
22+
>{} : {}
23+
24+
return new MW(x);
25+
>new MW(x) : import("tests/cases/conformance/jsdoc/MW")
26+
>MW : typeof import("tests/cases/conformance/jsdoc/MW")
27+
>x : any
28+
29+
};
30+
31+
=== tests/cases/conformance/jsdoc/MW.js ===
32+
/** @typedef {import("./MC")} MC */
33+
34+
class MW {
35+
>MW : MW
36+
37+
/**
38+
* @param {MC} compiler the compiler
39+
*/
40+
constructor(compiler) {
41+
>compiler : typeof MC
42+
43+
this.compiler = compiler;
44+
>this.compiler = compiler : typeof MC
45+
>this.compiler : typeof MC
46+
>this : this
47+
>compiler : typeof MC
48+
>compiler : typeof MC
49+
}
50+
}
51+
52+
module.exports = MW;
53+
>module.exports = MW : typeof MW
54+
>module.exports : typeof MW
55+
>module : { "\"tests/cases/conformance/jsdoc/MW\"": typeof MW; }
56+
>exports : typeof MW
57+
>MW : typeof MW
58+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @noEmit: true
2+
// @allowjs: true
3+
// @checkjs: true
4+
5+
// @Filename: MW.js
6+
/** @typedef {import("./MC")} MC */
7+
8+
class MW {
9+
/**
10+
* @param {MC} compiler the compiler
11+
*/
12+
constructor(compiler) {
13+
this.compiler = compiler;
14+
}
15+
}
16+
17+
module.exports = MW;
18+
19+
// @Filename: MC.js
20+
const MW = require("./MW");
21+
22+
/** @typedef {number} Cictema */
23+
24+
module.exports = class MC {
25+
watch() {
26+
return new MW(this);
27+
}
28+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @noEmit: true
2+
// @allowjs: true
3+
// @checkjs: true
4+
5+
// @Filename: MW.js
6+
/** @typedef {import("./MC")} MC */
7+
8+
class MW {
9+
/**
10+
* @param {MC} compiler the compiler
11+
*/
12+
constructor(compiler) {
13+
this.compiler = compiler;
14+
}
15+
}
16+
17+
module.exports = MW;
18+
19+
// @Filename: MC.js
20+
const MW = require("./MW");
21+
22+
/** @typedef {number} Meyerhauser */
23+
24+
/** @class */
25+
module.exports = function MC() {
26+
/** @type {any} */
27+
var x = {}
28+
return new MW(x);
29+
};

0 commit comments

Comments
 (0)