Skip to content

Dont allow namespace reexport symbols when looking up valid local names #43969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3935,12 +3935,12 @@ namespace ts {
return typeCopy;
}

function forEachSymbolTableInScope<T>(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable) => T): T {
function forEachSymbolTableInScope<T>(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean) => T): T {
let result: T;
for (let location = enclosingDeclaration; location; location = location.parent) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (result = callback(location.locals)) {
if (result = callback(location.locals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true)) {
return result;
}
}
Expand All @@ -3955,7 +3955,7 @@ namespace ts {
// `sym` may not have exports if this module declaration is backed by the symbol for a `const` that's being rewritten
// into a namespace - in such cases, it's best to just let the namespace appear empty (the const members couldn't have referred
// to one another anyway)
if (result = callback(sym?.exports || emptySymbols)) {
if (result = callback(sym?.exports || emptySymbols, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true)) {
return result;
}
break;
Expand Down Expand Up @@ -3983,7 +3983,7 @@ namespace ts {
}
}

return callback(globals);
return callback(globals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true);
}

function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
Expand All @@ -4006,12 +4006,12 @@ namespace ts {
/**
* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already)
*/
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean): Symbol[] | undefined {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean): Symbol[] | undefined {
if (!pushIfUnique(visitedSymbolTables!, symbols)) {
return undefined;
}

const result = trySymbolTable(symbols, ignoreQualification);
const result = trySymbolTable(symbols, ignoreQualification, isLocalNameLookup);
visitedSymbolTables!.pop();
return result;
}
Expand All @@ -4032,7 +4032,7 @@ namespace ts {
(ignoreQualification || canQualifySymbol(getMergedSymbol(symbolFromSymbolTable), meaning));
}

function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined): Symbol[] | undefined {
function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined, isLocalNameLookup: boolean | undefined): Symbol[] | undefined {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol!.escapedName)!, /*resolvedAliasSymbol*/ undefined, ignoreQualification)) {
return [symbol!];
Expand All @@ -4046,6 +4046,8 @@ namespace ts {
&& !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration)))
// If `!useOnlyExternalAliasing`, we can use any type of alias to get the name
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))
// If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it
&& (isLocalNameLookup ? !some(symbolFromSymbolTable.declarations, isNamespaceReexportDeclaration) : true)
// While exports are generally considered to be in scope, export-specifier declared symbols are _not_
// See similar comment in `resolveName` for details
&& (ignoreQualification || !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier))
Expand Down Expand Up @@ -4160,7 +4162,7 @@ namespace ts {
return hasAccessibleDeclarations;
}
}
else if (allowModules) {
if (allowModules) {
if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) {
if (shouldComputeAliasesToMakeVisible) {
earlyModuleBail = true;
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,10 @@ namespace ts {
return node.kind === SyntaxKind.TypeQuery;
}

export function isNamespaceReexportDeclaration(node: Node): boolean {
return isNamespaceExport(node) && !!node.parent.moduleSpecifier;
}

export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } {
return node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/compiler/declarationEmitDoesNotUseReexportedNamespaceAsLocal.ts] ////

//// [sub.ts]
export function a() {}
//// [index.ts]
export const x = add(import("./sub"));
export * as Q from "./sub";
declare function add<T>(x: Promise<T>): T;

//// [sub.js]
export function a() { }
//// [index.js]
export const x = add(import("./sub"));
export * as Q from "./sub";


//// [sub.d.ts]
export declare function a(): void;
//// [index.d.ts]
export declare const x: typeof import("./sub");
export * as Q from "./sub";
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== tests/cases/compiler/sub.ts ===
export function a() {}
>a : Symbol(a, Decl(sub.ts, 0, 0))

=== tests/cases/compiler/index.ts ===
export const x = add(import("./sub"));
>x : Symbol(x, Decl(index.ts, 0, 12))
>add : Symbol(add, Decl(index.ts, 1, 27))
>"./sub" : Symbol("tests/cases/compiler/sub", Decl(sub.ts, 0, 0))

export * as Q from "./sub";
>Q : Symbol(Q, Decl(index.ts, 1, 6))

declare function add<T>(x: Promise<T>): T;
>add : Symbol(add, Decl(index.ts, 1, 27))
>T : Symbol(T, Decl(index.ts, 2, 21))
>x : Symbol(x, Decl(index.ts, 2, 24))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>T : Symbol(T, Decl(index.ts, 2, 21))
>T : Symbol(T, Decl(index.ts, 2, 21))

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
=== tests/cases/compiler/sub.ts ===
export function a() {}
>a : () => void

=== tests/cases/compiler/index.ts ===
export const x = add(import("./sub"));
>x : typeof import("tests/cases/compiler/sub")
>add(import("./sub")) : typeof import("tests/cases/compiler/sub")
>add : <T>(x: Promise<T>) => T
>import("./sub") : Promise<typeof import("tests/cases/compiler/sub")>
>"./sub" : "./sub"

export * as Q from "./sub";
>Q : typeof import("tests/cases/compiler/sub")

declare function add<T>(x: Promise<T>): T;
>add : <T>(x: Promise<T>) => T
>x : Promise<T>

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const b = 2;

=== tests/cases/conformance/es2020/modules/1.ts ===
export * as ns from './0';
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/es2020/modules/0")

ns.a;
>ns.a : any
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/exportNamespace2.types
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A {}

=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export * as a from './a';
>a : typeof a
>a : typeof import("tests/cases/conformance/externalModules/typeOnly/a")

=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
import type { a } from './b';
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/exportNamespace3.types
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type { A } from './a';

=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export * as a from './b';
>a : typeof a
>a : typeof import("tests/cases/conformance/externalModules/typeOnly/b")

=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { a } from './c';
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/exportNamespace4.types
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type * from './a'; // Grammar error
No type information for this code.
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export type * as ns from './a'; // Grammar error
>ns : typeof ns
>ns : typeof import("tests/cases/conformance/externalModules/typeOnly/a")

=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { A } from './b';
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/exportStarNotElided.types
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ register("ok");
export * from "./register";
export * from "./data1";
export * as aliased from "./data1";
>aliased : typeof aliased
>aliased : typeof import("tests/cases/compiler/data1")

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class A { }

=== tests/cases/compiler/b.ts ===
export * as a from "./a";
>a : typeof a
>a : typeof import("tests/cases/compiler/a")

=== tests/cases/compiler/tslib.d.ts ===
declare module "tslib" {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @target: esnext
// @module: esnext
// @declaration: true
// @filename: sub.ts
export function a() {}
// @filename: index.ts
export const x = add(import("./sub"));
export * as Q from "./sub";
declare function add<T>(x: Promise<T>): T;