diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9cff537b07f..20033d4c80b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4734,14 +4734,49 @@ module ts { return type; } } + /*Transitively mark all linked imports as referenced*/ + function markLinkedImportsAsReferenced(node: ImportDeclaration): void { + var nodeLinks = getNodeLinks(node); + while (nodeLinks.importOnRightSide) { + var rightSide = nodeLinks.importOnRightSide; + nodeLinks.importOnRightSide = undefined; + + getSymbolLinks(rightSide).referenced = true; + Debug.assert((rightSide.flags & SymbolFlags.Import) !== 0); + + nodeLinks = getNodeLinks(getDeclarationOfKind(rightSide, SyntaxKind.ImportDeclaration)) + } + } function checkIdentifier(node: Identifier): Type { var symbol = getResolvedSymbol(node); if (symbol.flags & SymbolFlags.Import) { - // Mark the import as referenced so that we emit it in the final .js file. - // exception: identifiers that appear in type queries, const enums, modules that contain only const enums - getSymbolLinks(symbol).referenced = getSymbolLinks(symbol).referenced || (!isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol))); + var symbolLinks = getSymbolLinks(symbol); + if (!symbolLinks.referenced) { + var importOrExportAssignment = getLeftSideOfImportOrExportAssignment(node); + + // decision about whether import is referenced can be made now if + // - import that are used anywhere except right side of import declarations + // - imports that are used on the right side of exported import declarations + // for other cases defer decision until the check of left side + if (!importOrExportAssignment || + (importOrExportAssignment.flags & NodeFlags.Export) || + (importOrExportAssignment.kind === SyntaxKind.ExportAssignment)) { + // Mark the import as referenced so that we emit it in the final .js file. + // exception: identifiers that appear in type queries, const enums, modules that contain only const enums + symbolLinks.referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol)); + } + else { + var nodeLinks = getNodeLinks(importOrExportAssignment); + Debug.assert(!nodeLinks.importOnRightSide); + nodeLinks.importOnRightSide = symbol; + } + } + + if (symbolLinks.referenced) { + markLinkedImportsAsReferenced(getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration)); + } } checkCollisionWithCapturedSuperVariable(node, node); @@ -8872,6 +8907,8 @@ module ts { if (symbol && symbol.flags & SymbolFlags.Import) { // Mark the import as referenced so that we emit it in the final .js file. getSymbolLinks(symbol).referenced = true; + // mark any import declarations that depend upon this import as referenced + markLinkedImportsAsReferenced(getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration)) } } @@ -9097,19 +9134,24 @@ module ts { return false; } + function getLeftSideOfImportOrExportAssignment(nodeOnRightSide: EntityName): ImportDeclaration | ExportAssignment { + while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) { + nodeOnRightSide = nodeOnRightSide.parent; + } + + if (nodeOnRightSide.parent.kind === SyntaxKind.ImportDeclaration) { + return (nodeOnRightSide.parent).moduleReference === nodeOnRightSide && nodeOnRightSide.parent; + } + + if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) { + return (nodeOnRightSide.parent).exportName === nodeOnRightSide && nodeOnRightSide.parent; + } + + return undefined; + } + function isInRightSideOfImportOrExportAssignment(node: EntityName) { - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - - if (node.parent.kind === SyntaxKind.ImportDeclaration) { - return (node.parent).moduleReference === node; - } - if (node.parent.kind === SyntaxKind.ExportAssignment) { - return (node.parent).exportName === node; - } - - return false; + return getLeftSideOfImportOrExportAssignment(node) !== undefined; } function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 48288628a17..66a2ff25eee 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1222,6 +1222,7 @@ module ts { isVisible?: boolean; // Is this node visible localModuleName?: string; // Local name for module instance assignmentChecks?: Map; // Cache of assignment checks + importOnRightSide?: Symbol; // for import declarations - import that appear on the right side } export const enum TypeFlags { diff --git a/tests/baselines/reference/importedAliasesInTypePositions.js b/tests/baselines/reference/importedAliasesInTypePositions.js new file mode 100644 index 00000000000..fb56b1ac0d7 --- /dev/null +++ b/tests/baselines/reference/importedAliasesInTypePositions.js @@ -0,0 +1,55 @@ +//// [tests/cases/compiler/importedAliasesInTypePositions.ts] //// + +//// [file1.ts] +export module elaborate.nested.mod.name { + export class ReferredTo { + doSomething(): void { + } + } +} + +//// [file2.ts] +import RT_ALIAS = require("file1"); +import ReferredTo = RT_ALIAS.elaborate.nested.mod.name.ReferredTo; + +export module ImportingModule { + class UsesReferredType { + constructor(private referred: ReferredTo) { } + } +} + +//// [file1.js] +define(["require", "exports"], function (require, exports) { + var elaborate; + (function (elaborate) { + var nested; + (function (nested) { + var mod; + (function (mod) { + var name; + (function (name) { + var ReferredTo = (function () { + function ReferredTo() { + } + ReferredTo.prototype.doSomething = function () { + }; + return ReferredTo; + })(); + name.ReferredTo = ReferredTo; + })(name = mod.name || (mod.name = {})); + })(mod = nested.mod || (nested.mod = {})); + })(nested = elaborate.nested || (elaborate.nested = {})); + })(elaborate = exports.elaborate || (exports.elaborate = {})); +}); +//// [file2.js] +define(["require", "exports"], function (require, exports) { + var ImportingModule; + (function (ImportingModule) { + var UsesReferredType = (function () { + function UsesReferredType(referred) { + this.referred = referred; + } + return UsesReferredType; + })(); + })(ImportingModule = exports.ImportingModule || (exports.ImportingModule = {})); +}); diff --git a/tests/baselines/reference/importedAliasesInTypePositions.types b/tests/baselines/reference/importedAliasesInTypePositions.types new file mode 100644 index 00000000000..ed78188d275 --- /dev/null +++ b/tests/baselines/reference/importedAliasesInTypePositions.types @@ -0,0 +1,40 @@ +=== tests/cases/compiler/file2.ts === +import RT_ALIAS = require("file1"); +>RT_ALIAS : typeof RT_ALIAS + +import ReferredTo = RT_ALIAS.elaborate.nested.mod.name.ReferredTo; +>ReferredTo : typeof ReferredTo +>RT_ALIAS : typeof RT_ALIAS +>elaborate : typeof RT_ALIAS.elaborate +>nested : typeof RT_ALIAS.elaborate.nested +>mod : typeof RT_ALIAS.elaborate.nested.mod +>name : typeof RT_ALIAS.elaborate.nested.mod.name +>ReferredTo : ReferredTo + +export module ImportingModule { +>ImportingModule : typeof ImportingModule + + class UsesReferredType { +>UsesReferredType : UsesReferredType + + constructor(private referred: ReferredTo) { } +>referred : ReferredTo +>ReferredTo : ReferredTo + } +} +=== tests/cases/compiler/file1.ts === +export module elaborate.nested.mod.name { +>elaborate : typeof elaborate +>nested : typeof nested +>mod : typeof mod +>name : typeof name + + export class ReferredTo { +>ReferredTo : ReferredTo + + doSomething(): void { +>doSomething : () => void + } + } +} + diff --git a/tests/cases/compiler/importedAliasesInTypePositions.ts b/tests/cases/compiler/importedAliasesInTypePositions.ts new file mode 100644 index 00000000000..f1dd66f60a7 --- /dev/null +++ b/tests/cases/compiler/importedAliasesInTypePositions.ts @@ -0,0 +1,19 @@ +// @module:amd +// @Filename: file1.ts +export module elaborate.nested.mod.name { + export class ReferredTo { + doSomething(): void { + } + } +} + +// @Filename: file2.ts +// @module: amd +import RT_ALIAS = require("file1"); +import ReferredTo = RT_ALIAS.elaborate.nested.mod.name.ReferredTo; + +export module ImportingModule { + class UsesReferredType { + constructor(private referred: ReferredTo) { } + } +} \ No newline at end of file