Skip to content

Commit ba797f2

Browse files
authored
Add flag to skip qualification check when symbol is already in the process of being qualified (#21337)
1 parent a6d7a38 commit ba797f2

5 files changed

+194
-8
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,12 +2424,15 @@ namespace ts {
24242424
const visitedSymbolTables: SymbolTable[] = [];
24252425
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
24262426

2427-
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] | undefined {
2427+
/**
2428+
* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already)
2429+
*/
2430+
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean): Symbol[] | undefined {
24282431
if (!pushIfUnique(visitedSymbolTables, symbols)) {
24292432
return undefined;
24302433
}
24312434

2432-
const result = trySymbolTable(symbols);
2435+
const result = trySymbolTable(symbols, ignoreQualification);
24332436
visitedSymbolTables.pop();
24342437
return result;
24352438
}
@@ -2441,22 +2444,22 @@ namespace ts {
24412444
!!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
24422445
}
24432446

2444-
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
2447+
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol, ignoreQualification?: boolean) {
24452448
return symbol === (resolvedAliasSymbol || symbolFromSymbolTable) &&
24462449
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
24472450
// and if symbolFromSymbolTable or alias resolution matches the symbol,
24482451
// check the symbol can be qualified, it is only then this symbol is accessible
24492452
!some(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
2450-
canQualifySymbol(symbolFromSymbolTable, meaning);
2453+
(ignoreQualification || canQualifySymbol(symbolFromSymbolTable, meaning));
24512454
}
24522455

24532456
function isUMDExportSymbol(symbol: Symbol) {
24542457
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
24552458
}
24562459

2457-
function trySymbolTable(symbols: SymbolTable) {
2460+
function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined) {
24582461
// If symbol is directly available by its name in the symbol table
2459-
if (isAccessible(symbols.get(symbol.escapedName))) {
2462+
if (isAccessible(symbols.get(symbol.escapedName), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) {
24602463
return [symbol];
24612464
}
24622465

@@ -2469,14 +2472,14 @@ namespace ts {
24692472
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) {
24702473

24712474
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
2472-
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) {
2475+
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) {
24732476
return [symbolFromSymbolTable];
24742477
}
24752478

24762479
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
24772480
// but only if the symbolFromSymbolTable can be qualified
24782481
const candidateTable = getExportsOfSymbol(resolvedImportedSymbol);
2479-
const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable);
2482+
const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable, /*ignoreQualification*/ true);
24802483
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
24812484
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
24822485
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//// [tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts] ////
2+
3+
//// [index.ts]
4+
export * from "./account";
5+
6+
//// [account.ts]
7+
export interface Account {
8+
myAccNum: number;
9+
}
10+
interface Account2 {
11+
myAccNum: number;
12+
}
13+
export { Account2 as Acc };
14+
15+
//// [index.ts]
16+
declare global {
17+
interface Account {
18+
someProp: number;
19+
}
20+
interface Acc {
21+
someProp: number;
22+
}
23+
}
24+
import * as model from "./model";
25+
export const func = (account: model.Account, acc2: model.Acc) => {};
26+
27+
28+
//// [account.js]
29+
"use strict";
30+
exports.__esModule = true;
31+
//// [index.js]
32+
"use strict";
33+
exports.__esModule = true;
34+
//// [index.js]
35+
"use strict";
36+
exports.__esModule = true;
37+
exports.func = function (account, acc2) { };
38+
39+
40+
//// [account.d.ts]
41+
export interface Account {
42+
myAccNum: number;
43+
}
44+
interface Account2 {
45+
myAccNum: number;
46+
}
47+
export { Account2 as Acc };
48+
//// [index.d.ts]
49+
export * from "./account";
50+
//// [index.d.ts]
51+
declare global {
52+
interface Account {
53+
someProp: number;
54+
}
55+
interface Acc {
56+
someProp: number;
57+
}
58+
}
59+
import * as model from "./model";
60+
export declare const func: (account: model.Account, acc2: model.Acc) => void;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/compiler/model/index.ts ===
2+
export * from "./account";
3+
No type information for this code.
4+
No type information for this code.=== tests/cases/compiler/model/account.ts ===
5+
export interface Account {
6+
>Account : Symbol(Account, Decl(account.ts, 0, 0))
7+
8+
myAccNum: number;
9+
>myAccNum : Symbol(Account.myAccNum, Decl(account.ts, 0, 26))
10+
}
11+
interface Account2 {
12+
>Account2 : Symbol(Account2, Decl(account.ts, 2, 1))
13+
14+
myAccNum: number;
15+
>myAccNum : Symbol(Account2.myAccNum, Decl(account.ts, 3, 20))
16+
}
17+
export { Account2 as Acc };
18+
>Account2 : Symbol(Acc, Decl(account.ts, 6, 8))
19+
>Acc : Symbol(Acc, Decl(account.ts, 6, 8))
20+
21+
=== tests/cases/compiler/index.ts ===
22+
declare global {
23+
>global : Symbol(global, Decl(index.ts, 0, 0))
24+
25+
interface Account {
26+
>Account : Symbol(Account, Decl(index.ts, 0, 16))
27+
28+
someProp: number;
29+
>someProp : Symbol(Account.someProp, Decl(index.ts, 1, 23))
30+
}
31+
interface Acc {
32+
>Acc : Symbol(Acc, Decl(index.ts, 3, 5))
33+
34+
someProp: number;
35+
>someProp : Symbol(Acc.someProp, Decl(index.ts, 4, 19))
36+
}
37+
}
38+
import * as model from "./model";
39+
>model : Symbol(model, Decl(index.ts, 8, 6))
40+
41+
export const func = (account: model.Account, acc2: model.Acc) => {};
42+
>func : Symbol(func, Decl(index.ts, 9, 12))
43+
>account : Symbol(account, Decl(index.ts, 9, 21))
44+
>model : Symbol(model, Decl(index.ts, 8, 6))
45+
>Account : Symbol(model.Account, Decl(account.ts, 0, 0))
46+
>acc2 : Symbol(acc2, Decl(index.ts, 9, 44))
47+
>model : Symbol(model, Decl(index.ts, 8, 6))
48+
>Acc : Symbol(model.Acc, Decl(account.ts, 6, 8))
49+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
=== tests/cases/compiler/model/index.ts ===
2+
export * from "./account";
3+
No type information for this code.
4+
No type information for this code.=== tests/cases/compiler/model/account.ts ===
5+
export interface Account {
6+
>Account : Account
7+
8+
myAccNum: number;
9+
>myAccNum : number
10+
}
11+
interface Account2 {
12+
>Account2 : Account2
13+
14+
myAccNum: number;
15+
>myAccNum : number
16+
}
17+
export { Account2 as Acc };
18+
>Account2 : any
19+
>Acc : any
20+
21+
=== tests/cases/compiler/index.ts ===
22+
declare global {
23+
>global : any
24+
25+
interface Account {
26+
>Account : Account
27+
28+
someProp: number;
29+
>someProp : number
30+
}
31+
interface Acc {
32+
>Acc : Acc
33+
34+
someProp: number;
35+
>someProp : number
36+
}
37+
}
38+
import * as model from "./model";
39+
>model : typeof model
40+
41+
export const func = (account: model.Account, acc2: model.Acc) => {};
42+
>func : (account: model.Account, acc2: model.Acc) => void
43+
>(account: model.Account, acc2: model.Acc) => {} : (account: model.Account, acc2: model.Acc) => void
44+
>account : model.Account
45+
>model : any
46+
>Account : model.Account
47+
>acc2 : model.Acc
48+
>model : any
49+
>Acc : model.Acc
50+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @declaration: true
2+
// @filename: model/index.ts
3+
export * from "./account";
4+
5+
// @filename: model/account.ts
6+
export interface Account {
7+
myAccNum: number;
8+
}
9+
interface Account2 {
10+
myAccNum: number;
11+
}
12+
export { Account2 as Acc };
13+
14+
// @filename: index.ts
15+
declare global {
16+
interface Account {
17+
someProp: number;
18+
}
19+
interface Acc {
20+
someProp: number;
21+
}
22+
}
23+
import * as model from "./model";
24+
export const func = (account: model.Account, acc2: model.Acc) => {};

0 commit comments

Comments
 (0)