From b74c7b3220d4680f4cd75b271b4ee7fb204b6dca Mon Sep 17 00:00:00 2001 From: Gavin Kline Date: Fri, 9 Oct 2026 17:34:25 -1000 Subject: [PATCH 1/2] Merge export * tables without per-name collision trackers getExportsOfModuleWorker allocated an ExportCollision struct for every name contributed by every export * declaration, kept the names in a separate table before merging them, and then iterated that table once more to look for duplicates. For modules that re-export large modules this dominated the allocations of module export resolution. The merge now writes straight into the module's table, records only the first contributing declaration per name and only when a module has more than one export * declaration, and keeps the names that collided in a map that is usually empty. Modules without export * declarations no longer have their tables cloned when visited from an importer, and the names used to undo type-only marking are collected only when an 'export type *' was seen. Co-Authored-By: Claude Fable 5.1 --- tsc/internal/checker/checker.go | 118 ++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 345ea00ce1437..77494166ab5db 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -16533,53 +16533,36 @@ func (c *Checker) getExportsOfModule(moduleSymbol *ast.Symbol) ast.SymbolTable { return links.resolvedExports } -type ExportCollision struct { - specifierText string - exportsWithDuplicate []*ast.Node -} - -type ExportCollisionTable = map[string]*ExportCollision - func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports ast.SymbolTable, typeOnlyExportStarMap map[string]*ast.Node) { var visitedSymbols []*ast.Symbol - nonTypeOnlyNames := collections.NewSetWithSizeHint[string](len(moduleSymbol.Exports())) + // Modules reached through a non-type-only path. Their export names are removed from + // typeOnlyExportStarMap once the traversal is complete, since visiting a module via + // 'export *' overrides the type-onlyness its exports acquired through an 'export type *'. + var nonTypeOnlyModules []*ast.Symbol // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. var visit func(*ast.Symbol, *ast.Node, bool) ast.SymbolTable visit = func(symbol *ast.Symbol, exportStar *ast.Node, isTypeOnly bool) ast.SymbolTable { if !isTypeOnly && symbol != nil { - // Add non-type-only names before checking if we've visited this module, - // because we might have visited it via an 'export type *', and visiting - // again with 'export *' will override the type-onlyness of its exports. - for name := range symbol.Exports() { - nonTypeOnlyNames.Add(name) - } + nonTypeOnlyModules = append(nonTypeOnlyModules, symbol) } if symbol == nil || symbol.Exports() == nil || slices.Contains(visitedSymbols, symbol) { return nil } visitedSymbols = append(visitedSymbols, symbol) - symbols := maps.Clone(symbol.Exports()) // All export * declarations are collected in an __export symbol by the binder exportStars := symbol.Exports()[ast.InternalSymbolNameExportStar] - if exportStars != nil { - nestedSymbols := make(ast.SymbolTable) - lookupTable := make(ExportCollisionTable) - for _, node := range exportStars.Declarations() { - resolvedModule := c.resolveExternalModuleName(node, node.ModuleSpecifier(), false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node))) - exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.IsTypeOnly()) - c.extendExportSymbols(nestedSymbols, exportedSymbols, lookupTable, node) - } - for id, s := range lookupTable { - // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if id == ast.InternalSymbolNameExportEquals || len(s.exportsWithDuplicate) == 0 || symbols[id] != nil { - continue - } - for _, node := range s.exportsWithDuplicate { - c.addDiagnostic(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, s.specifierText, id)) - } + var symbols ast.SymbolTable + if exportStars == nil { + if exportStar == nil { + // The table of the module being resolved is cached and may be extended below. + symbols = maps.Clone(symbol.Exports()) + } else { + // Nested tables are only read while being merged into their importer's table. + symbols = symbol.Exports() } - c.extendExportSymbols(symbols, nestedSymbols, nil, nil) + } else { + symbols = c.getExportsWithExportStars(symbol, exportStars.Declarations(), isTypeOnly, visit) } if exportStar != nil && exportStar.IsTypeOnly() { if typeOnlyExportStarMap == nil { @@ -16617,34 +16600,65 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as } } } - for name := range nonTypeOnlyNames.Keys() { - delete(typeOnlyExportStarMap, name) + if typeOnlyExportStarMap != nil { + for _, symbol := range nonTypeOnlyModules { + for name := range symbol.Exports() { + delete(typeOnlyExportStarMap, name) + } + } } return exports, typeOnlyExportStarMap } -/** - * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument - * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables - */ -func (c *Checker) extendExportSymbols(target ast.SymbolTable, source ast.SymbolTable, lookupTable ExportCollisionTable, exportNode *ast.Node) { - for id, sourceSymbol := range source { - if id == ast.InternalSymbolNameDefault { - continue - } - targetSymbol := target[id] - if targetSymbol == nil { - target[id] = sourceSymbol - if lookupTable != nil && exportNode != nil { - lookupTable[id] = &ExportCollision{ - specifierText: scanner.GetTextOfNode(exportNode.ModuleSpecifier()), +// getExportsWithExportStars returns the exports of a module that contains export * declarations: +// its own exports, extended with the exports of each re-exported module. Names that more than one +// export * declaration contributes with different meanings are reported, unless the module itself +// declares the name. +func (c *Checker) getExportsWithExportStars(symbol *ast.Symbol, declarations []*ast.Node, isTypeOnly bool, visit func(*ast.Symbol, *ast.Node, bool) ast.SymbolTable) ast.SymbolTable { + exportedSymbols := make([]ast.SymbolTable, len(declarations)) + // Re-exported tables often overlap, so size for the largest one and let the map grow past that. + largest := 0 + for i, node := range declarations { + resolvedModule := c.resolveExternalModuleName(node, node.ModuleSpecifier(), false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node))) + exportedSymbols[i] = visit(resolvedModule, node, isTypeOnly || node.IsTypeOnly()) + largest = max(largest, len(exportedSymbols[i])) + } + symbols := make(ast.SymbolTable, len(symbol.Exports())+largest) + maps.Copy(symbols, symbol.Exports()) + // With a single export * declaration there is nothing to collide with, so the bookkeeping is skipped entirely. + var firstExportStar map[string]*ast.Node // name -> export * declaration that first contributed it + var duplicates map[string][]*ast.Node // name -> later export * declarations contributing a different symbol + if len(declarations) > 1 { + firstExportStar = make(map[string]*ast.Node, largest) + } + for i, node := range declarations { + for id, sourceSymbol := range exportedSymbols[i] { + if id == ast.InternalSymbolNameDefault { + continue + } + targetSymbol := symbols[id] + if targetSymbol == nil { + symbols[id] = sourceSymbol + if firstExportStar != nil { + firstExportStar[id] = node + } + } else if firstExportStar != nil && id != ast.InternalSymbolNameExportEquals { + if _, fromExportStar := firstExportStar[id]; fromExportStar && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { + if duplicates == nil { + duplicates = make(map[string][]*ast.Node) + } + duplicates[id] = append(duplicates[id], node) } } - } else if lookupTable != nil && exportNode != nil && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { - s := lookupTable[id] - s.exportsWithDuplicate = append(s.exportsWithDuplicate, exportNode) } } + for id, nodes := range duplicates { + specifierText := scanner.GetTextOfNode(firstExportStar[id].ModuleSpecifier()) + for _, node := range nodes { + c.addDiagnostic(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, specifierText, id)) + } + } + return symbols } func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) { From 3414ad3e3ecbf6764bd1f722298aaa6dfbd417a5 Mon Sep 17 00:00:00 2001 From: Gavin Kline Date: Fri, 9 Oct 2026 22:45:47 -1000 Subject: [PATCH 2/2] Add test for export * name collisions Covers the cases the export * merge distinguishes: colliding names from two and three declarations, the same symbol reached twice, a module's own export silencing the collision, a single export *, export type * on either side, and a chain of re-exporting modules. Baselines were generated before the merge was rewritten and are unchanged by it. Co-Authored-By: Claude Opus 5.5 --- .../exportStarNameCollisions.errors.txt | 107 +++++++++++ .../compiler/exportStarNameCollisions.js | 170 ++++++++++++++++++ .../compiler/exportStarNameCollisions.symbols | 146 +++++++++++++++ .../compiler/exportStarNameCollisions.types | 152 ++++++++++++++++ .../compiler/exportStarNameCollisions.ts | 70 ++++++++ 5 files changed, 645 insertions(+) create mode 100644 tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.js create mode 100644 tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.types create mode 100644 tsc/testdata/tests/cases/compiler/exportStarNameCollisions.ts diff --git a/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.errors.txt b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.errors.txt new file mode 100644 index 0000000000000..50f464a2bee0b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.errors.txt @@ -0,0 +1,107 @@ +consumer.ts(9,58): error TS1362: 'a5' cannot be used as a value because it was exported using 'export type'. +consumer.ts(9,70): error TS1362: 'b6' cannot be used as a value because it was exported using 'export type'. +localShadows.ts(3,1): error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. +threeStarsCollide.ts(3,1): error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. +threeStarsCollide.ts(3,1): error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. +threeStarsCollide.ts(4,1): error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. +twoStarsCollide.ts(3,1): error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. +twoStarsCollide.ts(3,1): error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. +typeOnlyThenValue.ts(2,1): error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. +typeOnlyThenValue.ts(2,1): error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. +valueThenTypeOnly.ts(2,1): error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. +valueThenTypeOnly.ts(2,1): error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + + +==== a.ts (0 errors) ==== + export const onlyA = 1; + export const shared = "a"; + export interface SharedType { fromA: true } + export default "a"; + +==== b.ts (0 errors) ==== + export const onlyB = 2; + export const shared = "b"; + export interface SharedType { fromB: true } + export default "b"; + +==== b2.ts (0 errors) ==== + export const shared = "b2"; + +==== sameSymbol.ts (0 errors) ==== + export { shared } from "./a"; + +==== twoStarsCollide.ts (2 errors) ==== + // 'shared' and 'SharedType' come from both modules with different meanings. + export * from "./a"; + export * from "./b"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + +==== threeStarsCollide.ts (3 errors) ==== + // Both later declarations are reported against the first one. + export * from "./a"; + export * from "./b"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + export * from "./b2"; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + +==== sameSymbolTwice.ts (0 errors) ==== + // Reaching the same symbol through two declarations is not a collision. + export * from "./a"; + export * from "./sameSymbol"; + +==== localShadows.ts (1 errors) ==== + // A module's own export takes precedence and silences the collision for shared, but not for SharedType. + export * from "./a"; + export * from "./b"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. + export const shared = "local"; + +==== singleStar.ts (0 errors) ==== + export * from "./a"; + +==== typeOnlyThenValue.ts (2 errors) ==== + export type * from "./a"; + export * from "./b"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + +==== valueThenTypeOnly.ts (2 errors) ==== + export * from "./a"; + export type * from "./b"; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'SharedType'. Consider explicitly re-exporting to resolve the ambiguity. + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2308: Module "./a" has already exported a member named 'shared'. Consider explicitly re-exporting to resolve the ambiguity. + +==== chained.ts (0 errors) ==== + export * from "./twoStarsCollide"; + export * from "./singleStar"; + +==== consumer.ts (2 errors) ==== + import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; + import { shared as s2 } from "./sameSymbolTwice"; + import { shared as s3 } from "./localShadows"; + import { shared as s4, onlyA as a4 } from "./singleStar"; + import { onlyA as a5, onlyB as b5 } from "./typeOnlyThenValue"; + import { onlyA as a6, onlyB as b6 } from "./valueThenTypeOnly"; + import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; + import type { SharedType } from "./twoStarsCollide"; + export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; + ~~ +!!! error TS1362: 'a5' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 typeOnlyThenValue.ts:1:1: 'a5' was exported here. + ~~ +!!! error TS1362: 'b6' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 valueThenTypeOnly.ts:2:1: 'b6' was exported here. + export let t: SharedType | undefined; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.js b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.js new file mode 100644 index 0000000000000..f590881266917 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.js @@ -0,0 +1,170 @@ +//// [tests/cases/compiler/exportStarNameCollisions.ts] //// + +//// [a.ts] +export const onlyA = 1; +export const shared = "a"; +export interface SharedType { fromA: true } +export default "a"; + +//// [b.ts] +export const onlyB = 2; +export const shared = "b"; +export interface SharedType { fromB: true } +export default "b"; + +//// [b2.ts] +export const shared = "b2"; + +//// [sameSymbol.ts] +export { shared } from "./a"; + +//// [twoStarsCollide.ts] +// 'shared' and 'SharedType' come from both modules with different meanings. +export * from "./a"; +export * from "./b"; + +//// [threeStarsCollide.ts] +// Both later declarations are reported against the first one. +export * from "./a"; +export * from "./b"; +export * from "./b2"; + +//// [sameSymbolTwice.ts] +// Reaching the same symbol through two declarations is not a collision. +export * from "./a"; +export * from "./sameSymbol"; + +//// [localShadows.ts] +// A module's own export takes precedence and silences the collision for shared, but not for SharedType. +export * from "./a"; +export * from "./b"; +export const shared = "local"; + +//// [singleStar.ts] +export * from "./a"; + +//// [typeOnlyThenValue.ts] +export type * from "./a"; +export * from "./b"; + +//// [valueThenTypeOnly.ts] +export * from "./a"; +export type * from "./b"; + +//// [chained.ts] +export * from "./twoStarsCollide"; +export * from "./singleStar"; + +//// [consumer.ts] +import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; +import { shared as s2 } from "./sameSymbolTwice"; +import { shared as s3 } from "./localShadows"; +import { shared as s4, onlyA as a4 } from "./singleStar"; +import { onlyA as a5, onlyB as b5 } from "./typeOnlyThenValue"; +import { onlyA as a6, onlyB as b6 } from "./valueThenTypeOnly"; +import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; +import type { SharedType } from "./twoStarsCollide"; +export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; +export let t: SharedType | undefined; + + +//// [a.js] +export const onlyA = 1; +export const shared = "a"; +export default "a"; +//// [b.js] +export const onlyB = 2; +export const shared = "b"; +export default "b"; +//// [b2.js] +export const shared = "b2"; +//// [sameSymbol.js] +export { shared } from "./a"; +//// [twoStarsCollide.js] +// 'shared' and 'SharedType' come from both modules with different meanings. +export * from "./a"; +export * from "./b"; +//// [threeStarsCollide.js] +// Both later declarations are reported against the first one. +export * from "./a"; +export * from "./b"; +export * from "./b2"; +//// [sameSymbolTwice.js] +// Reaching the same symbol through two declarations is not a collision. +export * from "./a"; +export * from "./sameSymbol"; +//// [localShadows.js] +// A module's own export takes precedence and silences the collision for shared, but not for SharedType. +export * from "./a"; +export * from "./b"; +export const shared = "local"; +//// [singleStar.js] +export * from "./a"; +//// [typeOnlyThenValue.js] +export * from "./b"; +//// [valueThenTypeOnly.js] +export * from "./a"; +//// [chained.js] +export * from "./twoStarsCollide"; +export * from "./singleStar"; +//// [consumer.js] +import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; +import { shared as s2 } from "./sameSymbolTwice"; +import { shared as s3 } from "./localShadows"; +import { shared as s4, onlyA as a4 } from "./singleStar"; +import { onlyB as b5 } from "./typeOnlyThenValue"; +import { onlyA as a6 } from "./valueThenTypeOnly"; +import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; +export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; +export let t; + + +//// [a.d.ts] +export declare const onlyA = 1; +export declare const shared = "a"; +export interface SharedType { + fromA: true; +} +declare const _default = "a"; +export default _default; +//// [b.d.ts] +export declare const onlyB = 2; +export declare const shared = "b"; +export interface SharedType { + fromB: true; +} +declare const _default = "b"; +export default _default; +//// [b2.d.ts] +export declare const shared = "b2"; +//// [sameSymbol.d.ts] +export { shared } from "./a"; +//// [twoStarsCollide.d.ts] +export * from "./a"; +export * from "./b"; +//// [threeStarsCollide.d.ts] +export * from "./a"; +export * from "./b"; +export * from "./b2"; +//// [sameSymbolTwice.d.ts] +export * from "./a"; +export * from "./sameSymbol"; +//// [localShadows.d.ts] +export * from "./a"; +export * from "./b"; +export declare const shared = "local"; +//// [singleStar.d.ts] +export * from "./a"; +//// [typeOnlyThenValue.d.ts] +export type * from "./a"; +export * from "./b"; +//// [valueThenTypeOnly.d.ts] +export * from "./a"; +export type * from "./b"; +//// [chained.d.ts] +export * from "./twoStarsCollide"; +export * from "./singleStar"; +//// [consumer.d.ts] +import type { SharedType } from "./twoStarsCollide"; +export declare const values: (string | number)[]; +export declare let t: SharedType | undefined; diff --git a/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.symbols b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.symbols new file mode 100644 index 0000000000000..a7f86323cd00e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.symbols @@ -0,0 +1,146 @@ +//// [tests/cases/compiler/exportStarNameCollisions.ts] //// + +=== a.ts === +export const onlyA = 1; +>onlyA : Symbol(onlyA, Decl(a.ts, 0, 12)) + +export const shared = "a"; +>shared : Symbol(shared, Decl(a.ts, 1, 12)) + +export interface SharedType { fromA: true } +>SharedType : Symbol(SharedType, Decl(a.ts, 1, 26)) +>fromA : Symbol(SharedType.fromA, Decl(a.ts, 2, 29)) + +export default "a"; + +=== b.ts === +export const onlyB = 2; +>onlyB : Symbol(onlyB, Decl(b.ts, 0, 12)) + +export const shared = "b"; +>shared : Symbol(shared, Decl(b.ts, 1, 12)) + +export interface SharedType { fromB: true } +>SharedType : Symbol(SharedType, Decl(b.ts, 1, 26)) +>fromB : Symbol(SharedType.fromB, Decl(b.ts, 2, 29)) + +export default "b"; + +=== b2.ts === +export const shared = "b2"; +>shared : Symbol(shared, Decl(b2.ts, 0, 12)) + +=== sameSymbol.ts === +export { shared } from "./a"; +>shared : Symbol(shared, Decl(sameSymbol.ts, 0, 8)) + +=== twoStarsCollide.ts === + +// 'shared' and 'SharedType' come from both modules with different meanings. +export * from "./a"; +export * from "./b"; + +=== threeStarsCollide.ts === + +// Both later declarations are reported against the first one. +export * from "./a"; +export * from "./b"; +export * from "./b2"; + +=== sameSymbolTwice.ts === + +// Reaching the same symbol through two declarations is not a collision. +export * from "./a"; +export * from "./sameSymbol"; + +=== localShadows.ts === +// A module's own export takes precedence and silences the collision for shared, but not for SharedType. +export * from "./a"; +export * from "./b"; +export const shared = "local"; +>shared : Symbol(shared, Decl(localShadows.ts, 3, 12)) + +=== singleStar.ts === + +export * from "./a"; + +=== typeOnlyThenValue.ts === + +export type * from "./a"; +export * from "./b"; + +=== valueThenTypeOnly.ts === + +export * from "./a"; +export type * from "./b"; + +=== chained.ts === + +export * from "./twoStarsCollide"; +export * from "./singleStar"; + +=== consumer.ts === +import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; +>onlyA : Symbol(onlyA, Decl(consumer.ts, 0, 8)) +>onlyB : Symbol(onlyB, Decl(consumer.ts, 0, 15)) +>shared : Symbol(s1, Decl(a.ts, 1, 12)) +>s1 : Symbol(s1, Decl(consumer.ts, 0, 22)) + +import { shared as s2 } from "./sameSymbolTwice"; +>shared : Symbol(s1, Decl(a.ts, 1, 12)) +>s2 : Symbol(s2, Decl(consumer.ts, 1, 8)) + +import { shared as s3 } from "./localShadows"; +>shared : Symbol(s3, Decl(localShadows.ts, 3, 12)) +>s3 : Symbol(s3, Decl(consumer.ts, 2, 8)) + +import { shared as s4, onlyA as a4 } from "./singleStar"; +>shared : Symbol(s1, Decl(a.ts, 1, 12)) +>s4 : Symbol(s4, Decl(consumer.ts, 3, 8)) +>onlyA : Symbol(onlyA, Decl(a.ts, 0, 12)) +>a4 : Symbol(a4, Decl(consumer.ts, 3, 22)) + +import { onlyA as a5, onlyB as b5 } from "./typeOnlyThenValue"; +>onlyA : Symbol(onlyA, Decl(a.ts, 0, 12)) +>a5 : Symbol(a5, Decl(consumer.ts, 4, 8)) +>onlyB : Symbol(onlyB, Decl(b.ts, 0, 12)) +>b5 : Symbol(b5, Decl(consumer.ts, 4, 21)) + +import { onlyA as a6, onlyB as b6 } from "./valueThenTypeOnly"; +>onlyA : Symbol(onlyA, Decl(a.ts, 0, 12)) +>a6 : Symbol(a6, Decl(consumer.ts, 5, 8)) +>onlyB : Symbol(onlyB, Decl(b.ts, 0, 12)) +>b6 : Symbol(b6, Decl(consumer.ts, 5, 21)) + +import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; +>onlyA : Symbol(onlyA, Decl(a.ts, 0, 12)) +>a7 : Symbol(a7, Decl(consumer.ts, 6, 8)) +>onlyB : Symbol(onlyB, Decl(b.ts, 0, 12)) +>b7 : Symbol(b7, Decl(consumer.ts, 6, 21)) +>shared : Symbol(s1, Decl(a.ts, 1, 12)) +>s7 : Symbol(s7, Decl(consumer.ts, 6, 34)) + +import type { SharedType } from "./twoStarsCollide"; +>SharedType : Symbol(SharedType, Decl(consumer.ts, 7, 13)) + +export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; +>values : Symbol(values, Decl(consumer.ts, 8, 12)) +>onlyA : Symbol(onlyA, Decl(consumer.ts, 0, 8)) +>onlyB : Symbol(onlyB, Decl(consumer.ts, 0, 15)) +>s1 : Symbol(s1, Decl(consumer.ts, 0, 22)) +>s2 : Symbol(s2, Decl(consumer.ts, 1, 8)) +>s3 : Symbol(s3, Decl(consumer.ts, 2, 8)) +>s4 : Symbol(s4, Decl(consumer.ts, 3, 8)) +>a4 : Symbol(a4, Decl(consumer.ts, 3, 22)) +>a5 : Symbol(a5, Decl(consumer.ts, 4, 8)) +>b5 : Symbol(b5, Decl(consumer.ts, 4, 21)) +>a6 : Symbol(a6, Decl(consumer.ts, 5, 8)) +>b6 : Symbol(b6, Decl(consumer.ts, 5, 21)) +>a7 : Symbol(a7, Decl(consumer.ts, 6, 8)) +>b7 : Symbol(b7, Decl(consumer.ts, 6, 21)) +>s7 : Symbol(s7, Decl(consumer.ts, 6, 34)) + +export let t: SharedType | undefined; +>t : Symbol(t, Decl(consumer.ts, 9, 10)) +>SharedType : Symbol(SharedType, Decl(consumer.ts, 7, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.types b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.types new file mode 100644 index 0000000000000..e064588c887a1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarNameCollisions.types @@ -0,0 +1,152 @@ +//// [tests/cases/compiler/exportStarNameCollisions.ts] //// + +=== a.ts === +export const onlyA = 1; +>onlyA : 1 +>1 : 1 + +export const shared = "a"; +>shared : "a" +>"a" : "a" + +export interface SharedType { fromA: true } +>fromA : true +>true : true + +export default "a"; + +=== b.ts === +export const onlyB = 2; +>onlyB : 2 +>2 : 2 + +export const shared = "b"; +>shared : "b" +>"b" : "b" + +export interface SharedType { fromB: true } +>fromB : true +>true : true + +export default "b"; + +=== b2.ts === +export const shared = "b2"; +>shared : "b2" +>"b2" : "b2" + +=== sameSymbol.ts === +export { shared } from "./a"; +>shared : "a" + +=== twoStarsCollide.ts === + +// 'shared' and 'SharedType' come from both modules with different meanings. +export * from "./a"; +export * from "./b"; + +=== threeStarsCollide.ts === + +// Both later declarations are reported against the first one. +export * from "./a"; +export * from "./b"; +export * from "./b2"; + +=== sameSymbolTwice.ts === + +// Reaching the same symbol through two declarations is not a collision. +export * from "./a"; +export * from "./sameSymbol"; + +=== localShadows.ts === +// A module's own export takes precedence and silences the collision for shared, but not for SharedType. +export * from "./a"; +export * from "./b"; +export const shared = "local"; +>shared : "local" +>"local" : "local" + +=== singleStar.ts === + +export * from "./a"; + +=== typeOnlyThenValue.ts === + +export type * from "./a"; +export * from "./b"; + +=== valueThenTypeOnly.ts === + +export * from "./a"; +export type * from "./b"; + +=== chained.ts === + +export * from "./twoStarsCollide"; +export * from "./singleStar"; + +=== consumer.ts === +import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; +>onlyA : 1 +>onlyB : 2 +>shared : "a" +>s1 : "a" + +import { shared as s2 } from "./sameSymbolTwice"; +>shared : "a" +>s2 : "a" + +import { shared as s3 } from "./localShadows"; +>shared : "local" +>s3 : "local" + +import { shared as s4, onlyA as a4 } from "./singleStar"; +>shared : "a" +>s4 : "a" +>onlyA : 1 +>a4 : 1 + +import { onlyA as a5, onlyB as b5 } from "./typeOnlyThenValue"; +>onlyA : 1 +>a5 : 1 +>onlyB : 2 +>b5 : 2 + +import { onlyA as a6, onlyB as b6 } from "./valueThenTypeOnly"; +>onlyA : 1 +>a6 : 1 +>onlyB : 2 +>b6 : 2 + +import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; +>onlyA : 1 +>a7 : 1 +>onlyB : 2 +>b7 : 2 +>shared : "a" +>s7 : "a" + +import type { SharedType } from "./twoStarsCollide"; +>SharedType : SharedType + +export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; +>values : (string | number)[] +>[onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7] : (string | number)[] +>onlyA : 1 +>onlyB : 2 +>s1 : "a" +>s2 : "a" +>s3 : "local" +>s4 : "a" +>a4 : 1 +>a5 : 1 +>b5 : 2 +>a6 : 1 +>b6 : 2 +>a7 : 1 +>b7 : 2 +>s7 : "a" + +export let t: SharedType | undefined; +>t : SharedType | undefined + diff --git a/tsc/testdata/tests/cases/compiler/exportStarNameCollisions.ts b/tsc/testdata/tests/cases/compiler/exportStarNameCollisions.ts new file mode 100644 index 0000000000000..b6a2c815863bc --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/exportStarNameCollisions.ts @@ -0,0 +1,70 @@ +// @module: esnext +// @target: es2015 +// @declaration: true + +// @filename: a.ts +export const onlyA = 1; +export const shared = "a"; +export interface SharedType { fromA: true } +export default "a"; + +// @filename: b.ts +export const onlyB = 2; +export const shared = "b"; +export interface SharedType { fromB: true } +export default "b"; + +// @filename: b2.ts +export const shared = "b2"; + +// @filename: sameSymbol.ts +export { shared } from "./a"; + +// @filename: twoStarsCollide.ts +// 'shared' and 'SharedType' come from both modules with different meanings. +export * from "./a"; +export * from "./b"; + +// @filename: threeStarsCollide.ts +// Both later declarations are reported against the first one. +export * from "./a"; +export * from "./b"; +export * from "./b2"; + +// @filename: sameSymbolTwice.ts +// Reaching the same symbol through two declarations is not a collision. +export * from "./a"; +export * from "./sameSymbol"; + +// @filename: localShadows.ts +// A module's own export takes precedence and silences the collision for shared, but not for SharedType. +export * from "./a"; +export * from "./b"; +export const shared = "local"; + +// @filename: singleStar.ts +export * from "./a"; + +// @filename: typeOnlyThenValue.ts +export type * from "./a"; +export * from "./b"; + +// @filename: valueThenTypeOnly.ts +export * from "./a"; +export type * from "./b"; + +// @filename: chained.ts +export * from "./twoStarsCollide"; +export * from "./singleStar"; + +// @filename: consumer.ts +import { onlyA, onlyB, shared as s1 } from "./twoStarsCollide"; +import { shared as s2 } from "./sameSymbolTwice"; +import { shared as s3 } from "./localShadows"; +import { shared as s4, onlyA as a4 } from "./singleStar"; +import { onlyA as a5, onlyB as b5 } from "./typeOnlyThenValue"; +import { onlyA as a6, onlyB as b6 } from "./valueThenTypeOnly"; +import { onlyA as a7, onlyB as b7, shared as s7 } from "./chained"; +import type { SharedType } from "./twoStarsCollide"; +export const values = [onlyA, onlyB, s1, s2, s3, s4, a4, a5, b5, a6, b6, a7, b7, s7]; +export let t: SharedType | undefined;