guard against visiting the same symbol table multiple times (#12818)

This commit is contained in:
Vladimir Matveev
2016-12-12 10:17:07 -08:00
committed by GitHub
parent 23ec976719
commit a604d84f5c
5 changed files with 127 additions and 24 deletions
+38 -24
View File
@@ -1742,7 +1742,19 @@ namespace ts {
}
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable) {
return getAccessibleSymbolChainFromSymbolTableWorker(symbols, []);
}
function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] {
if (contains(visitedSymbolTables, symbols)) {
return undefined;
}
visitedSymbolTables.push(symbols);
const result = trySymbolTable(symbols);
visitedSymbolTables.pop();
return result;
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
// If the symbol is equivalent and doesn't need further qualification, this symbol is accessible
if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) {
@@ -1764,34 +1776,36 @@ namespace ts {
}
}
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols[symbol.name])) {
return [symbol];
}
function trySymbolTable(symbols: SymbolTable) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols[symbol.name])) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachProperty(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
// Check if symbol is any of the alias
return forEachProperty(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
return [symbolFromSymbolTable];
}
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
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 accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined;
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
// 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 accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTableWorker(resolvedImportedSymbol.exports, visitedSymbolTables) : undefined;
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
}
}
});
});
}
}
if (symbol) {
@@ -0,0 +1,27 @@
//// [tests/cases/compiler/circularReferenceInImport.ts] ////
//// [db.d.ts]
declare namespace Db {
export import Types = Db;
}
export = Db;
//// [app.ts]
import * as Db from "./db"
export function foo() {
return new Object()
}
//// [app.js]
"use strict";
function foo() {
return new Object();
}
exports.foo = foo;
//// [app.d.ts]
export declare function foo(): Object;
@@ -0,0 +1,23 @@
=== tests/cases/compiler/db.d.ts ===
declare namespace Db {
>Db : Symbol(Types, Decl(db.d.ts, 0, 0))
export import Types = Db;
>Types : Symbol(Types, Decl(db.d.ts, 1, 22))
>Db : Symbol(Types, Decl(db.d.ts, 0, 0))
}
export = Db;
>Db : Symbol(Db, Decl(db.d.ts, 0, 0))
=== tests/cases/compiler/app.ts ===
import * as Db from "./db"
>Db : Symbol(Db, Decl(app.ts, 0, 6))
export function foo() {
>foo : Symbol(foo, Decl(app.ts, 0, 26))
return new Object()
>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
}
@@ -0,0 +1,24 @@
=== tests/cases/compiler/db.d.ts ===
declare namespace Db {
>Db : typeof Types
export import Types = Db;
>Types : typeof Types
>Db : typeof Types
}
export = Db;
>Db : typeof Db
=== tests/cases/compiler/app.ts ===
import * as Db from "./db"
>Db : typeof Db
export function foo() {
>foo : () => Object
return new Object()
>new Object() : Object
>Object : ObjectConstructor
}
@@ -0,0 +1,15 @@
// @declaration: true
// @filename: db.d.ts
declare namespace Db {
export import Types = Db;
}
export = Db;
// @filename: app.ts
import * as Db from "./db"
export function foo() {
return new Object()
}