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) { 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;