Skip to content

Revert "Revert "Support declaration emit for late bound element acces… #37035

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

Closed
Closed
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
33 changes: 28 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3069,9 +3069,20 @@ namespace ts {
function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined;
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
if (moduleSymbol) {
const links = getSymbolLinks(moduleSymbol);
if (!dontResolveAlias) {
links.resolvedExternalModuleSymbol = "circular";
}
const exportEquals = resolveSymbol(moduleSymbol.exports!.get(InternalSymbolName.ExportEquals), dontResolveAlias);
const exported = getCommonJsExportEquals(getMergedSymbol(exportEquals), getMergedSymbol(moduleSymbol));
return getMergedSymbol(exported) || moduleSymbol;
const result = getMergedSymbol(exported) || moduleSymbol;
if (!dontResolveAlias) {
// we "cache" this result, but because both the input _and_ the output are mergeable, literally every call could need
// a different result, assuming the inputs and outputs get merged with. So instead we just use this as a flag that
// export assignment resolution has occured on this symbol at least once. (because it might happen many times!)
links.resolvedExternalModuleSymbol = result;
}
return result;
}
return undefined!;
}
Expand Down Expand Up @@ -5543,7 +5554,7 @@ namespace ts {
function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean) {
const symbolName = unescapeLeadingUnderscores(symbol.escapedName);
const isDefault = symbol.escapedName === InternalSymbolName.Default;
if (isStringANonContextualKeyword(symbolName) && !isDefault) {
if ((isStringANonContextualKeyword(symbolName) || !isIdentifierText(symbolName, languageVersion)) && !isDefault && symbol.escapedName !== InternalSymbolName.ExportEquals) {
// Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :(
context.encounteredError = true;
// TODO: Issue error via symbol tracker?
Expand Down Expand Up @@ -5694,7 +5705,8 @@ namespace ts {
}

function getNamespaceMembersForSerialization(symbol: Symbol) {
return !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
const exports = getExportsOfSymbol(symbol);
return !exports ? [] : filter(arrayFrom((exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
}

function isTypeOnlyNamespace(symbol: Symbol) {
Expand Down Expand Up @@ -8896,6 +8908,17 @@ namespace ts {
function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind): UnderscoreEscapedMap<Symbol> {
const links = getSymbolLinks(symbol);
if (!links[resolutionKind]) {
// We _must_ resolve any possible commonjs export assignments _before_ merging in late bound members, as cjs export assignments may cause
// things to be merged (?!?) into this symbol; moreover, we _can't_ resolve those export assignments if we're already resolving the containing
// module (as then we'll issue a circularity error)
const p = symbol.valueDeclaration?.parent;
if (p && isSourceFile(p) && p.symbol && !getSymbolLinks(p.symbol).resolvedExternalModuleSymbol) {
const exported = resolveExternalModuleSymbol(p.symbol);
const targetLinks = exported && getSymbolLinks(exported);
if (targetLinks && targetLinks[resolutionKind]) {
return links[resolutionKind] = targetLinks[resolutionKind]!;
}
}
const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports;
const earlySymbols = !isStatic ? symbol.members :
symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol) :
Expand Down Expand Up @@ -35262,8 +35285,8 @@ namespace ts {
}
}

function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) {
const declaration = getParseTreeNode(declarationIn, isVariableLikeOrAccessor);
function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) {
const declaration = getParseTreeNode(declarationIn, isPossiblyPropertyLikeDeclaration);
if (!declaration) {
return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode;
}
Expand Down
13 changes: 10 additions & 3 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,13 +1157,20 @@ namespace ts {
fakespace.locals = createSymbolTable(props);
fakespace.symbol = props[0].parent!;
const declarations = mapDefined(props, p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined; // TODO GH#33569: Handle element access expressions that created late bound names (rather than silently omitting them)
if (!isPropertyAccessExpression(p.valueDeclaration) && !isElementAccessExpression(p.valueDeclaration) && !isBinaryExpression(p.valueDeclaration)) {
return undefined;
}
if (hasDynamicName(p.valueDeclaration) && !resolver.isLateBound(getParseTreeNode(p.valueDeclaration) as Declaration)) {
return undefined;
}
const name = unescapeLeadingUnderscores(p.escapedName);
if (!isIdentifierText(name, ScriptTarget.ES3)) {
return undefined; // TODO: Rather than quietly eliding (as is current behavior), maybe we should issue errors?
}
getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.valueDeclaration);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, fakespace, declarationEmitNodeBuilderFlags, symbolTracker);
getSymbolAccessibilityDiagnostic = oldDiag;
const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined);
const varDecl = createVariableDeclaration(name, type, /*initializer*/ undefined);
return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl]));
});
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
Expand Down
12 changes: 8 additions & 4 deletions src/compiler/transformers/declarations/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ namespace ts {
| TypeAliasDeclaration
| ConstructorDeclaration
| IndexSignatureDeclaration
| PropertyAccessExpression;
| PropertyAccessExpression
| ElementAccessExpression
| BinaryExpression;

export function canProduceDiagnostics(node: Node): node is DeclarationDiagnosticProducing {
return isVariableDeclaration(node) ||
Expand All @@ -48,7 +50,9 @@ namespace ts {
isTypeAliasDeclaration(node) ||
isConstructorDeclaration(node) ||
isIndexSignatureDeclaration(node) ||
isPropertyAccessExpression(node);
isPropertyAccessExpression(node) ||
isElementAccessExpression(node) ||
isBinaryExpression(node);
}

export function createGetSymbolAccessibilityDiagnosticForNodeName(node: DeclarationDiagnosticProducing) {
Expand Down Expand Up @@ -125,7 +129,7 @@ namespace ts {
}

export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic | undefined {
if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) {
if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node) || isBinaryExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) {
return getVariableDeclarationTypeVisibilityError;
}
else if (isSetAccessor(node) || isGetAccessor(node)) {
Expand Down Expand Up @@ -166,7 +170,7 @@ namespace ts {
}
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
// The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all.
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.PropertySignature ||
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression || node.kind === SyntaxKind.BinaryExpression || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && hasModifier(node.parent, ModifierFlags.Private))) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (hasModifier(node, ModifierFlags.Static)) {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3935,7 +3935,7 @@ namespace ts {
isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean;
isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean;
getPropertiesOfContainerFunction(node: Declaration): Symbol[];
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression;
Expand Down Expand Up @@ -4109,6 +4109,7 @@ namespace ts {
deferralParent?: Type; // Source union/intersection of a deferred type
cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target
typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
resolvedExternalModuleSymbol?: Symbol | "circular"; // Cached result of `resolveExternalModuleSymbol`
}

/* @internal */
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,10 @@ namespace ts {
return isVariableLike(node) || isAccessor(node);
}

export function isPossiblyPropertyLikeDeclaration(node: Node): node is AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression {
return isVariableLikeOrAccessor(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node) || isBinaryExpression(node);
}

export function isVariableDeclarationInVariableStatement(node: VariableDeclaration) {
return node.parent.kind === SyntaxKind.VariableDeclarationList
&& node.parent.parent.kind === SyntaxKind.VariableStatement;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [lateBoundElementAccessAssignmentDeclarations.ts]
export function foo() {}
foo.bar = 12;
const _private = Symbol();
foo[_private] = "ok";
const strMem = "strMemName";
foo[strMem] = "ok";
const dashStrMem = "dashed-str-mem";
foo[dashStrMem] = "ok";
const numMem = 42;
foo[numMem] = "ok";

const x: string = foo[_private];
const y: string = foo[strMem];
const z: string = foo[numMem];
const a: string = foo[dashStrMem];

//// [lateBoundElementAccessAssignmentDeclarations.js]
export function foo() { }
foo.bar = 12;
const _private = Symbol();
foo[_private] = "ok";
const strMem = "strMemName";
foo[strMem] = "ok";
const dashStrMem = "dashed-str-mem";
foo[dashStrMem] = "ok";
const numMem = 42;
foo[numMem] = "ok";
const x = foo[_private];
const y = foo[strMem];
const z = foo[numMem];
const a = foo[dashStrMem];


//// [lateBoundElementAccessAssignmentDeclarations.d.ts]
export declare function foo(): void;
export declare namespace foo {
var bar: number;
var strMemName: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
=== tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts ===
export function foo() {}
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)

foo.bar = 12;
>foo.bar : Symbol(foo.bar, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24))
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>bar : Symbol(foo.bar, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24))

const _private = Symbol();
>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

foo[_private] = "ok";
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5))

const strMem = "strMemName";
>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5))

foo[strMem] = "ok";
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5))

const dashStrMem = "dashed-str-mem";
>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5))

foo[dashStrMem] = "ok";
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5))

const numMem = 42;
>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5))

foo[numMem] = "ok";
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5))

const x: string = foo[_private];
>x : Symbol(x, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 11, 5))
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5))

const y: string = foo[strMem];
>y : Symbol(y, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 12, 5))
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5))

const z: string = foo[numMem];
>z : Symbol(z, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 13, 5))
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5))

const a: string = foo[dashStrMem];
>a : Symbol(a, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 14, 5))
>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more)
>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5))

Loading