diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 444cd39c8bb..ab0b663bcfd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -586,56 +586,102 @@ module ts { return (propertySymbol.valueDeclaration.flags & NodeFlags.QuestionMark) && propertySymbol.valueDeclaration.kind !== SyntaxKind.Parameter; } - function getAccessibleSymbol(symbol: Symbol, enclosingDeclaration: Node) { + function forEachSymbolTable(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T { + var result: T; + for (var location = enclosingDeclaration; location; location = location.parent) { + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if (location.locals && (location.kind !== SyntaxKind.SourceFile || location.flags & NodeFlags.ExternalModule)) { + if (result = callback(location.locals)) { + return result; + } + } + switch (location.kind) { + case SyntaxKind.SourceFile: + if (!(location.flags & NodeFlags.ExternalModule)) { + break; + } + case SyntaxKind.ModuleDeclaration: + if (result = callback(getSymbolOfNode(location).exports)) { + return result; + } + break; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + if (result = callback(getSymbolOfNode(location).members)) { + return result; + } + break; + } + } + + return callback(globals); + } + + function getAccessibleSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { function getAccessibleSymbolFromSymbolTable(symbols: SymbolTable) { + function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) { + if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) { + // If the symbol is equivalent and doesnt need futher qualification, this symbol is accessible + if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) { + return true; + } + + // isAccessible is parent is accessible + var accessibleParent = getAccessibleSymbol(symbolFromSymbolTable.parent, enclosingDeclaration, SymbolFlags.Namespace); + return !!accessibleParent; + } + } + // If symbol is directly available by its name in the symbol table - if (symbol === symbols[symbol.name]) { + if (isAccessible(symbols[symbol.name])) { return symbol; } // Check if symbol is any of the alias return forEachValue(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Import) { - var resolvedImportSymbol = resolveImport(symbolFromSymbolTable); - if (resolvedImportSymbol === symbol) { + if (isAccessible(symbolFromSymbolTable, resolveImport(symbolFromSymbolTable))) { return symbolFromSymbolTable; } } }); - } - - var accessibleSymbol: Symbol; - while (enclosingDeclaration) { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if (enclosingDeclaration.locals && (enclosingDeclaration.kind !== SyntaxKind.SourceFile || enclosingDeclaration.flags & NodeFlags.ExternalModule)) { - if (accessibleSymbol = getAccessibleSymbolFromSymbolTable(enclosingDeclaration.locals)) { - return accessibleSymbol; - } - } - switch (enclosingDeclaration.kind) { - case SyntaxKind.SourceFile: - if (!(enclosingDeclaration.flags & NodeFlags.ExternalModule)) { - break; - } - case SyntaxKind.ModuleDeclaration: - if (accessibleSymbol = getAccessibleSymbolFromSymbolTable(getSymbolOfNode(enclosingDeclaration).exports)) { - return accessibleSymbol; - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - if (accessibleSymbol = getAccessibleSymbolFromSymbolTable(getSymbolOfNode(enclosingDeclaration).members)) { - return accessibleSymbol; - } - break; - } - enclosingDeclaration = enclosingDeclaration.parent; } - return getAccessibleSymbolFromSymbolTable(globals); + if (symbol) { + return forEachSymbolTable(enclosingDeclaration, getAccessibleSymbolFromSymbolTable); + } } - function symbolToString(symbol: Symbol, enclosingDeclaration?: Node) { + function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { + var qualify = false; + forEachSymbolTable(enclosingDeclaration, symbolTable => { + // If symbol of this name is not available in the symbol table we are ok + if (!symbolTable[symbol.name]) { + // Continue to the next symbol table + return false; + } + // If the symbol with this name is present it should refer to the symbol + var symbolFromSymbolTable = symbolTable[symbol.name]; + if (symbolFromSymbolTable === symbol) { + // No need to qualify + return true; + } + + // Qualify if the symbol from symbol table has same meaning as expected + symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Import) ? resolveImport(symbolFromSymbolTable) : symbolFromSymbolTable; + if (symbolFromSymbolTable.flags & meaning) { + qualify = true; + return true; + } + + // Continue to the next symbol table + return false; + }); + + return qualify + } + + function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { function getSymbolName(symbol: Symbol) { if (symbol.declarations && symbol.declarations.length > 0) { var declaration = symbol.declarations[0]; @@ -651,12 +697,14 @@ module ts { !(symbol.flags & SymbolFlags.PropertyOrAccessor & SymbolFlags.Signature & SymbolFlags.Constructor & SymbolFlags.Method & SymbolFlags.TypeParameter)) { var symbolName: string; while (symbol) { - var accessibleParent = getAccessibleSymbol(symbol, enclosingDeclaration); - symbolName = getSymbolName(accessibleParent || symbol) + (symbolName ? ("." + symbolName) : ""); - if (accessibleParent) { + var isFirstName = !symbolName; + var meaningToLook = isFirstName ? meaning : SymbolFlags.Namespace; + var accessibleSymbol = getAccessibleSymbol(symbol, enclosingDeclaration, meaningToLook); + symbolName = getSymbolName(accessibleSymbol || symbol) + (isFirstName ? "" : ("." + symbolName)); + if (accessibleSymbol && !needsQualification(accessibleSymbol, enclosingDeclaration, meaningToLook)) { break; } - symbol = symbol.parent; + symbol = accessibleSymbol ? accessibleSymbol.parent : symbol.parent; } return symbolName; @@ -695,7 +743,7 @@ module ts { writeTypeReference(type); } else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) { - writer.write(symbolToString(type.symbol, enclosingDeclaration)); + writer.write(symbolToString(type.symbol, enclosingDeclaration, SymbolFlags.Type)); } else if (type.flags & TypeFlags.Anonymous) { writeAnonymousType(type, allowFunctionOrConstructorTypeLiteral); @@ -717,7 +765,7 @@ module ts { writer.write("[]"); } else { - writer.write(symbolToString(type.target.symbol, enclosingDeclaration)); + writer.write(symbolToString(type.target.symbol, enclosingDeclaration, SymbolFlags.Type)); writer.write("<"); for (var i = 0; i < type.typeArguments.length; i++) { if (i > 0) { @@ -751,7 +799,7 @@ module ts { function writeTypeofSymbol(type: ObjectType) { writer.write("typeof "); - writer.write(symbolToString(type.symbol, enclosingDeclaration)); + writer.write(symbolToString(type.symbol, enclosingDeclaration, SymbolFlags.Value)); } function writeLiteralType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) { diff --git a/tests/baselines/reference/declFileGenericType2.js b/tests/baselines/reference/declFileGenericType2.js index ef8ee547913..94cdaa32eaa 100644 --- a/tests/baselines/reference/declFileGenericType2.js +++ b/tests/baselines/reference/declFileGenericType2.js @@ -99,17 +99,17 @@ declare module templa.mvc { declare module templa.mvc.composite { } declare module templa.dom.mvc { - interface IElementController extends mvc.IController { + interface IElementController extends templa.mvc.IController { } } declare module templa.dom.mvc { - class AbstractElementController extends mvc.AbstractController implments IElementController { + class AbstractElementController extends templa.mvc.AbstractController implments IElementController { constructor(); } } declare module templa.dom.mvc.composite { - class AbstractCompositeElementController extends AbstractElementController { - _controllers: mvc.IController[]; + class AbstractCompositeElementController extends AbstractElementController { + _controllers: templa.mvc.IController[]; constructor(); } } diff --git a/tests/baselines/reference/declFileModuleContinuation.js b/tests/baselines/reference/declFileModuleContinuation.js index 88acd267fbf..490d599a2d9 100644 --- a/tests/baselines/reference/declFileModuleContinuation.js +++ b/tests/baselines/reference/declFileModuleContinuation.js @@ -33,6 +33,6 @@ declare module A.C { } } declare module A.B.C { - class W implments C.Z { + class W implments A.C.Z { } } diff --git a/tests/baselines/reference/declFileWithClassNameConflictingWithClassReferredByExtendsClause.js b/tests/baselines/reference/declFileWithClassNameConflictingWithClassReferredByExtendsClause.js index 72895c41a8f..ed695e40400 100644 --- a/tests/baselines/reference/declFileWithClassNameConflictingWithClassReferredByExtendsClause.js +++ b/tests/baselines/reference/declFileWithClassNameConflictingWithClassReferredByExtendsClause.js @@ -78,7 +78,7 @@ declare module X.Y.base { } } declare module X.Y.base.Z { - class W extends W { + class W extends base.W { value: boolean; } } diff --git a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause1.js b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause1.js index dd3f70dd5d1..98f3c1df276 100644 --- a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause1.js +++ b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause1.js @@ -38,6 +38,6 @@ declare module X.A.C { } } declare module X.A.B.C { - class W implments C.Z { + class W implments X.A.C.Z { } } diff --git a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause2.js b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause2.js index 786a8f897bc..6646ef907d1 100644 --- a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause2.js +++ b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause2.js @@ -41,7 +41,7 @@ declare module X.A.C { } } declare module X.A.B.C { - class W implments C.Z { + class W implments A.C.Z { } } declare module X.A.B.C { diff --git a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause3.js b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause3.js index 3cd54743c50..ca74a916fa8 100644 --- a/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause3.js +++ b/tests/baselines/reference/declFileWithInternalModuleNameConflictsInExtendsClause3.js @@ -41,7 +41,7 @@ declare module X.A.C { } } declare module X.A.B.C { - class W implments C.Z { + class W implments X.A.C.Z { } } declare module X.A.B.C { diff --git a/tests/baselines/reference/declarationEmit_nameConflicts.errors.txt b/tests/baselines/reference/declarationEmit_nameConflicts.errors.txt deleted file mode 100644 index 6a202ec3e60..00000000000 --- a/tests/baselines/reference/declarationEmit_nameConflicts.errors.txt +++ /dev/null @@ -1,53 +0,0 @@ -==== tests/cases/compiler/declarationEmit_nameConflicts_0.ts (1 errors) ==== - import im = require('declarationEmit_nameConflicts_1'); - export module M { - export function f() { } - export class C { } - export module N { - export function g() { }; - export interface I { } - } - - export import a = M.f; - export import b = M.C; - export import c = N; - export import d = im; - ~~~~~~~~~~~~~~~~~~~~~ -!!! Cannot find name 'im'. - } - - export module M.P { - export function f() { } - export class C { } - export module N { - export function g() { }; - export interface I { } - } - export import im = M.P.f; - // Bug 887180: Invalid .d.ts when an aliased entity is referenced, and a different entity is closer in scope - export var a = M.a; // emitted incorrectly as typeof f - export var b = M.b; // ok - export var c = M.c; // ok - export var g = M.c.g; // ok - export var d = M.d; // emitted incorrectly as typeof im - } - - export module M.Q { - export function f() { } - export class C { } - export module N { - export function g() { }; - export interface I { } - } - export interface b extends M.b { } // ok - export interface I extends M.c.I { } // ok - export module c { - export interface I extends M.c.I { } // ok - } - } -==== tests/cases/compiler/declarationEmit_nameConflicts_1.ts (1 errors) ==== - function f() { } - export = f; - ~~~~~~~~~~~ -!!! Cannot compile external modules unless the '--module' flag is provided. - \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmit_nameConflicts.js b/tests/baselines/reference/declarationEmit_nameConflicts.js index 1f9b13b7df6..7fc044a8aa9 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts.js @@ -1,7 +1,7 @@ //// [tests/cases/compiler/declarationEmit_nameConflicts.ts] //// //// [declarationEmit_nameConflicts_1.ts] -function f() { } +module f { export class c { } } export = f; //// [declarationEmit_nameConflicts_0.ts] @@ -51,10 +51,18 @@ export module M.Q { } //// [declarationEmit_nameConflicts_1.js] -function f() { -} +var f; +(function (f) { + var c = (function () { + function c() { + } + return c; + })(); + f.c = c; +})(f || (f = {})); module.exports = f; //// [declarationEmit_nameConflicts_0.js] +var im = require('declarationEmit_nameConflicts_1'); (function (M) { function f() { } @@ -75,6 +83,7 @@ module.exports = f; M.a = M.f; M.b = M.C; M.c = N; + M.d = im; })(exports.M || (exports.M = {})); var M = exports.M; (function (M) { @@ -127,3 +136,60 @@ var M = exports.M; var Q = M.Q; })(exports.M || (exports.M = {})); var M = exports.M; + + +//// [declarationEmit_nameConflicts_1.d.ts] +declare module f { + class c { + } +} +export = f; +//// [declarationEmit_nameConflicts_0.d.ts] +export declare module M { + function f(): void; + class C { + } + module N { + function g(): void; + interface I { + } + } + export import a = M.f; + export import b = M.C; + export import c = N; + export import d = im; +} +export declare module M.P { + function f(): void; + class C { + } + module N { + function g(): void; + interface I { + } + } + export import im = M.P.f; + var a: () => void; + var b: typeof M.C; + var c: typeof M.N; + var g: () => void; + var d: typeof M.d; +} +export declare module M.Q { + function f(): void; + class C { + } + module N { + function g(): void; + interface I { + } + } + interface b extends M.C { + } + interface I extends M.N.I { + } + module c { + interface I extends M.N.I { + } + } +} diff --git a/tests/baselines/reference/declarationEmit_nameConflicts2.js b/tests/baselines/reference/declarationEmit_nameConflicts2.js index 7db130615a7..bc8ba2f63e9 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts2.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts2.js @@ -73,7 +73,7 @@ declare module X.Y.base { } declare module X.Y.base.Z { var f: () => void; - var C: typeof C; - var M: typeof M; - var E: typeof E; + var C: typeof base.C; + var M: typeof base.M; + var E: typeof base.E; } diff --git a/tests/baselines/reference/declarationEmit_nameConflicts3.js b/tests/baselines/reference/declarationEmit_nameConflicts3.js index db5a7ba323b..cfec6264a77 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts3.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts3.js @@ -110,7 +110,7 @@ declare module M.P { enum D { f = 0, } - var v: D; + var v: M.D; var w: () => void; var x: () => void; var x: () => void; diff --git a/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.errors.txt b/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.errors.txt deleted file mode 100644 index 100bf85427e..00000000000 --- a/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -==== tests/cases/compiler/declarationEmit_nameConflictsWithAlias.ts (1 errors) ==== - // Bug 887180 - export module C { export interface I { } } - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! Cannot compile external modules unless the '--module' flag is provided. - export import v = C; - export module M { - export module C { export interface I { } } - export var w: v.I; // Gets emitted as C.I, which is the wrong interface - } \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.js b/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.js index eade0352272..72b2a9295f5 100644 --- a/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.js +++ b/tests/baselines/reference/declarationEmit_nameConflictsWithAlias.js @@ -12,3 +12,18 @@ export module M { M.w; })(exports.M || (exports.M = {})); var M = exports.M; + + +//// [declarationEmit_nameConflictsWithAlias.d.ts] +export declare module C { + interface I { + } +} +export import v = C; +export declare module M { + module C { + interface I { + } + } + var w: v.I; +} diff --git a/tests/cases/compiler/declarationEmit_nameConflicts.ts b/tests/cases/compiler/declarationEmit_nameConflicts.ts index 40ae28d8c14..49218d2ef1e 100644 --- a/tests/cases/compiler/declarationEmit_nameConflicts.ts +++ b/tests/cases/compiler/declarationEmit_nameConflicts.ts @@ -1,6 +1,7 @@ // @declaration: true +// @module: commonjs // @Filename: declarationEmit_nameConflicts_1.ts -function f() { } +module f { export class c { } } export = f; // @Filename: declarationEmit_nameConflicts_0.ts diff --git a/tests/cases/compiler/declarationEmit_nameConflicts3.ts b/tests/cases/compiler/declarationEmit_nameConflicts3.ts index 98f7ce91d5e..b854841b4b4 100644 --- a/tests/cases/compiler/declarationEmit_nameConflicts3.ts +++ b/tests/cases/compiler/declarationEmit_nameConflicts3.ts @@ -1,4 +1,5 @@ // @declaration: true +// @module: commonjs module M { export interface D { } export module D { diff --git a/tests/cases/compiler/declarationEmit_nameConflictsWithAlias.ts b/tests/cases/compiler/declarationEmit_nameConflictsWithAlias.ts index e6771f2d848..87cdca3fd41 100644 --- a/tests/cases/compiler/declarationEmit_nameConflictsWithAlias.ts +++ b/tests/cases/compiler/declarationEmit_nameConflictsWithAlias.ts @@ -1,4 +1,5 @@ // @declaration: true +// @module: commonjs // Bug 887180 export module C { export interface I { } } export import v = C;