Check if accessible symbol needs futher qualification

This commit is contained in:
Sheetal Nandi 2014-07-15 16:03:25 -07:00
parent d8f3481d38
commit 5a23dd0684
16 changed files with 190 additions and 121 deletions

View File

@ -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<T>(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(<TypeReference>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(<ObjectType>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) {

View File

@ -99,17 +99,17 @@ declare module templa.mvc {
declare module templa.mvc.composite {
}
declare module templa.dom.mvc {
interface IElementController<ModelType extends mvc.IModel> extends mvc.IController<ModelType> {
interface IElementController<ModelType extends templa.mvc.IModel> extends templa.mvc.IController<ModelType> {
}
}
declare module templa.dom.mvc {
class AbstractElementController<ModelType extends mvc.IModel> extends mvc.AbstractController<ModelType> implments IElementController<ModelType> {
class AbstractElementController<ModelType extends templa.mvc.IModel> extends templa.mvc.AbstractController<ModelType> implments IElementController<ModelType> {
constructor();
}
}
declare module templa.dom.mvc.composite {
class AbstractCompositeElementController<ModelType extends mvc.composite.ICompositeControllerModel> extends AbstractElementController<ModelType> {
_controllers: mvc.IController<mvc.IModel>[];
class AbstractCompositeElementController<ModelType extends templa.mvc.composite.ICompositeControllerModel> extends AbstractElementController<ModelType> {
_controllers: templa.mvc.IController<templa.mvc.IModel>[];
constructor();
}
}

View File

@ -33,6 +33,6 @@ declare module A.C {
}
}
declare module A.B.C {
class W implments C.Z {
class W implments A.C.Z {
}
}

View File

@ -78,7 +78,7 @@ declare module X.Y.base {
}
}
declare module X.Y.base.Z {
class W<TValue> extends W {
class W<TValue> extends base.W {
value: boolean;
}
}

View File

@ -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 {
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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.

View File

@ -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 {
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -1,4 +1,5 @@
// @declaration: true
// @module: commonjs
module M {
export interface D { }
export module D {

View File

@ -1,4 +1,5 @@
// @declaration: true
// @module: commonjs
// Bug 887180
export module C { export interface I { } }
export import v = C;