From daba11f6cb9c26405fd8a7b9ce46604c88587d91 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 22 Aug 2022 15:32:34 -0700 Subject: [PATCH 1/8] Resolve aliases past first merge in `resolveName` --- src/compiler/checker.ts | 53 +++++++++++++++---- ...owImportClausesToMergeWithTypes.errors.txt | 5 +- .../allowImportClausesToMergeWithTypes.js | 2 +- ...allowImportClausesToMergeWithTypes.symbols | 1 + .../noCrashOnImportShadowing.errors.txt | 5 +- .../reference/noCrashOnImportShadowing.js | 2 +- .../noCrashOnImportShadowing.symbols | 1 + .../reference/typeAndNamespaceExportMerge.js | 37 +++++++++++++ .../typeAndNamespaceExportMerge.symbols | 28 ++++++++++ .../typeAndNamespaceExportMerge.types | 31 +++++++++++ .../typeAndNamespaceExportMerge.ts | 16 ++++++ 11 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/typeAndNamespaceExportMerge.js create mode 100644 tests/baselines/reference/typeAndNamespaceExportMerge.symbols create mode 100644 tests/baselines/reference/typeAndNamespaceExportMerge.types create mode 100644 tests/cases/conformance/externalModules/typeAndNamespaceExportMerge.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 939f82cca0ad9..203c1338cbadc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1530,9 +1530,9 @@ namespace ts { return symbol; } if (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); + const targetFlags = getAllSymbolFlags(symbol); // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (target === unknownSymbol || target.flags & meaning) { + if (targetFlags === undefined || targetFlags & meaning) { return symbol; } } @@ -2214,8 +2214,8 @@ namespace ts { !checkAndReportErrorForExtendingInterface(errorLocation) && !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) && + !checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && - !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) { let suggestion: Symbol | undefined; let suggestedLib: string | undefined; @@ -2503,7 +2503,7 @@ namespace ts { } function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) { + if (meaning & SymbolFlags.Value) { if (isPrimitiveTypeName(name)) { if (isExtendedByInterface(errorLocation)) { error(errorLocation, Diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_an_interface_can_only_extend_named_types_and_classes, unescapeLeadingUnderscores(name)); @@ -2514,7 +2514,8 @@ namespace ts { return true; } const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); - if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) { + const allFlags = symbol && getAllSymbolFlags(symbol); + if (symbol && allFlags !== undefined && !(allFlags & SymbolFlags.Value)) { const rawName = unescapeLeadingUnderscores(name); if (isES2015OrLaterConstructorName(name)) { error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, rawName); @@ -2565,9 +2566,9 @@ namespace ts { return false; } - function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { - if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); + function checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { + if (meaning & (SymbolFlags.Value & ~SymbolFlags.Type)) { + const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); if (symbol) { error( errorLocation, @@ -2576,8 +2577,8 @@ namespace ts { return true; } } - else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); + else if (meaning & (SymbolFlags.Type & ~SymbolFlags.Value)) { + const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Module, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); if (symbol) { error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name)); return true; @@ -3225,6 +3226,38 @@ namespace ts { return undefined; } + /** + * Gets combined flags of a `symbol` and all alias targets it resolves to. `resolveAlias` + * is typically recursive over chains of aliases, but stops mid-chain if an alias is merged + * with another exported symbol, e.g. + * ```ts + * // a.ts + * export const a = 0; + * // b.ts + * export { a } from "./a"; + * export type a = number; + * // c.ts + * import { a } from "./b"; + * ``` + * Calling `resolveAlias` on the `a` in c.ts would stop at the merged symbol exported + * from b.ts, even though there is still more alias to resolve. Consequently, if we were + * trying to determine if the `a` in c.ts has a value meaning, looking at the flags on + * the local symbol and on the symbol returned by `resolveAlias` is not enough. + * @returns undefined if `symbol` is an alias that ultimately resolves to `unknown`; + * combined flags of all alias targets otherwise. + */ + function getAllSymbolFlags(symbol: Symbol): SymbolFlags | undefined { + let flags = symbol.flags; + while (symbol.flags & SymbolFlags.Alias) { + symbol = resolveAlias(symbol); + if (symbol === unknownSymbol) { + return undefined; + } + flags |= symbol.flags; + } + return flags; + } + /** * Marks a symbol as type-only if its declaration is syntactically type-only. * If it is not itself marked type-only, but resolves to a type-only alias diff --git a/tests/baselines/reference/allowImportClausesToMergeWithTypes.errors.txt b/tests/baselines/reference/allowImportClausesToMergeWithTypes.errors.txt index 63cb28ea7d626..864ac1e6335bc 100644 --- a/tests/baselines/reference/allowImportClausesToMergeWithTypes.errors.txt +++ b/tests/baselines/reference/allowImportClausesToMergeWithTypes.errors.txt @@ -1,4 +1,3 @@ -tests/cases/compiler/index.ts(4,1): error TS2693: 'zzz' only refers to a type, but is being used as a value here. tests/cases/compiler/index.ts(9,10): error TS2749: 'originalZZZ' refers to a value, but is being used as a type here. Did you mean 'typeof originalZZZ'? @@ -18,13 +17,11 @@ tests/cases/compiler/index.ts(9,10): error TS2749: 'originalZZZ' refers to a val export { zzz as default }; -==== tests/cases/compiler/index.ts (2 errors) ==== +==== tests/cases/compiler/index.ts (1 errors) ==== import zzz from "./a"; const x: zzz = { x: "" }; zzz; - ~~~ -!!! error TS2693: 'zzz' only refers to a type, but is being used as a value here. import originalZZZ from "./b"; originalZZZ; diff --git a/tests/baselines/reference/allowImportClausesToMergeWithTypes.js b/tests/baselines/reference/allowImportClausesToMergeWithTypes.js index 9c50acdfb1a90..8c171dbfceae3 100644 --- a/tests/baselines/reference/allowImportClausesToMergeWithTypes.js +++ b/tests/baselines/reference/allowImportClausesToMergeWithTypes.js @@ -45,7 +45,7 @@ b_1["default"]; "use strict"; exports.__esModule = true; var x = { x: "" }; -zzz; +a_1["default"]; var b_1 = require("./b"); b_1["default"]; var y = x; diff --git a/tests/baselines/reference/allowImportClausesToMergeWithTypes.symbols b/tests/baselines/reference/allowImportClausesToMergeWithTypes.symbols index 769ec5574c18e..af6bdfccb99cb 100644 --- a/tests/baselines/reference/allowImportClausesToMergeWithTypes.symbols +++ b/tests/baselines/reference/allowImportClausesToMergeWithTypes.symbols @@ -38,6 +38,7 @@ const x: zzz = { x: "" }; >x : Symbol(x, Decl(index.ts, 2, 16)) zzz; +>zzz : Symbol(zzz, Decl(index.ts, 0, 6)) import originalZZZ from "./b"; >originalZZZ : Symbol(originalZZZ, Decl(index.ts, 5, 6)) diff --git a/tests/baselines/reference/noCrashOnImportShadowing.errors.txt b/tests/baselines/reference/noCrashOnImportShadowing.errors.txt index ddd49c0c2c719..59fee7c6950d7 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.errors.txt +++ b/tests/baselines/reference/noCrashOnImportShadowing.errors.txt @@ -1,4 +1,3 @@ -tests/cases/compiler/index.ts(4,1): error TS2693: 'B' only refers to a type, but is being used as a value here. tests/cases/compiler/index.ts(9,10): error TS2709: Cannot use namespace 'OriginalB' as a type. @@ -17,13 +16,11 @@ tests/cases/compiler/index.ts(9,10): error TS2709: Cannot use namespace 'Origina export { B }; -==== tests/cases/compiler/index.ts (2 errors) ==== +==== tests/cases/compiler/index.ts (1 errors) ==== import { B } from "./a"; const x: B = { x: "" }; B.zzz; - ~ -!!! error TS2693: 'B' only refers to a type, but is being used as a value here. import * as OriginalB from "./b"; OriginalB.zzz; diff --git a/tests/baselines/reference/noCrashOnImportShadowing.js b/tests/baselines/reference/noCrashOnImportShadowing.js index d02e861f20349..9c8ff2f8452f3 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.js +++ b/tests/baselines/reference/noCrashOnImportShadowing.js @@ -41,7 +41,7 @@ B.zzz; "use strict"; exports.__esModule = true; var x = { x: "" }; -B.zzz; +a_1.B.zzz; var OriginalB = require("./b"); OriginalB.zzz; var y = x; diff --git a/tests/baselines/reference/noCrashOnImportShadowing.symbols b/tests/baselines/reference/noCrashOnImportShadowing.symbols index da5f5146ec27f..375fcf289c726 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.symbols +++ b/tests/baselines/reference/noCrashOnImportShadowing.symbols @@ -36,6 +36,7 @@ const x: B = { x: "" }; >x : Symbol(x, Decl(index.ts, 2, 14)) B.zzz; +>B : Symbol(B, Decl(index.ts, 0, 8)) import * as OriginalB from "./b"; >OriginalB : Symbol(OriginalB, Decl(index.ts, 5, 6)) diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.js b/tests/baselines/reference/typeAndNamespaceExportMerge.js new file mode 100644 index 0000000000000..893d8330d2bd9 --- /dev/null +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.js @@ -0,0 +1,37 @@ +//// [tests/cases/conformance/externalModules/typeAndNamespaceExportMerge.ts] //// + +//// [constants.ts] +// @strict + +export const COFFEE = 0; +export const TEA = 1; + + +//// [drink.ts] +export type Drink = 0 | 1; +export * as Drink from "./constants"; + + +//// [index.ts] +import { Drink } from "./drink"; +// 'Drink' only refers to a type, but is being used as a value here +const x: Drink = Drink.TEA; + + +//// [constants.js] +"use strict"; +// @strict +exports.__esModule = true; +exports.TEA = exports.COFFEE = void 0; +exports.COFFEE = 0; +exports.TEA = 1; +//// [drink.js] +"use strict"; +exports.__esModule = true; +exports.Drink = void 0; +exports.Drink = require("./constants"); +//// [index.js] +"use strict"; +exports.__esModule = true; +// 'Drink' only refers to a type, but is being used as a value here +var x = drink_1.Drink.TEA; diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.symbols b/tests/baselines/reference/typeAndNamespaceExportMerge.symbols new file mode 100644 index 0000000000000..583c2e8750bc6 --- /dev/null +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.symbols @@ -0,0 +1,28 @@ +=== tests/cases/conformance/externalModules/constants.ts === +// @strict + +export const COFFEE = 0; +>COFFEE : Symbol(COFFEE, Decl(constants.ts, 2, 12)) + +export const TEA = 1; +>TEA : Symbol(TEA, Decl(constants.ts, 3, 12)) + + +=== tests/cases/conformance/externalModules/drink.ts === +export type Drink = 0 | 1; +>Drink : Symbol(Drink, Decl(drink.ts, 0, 0), Decl(drink.ts, 1, 6)) + +export * as Drink from "./constants"; +>Drink : Symbol(Drink, Decl(drink.ts, 0, 0), Decl(drink.ts, 1, 6)) + + +=== tests/cases/conformance/externalModules/index.ts === +import { Drink } from "./drink"; +>Drink : Symbol(Drink, Decl(index.ts, 0, 8)) + +// 'Drink' only refers to a type, but is being used as a value here +const x: Drink = Drink.TEA; +>x : Symbol(x, Decl(index.ts, 2, 5)) +>Drink : Symbol(Drink, Decl(index.ts, 0, 8)) +>Drink : Symbol(Drink, Decl(index.ts, 0, 8)) + diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.types b/tests/baselines/reference/typeAndNamespaceExportMerge.types new file mode 100644 index 0000000000000..bb2ebf9800cd8 --- /dev/null +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.types @@ -0,0 +1,31 @@ +=== tests/cases/conformance/externalModules/constants.ts === +// @strict + +export const COFFEE = 0; +>COFFEE : 0 +>0 : 0 + +export const TEA = 1; +>TEA : 1 +>1 : 1 + + +=== tests/cases/conformance/externalModules/drink.ts === +export type Drink = 0 | 1; +>Drink : 0 | 1 + +export * as Drink from "./constants"; +>Drink : typeof import("tests/cases/conformance/externalModules/constants") + + +=== tests/cases/conformance/externalModules/index.ts === +import { Drink } from "./drink"; +>Drink : any + +// 'Drink' only refers to a type, but is being used as a value here +const x: Drink = Drink.TEA; +>x : Drink +>Drink.TEA : error +>Drink : any +>TEA : any + diff --git a/tests/cases/conformance/externalModules/typeAndNamespaceExportMerge.ts b/tests/cases/conformance/externalModules/typeAndNamespaceExportMerge.ts new file mode 100644 index 0000000000000..a7a7d3f3e5698 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeAndNamespaceExportMerge.ts @@ -0,0 +1,16 @@ +// @strict + +// @Filename: constants.ts +export const COFFEE = 0; +export const TEA = 1; + + +// @Filename: drink.ts +export type Drink = 0 | 1; +export * as Drink from "./constants"; + + +// @Filename: index.ts +import { Drink } from "./drink"; +// 'Drink' only refers to a type, but is being used as a value here +const x: Drink = Drink.TEA; From bcdf0c2a63b3a2baff086e86f792b5ee807d8e00 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 25 Aug 2022 10:24:47 -0700 Subject: [PATCH 2/8] WIP --- src/compiler/checker.ts | 26 ++++++++----- .../reference/typeOnlyMerge2.errors.txt | 24 ++++++++++++ tests/baselines/reference/typeOnlyMerge2.js | 37 +++++++++++++++++++ .../reference/typeOnlyMerge2.symbols | 34 +++++++++++++++++ .../baselines/reference/typeOnlyMerge2.types | 33 +++++++++++++++++ .../externalModules/typeOnlyMerge1.ts | 12 ++++++ .../externalModules/typeOnlyMerge2.ts | 17 +++++++++ .../externalModules/typeOnlyMerge3.ts | 16 ++++++++ .../conformance/externalModules/wtfMerge.ts | 12 ++++++ 9 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/typeOnlyMerge2.errors.txt create mode 100644 tests/baselines/reference/typeOnlyMerge2.js create mode 100644 tests/baselines/reference/typeOnlyMerge2.symbols create mode 100644 tests/baselines/reference/typeOnlyMerge2.types create mode 100644 tests/cases/conformance/externalModules/typeOnlyMerge1.ts create mode 100644 tests/cases/conformance/externalModules/typeOnlyMerge2.ts create mode 100644 tests/cases/conformance/externalModules/typeOnlyMerge3.ts create mode 100644 tests/cases/conformance/externalModules/wtfMerge.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 203c1338cbadc..e253fb2fd60f3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2308,7 +2308,7 @@ namespace ts { } } if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias && !(result.flags & SymbolFlags.Value) && !isValidTypeOnlyAliasUseSite(errorLocation)) { - const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); + const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result, SymbolFlags.Value); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type @@ -3310,12 +3310,18 @@ namespace ts { } /** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */ - function getTypeOnlyAliasDeclaration(symbol: Symbol): TypeOnlyAliasDeclaration | undefined { + function getTypeOnlyAliasDeclaration(symbol: Symbol, include?: SymbolFlags): TypeOnlyAliasDeclaration | undefined { if (!(symbol.flags & SymbolFlags.Alias)) { return undefined; } const links = getSymbolLinks(symbol); - return links.typeOnlyDeclaration || undefined; + if (include === undefined) { + return links.typeOnlyDeclaration || undefined; + } + if (links.typeOnlyDeclaration) { + return (getAllSymbolFlags(resolveAlias(links.typeOnlyDeclaration.symbol)) ?? -1) & include ? links.typeOnlyDeclaration : undefined; + } + return undefined; } function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) { @@ -3323,7 +3329,7 @@ namespace ts { const target = resolveAlias(symbol); if (target) { const markAlias = target === unknownSymbol || - ((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol)); + (((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)); if (markAlias) { markAliasSymbolAsReferenced(symbol); @@ -25842,9 +25848,9 @@ namespace ts { } function markAliasReferenced(symbol: Symbol, location: Node) { - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol)) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)) { const target = resolveAlias(symbol); - if (target.flags & SymbolFlags.Value) { + if ((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { // An alias resolving to a const enum cannot be elided if (1) 'isolatedModules' is enabled // (because the const enum value will not be inlined), or if (2) the alias is an export // of a const enum declaration that will be preserved. @@ -43147,7 +43153,7 @@ namespace ts { const symbol = getReferencedValueSymbol(node); // We should only get the declaration of an alias if there isn't a local value // declaration for the symbol - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !getTypeOnlyAliasDeclaration(symbol)) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)) { return getDeclarationOfAliasSymbol(symbol); } } @@ -43244,7 +43250,7 @@ namespace ts { case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: const symbol = getSymbolOfNode(node); - return !!symbol && isAliasResolvedToValue(symbol) && !getTypeOnlyAliasDeclaration(symbol); + return !!symbol && isAliasResolvedToValue(symbol) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value); case SyntaxKind.ExportDeclaration: const exportClause = (node as ExportDeclaration).exportClause; return !!exportClause && ( @@ -43280,7 +43286,7 @@ namespace ts { } // const enums and modules that contain only const enums are not considered values from the emit perspective // unless 'preserveConstEnums' option is set to true - return !!(target.flags & SymbolFlags.Value) && + return !!((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) && (shouldPreserveConstEnums(compilerOptions) || !isConstEnumOrConstEnumOnlyModule(target)); } @@ -43297,7 +43303,7 @@ namespace ts { } const target = getSymbolLinks(symbol!).aliasTarget; // TODO: GH#18217 if (target && getEffectiveModifierFlags(node) & ModifierFlags.Export && - target.flags & SymbolFlags.Value && + (getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value && (shouldPreserveConstEnums(compilerOptions) || !isConstEnumOrConstEnumOnlyModule(target))) { // An `export import ... =` of a value symbol is always considered referenced return true; diff --git a/tests/baselines/reference/typeOnlyMerge2.errors.txt b/tests/baselines/reference/typeOnlyMerge2.errors.txt new file mode 100644 index 0000000000000..de777c96d996b --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge2.errors.txt @@ -0,0 +1,24 @@ +tests/cases/conformance/externalModules/d.ts(2,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. + + +==== tests/cases/conformance/externalModules/a.ts (0 errors) ==== + const A = {} + export { A }; + +==== tests/cases/conformance/externalModules/b.ts (0 errors) ==== + import { A } from "./a"; + type A = any; + export type { A }; + +==== tests/cases/conformance/externalModules/c.ts (0 errors) ==== + import { A } from "./b"; + namespace A {} + export { A }; + +==== tests/cases/conformance/externalModules/d.ts (1 errors) ==== + import { A } from "./c"; + A; + ~ +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 tests/cases/conformance/externalModules/b.ts:3:15: 'A' was exported here. + \ No newline at end of file diff --git a/tests/baselines/reference/typeOnlyMerge2.js b/tests/baselines/reference/typeOnlyMerge2.js new file mode 100644 index 0000000000000..39930739f7094 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge2.js @@ -0,0 +1,37 @@ +//// [tests/cases/conformance/externalModules/typeOnlyMerge2.ts] //// + +//// [a.ts] +const A = {} +export { A }; + +//// [b.ts] +import { A } from "./a"; +type A = any; +export type { A }; + +//// [c.ts] +import { A } from "./b"; +namespace A {} +export { A }; + +//// [d.ts] +import { A } from "./c"; +A; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports.A = void 0; +var A = {}; +exports.A = A; +//// [b.js] +"use strict"; +exports.__esModule = true; +//// [c.js] +"use strict"; +exports.__esModule = true; +//// [d.js] +"use strict"; +exports.__esModule = true; +A; diff --git a/tests/baselines/reference/typeOnlyMerge2.symbols b/tests/baselines/reference/typeOnlyMerge2.symbols new file mode 100644 index 0000000000000..48c1a7d8cec80 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge2.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/externalModules/a.ts === +const A = {} +>A : Symbol(A, Decl(a.ts, 0, 5)) + +export { A }; +>A : Symbol(A, Decl(a.ts, 1, 8)) + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + +type A = any; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + +export type { A }; +>A : Symbol(A, Decl(b.ts, 2, 13)) + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) + +namespace A {} +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) + +export { A }; +>A : Symbol(A, Decl(c.ts, 2, 8)) + +=== tests/cases/conformance/externalModules/d.ts === +import { A } from "./c"; +>A : Symbol(A, Decl(d.ts, 0, 8)) + +A; +>A : Symbol(A, Decl(d.ts, 0, 8)) + diff --git a/tests/baselines/reference/typeOnlyMerge2.types b/tests/baselines/reference/typeOnlyMerge2.types new file mode 100644 index 0000000000000..72728f6de4364 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge2.types @@ -0,0 +1,33 @@ +=== tests/cases/conformance/externalModules/a.ts === +const A = {} +>A : {} +>{} : {} + +export { A }; +>A : {} + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : {} + +type A = any; +>A : any + +export type { A }; +>A : any + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : any + +namespace A {} +export { A }; +>A : any + +=== tests/cases/conformance/externalModules/d.ts === +import { A } from "./c"; +>A : any + +A; +>A : any + diff --git a/tests/cases/conformance/externalModules/typeOnlyMerge1.ts b/tests/cases/conformance/externalModules/typeOnlyMerge1.ts new file mode 100644 index 0000000000000..59e77388deb48 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnlyMerge1.ts @@ -0,0 +1,12 @@ +// @Filename: a.ts +interface A {} +export type { A }; + +// @Filename: b.ts +import { A } from "./a"; +const A = 0; +export { A }; + +// @Filename: c.ts +import { A } from "./b"; +A; diff --git a/tests/cases/conformance/externalModules/typeOnlyMerge2.ts b/tests/cases/conformance/externalModules/typeOnlyMerge2.ts new file mode 100644 index 0000000000000..54d69f7d2c3ad --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnlyMerge2.ts @@ -0,0 +1,17 @@ +// @Filename: a.ts +const A = {} +export { A }; + +// @Filename: b.ts +import { A } from "./a"; +type A = any; +export type { A }; + +// @Filename: c.ts +import { A } from "./b"; +namespace A {} +export { A }; + +// @Filename: d.ts +import { A } from "./c"; +A; diff --git a/tests/cases/conformance/externalModules/typeOnlyMerge3.ts b/tests/cases/conformance/externalModules/typeOnlyMerge3.ts new file mode 100644 index 0000000000000..d5085b05d86bf --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnlyMerge3.ts @@ -0,0 +1,16 @@ +// @Filename: a.ts +function A() {} +export type { A }; + +// @Filename: b.ts +import { A } from "./a"; +namespace A { + export const displayName = "A"; +} +export { A }; + +// @Filename: c.ts +import { A } from "./b"; +A; +A.displayName; +A(); diff --git a/tests/cases/conformance/externalModules/wtfMerge.ts b/tests/cases/conformance/externalModules/wtfMerge.ts new file mode 100644 index 0000000000000..b1bb65f365560 --- /dev/null +++ b/tests/cases/conformance/externalModules/wtfMerge.ts @@ -0,0 +1,12 @@ +// @Filename: a.ts +function A() {} +export { A }; + +// @Filename: b.ts +import { A } from "./a"; +namespace A { + export const displayName = "A"; +} + +A(); +A.displayName; From d199f2c8d3c89fc655d9d85bb75d1f18895a7822 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 25 Aug 2022 17:02:34 -0700 Subject: [PATCH 3/8] =?UTF-8?q?Fix=20when=20a=20namespace=20merges=20with?= =?UTF-8?q?=20an=20alias=20that=20resolves=20to=20a=20type=20merged=20with?= =?UTF-8?q?=20an=20alias=20that=20resolves=20to=20a=20value=20=F0=9F=98=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/checker.ts | 5 ++- .../valuesMergingAcrossModules.errors.txt | 28 +++++++++++++ .../reference/valuesMergingAcrossModules.js | 42 +++++++++++++++++++ .../valuesMergingAcrossModules.symbols | 36 ++++++++++++++++ .../valuesMergingAcrossModules.types | 38 +++++++++++++++++ ...Merge.ts => valuesMergingAcrossModules.ts} | 5 +++ 6 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/valuesMergingAcrossModules.errors.txt create mode 100644 tests/baselines/reference/valuesMergingAcrossModules.js create mode 100644 tests/baselines/reference/valuesMergingAcrossModules.symbols create mode 100644 tests/baselines/reference/valuesMergingAcrossModules.types rename tests/cases/conformance/externalModules/{wtfMerge.ts => valuesMergingAcrossModules.ts} (69%) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e253fb2fd60f3..9a04a4427d437 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -41266,11 +41266,12 @@ namespace ts { return; } + const targetFlags = getAllSymbolFlags(target) ?? target.flags; const excludedMeanings = (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | (symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0); - if (target.flags & excludedMeanings) { + if (targetFlags & excludedMeanings) { const message = node.kind === SyntaxKind.ExportSpecifier ? Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 : Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0; @@ -41281,7 +41282,7 @@ namespace ts { && !isTypeOnlyImportOrExportDeclaration(node) && !(node.flags & NodeFlags.Ambient)) { const typeOnlyAlias = getTypeOnlyAliasDeclaration(symbol); - const isType = !(target.flags & SymbolFlags.Value); + const isType = !(targetFlags & SymbolFlags.Value); if (isType || typeOnlyAlias) { switch (node.kind) { case SyntaxKind.ImportClause: diff --git a/tests/baselines/reference/valuesMergingAcrossModules.errors.txt b/tests/baselines/reference/valuesMergingAcrossModules.errors.txt new file mode 100644 index 0000000000000..de88fe9c53b05 --- /dev/null +++ b/tests/baselines/reference/valuesMergingAcrossModules.errors.txt @@ -0,0 +1,28 @@ +tests/cases/conformance/externalModules/c.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'A'. +tests/cases/conformance/externalModules/c.ts(6,1): error TS2349: This expression is not callable. + Type 'typeof A' has no call signatures. + + +==== tests/cases/conformance/externalModules/a.ts (0 errors) ==== + function A() {} + export { A }; + +==== tests/cases/conformance/externalModules/b.ts (0 errors) ==== + import { A } from "./a"; + type A = 0; + export { A }; + +==== tests/cases/conformance/externalModules/c.ts (2 errors) ==== + import { A } from "./b"; + ~ +!!! error TS2440: Import declaration conflicts with local declaration of 'A'. + namespace A { + export const displayName = "A"; + } + + A(); + ~ +!!! error TS2349: This expression is not callable. +!!! error TS2349: Type 'typeof A' has no call signatures. + A.displayName; + \ No newline at end of file diff --git a/tests/baselines/reference/valuesMergingAcrossModules.js b/tests/baselines/reference/valuesMergingAcrossModules.js new file mode 100644 index 0000000000000..b8b3c0a0a5c84 --- /dev/null +++ b/tests/baselines/reference/valuesMergingAcrossModules.js @@ -0,0 +1,42 @@ +//// [tests/cases/conformance/externalModules/valuesMergingAcrossModules.ts] //// + +//// [a.ts] +function A() {} +export { A }; + +//// [b.ts] +import { A } from "./a"; +type A = 0; +export { A }; + +//// [c.ts] +import { A } from "./b"; +namespace A { + export const displayName = "A"; +} + +A(); +A.displayName; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports.A = void 0; +function A() { } +exports.A = A; +//// [b.js] +"use strict"; +exports.__esModule = true; +exports.A = void 0; +var a_1 = require("./a"); +exports.A = a_1.A; +//// [c.js] +"use strict"; +exports.__esModule = true; +var A; +(function (A) { + A.displayName = "A"; +})(A || (A = {})); +A(); +A.displayName; diff --git a/tests/baselines/reference/valuesMergingAcrossModules.symbols b/tests/baselines/reference/valuesMergingAcrossModules.symbols new file mode 100644 index 0000000000000..fd854e4cbd42a --- /dev/null +++ b/tests/baselines/reference/valuesMergingAcrossModules.symbols @@ -0,0 +1,36 @@ +=== tests/cases/conformance/externalModules/a.ts === +function A() {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +export { A }; +>A : Symbol(A, Decl(a.ts, 1, 8)) + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + +type A = 0; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + +export { A }; +>A : Symbol(A, Decl(b.ts, 2, 8)) + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) + +namespace A { +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) + + export const displayName = "A"; +>displayName : Symbol(displayName, Decl(c.ts, 2, 14)) +} + +A(); +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) + +A.displayName; +>A.displayName : Symbol(A.displayName, Decl(c.ts, 2, 14)) +>A : Symbol(A, Decl(c.ts, 0, 8), Decl(c.ts, 0, 24)) +>displayName : Symbol(A.displayName, Decl(c.ts, 2, 14)) + diff --git a/tests/baselines/reference/valuesMergingAcrossModules.types b/tests/baselines/reference/valuesMergingAcrossModules.types new file mode 100644 index 0000000000000..6a3e12310fa8d --- /dev/null +++ b/tests/baselines/reference/valuesMergingAcrossModules.types @@ -0,0 +1,38 @@ +=== tests/cases/conformance/externalModules/a.ts === +function A() {} +>A : () => void + +export { A }; +>A : () => void + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : () => void + +type A = 0; +>A : 0 + +export { A }; +>A : any + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : typeof A + +namespace A { +>A : typeof A + + export const displayName = "A"; +>displayName : "A" +>"A" : "A" +} + +A(); +>A() : any +>A : typeof A + +A.displayName; +>A.displayName : "A" +>A : typeof A +>displayName : "A" + diff --git a/tests/cases/conformance/externalModules/wtfMerge.ts b/tests/cases/conformance/externalModules/valuesMergingAcrossModules.ts similarity index 69% rename from tests/cases/conformance/externalModules/wtfMerge.ts rename to tests/cases/conformance/externalModules/valuesMergingAcrossModules.ts index b1bb65f365560..00ae4f8fa63e6 100644 --- a/tests/cases/conformance/externalModules/wtfMerge.ts +++ b/tests/cases/conformance/externalModules/valuesMergingAcrossModules.ts @@ -4,6 +4,11 @@ export { A }; // @Filename: b.ts import { A } from "./a"; +type A = 0; +export { A }; + +// @Filename: c.ts +import { A } from "./b"; namespace A { export const displayName = "A"; } From 7b684c5622141800b722c2b7503bd87b40dfaf2d Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 26 Aug 2022 14:20:40 -0700 Subject: [PATCH 4/8] Audit all resolveSymbol and resolveName calls --- src/compiler/checker.ts | 44 ++++++++----- .../allowImportClausesToMergeWithTypes.js | 1 + .../allowImportClausesToMergeWithTypes.types | 4 +- .../reference/exportSpecifierForAGlobal.js | 2 +- .../reference/exportSpecifierForAGlobal.types | 8 +-- ...cifierReferencingOuterDeclaration1.symbols | 2 +- ...cifierReferencingOuterDeclaration2.symbols | 2 +- .../importElisionConstEnumMerge1.errors.txt | 21 ++++++ .../reference/importElisionConstEnumMerge1.js | 31 +++++++++ .../importElisionConstEnumMerge1.symbols | 30 +++++++++ .../importElisionConstEnumMerge1.types | 29 +++++++++ ...onsImportAliasExposedWithinNamespace.types | 2 +- .../reference/noCrashOnImportShadowing.js | 3 + .../noCrashOnImportShadowing.symbols | 2 + .../reference/noCrashOnImportShadowing.types | 10 +-- .../reExportGlobalDeclaration3.types | 20 +++--- .../reExportGlobalDeclaration4.types | 20 +++--- .../shadowedInternalModule.errors.txt | 42 +++++++++++- .../reference/shadowedInternalModule.js | 50 ++++++++++++++- .../reference/shadowedInternalModule.symbols | 64 +++++++++++++++++++ .../reference/shadowedInternalModule.types | 61 ++++++++++++++++++ .../stackDepthLimitCastingType.types | 2 +- .../reference/typeAndNamespaceExportMerge.js | 1 + .../typeAndNamespaceExportMerge.symbols | 2 + .../typeAndNamespaceExportMerge.types | 8 +-- tests/baselines/reference/typeOnlyMerge1.js | 30 +++++++++ .../reference/typeOnlyMerge1.symbols | 24 +++++++ .../baselines/reference/typeOnlyMerge1.types | 23 +++++++ .../baselines/reference/typeOnlyMerge2.types | 8 +-- .../reference/typeOnlyMerge3.errors.txt | 39 +++++++++++ tests/baselines/reference/typeOnlyMerge3.js | 37 +++++++++++ .../reference/typeOnlyMerge3.symbols | 35 ++++++++++ .../baselines/reference/typeOnlyMerge3.types | 37 +++++++++++ .../valuesMergingAcrossModules.types | 2 +- .../importElisionConstEnumMerge1.ts | 15 +++++ .../shadowedInternalModule.ts | 33 +++++++++- 36 files changed, 680 insertions(+), 64 deletions(-) create mode 100644 tests/baselines/reference/importElisionConstEnumMerge1.errors.txt create mode 100644 tests/baselines/reference/importElisionConstEnumMerge1.js create mode 100644 tests/baselines/reference/importElisionConstEnumMerge1.symbols create mode 100644 tests/baselines/reference/importElisionConstEnumMerge1.types create mode 100644 tests/baselines/reference/typeOnlyMerge1.js create mode 100644 tests/baselines/reference/typeOnlyMerge1.symbols create mode 100644 tests/baselines/reference/typeOnlyMerge1.types create mode 100644 tests/baselines/reference/typeOnlyMerge3.errors.txt create mode 100644 tests/baselines/reference/typeOnlyMerge3.js create mode 100644 tests/baselines/reference/typeOnlyMerge3.symbols create mode 100644 tests/baselines/reference/typeOnlyMerge3.types create mode 100644 tests/cases/conformance/constEnums/importElisionConstEnumMerge1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a04a4427d437..180c1be5e1059 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3248,12 +3248,25 @@ namespace ts { */ function getAllSymbolFlags(symbol: Symbol): SymbolFlags | undefined { let flags = symbol.flags; + let seenSymbols; while (symbol.flags & SymbolFlags.Alias) { - symbol = resolveAlias(symbol); - if (symbol === unknownSymbol) { + const target = resolveAlias(symbol); + if (target === unknownSymbol) { return undefined; } - flags |= symbol.flags; + + // Optimization - avoid creating `seenSymbols` Set in most cases + if (target === symbol) { + break; + } + else if (target.flags & SymbolFlags.Alias && !seenSymbols) { + seenSymbols = new Set([symbol, target]); + } + else if (seenSymbols?.has(target)) { + break; + } + flags |= target.flags; + symbol = target; } return flags; } @@ -3350,8 +3363,8 @@ namespace ts { // This way a chain of imports can be elided if ultimately the final input is only used in a type // position. if (isInternalModuleImportEqualsDeclaration(node)) { - const target = resolveSymbol(symbol); - if (target === unknownSymbol || target.flags & SymbolFlags.Value) { + const targetFlags = getAllSymbolFlags(resolveSymbol(symbol)); + if (targetFlags === undefined || targetFlags & SymbolFlags.Value) { // import foo = checkExpressionCached(node.moduleReference as Expression); } @@ -4277,7 +4290,7 @@ namespace ts { function symbolIsValue(symbol: Symbol, includeTypeOnlyMembers?: boolean): boolean { return !!( symbol.flags & SymbolFlags.Value || - symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol))); + symbol.flags & SymbolFlags.Alias && (getAllSymbolFlags(symbol) ?? -1) & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol))); } function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined { @@ -4572,7 +4585,7 @@ namespace ts { // Qualify if the symbol from symbol table has same meaning as expected symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; - if (symbolFromSymbolTable.flags & meaning) { + if ((getAllSymbolFlags(symbolFromSymbolTable) ?? unknownSymbol.flags) & meaning) { qualify = true; return true; } @@ -7535,7 +7548,7 @@ namespace ts { } function isTypeOnlyNamespace(symbol: Symbol) { - return every(getNamespaceMembersForSerialization(symbol), m => !(resolveSymbol(m).flags & SymbolFlags.Value)); + return every(getNamespaceMembersForSerialization(symbol), m => !((getAllSymbolFlags(resolveSymbol(m)) ?? unknownSymbol.flags) & SymbolFlags.Value)); } function serializeModule(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { @@ -10013,7 +10026,7 @@ namespace ts { links.type = exportSymbol?.declarations && isDuplicatedCommonJSExport(exportSymbol.declarations) && symbol.declarations!.length ? getFlowTypeFromCommonJSExport(exportSymbol) : isDuplicatedCommonJSExport(symbol.declarations) ? autoType : declaredType ? declaredType - : targetSymbol.flags & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) + : (getAllSymbolFlags(targetSymbol) ?? -1) & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) : errorType; } return links.type; @@ -32350,7 +32363,7 @@ namespace ts { if (symbol && symbol.flags & SymbolFlags.Alias) { symbol = resolveAlias(symbol); } - return !!(symbol && (symbol.flags & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); + return !!(symbol && ((getAllSymbolFlags(symbol) ?? unknownSymbol.flags) & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); } return false; } @@ -41460,14 +41473,15 @@ namespace ts { if (node.moduleReference.kind !== SyntaxKind.ExternalModuleReference) { const target = resolveAlias(getSymbolOfNode(node)); if (target !== unknownSymbol) { - if (target.flags & SymbolFlags.Value) { + const targetFlags = getAllSymbolFlags(target) ?? unknownSymbol.flags; + if (targetFlags & SymbolFlags.Value) { // Target is a value symbol, check that it is not hidden by a local declaration with the same name const moduleName = getFirstIdentifier(node.moduleReference); if (!(resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace)!.flags & SymbolFlags.Namespace)) { error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); } } - if (target.flags & SymbolFlags.Type) { + if (targetFlags & SymbolFlags.Type) { checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); } } @@ -41618,7 +41632,7 @@ namespace ts { markExportAsReferenced(node); } const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol); - if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) { + if (!target || (getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { checkExpressionCached(node.propertyName || node.name); } } @@ -41670,7 +41684,7 @@ namespace ts { markAliasReferenced(sym, id); // If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`) const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym; - if (target === unknownSymbol || target.flags & SymbolFlags.Value) { + if ((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { // However if it is a value, we need to check it's being used correctly checkExpressionCached(node.expression); } @@ -43099,7 +43113,7 @@ namespace ts { function isValue(s: Symbol): boolean { s = resolveSymbol(s); - return s && !!(s.flags & SymbolFlags.Value); + return s && !!((getAllSymbolFlags(s) ?? unknownSymbol.flags) & SymbolFlags.Value); } } diff --git a/tests/baselines/reference/allowImportClausesToMergeWithTypes.js b/tests/baselines/reference/allowImportClausesToMergeWithTypes.js index 8c171dbfceae3..fa9e5407b7884 100644 --- a/tests/baselines/reference/allowImportClausesToMergeWithTypes.js +++ b/tests/baselines/reference/allowImportClausesToMergeWithTypes.js @@ -44,6 +44,7 @@ b_1["default"]; //// [index.js] "use strict"; exports.__esModule = true; +var a_1 = require("./a"); var x = { x: "" }; a_1["default"]; var b_1 = require("./b"); diff --git a/tests/baselines/reference/allowImportClausesToMergeWithTypes.types b/tests/baselines/reference/allowImportClausesToMergeWithTypes.types index e153d2fa23302..1848fcb9fe52b 100644 --- a/tests/baselines/reference/allowImportClausesToMergeWithTypes.types +++ b/tests/baselines/reference/allowImportClausesToMergeWithTypes.types @@ -30,7 +30,7 @@ export { zzz as default }; === tests/cases/compiler/index.ts === import zzz from "./a"; ->zzz : any +>zzz : 123 const x: zzz = { x: "" }; >x : zzz @@ -39,7 +39,7 @@ const x: zzz = { x: "" }; >"" : "" zzz; ->zzz : any +>zzz : 123 import originalZZZ from "./b"; >originalZZZ : 123 diff --git a/tests/baselines/reference/exportSpecifierForAGlobal.js b/tests/baselines/reference/exportSpecifierForAGlobal.js index 0154ba30d6121..392b21e0f21c8 100644 --- a/tests/baselines/reference/exportSpecifierForAGlobal.js +++ b/tests/baselines/reference/exportSpecifierForAGlobal.js @@ -24,4 +24,4 @@ exports.f = f; //// [b.d.ts] export { X }; -export declare function f(): X; +export declare function f(): globalThis.X; diff --git a/tests/baselines/reference/exportSpecifierForAGlobal.types b/tests/baselines/reference/exportSpecifierForAGlobal.types index 4331a7b84d3b8..d18a1c6b71c6a 100644 --- a/tests/baselines/reference/exportSpecifierForAGlobal.types +++ b/tests/baselines/reference/exportSpecifierForAGlobal.types @@ -4,15 +4,15 @@ declare class X { } === tests/cases/compiler/b.ts === export {X}; ->X : typeof X +>X : typeof globalThis.X export function f() { ->f : () => X +>f : () => globalThis.X var x: X; ->x : X +>x : globalThis.X return x; ->x : X +>x : globalThis.X } diff --git a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols index dd586f52d7828..0b0ff075991ae 100644 --- a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols +++ b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols @@ -12,5 +12,5 @@ declare module "m" { export function foo(): X.bar; >foo : Symbol(foo, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 2, 17)) >X : Symbol(X, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 0)) ->bar : Symbol(X.bar, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 18)) +>bar : Symbol(globalThis.X.bar, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 18)) } diff --git a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols index 9a57645b1cbcd..0825a055433e6 100644 --- a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols +++ b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols @@ -10,5 +10,5 @@ export { X }; export declare function foo(): X.bar; >foo : Symbol(foo, Decl(exportSpecifierReferencingOuterDeclaration2_B.ts, 0, 13)) >X : Symbol(X, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 0)) ->bar : Symbol(X.bar, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 18)) +>bar : Symbol(globalThis.X.bar, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 18)) diff --git a/tests/baselines/reference/importElisionConstEnumMerge1.errors.txt b/tests/baselines/reference/importElisionConstEnumMerge1.errors.txt new file mode 100644 index 0000000000000..2d3b712f8720a --- /dev/null +++ b/tests/baselines/reference/importElisionConstEnumMerge1.errors.txt @@ -0,0 +1,21 @@ +tests/cases/conformance/constEnums/merge.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'Enum'. + + +==== tests/cases/conformance/constEnums/enum.ts (0 errors) ==== + export const enum Enum { + One = 1, + } + +==== tests/cases/conformance/constEnums/merge.ts (1 errors) ==== + import { Enum } from "./enum"; + ~~~~ +!!! error TS2440: Import declaration conflicts with local declaration of 'Enum'. + namespace Enum { + export type Foo = number; + } + export { Enum }; + +==== tests/cases/conformance/constEnums/index.ts (0 errors) ==== + import { Enum } from "./merge"; + Enum.One; + \ No newline at end of file diff --git a/tests/baselines/reference/importElisionConstEnumMerge1.js b/tests/baselines/reference/importElisionConstEnumMerge1.js new file mode 100644 index 0000000000000..3e63187bcc39d --- /dev/null +++ b/tests/baselines/reference/importElisionConstEnumMerge1.js @@ -0,0 +1,31 @@ +//// [tests/cases/conformance/constEnums/importElisionConstEnumMerge1.ts] //// + +//// [enum.ts] +export const enum Enum { + One = 1, +} + +//// [merge.ts] +import { Enum } from "./enum"; +namespace Enum { + export type Foo = number; +} +export { Enum }; + +//// [index.ts] +import { Enum } from "./merge"; +Enum.One; + + +//// [enum.js] +"use strict"; +exports.__esModule = true; +//// [merge.js] +"use strict"; +exports.__esModule = true; +exports.Enum = void 0; +//// [index.js] +"use strict"; +exports.__esModule = true; +var merge_1 = require("./merge"); +1 /* Enum.One */; diff --git a/tests/baselines/reference/importElisionConstEnumMerge1.symbols b/tests/baselines/reference/importElisionConstEnumMerge1.symbols new file mode 100644 index 0000000000000..c8d5232899a32 --- /dev/null +++ b/tests/baselines/reference/importElisionConstEnumMerge1.symbols @@ -0,0 +1,30 @@ +=== tests/cases/conformance/constEnums/enum.ts === +export const enum Enum { +>Enum : Symbol(Enum, Decl(enum.ts, 0, 0)) + + One = 1, +>One : Symbol(Enum.One, Decl(enum.ts, 0, 24)) +} + +=== tests/cases/conformance/constEnums/merge.ts === +import { Enum } from "./enum"; +>Enum : Symbol(Enum, Decl(merge.ts, 0, 8), Decl(merge.ts, 0, 30)) + +namespace Enum { +>Enum : Symbol(Enum, Decl(merge.ts, 0, 8), Decl(merge.ts, 0, 30)) + + export type Foo = number; +>Foo : Symbol(Foo, Decl(merge.ts, 1, 16)) +} +export { Enum }; +>Enum : Symbol(Enum, Decl(merge.ts, 4, 8)) + +=== tests/cases/conformance/constEnums/index.ts === +import { Enum } from "./merge"; +>Enum : Symbol(Enum, Decl(index.ts, 0, 8)) + +Enum.One; +>Enum.One : Symbol(Enum.One, Decl(enum.ts, 0, 24)) +>Enum : Symbol(Enum, Decl(index.ts, 0, 8)) +>One : Symbol(Enum.One, Decl(enum.ts, 0, 24)) + diff --git a/tests/baselines/reference/importElisionConstEnumMerge1.types b/tests/baselines/reference/importElisionConstEnumMerge1.types new file mode 100644 index 0000000000000..6348e869642f3 --- /dev/null +++ b/tests/baselines/reference/importElisionConstEnumMerge1.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/constEnums/enum.ts === +export const enum Enum { +>Enum : Enum + + One = 1, +>One : Enum.One +>1 : 1 +} + +=== tests/cases/conformance/constEnums/merge.ts === +import { Enum } from "./enum"; +>Enum : typeof Enum + +namespace Enum { + export type Foo = number; +>Foo : number +} +export { Enum }; +>Enum : typeof Enum + +=== tests/cases/conformance/constEnums/index.ts === +import { Enum } from "./merge"; +>Enum : typeof import("tests/cases/conformance/constEnums/enum").Enum + +Enum.One; +>Enum.One : import("tests/cases/conformance/constEnums/enum").Enum +>Enum : typeof import("tests/cases/conformance/constEnums/enum").Enum +>One : import("tests/cases/conformance/constEnums/enum").Enum + diff --git a/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.types b/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.types index 28424f9792bc4..7c29ca32963cc 100644 --- a/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.types +++ b/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.types @@ -26,7 +26,7 @@ export {myTypes}; === tests/cases/conformance/jsdoc/declarations/file2.js === import {myTypes} from './file.js'; ->myTypes : any +>myTypes : { [x: string]: any; } /** * @namespace testFnTypes diff --git a/tests/baselines/reference/noCrashOnImportShadowing.js b/tests/baselines/reference/noCrashOnImportShadowing.js index 9c8ff2f8452f3..fe540d8585454 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.js +++ b/tests/baselines/reference/noCrashOnImportShadowing.js @@ -34,12 +34,15 @@ exports.zzz = 123; //// [a.js] "use strict"; exports.__esModule = true; +exports.B = void 0; var B = require("./b"); +exports.B = B; var x = { x: "" }; B.zzz; //// [index.js] "use strict"; exports.__esModule = true; +var a_1 = require("./a"); var x = { x: "" }; a_1.B.zzz; var OriginalB = require("./b"); diff --git a/tests/baselines/reference/noCrashOnImportShadowing.symbols b/tests/baselines/reference/noCrashOnImportShadowing.symbols index 375fcf289c726..a9093debbb0f4 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.symbols +++ b/tests/baselines/reference/noCrashOnImportShadowing.symbols @@ -36,7 +36,9 @@ const x: B = { x: "" }; >x : Symbol(x, Decl(index.ts, 2, 14)) B.zzz; +>B.zzz : Symbol(OriginalB.zzz, Decl(b.ts, 0, 12)) >B : Symbol(B, Decl(index.ts, 0, 8)) +>zzz : Symbol(OriginalB.zzz, Decl(b.ts, 0, 12)) import * as OriginalB from "./b"; >OriginalB : Symbol(OriginalB, Decl(index.ts, 5, 6)) diff --git a/tests/baselines/reference/noCrashOnImportShadowing.types b/tests/baselines/reference/noCrashOnImportShadowing.types index a4c75125716ec..3d4d58b20a126 100644 --- a/tests/baselines/reference/noCrashOnImportShadowing.types +++ b/tests/baselines/reference/noCrashOnImportShadowing.types @@ -24,11 +24,11 @@ B.zzz; >zzz : 123 export { B }; ->B : any +>B : typeof B === tests/cases/compiler/index.ts === import { B } from "./a"; ->B : any +>B : typeof OriginalB const x: B = { x: "" }; >x : B @@ -37,9 +37,9 @@ const x: B = { x: "" }; >"" : "" B.zzz; ->B.zzz : any ->B : any ->zzz : any +>B.zzz : 123 +>B : typeof OriginalB +>zzz : 123 import * as OriginalB from "./b"; >OriginalB : typeof OriginalB diff --git a/tests/baselines/reference/reExportGlobalDeclaration3.types b/tests/baselines/reference/reExportGlobalDeclaration3.types index 01faa99a68676..a2cca62a85114 100644 --- a/tests/baselines/reference/reExportGlobalDeclaration3.types +++ b/tests/baselines/reference/reExportGlobalDeclaration3.types @@ -15,20 +15,20 @@ declare namespace NS2 { === tests/cases/compiler/file2.ts === export {NS1, NS1 as NNS1}; ->NS1 : typeof NS1 ->NS1 : typeof NS1 ->NNS1 : typeof NS1 +>NS1 : typeof globalThis.NS1 +>NS1 : typeof globalThis.NS1 +>NNS1 : typeof globalThis.NS1 export {NS2, NS2 as NNS2}; ->NS2 : typeof NS2 ->NS2 : typeof NS2 ->NNS2 : typeof NS2 +>NS2 : typeof globalThis.NS2 +>NS2 : typeof globalThis.NS2 +>NNS2 : typeof globalThis.NS2 export {NS1 as NNNS1}; ->NS1 : typeof NS1 ->NNNS1 : typeof NS1 +>NS1 : typeof globalThis.NS1 +>NNNS1 : typeof globalThis.NS1 export {NS2 as NNNS2}; ->NS2 : typeof NS2 ->NNNS2 : typeof NS2 +>NS2 : typeof globalThis.NS2 +>NNNS2 : typeof globalThis.NS2 diff --git a/tests/baselines/reference/reExportGlobalDeclaration4.types b/tests/baselines/reference/reExportGlobalDeclaration4.types index 889b35c2d63f7..2599d84ec9399 100644 --- a/tests/baselines/reference/reExportGlobalDeclaration4.types +++ b/tests/baselines/reference/reExportGlobalDeclaration4.types @@ -15,20 +15,20 @@ declare class Cls2 { === tests/cases/compiler/file2.ts === export {Cls1, Cls1 as CCls1}; ->Cls1 : typeof Cls1 ->Cls1 : typeof Cls1 ->CCls1 : typeof Cls1 +>Cls1 : typeof globalThis.Cls1 +>Cls1 : typeof globalThis.Cls1 +>CCls1 : typeof globalThis.Cls1 export {Cls2, Cls2 as CCls2}; ->Cls2 : typeof Cls2 ->Cls2 : typeof Cls2 ->CCls2 : typeof Cls2 +>Cls2 : typeof globalThis.Cls2 +>Cls2 : typeof globalThis.Cls2 +>CCls2 : typeof globalThis.Cls2 export {Cls1 as CCCls1}; ->Cls1 : typeof Cls1 ->CCCls1 : typeof Cls1 +>Cls1 : typeof globalThis.Cls1 +>CCCls1 : typeof globalThis.Cls1 export {Cls2 as CCCls2}; ->Cls2 : typeof Cls2 ->CCCls2 : typeof Cls2 +>Cls2 : typeof globalThis.Cls2 +>CCCls2 : typeof globalThis.Cls2 diff --git a/tests/baselines/reference/shadowedInternalModule.errors.txt b/tests/baselines/reference/shadowedInternalModule.errors.txt index f278ebcb6066e..eb33b7e12d3bd 100644 --- a/tests/baselines/reference/shadowedInternalModule.errors.txt +++ b/tests/baselines/reference/shadowedInternalModule.errors.txt @@ -1,8 +1,10 @@ tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts(13,20): error TS2437: Module 'A' is hidden by a local declaration with the same name. tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts(30,5): error TS2440: Import declaration conflicts with local declaration of 'Y'. +tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts(47,10): error TS2438: Import name cannot be 'any'. +tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts(62,3): error TS2440: Import declaration conflicts with local declaration of 'Q'. -==== tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts (2 errors) ==== +==== tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts (4 errors) ==== // all errors imported modules conflict with local variables module A { @@ -39,4 +41,40 @@ tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModul !!! error TS2440: Import declaration conflicts with local declaration of 'Y'. var Y = 12; - } \ No newline at end of file + } + + // + + module a { + export type A = number; + } + + module b { + export import A = a.A; + export module A {} + } + + module c { + import any = b.A; + ~~~ +!!! error TS2438: Import name cannot be 'any'. + } + + // + + module q { + export const Q = {}; + } + + module r { + export import Q = q.Q; + export type Q = number; + } + + module s { + import Q = r.Q; + ~~~~~~~~~~~~~~~ +!!! error TS2440: Import declaration conflicts with local declaration of 'Q'. + const Q = 0; + } + \ No newline at end of file diff --git a/tests/baselines/reference/shadowedInternalModule.js b/tests/baselines/reference/shadowedInternalModule.js index 5d8e33d5eeb6c..63d2e6be2f5f5 100644 --- a/tests/baselines/reference/shadowedInternalModule.js +++ b/tests/baselines/reference/shadowedInternalModule.js @@ -31,7 +31,39 @@ module Z { import Y = X.Y; var Y = 12; -} +} + +// + +module a { + export type A = number; +} + +module b { + export import A = a.A; + export module A {} +} + +module c { + import any = b.A; +} + +// + +module q { + export const Q = {}; +} + +module r { + export import Q = q.Q; + export type Q = number; +} + +module s { + import Q = r.Q; + const Q = 0; +} + //// [shadowedInternalModule.js] // all errors imported modules conflict with local variables @@ -56,3 +88,19 @@ var Z; (function (Z) { var Y = 12; })(Z || (Z = {})); +var b; +(function (b) { +})(b || (b = {})); +// +var q; +(function (q) { + q.Q = {}; +})(q || (q = {})); +var r; +(function (r) { + r.Q = q.Q; +})(r || (r = {})); +var s; +(function (s) { + var Q = 0; +})(s || (s = {})); diff --git a/tests/baselines/reference/shadowedInternalModule.symbols b/tests/baselines/reference/shadowedInternalModule.symbols index b6293023cc877..02a12b4966679 100644 --- a/tests/baselines/reference/shadowedInternalModule.symbols +++ b/tests/baselines/reference/shadowedInternalModule.symbols @@ -69,3 +69,67 @@ module Z { var Y = 12; >Y : Symbol(Y, Decl(shadowedInternalModule.ts, 28, 10), Decl(shadowedInternalModule.ts, 31, 7)) } + +// + +module a { +>a : Symbol(a, Decl(shadowedInternalModule.ts, 32, 1)) + + export type A = number; +>A : Symbol(A, Decl(shadowedInternalModule.ts, 36, 10)) +} + +module b { +>b : Symbol(b, Decl(shadowedInternalModule.ts, 38, 1)) + + export import A = a.A; +>A : Symbol(A, Decl(shadowedInternalModule.ts, 40, 10), Decl(shadowedInternalModule.ts, 41, 24)) +>a : Symbol(a, Decl(shadowedInternalModule.ts, 32, 1)) +>A : Symbol(A, Decl(shadowedInternalModule.ts, 36, 10)) + + export module A {} +>A : Symbol(A, Decl(shadowedInternalModule.ts, 40, 10), Decl(shadowedInternalModule.ts, 41, 24)) +} + +module c { +>c : Symbol(c, Decl(shadowedInternalModule.ts, 43, 1)) + + import any = b.A; +>any : Symbol(any, Decl(shadowedInternalModule.ts, 45, 10)) +>b : Symbol(b, Decl(shadowedInternalModule.ts, 38, 1)) +>A : Symbol(any, Decl(shadowedInternalModule.ts, 40, 10), Decl(shadowedInternalModule.ts, 41, 24)) +} + +// + +module q { +>q : Symbol(q, Decl(shadowedInternalModule.ts, 47, 1)) + + export const Q = {}; +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 52, 14)) +} + +module r { +>r : Symbol(r, Decl(shadowedInternalModule.ts, 53, 1)) + + export import Q = q.Q; +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 55, 10), Decl(shadowedInternalModule.ts, 56, 24)) +>q : Symbol(q, Decl(shadowedInternalModule.ts, 47, 1)) +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 52, 14)) + + export type Q = number; +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 55, 10), Decl(shadowedInternalModule.ts, 56, 24)) +} + +module s { +>s : Symbol(s, Decl(shadowedInternalModule.ts, 58, 1)) + + import Q = r.Q; +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 60, 10), Decl(shadowedInternalModule.ts, 62, 7)) +>r : Symbol(r, Decl(shadowedInternalModule.ts, 53, 1)) +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 55, 10), Decl(shadowedInternalModule.ts, 56, 24)) + + const Q = 0; +>Q : Symbol(Q, Decl(shadowedInternalModule.ts, 60, 10), Decl(shadowedInternalModule.ts, 62, 7)) +} + diff --git a/tests/baselines/reference/shadowedInternalModule.types b/tests/baselines/reference/shadowedInternalModule.types index f3e7ee5e81451..5c52bf84a810a 100644 --- a/tests/baselines/reference/shadowedInternalModule.types +++ b/tests/baselines/reference/shadowedInternalModule.types @@ -70,3 +70,64 @@ module Z { >Y : number >12 : 12 } + +// + +module a { + export type A = number; +>A : number +} + +module b { +>b : typeof b + + export import A = a.A; +>A : any +>a : any +>A : number + + export module A {} +} + +module c { + import any = b.A; +>any : any +>b : typeof b +>A : number +} + +// + +module q { +>q : typeof q + + export const Q = {}; +>Q : {} +>{} : {} +} + +module r { +>r : typeof r + + export import Q = q.Q; +>Q : {} +>q : typeof q +>Q : {} + + export type Q = number; +>Q : number +} + +module s { +>s : typeof s + + import Q = r.Q; +>Q : 0 +>r : typeof r +>Q : number + + const Q = 0; +>Q : 0 +>0 : 0 +} + diff --git a/tests/baselines/reference/stackDepthLimitCastingType.types b/tests/baselines/reference/stackDepthLimitCastingType.types index 76d08fdd570bd..bba5d949ff7a0 100644 --- a/tests/baselines/reference/stackDepthLimitCastingType.types +++ b/tests/baselines/reference/stackDepthLimitCastingType.types @@ -32,7 +32,7 @@ declare global { export { Model ->Model : typeof Model +>Model : typeof global.Model }; export as namespace Backbone; diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.js b/tests/baselines/reference/typeAndNamespaceExportMerge.js index 893d8330d2bd9..8c939fca7d582 100644 --- a/tests/baselines/reference/typeAndNamespaceExportMerge.js +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.js @@ -33,5 +33,6 @@ exports.Drink = require("./constants"); //// [index.js] "use strict"; exports.__esModule = true; +var drink_1 = require("./drink"); // 'Drink' only refers to a type, but is being used as a value here var x = drink_1.Drink.TEA; diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.symbols b/tests/baselines/reference/typeAndNamespaceExportMerge.symbols index 583c2e8750bc6..77cbe73f5cae7 100644 --- a/tests/baselines/reference/typeAndNamespaceExportMerge.symbols +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.symbols @@ -24,5 +24,7 @@ import { Drink } from "./drink"; const x: Drink = Drink.TEA; >x : Symbol(x, Decl(index.ts, 2, 5)) >Drink : Symbol(Drink, Decl(index.ts, 0, 8)) +>Drink.TEA : Symbol(TEA, Decl(constants.ts, 3, 12)) >Drink : Symbol(Drink, Decl(index.ts, 0, 8)) +>TEA : Symbol(TEA, Decl(constants.ts, 3, 12)) diff --git a/tests/baselines/reference/typeAndNamespaceExportMerge.types b/tests/baselines/reference/typeAndNamespaceExportMerge.types index bb2ebf9800cd8..86a89b6c4a4c1 100644 --- a/tests/baselines/reference/typeAndNamespaceExportMerge.types +++ b/tests/baselines/reference/typeAndNamespaceExportMerge.types @@ -20,12 +20,12 @@ export * as Drink from "./constants"; === tests/cases/conformance/externalModules/index.ts === import { Drink } from "./drink"; ->Drink : any +>Drink : typeof import("tests/cases/conformance/externalModules/constants") // 'Drink' only refers to a type, but is being used as a value here const x: Drink = Drink.TEA; >x : Drink ->Drink.TEA : error ->Drink : any ->TEA : any +>Drink.TEA : 1 +>Drink : typeof import("tests/cases/conformance/externalModules/constants") +>TEA : 1 diff --git a/tests/baselines/reference/typeOnlyMerge1.js b/tests/baselines/reference/typeOnlyMerge1.js new file mode 100644 index 0000000000000..0def9774590d2 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge1.js @@ -0,0 +1,30 @@ +//// [tests/cases/conformance/externalModules/typeOnlyMerge1.ts] //// + +//// [a.ts] +interface A {} +export type { A }; + +//// [b.ts] +import { A } from "./a"; +const A = 0; +export { A }; + +//// [c.ts] +import { A } from "./b"; +A; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +//// [b.js] +"use strict"; +exports.__esModule = true; +exports.A = void 0; +var A = 0; +exports.A = A; +//// [c.js] +"use strict"; +exports.__esModule = true; +var b_1 = require("./b"); +b_1.A; diff --git a/tests/baselines/reference/typeOnlyMerge1.symbols b/tests/baselines/reference/typeOnlyMerge1.symbols new file mode 100644 index 0000000000000..f00f855f24f6e --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge1.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/externalModules/a.ts === +interface A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 1, 5)) + +const A = 0; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 1, 5)) + +export { A }; +>A : Symbol(A, Decl(b.ts, 2, 8)) + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : Symbol(A, Decl(c.ts, 0, 8)) + +A; +>A : Symbol(A, Decl(c.ts, 0, 8)) + diff --git a/tests/baselines/reference/typeOnlyMerge1.types b/tests/baselines/reference/typeOnlyMerge1.types new file mode 100644 index 0000000000000..bd7c9f0d4f5df --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge1.types @@ -0,0 +1,23 @@ +=== tests/cases/conformance/externalModules/a.ts === +interface A {} +export type { A }; +>A : A + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : 0 + +const A = 0; +>A : 0 +>0 : 0 + +export { A }; +>A : 0 + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : 0 + +A; +>A : 0 + diff --git a/tests/baselines/reference/typeOnlyMerge2.types b/tests/baselines/reference/typeOnlyMerge2.types index 72728f6de4364..7200dd36c1bfe 100644 --- a/tests/baselines/reference/typeOnlyMerge2.types +++ b/tests/baselines/reference/typeOnlyMerge2.types @@ -18,16 +18,16 @@ export type { A }; === tests/cases/conformance/externalModules/c.ts === import { A } from "./b"; ->A : any +>A : {} namespace A {} export { A }; ->A : any +>A : {} === tests/cases/conformance/externalModules/d.ts === import { A } from "./c"; ->A : any +>A : {} A; ->A : any +>A : {} diff --git a/tests/baselines/reference/typeOnlyMerge3.errors.txt b/tests/baselines/reference/typeOnlyMerge3.errors.txt new file mode 100644 index 0000000000000..84a31d71065ad --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge3.errors.txt @@ -0,0 +1,39 @@ +tests/cases/conformance/externalModules/b.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'A'. +tests/cases/conformance/externalModules/c.ts(2,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +tests/cases/conformance/externalModules/c.ts(3,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +tests/cases/conformance/externalModules/c.ts(4,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +tests/cases/conformance/externalModules/c.ts(4,1): error TS2349: This expression is not callable. + Type 'typeof A' has no call signatures. + + +==== tests/cases/conformance/externalModules/a.ts (0 errors) ==== + function A() {} + export type { A }; + +==== tests/cases/conformance/externalModules/b.ts (1 errors) ==== + import { A } from "./a"; + ~ +!!! error TS2440: Import declaration conflicts with local declaration of 'A'. + namespace A { + export const displayName = "A"; + } + export { A }; + +==== tests/cases/conformance/externalModules/c.ts (4 errors) ==== + import { A } from "./b"; + A; + ~ +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 tests/cases/conformance/externalModules/a.ts:2:15: 'A' was exported here. + A.displayName; + ~ +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 tests/cases/conformance/externalModules/a.ts:2:15: 'A' was exported here. + A(); + ~ +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 tests/cases/conformance/externalModules/a.ts:2:15: 'A' was exported here. + ~ +!!! error TS2349: This expression is not callable. +!!! error TS2349: Type 'typeof A' has no call signatures. + \ No newline at end of file diff --git a/tests/baselines/reference/typeOnlyMerge3.js b/tests/baselines/reference/typeOnlyMerge3.js new file mode 100644 index 0000000000000..d3afde76abd6f --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge3.js @@ -0,0 +1,37 @@ +//// [tests/cases/conformance/externalModules/typeOnlyMerge3.ts] //// + +//// [a.ts] +function A() {} +export type { A }; + +//// [b.ts] +import { A } from "./a"; +namespace A { + export const displayName = "A"; +} +export { A }; + +//// [c.ts] +import { A } from "./b"; +A; +A.displayName; +A(); + + +//// [a.js] +"use strict"; +exports.__esModule = true; +function A() { } +//// [b.js] +"use strict"; +exports.__esModule = true; +var A; +(function (A) { + A.displayName = "A"; +})(A || (A = {})); +//// [c.js] +"use strict"; +exports.__esModule = true; +A; +A.displayName; +A(); diff --git a/tests/baselines/reference/typeOnlyMerge3.symbols b/tests/baselines/reference/typeOnlyMerge3.symbols new file mode 100644 index 0000000000000..12ce75f56da55 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge3.symbols @@ -0,0 +1,35 @@ +=== tests/cases/conformance/externalModules/a.ts === +function A() {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + +namespace A { +>A : Symbol(A, Decl(b.ts, 0, 8), Decl(b.ts, 0, 24)) + + export const displayName = "A"; +>displayName : Symbol(displayName, Decl(b.ts, 2, 14)) +} +export { A }; +>A : Symbol(A, Decl(b.ts, 4, 8)) + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : Symbol(A, Decl(c.ts, 0, 8)) + +A; +>A : Symbol(A, Decl(c.ts, 0, 8)) + +A.displayName; +>A.displayName : Symbol(A.displayName, Decl(b.ts, 2, 14)) +>A : Symbol(A, Decl(c.ts, 0, 8)) +>displayName : Symbol(A.displayName, Decl(b.ts, 2, 14)) + +A(); +>A : Symbol(A, Decl(c.ts, 0, 8)) + diff --git a/tests/baselines/reference/typeOnlyMerge3.types b/tests/baselines/reference/typeOnlyMerge3.types new file mode 100644 index 0000000000000..b2a860c61abb5 --- /dev/null +++ b/tests/baselines/reference/typeOnlyMerge3.types @@ -0,0 +1,37 @@ +=== tests/cases/conformance/externalModules/a.ts === +function A() {} +>A : () => void + +export type { A }; +>A : any + +=== tests/cases/conformance/externalModules/b.ts === +import { A } from "./a"; +>A : typeof A + +namespace A { +>A : typeof A + + export const displayName = "A"; +>displayName : "A" +>"A" : "A" +} +export { A }; +>A : typeof A + +=== tests/cases/conformance/externalModules/c.ts === +import { A } from "./b"; +>A : typeof A + +A; +>A : typeof A + +A.displayName; +>A.displayName : "A" +>A : typeof A +>displayName : "A" + +A(); +>A() : any +>A : typeof A + diff --git a/tests/baselines/reference/valuesMergingAcrossModules.types b/tests/baselines/reference/valuesMergingAcrossModules.types index 6a3e12310fa8d..9e47dd9945bea 100644 --- a/tests/baselines/reference/valuesMergingAcrossModules.types +++ b/tests/baselines/reference/valuesMergingAcrossModules.types @@ -13,7 +13,7 @@ type A = 0; >A : 0 export { A }; ->A : any +>A : () => void === tests/cases/conformance/externalModules/c.ts === import { A } from "./b"; diff --git a/tests/cases/conformance/constEnums/importElisionConstEnumMerge1.ts b/tests/cases/conformance/constEnums/importElisionConstEnumMerge1.ts new file mode 100644 index 0000000000000..9884a7615a362 --- /dev/null +++ b/tests/cases/conformance/constEnums/importElisionConstEnumMerge1.ts @@ -0,0 +1,15 @@ +// @Filename: enum.ts +export const enum Enum { + One = 1, +} + +// @Filename: merge.ts +import { Enum } from "./enum"; +namespace Enum { + export type Foo = number; +} +export { Enum }; + +// @Filename: index.ts +import { Enum } from "./merge"; +Enum.One; diff --git a/tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts b/tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts index dcdcdbd1eb61c..a2c9738455bb5 100644 --- a/tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts +++ b/tests/cases/conformance/internalModules/importDeclarations/shadowedInternalModule.ts @@ -30,4 +30,35 @@ module Z { import Y = X.Y; var Y = 12; -} \ No newline at end of file +} + +// + +module a { + export type A = number; +} + +module b { + export import A = a.A; + export module A {} +} + +module c { + import any = b.A; +} + +// + +module q { + export const Q = {}; +} + +module r { + export import Q = q.Q; + export type Q = number; +} + +module s { + import Q = r.Q; + const Q = 0; +} From a97a11e73b262cc2fa382db1c57b9b84f875f2b8 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 19 Sep 2022 16:56:09 -0700 Subject: [PATCH 5/8] Fix qualification in re-exporting global cases --- src/compiler/checker.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70b413cc6b66b..4bf43afdc3ba9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4595,8 +4595,10 @@ namespace ts { } // Qualify if the symbol from symbol table has same meaning as expected - symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; - if ((getAllSymbolFlags(symbolFromSymbolTable) ?? unknownSymbol.flags) & meaning) { + const shouldResolveAlias = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)); + symbolFromSymbolTable = shouldResolveAlias ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; + const flags = shouldResolveAlias ? (getAllSymbolFlags(symbolFromSymbolTable) ?? unknownSymbol.flags) : symbolFromSymbolTable.flags; + if (flags & meaning) { qualify = true; return true; } From 33add30e1f8865e9a5da8912d86fa0c0d380ae7b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 20 Sep 2022 09:12:57 -0700 Subject: [PATCH 6/8] Fix infinite loop detection --- src/compiler/checker.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4bf43afdc3ba9..30352e0c115f1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3266,15 +3266,18 @@ namespace ts { return undefined; } - // Optimization - avoid creating `seenSymbols` Set in most cases - if (target === symbol) { + // Optimizations - try to avoid creating or adding to + // `seenSymbols` if possible + if (target === symbol || seenSymbols?.has(target)) { break; } - else if (target.flags & SymbolFlags.Alias && !seenSymbols) { - seenSymbols = new Set([symbol, target]); - } - else if (seenSymbols?.has(target)) { - break; + if (target.flags & SymbolFlags.Alias) { + if (seenSymbols) { + seenSymbols.add(target); + } + else { + seenSymbols = new Set([symbol, target]); + } } flags |= target.flags; symbol = target; From fcefb22e3b3e1a8028c249a9b935c3d2522c8673 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 20 Sep 2022 09:13:05 -0700 Subject: [PATCH 7/8] Update baselines --- .../reference/exportSpecifierForAGlobal.js | 2 +- .../reference/exportSpecifierForAGlobal.types | 8 ++++---- ...cifierReferencingOuterDeclaration1.symbols | 2 +- ...cifierReferencingOuterDeclaration2.symbols | 2 +- .../reExportGlobalDeclaration3.types | 20 +++++++++---------- .../reExportGlobalDeclaration4.types | 20 +++++++++---------- .../stackDepthLimitCastingType.types | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/baselines/reference/exportSpecifierForAGlobal.js b/tests/baselines/reference/exportSpecifierForAGlobal.js index 392b21e0f21c8..0154ba30d6121 100644 --- a/tests/baselines/reference/exportSpecifierForAGlobal.js +++ b/tests/baselines/reference/exportSpecifierForAGlobal.js @@ -24,4 +24,4 @@ exports.f = f; //// [b.d.ts] export { X }; -export declare function f(): globalThis.X; +export declare function f(): X; diff --git a/tests/baselines/reference/exportSpecifierForAGlobal.types b/tests/baselines/reference/exportSpecifierForAGlobal.types index d18a1c6b71c6a..4331a7b84d3b8 100644 --- a/tests/baselines/reference/exportSpecifierForAGlobal.types +++ b/tests/baselines/reference/exportSpecifierForAGlobal.types @@ -4,15 +4,15 @@ declare class X { } === tests/cases/compiler/b.ts === export {X}; ->X : typeof globalThis.X +>X : typeof X export function f() { ->f : () => globalThis.X +>f : () => X var x: X; ->x : globalThis.X +>x : X return x; ->x : globalThis.X +>x : X } diff --git a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols index 0b0ff075991ae..dd586f52d7828 100644 --- a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols +++ b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration1.symbols @@ -12,5 +12,5 @@ declare module "m" { export function foo(): X.bar; >foo : Symbol(foo, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 2, 17)) >X : Symbol(X, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 0)) ->bar : Symbol(globalThis.X.bar, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 18)) +>bar : Symbol(X.bar, Decl(exportSpecifierReferencingOuterDeclaration1.ts, 0, 18)) } diff --git a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols index 0825a055433e6..9a57645b1cbcd 100644 --- a/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols +++ b/tests/baselines/reference/exportSpecifierReferencingOuterDeclaration2.symbols @@ -10,5 +10,5 @@ export { X }; export declare function foo(): X.bar; >foo : Symbol(foo, Decl(exportSpecifierReferencingOuterDeclaration2_B.ts, 0, 13)) >X : Symbol(X, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 0)) ->bar : Symbol(globalThis.X.bar, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 18)) +>bar : Symbol(X.bar, Decl(exportSpecifierReferencingOuterDeclaration2_A.ts, 0, 18)) diff --git a/tests/baselines/reference/reExportGlobalDeclaration3.types b/tests/baselines/reference/reExportGlobalDeclaration3.types index a2cca62a85114..01faa99a68676 100644 --- a/tests/baselines/reference/reExportGlobalDeclaration3.types +++ b/tests/baselines/reference/reExportGlobalDeclaration3.types @@ -15,20 +15,20 @@ declare namespace NS2 { === tests/cases/compiler/file2.ts === export {NS1, NS1 as NNS1}; ->NS1 : typeof globalThis.NS1 ->NS1 : typeof globalThis.NS1 ->NNS1 : typeof globalThis.NS1 +>NS1 : typeof NS1 +>NS1 : typeof NS1 +>NNS1 : typeof NS1 export {NS2, NS2 as NNS2}; ->NS2 : typeof globalThis.NS2 ->NS2 : typeof globalThis.NS2 ->NNS2 : typeof globalThis.NS2 +>NS2 : typeof NS2 +>NS2 : typeof NS2 +>NNS2 : typeof NS2 export {NS1 as NNNS1}; ->NS1 : typeof globalThis.NS1 ->NNNS1 : typeof globalThis.NS1 +>NS1 : typeof NS1 +>NNNS1 : typeof NS1 export {NS2 as NNNS2}; ->NS2 : typeof globalThis.NS2 ->NNNS2 : typeof globalThis.NS2 +>NS2 : typeof NS2 +>NNNS2 : typeof NS2 diff --git a/tests/baselines/reference/reExportGlobalDeclaration4.types b/tests/baselines/reference/reExportGlobalDeclaration4.types index 2599d84ec9399..889b35c2d63f7 100644 --- a/tests/baselines/reference/reExportGlobalDeclaration4.types +++ b/tests/baselines/reference/reExportGlobalDeclaration4.types @@ -15,20 +15,20 @@ declare class Cls2 { === tests/cases/compiler/file2.ts === export {Cls1, Cls1 as CCls1}; ->Cls1 : typeof globalThis.Cls1 ->Cls1 : typeof globalThis.Cls1 ->CCls1 : typeof globalThis.Cls1 +>Cls1 : typeof Cls1 +>Cls1 : typeof Cls1 +>CCls1 : typeof Cls1 export {Cls2, Cls2 as CCls2}; ->Cls2 : typeof globalThis.Cls2 ->Cls2 : typeof globalThis.Cls2 ->CCls2 : typeof globalThis.Cls2 +>Cls2 : typeof Cls2 +>Cls2 : typeof Cls2 +>CCls2 : typeof Cls2 export {Cls1 as CCCls1}; ->Cls1 : typeof globalThis.Cls1 ->CCCls1 : typeof globalThis.Cls1 +>Cls1 : typeof Cls1 +>CCCls1 : typeof Cls1 export {Cls2 as CCCls2}; ->Cls2 : typeof globalThis.Cls2 ->CCCls2 : typeof globalThis.Cls2 +>Cls2 : typeof Cls2 +>CCCls2 : typeof Cls2 diff --git a/tests/baselines/reference/stackDepthLimitCastingType.types b/tests/baselines/reference/stackDepthLimitCastingType.types index bba5d949ff7a0..76d08fdd570bd 100644 --- a/tests/baselines/reference/stackDepthLimitCastingType.types +++ b/tests/baselines/reference/stackDepthLimitCastingType.types @@ -32,7 +32,7 @@ declare global { export { Model ->Model : typeof global.Model +>Model : typeof Model }; export as namespace Backbone; From 72ff9e53727c25c5f61120e43e3e6a463a9da42e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 29 Sep 2022 11:42:39 -0700 Subject: [PATCH 8/8] Just make getAllSymbolFlags default to All --- src/compiler/checker.ts | 89 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30352e0c115f1..dd4eba16f32ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1537,8 +1537,8 @@ namespace ts { } if (symbol.flags & SymbolFlags.Alias) { const targetFlags = getAllSymbolFlags(symbol); - // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (targetFlags === undefined || targetFlags & meaning) { + // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors + if (targetFlags & meaning) { return symbol; } } @@ -3254,35 +3254,35 @@ namespace ts { * from b.ts, even though there is still more alias to resolve. Consequently, if we were * trying to determine if the `a` in c.ts has a value meaning, looking at the flags on * the local symbol and on the symbol returned by `resolveAlias` is not enough. - * @returns undefined if `symbol` is an alias that ultimately resolves to `unknown`; + * @returns SymbolFlags.All if `symbol` is an alias that ultimately resolves to `unknown`; * combined flags of all alias targets otherwise. */ - function getAllSymbolFlags(symbol: Symbol): SymbolFlags | undefined { - let flags = symbol.flags; - let seenSymbols; - while (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); - if (target === unknownSymbol) { - return undefined; - } - - // Optimizations - try to avoid creating or adding to - // `seenSymbols` if possible - if (target === symbol || seenSymbols?.has(target)) { - break; - } - if (target.flags & SymbolFlags.Alias) { - if (seenSymbols) { - seenSymbols.add(target); - } - else { - seenSymbols = new Set([symbol, target]); - } - } - flags |= target.flags; - symbol = target; - } - return flags; + function getAllSymbolFlags(symbol: Symbol): SymbolFlags { + let flags = symbol.flags; + let seenSymbols; + while (symbol.flags & SymbolFlags.Alias) { + const target = resolveAlias(symbol); + if (target === unknownSymbol) { + return SymbolFlags.All; + } + + // Optimizations - try to avoid creating or adding to + // `seenSymbols` if possible + if (target === symbol || seenSymbols?.has(target)) { + break; + } + if (target.flags & SymbolFlags.Alias) { + if (seenSymbols) { + seenSymbols.add(target); + } + else { + seenSymbols = new Set([symbol, target]); + } + } + flags |= target.flags; + symbol = target; + } + return flags; } /** @@ -3346,7 +3346,7 @@ namespace ts { return links.typeOnlyDeclaration || undefined; } if (links.typeOnlyDeclaration) { - return (getAllSymbolFlags(resolveAlias(links.typeOnlyDeclaration.symbol)) ?? -1) & include ? links.typeOnlyDeclaration : undefined; + return getAllSymbolFlags(resolveAlias(links.typeOnlyDeclaration.symbol)) & include ? links.typeOnlyDeclaration : undefined; } return undefined; } @@ -3356,7 +3356,7 @@ namespace ts { const target = resolveAlias(symbol); if (target) { const markAlias = target === unknownSymbol || - (((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)); + ((getAllSymbolFlags(target) & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)); if (markAlias) { markAliasSymbolAsReferenced(symbol); @@ -3377,8 +3377,7 @@ namespace ts { // This way a chain of imports can be elided if ultimately the final input is only used in a type // position. if (isInternalModuleImportEqualsDeclaration(node)) { - const targetFlags = getAllSymbolFlags(resolveSymbol(symbol)); - if (targetFlags === undefined || targetFlags & SymbolFlags.Value) { + if (getAllSymbolFlags(resolveSymbol(symbol)) & SymbolFlags.Value) { // import foo = checkExpressionCached(node.moduleReference as Expression); } @@ -4304,7 +4303,7 @@ namespace ts { function symbolIsValue(symbol: Symbol, includeTypeOnlyMembers?: boolean): boolean { return !!( symbol.flags & SymbolFlags.Value || - symbol.flags & SymbolFlags.Alias && (getAllSymbolFlags(symbol) ?? -1) & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol))); + symbol.flags & SymbolFlags.Alias && getAllSymbolFlags(symbol) & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol))); } function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined { @@ -4600,7 +4599,7 @@ namespace ts { // Qualify if the symbol from symbol table has same meaning as expected const shouldResolveAlias = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)); symbolFromSymbolTable = shouldResolveAlias ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; - const flags = shouldResolveAlias ? (getAllSymbolFlags(symbolFromSymbolTable) ?? unknownSymbol.flags) : symbolFromSymbolTable.flags; + const flags = shouldResolveAlias ? getAllSymbolFlags(symbolFromSymbolTable) : symbolFromSymbolTable.flags; if (flags & meaning) { qualify = true; return true; @@ -7574,7 +7573,7 @@ namespace ts { } function isTypeOnlyNamespace(symbol: Symbol) { - return every(getNamespaceMembersForSerialization(symbol), m => !((getAllSymbolFlags(resolveSymbol(m)) ?? unknownSymbol.flags) & SymbolFlags.Value)); + return every(getNamespaceMembersForSerialization(symbol), m => !(getAllSymbolFlags(resolveSymbol(m)) & SymbolFlags.Value)); } function serializeModule(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { @@ -10072,7 +10071,7 @@ namespace ts { links.type = exportSymbol?.declarations && isDuplicatedCommonJSExport(exportSymbol.declarations) && symbol.declarations!.length ? getFlowTypeFromCommonJSExport(exportSymbol) : isDuplicatedCommonJSExport(symbol.declarations) ? autoType : declaredType ? declaredType - : (getAllSymbolFlags(targetSymbol) ?? -1) & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) + : getAllSymbolFlags(targetSymbol) & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) : errorType; } return links.type; @@ -25974,7 +25973,7 @@ namespace ts { function markAliasReferenced(symbol: Symbol, location: Node) { if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)) { const target = resolveAlias(symbol); - if ((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { + if (getAllSymbolFlags(target) & SymbolFlags.Value) { // An alias resolving to a const enum cannot be elided if (1) 'isolatedModules' is enabled // (because the const enum value will not be inlined), or if (2) the alias is an export // of a const enum declaration that will be preserved. @@ -32521,7 +32520,7 @@ namespace ts { if (symbol && symbol.flags & SymbolFlags.Alias) { symbol = resolveAlias(symbol); } - return !!(symbol && ((getAllSymbolFlags(symbol) ?? unknownSymbol.flags) & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); + return !!(symbol && (getAllSymbolFlags(symbol) & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); } return false; } @@ -41427,7 +41426,7 @@ namespace ts { return; } - const targetFlags = getAllSymbolFlags(target) ?? target.flags; + const targetFlags = getAllSymbolFlags(target); const excludedMeanings = (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | @@ -41621,7 +41620,7 @@ namespace ts { if (node.moduleReference.kind !== SyntaxKind.ExternalModuleReference) { const target = resolveAlias(getSymbolOfNode(node)); if (target !== unknownSymbol) { - const targetFlags = getAllSymbolFlags(target) ?? unknownSymbol.flags; + const targetFlags = getAllSymbolFlags(target); if (targetFlags & SymbolFlags.Value) { // Target is a value symbol, check that it is not hidden by a local declaration with the same name const moduleName = getFirstIdentifier(node.moduleReference); @@ -41780,7 +41779,7 @@ namespace ts { markExportAsReferenced(node); } const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol); - if (!target || (getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { + if (!target || getAllSymbolFlags(target) & SymbolFlags.Value) { checkExpressionCached(node.propertyName || node.name); } } @@ -41832,7 +41831,7 @@ namespace ts { markAliasReferenced(sym, id); // If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`) const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym; - if ((getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value) { + if (getAllSymbolFlags(target) & SymbolFlags.Value) { // However if it is a value, we need to check it's being used correctly checkExpressionCached(node.expression); } @@ -43290,7 +43289,7 @@ namespace ts { function isValue(s: Symbol): boolean { s = resolveSymbol(s); - return s && !!((getAllSymbolFlags(s) ?? unknownSymbol.flags) & SymbolFlags.Value); + return s && !!(getAllSymbolFlags(s) & SymbolFlags.Value); } } @@ -43496,7 +43495,7 @@ namespace ts { } const target = getSymbolLinks(symbol!).aliasTarget; // TODO: GH#18217 if (target && getEffectiveModifierFlags(node) & ModifierFlags.Export && - (getAllSymbolFlags(target) ?? -1) & SymbolFlags.Value && + getAllSymbolFlags(target) & SymbolFlags.Value && (shouldPreserveConstEnums(compilerOptions) || !isConstEnumOrConstEnumOnlyModule(target))) { // An `export import ... =` of a value symbol is always considered referenced return true;