diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab0b663bcfd..8e08f328d3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -917,6 +917,125 @@ module ts { } } + function isDeclarationVisible(node: Declaration): boolean { + function isGlobalSourceFile(node: Node) { + return node.kind === SyntaxKind.SourceFile && !(node.flags & NodeFlags.ExternalModule); + } + + function getExternalModule(node: Node) { + for (; node; node = node.parent) { + if (node.kind === SyntaxKind.ModuleDeclaration) { + if ((node).name.kind === SyntaxKind.StringLiteral) { + return node; + } + } + else if (node.kind === SyntaxKind.SourceFile) { + return (node.flags & NodeFlags.ExternalModule) ? node : null; + } + } + } + + function isUsedInExportAssignment(node: Node) { + // Get source File and see if it is external module and has export assigned symbol + var externalModule = getExternalModule(node); + if (externalModule) { + // This is export assigned symbol node + var externalModuleSymbol = getSymbolOfNode(externalModule); + var exportAssignmentSymbol = getExportAssignmentSymbol(externalModuleSymbol); + var symbolOfNode = getSymbolOfNode(node); + if (exportAssignmentSymbol === symbolOfNode) { + return true; + } + + if (exportAssignmentSymbol && !!(exportAssignmentSymbol.flags & SymbolFlags.Import)) { + // if export assigned symbol is import declaration, resolve the import + var resolvedExportSymbol = resolveImport(exportAssignmentSymbol); + if (resolvedExportSymbol === symbolOfNode) { + return true; + } + + // TODO(shkamat): Chained import assignment + // eg. a should be visible too. + //module m { + // export module c { + // export class c { + // } + // } + //} + //import a = m.c; + //import b = a; + //export = b; + + // Container of resolvedExportSymbol is visible + return forEach(resolvedExportSymbol.declarations, declaration => { + while (declaration) { + if (declaration === node) { + return true; + } + declaration = declaration.parent; + } + }); + } + } + } + + function determineIfDeclarationIsVisible() { + switch (node.kind) { + case SyntaxKind.VariableDeclaration: + if (!(node.flags & NodeFlags.Export)) { + // node.parent is variable statement so look at the variable statement's parent + return isGlobalSourceFile(node.parent.parent) || isUsedInExportAssignment(node); + } + // Exported members are visible if parent is visible + return isDeclarationVisible(node.parent.parent); + + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ImportDeclaration: + if (!(node.flags & NodeFlags.Export)) { + // TODO(shkamat): non exported aliases can be visible if they are referenced else where for value/type/namespace + return isGlobalSourceFile(node.parent) || isUsedInExportAssignment(node); + } + // Exported members are visible if parent is visible + return isDeclarationVisible(node.parent); + + case SyntaxKind.Property: + case SyntaxKind.Method: + if (node.flags & NodeFlags.Private) { + // Private properties/methods are not visible + return false; + } + // Public properties/methods are visible if its parents are visible, so let it fall into next case statement + + case SyntaxKind.Constructor: + case SyntaxKind.ConstructSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.Parameter: + case SyntaxKind.ModuleBlock: + return isDeclarationVisible(node.parent); + + // Source file is always visible + case SyntaxKind.SourceFile: + return true; + + default: + Debug.fail("isDeclarationVisible unknown: SyntaxKind: " + SyntaxKind[node.kind]); + } + } + + if (node) { + var links = getNodeLinks(node); + if (links.isVisible === undefined) { + links.isVisible = determineIfDeclarationIsVisible(); + } + return links.isVisible; + } + } + function getApparentType(type: Type): ApparentType { if (type.flags & TypeFlags.TypeParameter) { do { @@ -4892,7 +5011,7 @@ module ts { checkTypeAssignableTo(checkAndMarkExpression(node.initializer, type), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } } - + checkCollisionWithCapturedSuperVariable(node, node.name); if (!useTypeFromValueDeclaration) { // TypeScript 1.0 spec (April 2014): 5.1 @@ -5870,22 +5989,6 @@ module ts { return false; } - function isReferencedInExportAssignment(node: Declaration): boolean { - var exportAssignedSymbol = getExportAssignmentSymbol(getSymbolOfNode(getContainerOfModuleElementDeclaration(node))); - if (exportAssignedSymbol) { - var symbol = getSymbolOfNode(node); - if (exportAssignedSymbol === symbol) { - // This symbol was export assigned symbol - return true; - } - - // TODO(shkamat): if export assignment is alias, the alias target would make the node as referenced in export assignment - - } - - return false; - } - function isImplementationOfOverload(node: FunctionDeclaration) { if (node.body) { var symbol = getSymbolOfNode(node); @@ -5927,7 +6030,7 @@ module ts { getEnumMemberValue: getEnumMemberValue, isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName, shouldEmitDeclarations: shouldEmitDeclarations, - isReferencedInExportAssignment: isReferencedInExportAssignment, + isDeclarationVisible: isDeclarationVisible, isImplementationOfOverload: isImplementationOfOverload, writeTypeAtLocation: writeTypeAtLocation, writeReturnTypeOfSignatureDeclaration: writeReturnTypeOfSignatureDeclaration diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 20999a77cd4..98fba811159 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1881,34 +1881,6 @@ module ts { writeLine(); } - function isModuleElementExternallyVisible(node: Declaration) { - if (node.flags & NodeFlags.Export) { - // Exported member - emit this declaration - return true; - } - - // If this node is in external module, check if this is export assigned - var moduleDeclaration = getContainerOfModuleElementDeclaration(node); - if ((moduleDeclaration.flags & NodeFlags.ExternalModule) || // Source file with external module flag - // Ambient external module declaration - (moduleDeclaration.kind === SyntaxKind.ModuleDeclaration && (moduleDeclaration).name.kind === SyntaxKind.StringLiteral)) { - return resolver.isReferencedInExportAssignment(node); - } - - return false; - } - - function canEmitModuleElementDeclaration(node: Declaration) { - if (isModuleElementExternallyVisible(node)) { - // Either exported module element or is referenced in export assignment - return true; - } - - // emit the declaration if this is in global scope source file - var moduleDeclaration = getContainerOfModuleElementDeclaration(node); - return moduleDeclaration.kind === SyntaxKind.SourceFile && !(moduleDeclaration.flags & NodeFlags.ExternalModule); - } - function emitDeclarationFlags(node: Declaration) { if (node.flags & NodeFlags.Static) { if (node.flags & NodeFlags.Private) { @@ -1935,8 +1907,7 @@ module ts { } function emitImportDeclaration(node: ImportDeclaration) { - // TODO(shkamat): Emit if import decl is used to declare type in this context - if (isModuleElementExternallyVisible(node)) { + if (resolver.isDeclarationVisible(node)) { if (node.flags & NodeFlags.Export) { write("export "); } @@ -1957,7 +1928,7 @@ module ts { } function emitModuleDeclaration(node: ModuleDeclaration) { - if (canEmitModuleElementDeclaration(node)) { + if (resolver.isDeclarationVisible(node)) { emitDeclarationFlags(node); write("module "); emitSourceTextOfNode(node.name); @@ -1980,7 +1951,7 @@ module ts { } function emitEnumDeclaration(node: EnumDeclaration) { - if (canEmitModuleElementDeclaration(node)) { + if (resolver.isDeclarationVisible(node)) { emitDeclarationFlags(node); write("enum "); emitSourceTextOfNode(node.name); @@ -2043,7 +2014,7 @@ module ts { } } - if (canEmitModuleElementDeclaration(node)) { + if (resolver.isDeclarationVisible(node)) { emitDeclarationFlags(node); write("class "); emitSourceTextOfNode(node.name); @@ -2067,7 +2038,7 @@ module ts { } function emitInterfaceDeclaration(node: InterfaceDeclaration) { - if (canEmitModuleElementDeclaration(node)) { + if (resolver.isDeclarationVisible(node)) { emitDeclarationFlags(node); write("interface "); emitSourceTextOfNode(node.name); @@ -2094,8 +2065,9 @@ module ts { } function emitVariableDeclaration(node: VariableDeclaration) { - // If we are emitting property it isnt moduleElement and doesnt need canEmitModuleElement check - if (node.kind !== SyntaxKind.VariableDeclaration || canEmitModuleElementDeclaration(node)) { + // If we are emitting property it isnt moduleElement and hence we already know it needs to be emitted + // so there is no check needed to see if declaration is visible + if (node.kind !== SyntaxKind.VariableDeclaration || resolver.isDeclarationVisible(node)) { emitSourceTextOfNode(node.name); // If optional property emit ? if (node.kind === SyntaxKind.Property && (node.flags & NodeFlags.QuestionMark)) { @@ -2109,7 +2081,7 @@ module ts { } function emitVariableStatement(node: VariableStatement) { - var hasDeclarationWithEmit = forEach(node.declarations, varDeclaration => canEmitModuleElementDeclaration(varDeclaration)); + var hasDeclarationWithEmit = forEach(node.declarations, varDeclaration => resolver.isDeclarationVisible(varDeclaration)); if (hasDeclarationWithEmit) { emitDeclarationFlags(node); write("var "); @@ -2134,8 +2106,9 @@ module ts { } function emitFunctionDeclaration(node: FunctionDeclaration) { - // If we are emitting Method/Constructor it isnt moduleElement and doesnt need canEmitModuleElement check - if ((node.kind !== SyntaxKind.FunctionDeclaration || canEmitModuleElementDeclaration(node)) && + // If we are emitting Method/Constructor it isnt moduleElement and hence already determined to be emitting + // so no need to verify if the declaration is visible + if ((node.kind !== SyntaxKind.FunctionDeclaration || resolver.isDeclarationVisible(node)) && !resolver.isImplementationOfOverload(node)) { emitDeclarationFlags(node); if (node.kind === SyntaxKind.FunctionDeclaration) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d3e942be2fe..e39d8ab3694 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -273,12 +273,6 @@ module ts { return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0; } - export function getContainerOfModuleElementDeclaration(node: Declaration) { - // If the declaration is var declaration, then the parent is variable statement but we actually want the module - var container = node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent : node.parent; - return container.kind == SyntaxKind.ModuleBlock ? container.parent : container; - } - enum ParsingContext { SourceElements, // Elements in source file ModuleElements, // Elements in module declaration diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c63d5ef1242..f9068868850 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -616,7 +616,7 @@ module ts { getNodeCheckFlags(node: Node): NodeCheckFlags; getEnumMemberValue(node: EnumMember): number; shouldEmitDeclarations(): boolean; - isReferencedInExportAssignment(node: Declaration): boolean; + isDeclarationVisible(node: Declaration): boolean; isImplementationOfOverload(node: FunctionDeclaration): boolean; writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; @@ -736,6 +736,7 @@ module ts { flags?: NodeCheckFlags; // Set of flags specific to Node enumMemberValue?: number; // Constant value of enum member isIllegalTypeReferenceInConstraint?: boolean; // Is type reference in constraint refers to the type parameter from the same list + isVisible?: boolean; // Is this node visible } export enum TypeFlags { diff --git a/tests/baselines/reference/declFileExportAssignmentImportInternalModule.js b/tests/baselines/reference/declFileExportAssignmentImportInternalModule.js index 2ead0783eeb..f4cd27e6c01 100644 --- a/tests/baselines/reference/declFileExportAssignmentImportInternalModule.js +++ b/tests/baselines/reference/declFileExportAssignmentImportInternalModule.js @@ -31,5 +31,21 @@ module.exports = m; //// [declFileExportAssignmentImportInternalModule.d.ts] +declare module m3 { + module m2 { + interface connectModule { + (res: any, req: any, next: any): void; + } + interface connectExport { + use: (mod: connectModule) => connectExport; + listen: (port: number) => void; + } + } + var server: { + (): m2.connectExport; + test1: m2.connectModule; + test2(): m2.connectModule; + }; +} import m = m3; export = m;