Skip to content

Commit ca64946

Browse files
Set parents of augmented module exports (#59609)
Co-authored-by: Isabel Duan <isabelduan@microsoft.com>
1 parent bcb1545 commit ca64946

5 files changed

+272
-3
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,7 +2683,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
26832683
}
26842684
if (source.exports) {
26852685
if (!target.exports) target.exports = createSymbolTable();
2686-
mergeSymbolTable(target.exports, source.exports, unidirectional);
2686+
mergeSymbolTable(target.exports, source.exports, unidirectional, target);
26872687
}
26882688
if (!unidirectional) {
26892689
recordMergedSymbol(target, source);
@@ -2772,10 +2772,29 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27722772
return combined;
27732773
}
27742774

2775-
function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false) {
2775+
function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false, mergedParent?: Symbol) {
27762776
source.forEach((sourceSymbol, id) => {
27772777
const targetSymbol = target.get(id);
2778-
target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : getMergedSymbol(sourceSymbol));
2778+
const merged = targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : getMergedSymbol(sourceSymbol);
2779+
if (mergedParent && targetSymbol) {
2780+
// If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge
2781+
// of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent:
2782+
//
2783+
// // a.ts
2784+
// export interface A { x: number; }
2785+
//
2786+
// // b.ts
2787+
// declare module "./a" {
2788+
// interface A { y: number; }
2789+
// interface B {}
2790+
// }
2791+
//
2792+
// When merging the module augmentation into a.ts, the symbol for `A` will itself be merged, so its parent
2793+
// should be the merged module symbol. But the symbol for `B` has only one declaration, so its parent should
2794+
// be the module augmentation symbol, which contains its only declaration.
2795+
merged.parent = mergedParent;
2796+
}
2797+
target.set(id, merged);
27792798
});
27802799
}
27812800

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/// <reference path="fourslash.ts" />
2+
// @module: nodenext
3+
4+
// @Filename: /node_modules/@sapphire/pieces/index.d.ts
5+
//// interface Container {
6+
//// stores: unknown;
7+
//// }
8+
////
9+
//// declare class Piece {
10+
//// container: Container;
11+
//// }
12+
////
13+
//// export { Piece, type Container };
14+
15+
// @FileName: /augmentation.ts
16+
//// declare module "@sapphire/pieces" {
17+
//// interface Container {
18+
//// client: unknown;
19+
//// }
20+
//// export { Container };
21+
//// }
22+
23+
// @Filename: /index.ts
24+
//// import { Piece } from "@sapphire/pieces";
25+
//// class FullPiece extends Piece {
26+
//// /*1*/
27+
//// }
28+
29+
const preferences = {
30+
includeCompletionsWithClassMemberSnippets: true,
31+
includeCompletionsWithInsertText: true,
32+
};
33+
34+
verify.completions({
35+
marker: "1",
36+
includes: [
37+
{
38+
name: "container",
39+
insertText: "container: Container;",
40+
filterText: "container",
41+
hasAction: true,
42+
source: "ClassMemberSnippet/",
43+
},
44+
],
45+
preferences,
46+
isNewIdentifierLocation: true,
47+
});
48+
49+
verify.applyCodeActionFromCompletion("1", {
50+
name: "container",
51+
source: "ClassMemberSnippet/",
52+
description: `Includes imports of types referenced by 'container'`,
53+
newFileContent: `import { Container, Piece } from "@sapphire/pieces";
54+
class FullPiece extends Piece {
55+
56+
}`,
57+
preferences,
58+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/// <reference path="fourslash.ts" />
2+
// @module: nodenext
3+
4+
// @Filename: /node_modules/@sapphire/pieces/index.d.ts
5+
//// interface Container {
6+
//// stores: unknown;
7+
//// }
8+
////
9+
//// declare class Piece {
10+
//// get container(): Container;
11+
//// }
12+
////
13+
//// declare class AliasPiece extends Piece {}
14+
////
15+
//// export { AliasPiece, type Container };
16+
17+
// @Filename: /node_modules/@sapphire/framework/index.d.ts
18+
//// import { AliasPiece } from "@sapphire/pieces";
19+
////
20+
//// declare class Command extends AliasPiece {}
21+
////
22+
//// declare module "@sapphire/pieces" {
23+
//// interface Container {
24+
//// client: unknown;
25+
//// }
26+
//// }
27+
////
28+
//// export { Command };
29+
30+
// @Filename: /index.ts
31+
//// import "@sapphire/pieces";
32+
//// import { Command } from "@sapphire/framework";
33+
//// class PingCommand extends Command {
34+
//// /*1*/
35+
//// }
36+
37+
const preferences = {
38+
includeCompletionsWithClassMemberSnippets: true,
39+
includeCompletionsWithInsertText: true,
40+
};
41+
42+
verify.completions({
43+
marker: "1",
44+
includes: [
45+
{
46+
name: "container",
47+
insertText: "get container(): Container {\n}",
48+
filterText: "container",
49+
hasAction: true,
50+
source: "ClassMemberSnippet/",
51+
},
52+
],
53+
preferences,
54+
isNewIdentifierLocation: true,
55+
});
56+
57+
verify.applyCodeActionFromCompletion("1", {
58+
name: "container",
59+
source: "ClassMemberSnippet/",
60+
description: `Includes imports of types referenced by 'container'`,
61+
newFileContent: `import "@sapphire/pieces";
62+
import { Command } from "@sapphire/framework";
63+
import { Container } from "@sapphire/pieces";
64+
class PingCommand extends Command {
65+
66+
}`,
67+
preferences,
68+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// <reference path="fourslash.ts" />
2+
// @module: nodenext
3+
4+
// @Filename: /node_modules/@sapphire/pieces/index.d.ts
5+
//// export interface Container {
6+
//// stores: unknown;
7+
//// }
8+
////
9+
//// declare class Piece {
10+
//// container: Container;
11+
//// }
12+
////
13+
//// export { Piece };
14+
15+
// @FileName: /augmentation.ts
16+
//// declare module "@sapphire/pieces" {
17+
//// interface Container {
18+
//// client: unknown;
19+
//// }
20+
//// }
21+
22+
// @Filename: /index.ts
23+
//// import { Piece } from "@sapphire/pieces";
24+
//// class FullPiece extends Piece {
25+
//// /*1*/
26+
//// }
27+
28+
const preferences = {
29+
includeCompletionsWithClassMemberSnippets: true,
30+
includeCompletionsWithInsertText: true,
31+
};
32+
33+
verify.completions({
34+
marker: "1",
35+
includes: [
36+
{
37+
name: "container",
38+
insertText: "container: Container;",
39+
filterText: "container",
40+
hasAction: true,
41+
source: "ClassMemberSnippet/",
42+
},
43+
],
44+
preferences,
45+
isNewIdentifierLocation: true,
46+
});
47+
48+
verify.applyCodeActionFromCompletion("1", {
49+
name: "container",
50+
source: "ClassMemberSnippet/",
51+
description: `Includes imports of types referenced by 'container'`,
52+
newFileContent: `import { Container, Piece } from "@sapphire/pieces";
53+
class FullPiece extends Piece {
54+
55+
}`,
56+
preferences,
57+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/@sapphire/pieces/index.d.ts
6+
//// interface Container {
7+
//// stores: unknown;
8+
//// }
9+
////
10+
//// declare class Piece {
11+
//// get container(): Container;
12+
//// }
13+
////
14+
//// export { Piece as Alias, type Container };
15+
16+
// @Filename: /node_modules/@sapphire/framework/index.d.ts
17+
//// import { Alias } from "@sapphire/pieces";
18+
////
19+
//// declare class Command extends Alias {}
20+
////
21+
//// declare module "@sapphire/pieces" {
22+
//// interface Container {
23+
//// client: unknown;
24+
//// }
25+
//// }
26+
////
27+
//// export { Command as CommandAlias };
28+
29+
// @Filename: /index.ts
30+
//// import "@sapphire/pieces";
31+
//// import { CommandAlias } from "@sapphire/framework";
32+
//// class PingCommand extends CommandAlias {
33+
//// /*1*/
34+
//// }
35+
36+
const preferences = {
37+
includeCompletionsWithClassMemberSnippets: true,
38+
includeCompletionsWithInsertText: true,
39+
};
40+
41+
verify.completions({
42+
marker: "1",
43+
includes: [
44+
{
45+
name: "container",
46+
insertText: "get container(): Container {\n}",
47+
filterText: "container",
48+
hasAction: true,
49+
source: "ClassMemberSnippet/",
50+
},
51+
],
52+
preferences,
53+
isNewIdentifierLocation: true,
54+
});
55+
56+
verify.applyCodeActionFromCompletion("1", {
57+
name: "container",
58+
source: "ClassMemberSnippet/",
59+
description: `Includes imports of types referenced by 'container'`,
60+
newFileContent: `import "@sapphire/pieces";
61+
import { CommandAlias } from "@sapphire/framework";
62+
import { Container } from "@sapphire/pieces";
63+
class PingCommand extends CommandAlias {
64+
65+
}`,
66+
preferences,
67+
});

0 commit comments

Comments
 (0)