Add flag to skip qualification check when symbol is already in the process of being qualified (#21337)

This commit is contained in:
Wesley Wigham 2018-01-24 12:37:45 -08:00 committed by GitHub
parent a6d7a38c34
commit ba797f2c50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 194 additions and 8 deletions

View File

@ -2424,12 +2424,15 @@ namespace ts {
const visitedSymbolTables: SymbolTable[] = [];
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] | undefined {
/**
* @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)
*/
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean): Symbol[] | undefined {
if (!pushIfUnique(visitedSymbolTables, symbols)) {
return undefined;
}
const result = trySymbolTable(symbols);
const result = trySymbolTable(symbols, ignoreQualification);
visitedSymbolTables.pop();
return result;
}
@ -2441,22 +2444,22 @@ namespace ts {
!!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
}
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol, ignoreQualification?: boolean) {
return symbol === (resolvedAliasSymbol || symbolFromSymbolTable) &&
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
// and if symbolFromSymbolTable or alias resolution matches the symbol,
// check the symbol can be qualified, it is only then this symbol is accessible
!some(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
canQualifySymbol(symbolFromSymbolTable, meaning);
(ignoreQualification || canQualifySymbol(symbolFromSymbolTable, meaning));
}
function isUMDExportSymbol(symbol: Symbol) {
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
}
function trySymbolTable(symbols: SymbolTable) {
function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol.escapedName))) {
if (isAccessible(symbols.get(symbol.escapedName), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) {
return [symbol];
}
@ -2469,14 +2472,14 @@ namespace ts {
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) {
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) {
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) {
return [symbolFromSymbolTable];
}
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
// but only if the symbolFromSymbolTable can be qualified
const candidateTable = getExportsOfSymbol(resolvedImportedSymbol);
const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable);
const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable, /*ignoreQualification*/ true);
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}

View File

@ -0,0 +1,60 @@
//// [tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts] ////
//// [index.ts]
export * from "./account";
//// [account.ts]
export interface Account {
myAccNum: number;
}
interface Account2 {
myAccNum: number;
}
export { Account2 as Acc };
//// [index.ts]
declare global {
interface Account {
someProp: number;
}
interface Acc {
someProp: number;
}
}
import * as model from "./model";
export const func = (account: model.Account, acc2: model.Acc) => {};
//// [account.js]
"use strict";
exports.__esModule = true;
//// [index.js]
"use strict";
exports.__esModule = true;
//// [index.js]
"use strict";
exports.__esModule = true;
exports.func = function (account, acc2) { };
//// [account.d.ts]
export interface Account {
myAccNum: number;
}
interface Account2 {
myAccNum: number;
}
export { Account2 as Acc };
//// [index.d.ts]
export * from "./account";
//// [index.d.ts]
declare global {
interface Account {
someProp: number;
}
interface Acc {
someProp: number;
}
}
import * as model from "./model";
export declare const func: (account: model.Account, acc2: model.Acc) => void;

View File

@ -0,0 +1,49 @@
=== tests/cases/compiler/model/index.ts ===
export * from "./account";
No type information for this code.
No type information for this code.=== tests/cases/compiler/model/account.ts ===
export interface Account {
>Account : Symbol(Account, Decl(account.ts, 0, 0))
myAccNum: number;
>myAccNum : Symbol(Account.myAccNum, Decl(account.ts, 0, 26))
}
interface Account2 {
>Account2 : Symbol(Account2, Decl(account.ts, 2, 1))
myAccNum: number;
>myAccNum : Symbol(Account2.myAccNum, Decl(account.ts, 3, 20))
}
export { Account2 as Acc };
>Account2 : Symbol(Acc, Decl(account.ts, 6, 8))
>Acc : Symbol(Acc, Decl(account.ts, 6, 8))
=== tests/cases/compiler/index.ts ===
declare global {
>global : Symbol(global, Decl(index.ts, 0, 0))
interface Account {
>Account : Symbol(Account, Decl(index.ts, 0, 16))
someProp: number;
>someProp : Symbol(Account.someProp, Decl(index.ts, 1, 23))
}
interface Acc {
>Acc : Symbol(Acc, Decl(index.ts, 3, 5))
someProp: number;
>someProp : Symbol(Acc.someProp, Decl(index.ts, 4, 19))
}
}
import * as model from "./model";
>model : Symbol(model, Decl(index.ts, 8, 6))
export const func = (account: model.Account, acc2: model.Acc) => {};
>func : Symbol(func, Decl(index.ts, 9, 12))
>account : Symbol(account, Decl(index.ts, 9, 21))
>model : Symbol(model, Decl(index.ts, 8, 6))
>Account : Symbol(model.Account, Decl(account.ts, 0, 0))
>acc2 : Symbol(acc2, Decl(index.ts, 9, 44))
>model : Symbol(model, Decl(index.ts, 8, 6))
>Acc : Symbol(model.Acc, Decl(account.ts, 6, 8))

View File

@ -0,0 +1,50 @@
=== tests/cases/compiler/model/index.ts ===
export * from "./account";
No type information for this code.
No type information for this code.=== tests/cases/compiler/model/account.ts ===
export interface Account {
>Account : Account
myAccNum: number;
>myAccNum : number
}
interface Account2 {
>Account2 : Account2
myAccNum: number;
>myAccNum : number
}
export { Account2 as Acc };
>Account2 : any
>Acc : any
=== tests/cases/compiler/index.ts ===
declare global {
>global : any
interface Account {
>Account : Account
someProp: number;
>someProp : number
}
interface Acc {
>Acc : Acc
someProp: number;
>someProp : number
}
}
import * as model from "./model";
>model : typeof model
export const func = (account: model.Account, acc2: model.Acc) => {};
>func : (account: model.Account, acc2: model.Acc) => void
>(account: model.Account, acc2: model.Acc) => {} : (account: model.Account, acc2: model.Acc) => void
>account : model.Account
>model : any
>Account : model.Account
>acc2 : model.Acc
>model : any
>Acc : model.Acc

View File

@ -0,0 +1,24 @@
// @declaration: true
// @filename: model/index.ts
export * from "./account";
// @filename: model/account.ts
export interface Account {
myAccNum: number;
}
interface Account2 {
myAccNum: number;
}
export { Account2 as Acc };
// @filename: index.ts
declare global {
interface Account {
someProp: number;
}
interface Acc {
someProp: number;
}
}
import * as model from "./model";
export const func = (account: model.Account, acc2: model.Acc) => {};