mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 20:37:00 -05:00
do not treat module that contains only const enums as instantiated
This commit is contained in:
@@ -5,29 +5,51 @@
|
||||
|
||||
module ts {
|
||||
|
||||
export function isInstantiated(node: Node, treatConstEnumsAsValues: boolean): boolean {
|
||||
export enum ModuleInstanceState {
|
||||
NonInstantiated = 0,
|
||||
Instantiated = 1,
|
||||
ConstEnumOnly = 2
|
||||
}
|
||||
|
||||
export function getModuleInstanceState(node: Node): ModuleInstanceState {
|
||||
// A module is uninstantiated if it contains only
|
||||
// 1. interface declarations
|
||||
if (node.kind === SyntaxKind.InterfaceDeclaration) {
|
||||
return false;
|
||||
return ModuleInstanceState.NonInstantiated;
|
||||
}
|
||||
// 2. const enum declarations don't make module instantiated
|
||||
else if (!treatConstEnumsAsValues && node.kind === SyntaxKind.EnumDeclaration && isConstEnumDeclaration(<EnumDeclaration>node)) {
|
||||
return false;
|
||||
else if (node.kind === SyntaxKind.EnumDeclaration && isConstEnumDeclaration(<EnumDeclaration>node)) {
|
||||
return ModuleInstanceState.ConstEnumOnly;
|
||||
}
|
||||
// 3. non - exported import declarations
|
||||
else if (node.kind === SyntaxKind.ImportDeclaration && !(node.flags & NodeFlags.Export)) {
|
||||
return false;
|
||||
return ModuleInstanceState.NonInstantiated;
|
||||
}
|
||||
// 4. other uninstantiated module declarations.
|
||||
else if (node.kind === SyntaxKind.ModuleBlock && !forEachChild(node, n => isInstantiated(n, treatConstEnumsAsValues))) {
|
||||
return false;
|
||||
else if (node.kind === SyntaxKind.ModuleBlock) {
|
||||
var state = ModuleInstanceState.NonInstantiated;
|
||||
forEachChild(node, n => {
|
||||
switch (getModuleInstanceState(n)) {
|
||||
case ModuleInstanceState.NonInstantiated:
|
||||
// child is non-instantiated - continue searching
|
||||
return false;
|
||||
case ModuleInstanceState.ConstEnumOnly:
|
||||
// child is const enum only - record state and continue searching
|
||||
state = ModuleInstanceState.ConstEnumOnly;
|
||||
return false;
|
||||
case ModuleInstanceState.Instantiated:
|
||||
// child is instantiated - record state and stop
|
||||
state = ModuleInstanceState.Instantiated;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return state;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated((<ModuleDeclaration>node).body, treatConstEnumsAsValues)) {
|
||||
return false;
|
||||
else if (node.kind === SyntaxKind.ModuleDeclaration) {
|
||||
return getModuleInstanceState((<ModuleDeclaration>node).body);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
return ModuleInstanceState.Instantiated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,11 +274,22 @@ module ts {
|
||||
if (node.name.kind === SyntaxKind.StringLiteral) {
|
||||
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true);
|
||||
}
|
||||
else if (isInstantiated(node, /*treatConstEnumsAsValues*/ true)) {
|
||||
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true);
|
||||
}
|
||||
else {
|
||||
bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true);
|
||||
var state = getModuleInstanceState(node);
|
||||
if (state === ModuleInstanceState.NonInstantiated) {
|
||||
bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true);
|
||||
}
|
||||
else {
|
||||
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true);
|
||||
if (state === ModuleInstanceState.ConstEnumOnly) {
|
||||
// mark value module as module that contains only enums
|
||||
node.symbol.constEnumOnlyModule = true;
|
||||
}
|
||||
else if (node.symbol.constEnumOnlyModule) {
|
||||
// const only value module was merged with instantiated module - reset flag
|
||||
node.symbol.constEnumOnlyModule = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ module ts {
|
||||
result.declarations = symbol.declarations.slice(0);
|
||||
result.parent = symbol.parent;
|
||||
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
|
||||
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
|
||||
if (symbol.members) result.members = cloneSymbolTable(symbol.members);
|
||||
if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
|
||||
recordMergedSymbol(result, symbol);
|
||||
@@ -216,6 +217,10 @@ module ts {
|
||||
|
||||
function extendSymbol(target: Symbol, source: Symbol) {
|
||||
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
|
||||
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
|
||||
// reset flag when merging instantiated module into value module that has only const enums
|
||||
target.constEnumOnlyModule = false;
|
||||
}
|
||||
target.flags |= source.flags;
|
||||
if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration;
|
||||
forEach(source.declarations, node => {
|
||||
@@ -4432,8 +4437,8 @@ module ts {
|
||||
|
||||
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
|
||||
getSymbolLinks(symbol).referenced = !isInTypeQuery(node) && !isConstEnumSymbol(resolveImport(symbol));
|
||||
// exception: identifiers that appear in type queries, const enums, modules that contain only const enums
|
||||
getSymbolLinks(symbol).referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol));
|
||||
}
|
||||
|
||||
checkCollisionWithCapturedSuperVariable(node, node);
|
||||
@@ -6883,7 +6888,7 @@ module ts {
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return SymbolFlags.ExportType;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return (<ModuleDeclaration>d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d, /*treatConstEnumsAsValues*/ true)
|
||||
return (<ModuleDeclaration>d).name.kind === SyntaxKind.StringLiteral || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated
|
||||
? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue
|
||||
: SymbolFlags.ExportNamespace;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
@@ -7120,7 +7125,7 @@ module ts {
|
||||
}
|
||||
|
||||
// Uninstantiated modules shouldnt do this check
|
||||
if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated(node, /*treatConstEnumsAsValues*/ false)) {
|
||||
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8673,8 +8678,7 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
var symbol = getSymbolOfNode(node);
|
||||
var target = resolveImport(symbol);
|
||||
return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0) && !isConstEnumSymbol(target);
|
||||
return isImportResolvedToValue(getSymbolOfNode(node));
|
||||
}
|
||||
|
||||
function hasSemanticErrors() {
|
||||
@@ -8686,6 +8690,16 @@ module ts {
|
||||
return forEach(getDiagnostics(sourceFile), d => d.isEarly);
|
||||
}
|
||||
|
||||
function isImportResolvedToValue(symbol: Symbol): boolean {
|
||||
var target = resolveImport(symbol);
|
||||
// const enums and modules that contain only const enums are not considered values from the emit perespective
|
||||
return target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target);
|
||||
}
|
||||
|
||||
function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean {
|
||||
return isConstEnumSymbol(s) || s.constEnumOnlyModule;
|
||||
}
|
||||
|
||||
function isReferencedImportDeclaration(node: ImportDeclaration): boolean {
|
||||
var symbol = getSymbolOfNode(node);
|
||||
if (getSymbolLinks(symbol).referenced) {
|
||||
@@ -8694,11 +8708,7 @@ module ts {
|
||||
// logic below will answer 'true' for exported import declaration in a nested module that itself is not exported.
|
||||
// As a consequence this might cause emitting extra.
|
||||
if (node.flags & NodeFlags.Export) {
|
||||
var target = resolveImport(symbol);
|
||||
// importing const enum does not cause import to be referenced
|
||||
if (target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumSymbol(target)) {
|
||||
return true;
|
||||
}
|
||||
return isImportResolvedToValue(symbol);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1838,7 +1838,7 @@ module ts {
|
||||
}
|
||||
|
||||
function emitModuleDeclaration(node: ModuleDeclaration) {
|
||||
if (!isInstantiated(node, /*treatConstEnumsAsValues*/ false)) {
|
||||
if (getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
|
||||
return emitPinnedOrTripleSlashComments(node);
|
||||
}
|
||||
emitLeadingComments(node);
|
||||
|
||||
@@ -842,7 +842,8 @@ module ts {
|
||||
members?: SymbolTable; // Class, interface or literal instance members
|
||||
exports?: SymbolTable; // Module exports
|
||||
exportSymbol?: Symbol; // Exported symbol associated with this symbol
|
||||
valueDeclaration?: Declaration // First value declaration of the symbol
|
||||
valueDeclaration?: Declaration // First value declaration of the symbol,
|
||||
constEnumOnlyModule?: boolean // For modules - if true - module contains only const enums or other modules with only const enums.
|
||||
}
|
||||
|
||||
export interface SymbolLinks {
|
||||
|
||||
@@ -178,7 +178,7 @@ module ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
// span on complete module if it is instantiated
|
||||
if (!isInstantiated(node, /*treatConstEnumsAsValues*/ false)) {
|
||||
if (getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ module ts.BreakpointResolver {
|
||||
function spanInBlock(block: Block): TypeScript.TextSpan {
|
||||
switch (block.parent.kind) {
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (!isInstantiated(block.parent, /*treatConstEnumsAsValues*/ false)) {
|
||||
if (getModuleInstanceState(block.parent) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ module ts.BreakpointResolver {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
// If this is not instantiated module block no bp span
|
||||
if (!isInstantiated(node.parent.parent, /*treatConstEnumsAsValues*/ false)) {
|
||||
if (getModuleInstanceState(node.parent.parent) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -4549,7 +4549,7 @@ module ts {
|
||||
if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
else if (isInstantiated(node, /*treatConstEnumsAsValues*/ false)) {
|
||||
else if (getModuleInstanceState(node) === ModuleInstanceState.Instantiated) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
else {
|
||||
@@ -4826,7 +4826,7 @@ module ts {
|
||||
*/
|
||||
function hasValueSideModule(symbol: Symbol): boolean {
|
||||
return forEach(symbol.declarations, declaration => {
|
||||
return declaration.kind === SyntaxKind.ModuleDeclaration && isInstantiated(declaration, /*treatConstEnumsAsValues*/ false);
|
||||
return declaration.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(declaration) == ModuleInstanceState.Instantiated;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,35 @@ module A {
|
||||
}
|
||||
}
|
||||
|
||||
module A1 {
|
||||
export module B {
|
||||
export module C {
|
||||
export const enum E {
|
||||
V1 = 10,
|
||||
V2 = 110,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module A2 {
|
||||
export module B {
|
||||
export module C {
|
||||
export const enum E {
|
||||
V1 = 10,
|
||||
V2 = 110,
|
||||
}
|
||||
}
|
||||
// module C will be classified as value
|
||||
export module C {
|
||||
var x = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import I = A.B.C.E;
|
||||
import I1 = A1.B;
|
||||
import I2 = A2.B;
|
||||
|
||||
function foo0(e: I): void {
|
||||
if (e === I.V1) {
|
||||
@@ -67,6 +95,21 @@ function foo0(e: I): void {
|
||||
}
|
||||
}
|
||||
|
||||
function foo1(e: I1.C.E): void {
|
||||
if (e === I1.C.E.V1) {
|
||||
}
|
||||
else if (e === I1.C.E.V2) {
|
||||
}
|
||||
}
|
||||
|
||||
function foo2(e: I2.C.E): void {
|
||||
if (e === I2.C.E.V1) {
|
||||
}
|
||||
else if (e === I2.C.E.V2) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function foo(x: Enum1) {
|
||||
switch (x) {
|
||||
case Enum1.A:
|
||||
@@ -109,12 +152,36 @@ function bar(e: A.B.C.E): number {
|
||||
}
|
||||
|
||||
//// [constEnums.js]
|
||||
var A2;
|
||||
(function (A2) {
|
||||
var B;
|
||||
(function (B) {
|
||||
// module C will be classified as value
|
||||
var C;
|
||||
(function (C) {
|
||||
var x = 1;
|
||||
})(C = B.C || (B.C = {}));
|
||||
})(B = A2.B || (A2.B = {}));
|
||||
})(A2 || (A2 = {}));
|
||||
var I2 = A2.B;
|
||||
function foo0(e) {
|
||||
if (e === 1 /* V1 */) {
|
||||
}
|
||||
else if (e === 101 /* V2 */) {
|
||||
}
|
||||
}
|
||||
function foo1(e) {
|
||||
if (e === 10 /* V1 */) {
|
||||
}
|
||||
else if (e === 110 /* V2 */) {
|
||||
}
|
||||
}
|
||||
function foo2(e) {
|
||||
if (e === 10 /* V1 */) {
|
||||
}
|
||||
else if (e === 110 /* V2 */) {
|
||||
}
|
||||
}
|
||||
function foo(x) {
|
||||
switch (x) {
|
||||
case 0 /* A */:
|
||||
|
||||
@@ -211,6 +211,57 @@ module A {
|
||||
}
|
||||
}
|
||||
|
||||
module A1 {
|
||||
>A1 : typeof A1
|
||||
|
||||
export module B {
|
||||
>B : typeof B
|
||||
|
||||
export module C {
|
||||
>C : typeof C
|
||||
|
||||
export const enum E {
|
||||
>E : E
|
||||
|
||||
V1 = 10,
|
||||
>V1 : E
|
||||
|
||||
V2 = 110,
|
||||
>V2 : E
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module A2 {
|
||||
>A2 : typeof A2
|
||||
|
||||
export module B {
|
||||
>B : typeof B
|
||||
|
||||
export module C {
|
||||
>C : typeof C
|
||||
|
||||
export const enum E {
|
||||
>E : E
|
||||
|
||||
V1 = 10,
|
||||
>V1 : E
|
||||
|
||||
V2 = 110,
|
||||
>V2 : E
|
||||
}
|
||||
}
|
||||
// module C will be classified as value
|
||||
export module C {
|
||||
>C : typeof C
|
||||
|
||||
var x = 1
|
||||
>x : number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import I = A.B.C.E;
|
||||
>I : typeof I
|
||||
>A : typeof A
|
||||
@@ -218,6 +269,16 @@ import I = A.B.C.E;
|
||||
>C : typeof A.B.C
|
||||
>E : I
|
||||
|
||||
import I1 = A1.B;
|
||||
>I1 : typeof I1
|
||||
>A1 : typeof A1
|
||||
>B : typeof I1
|
||||
|
||||
import I2 = A2.B;
|
||||
>I2 : typeof I2
|
||||
>A2 : typeof A2
|
||||
>B : typeof I2
|
||||
|
||||
function foo0(e: I): void {
|
||||
>foo0 : (e: I) => void
|
||||
>e : I
|
||||
@@ -239,6 +300,69 @@ function foo0(e: I): void {
|
||||
}
|
||||
}
|
||||
|
||||
function foo1(e: I1.C.E): void {
|
||||
>foo1 : (e: I1.C.E) => void
|
||||
>e : I1.C.E
|
||||
>I1 : unknown
|
||||
>C : unknown
|
||||
>E : I1.C.E
|
||||
|
||||
if (e === I1.C.E.V1) {
|
||||
>e === I1.C.E.V1 : boolean
|
||||
>e : I1.C.E
|
||||
>I1.C.E.V1 : I1.C.E
|
||||
>I1.C.E : typeof I1.C.E
|
||||
>I1.C : typeof I1.C
|
||||
>I1 : typeof I1
|
||||
>C : typeof I1.C
|
||||
>E : typeof I1.C.E
|
||||
>V1 : I1.C.E
|
||||
}
|
||||
else if (e === I1.C.E.V2) {
|
||||
>e === I1.C.E.V2 : boolean
|
||||
>e : I1.C.E
|
||||
>I1.C.E.V2 : I1.C.E
|
||||
>I1.C.E : typeof I1.C.E
|
||||
>I1.C : typeof I1.C
|
||||
>I1 : typeof I1
|
||||
>C : typeof I1.C
|
||||
>E : typeof I1.C.E
|
||||
>V2 : I1.C.E
|
||||
}
|
||||
}
|
||||
|
||||
function foo2(e: I2.C.E): void {
|
||||
>foo2 : (e: I2.C.E) => void
|
||||
>e : I2.C.E
|
||||
>I2 : unknown
|
||||
>C : unknown
|
||||
>E : I2.C.E
|
||||
|
||||
if (e === I2.C.E.V1) {
|
||||
>e === I2.C.E.V1 : boolean
|
||||
>e : I2.C.E
|
||||
>I2.C.E.V1 : I2.C.E
|
||||
>I2.C.E : typeof I2.C.E
|
||||
>I2.C : typeof I2.C
|
||||
>I2 : typeof I2
|
||||
>C : typeof I2.C
|
||||
>E : typeof I2.C.E
|
||||
>V1 : I2.C.E
|
||||
}
|
||||
else if (e === I2.C.E.V2) {
|
||||
>e === I2.C.E.V2 : boolean
|
||||
>e : I2.C.E
|
||||
>I2.C.E.V2 : I2.C.E
|
||||
>I2.C.E : typeof I2.C.E
|
||||
>I2.C : typeof I2.C
|
||||
>I2 : typeof I2
|
||||
>C : typeof I2.C
|
||||
>E : typeof I2.C.E
|
||||
>V2 : I2.C.E
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function foo(x: Enum1) {
|
||||
>foo : (x: Enum1) => void
|
||||
>x : Enum1
|
||||
|
||||
@@ -57,7 +57,35 @@ module A {
|
||||
}
|
||||
}
|
||||
|
||||
module A1 {
|
||||
export module B {
|
||||
export module C {
|
||||
export const enum E {
|
||||
V1 = 10,
|
||||
V2 = 110,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module A2 {
|
||||
export module B {
|
||||
export module C {
|
||||
export const enum E {
|
||||
V1 = 10,
|
||||
V2 = 110,
|
||||
}
|
||||
}
|
||||
// module C will be classified as value
|
||||
export module C {
|
||||
var x = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import I = A.B.C.E;
|
||||
import I1 = A1.B;
|
||||
import I2 = A2.B;
|
||||
|
||||
function foo0(e: I): void {
|
||||
if (e === I.V1) {
|
||||
@@ -66,6 +94,21 @@ function foo0(e: I): void {
|
||||
}
|
||||
}
|
||||
|
||||
function foo1(e: I1.C.E): void {
|
||||
if (e === I1.C.E.V1) {
|
||||
}
|
||||
else if (e === I1.C.E.V2) {
|
||||
}
|
||||
}
|
||||
|
||||
function foo2(e: I2.C.E): void {
|
||||
if (e === I2.C.E.V1) {
|
||||
}
|
||||
else if (e === I2.C.E.V2) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function foo(x: Enum1) {
|
||||
switch (x) {
|
||||
case Enum1.A:
|
||||
|
||||
Reference in New Issue
Block a user