From d433c6ed05f12883663a049837dfd2b065487b08 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 17 Aug 2018 16:30:01 -0700 Subject: [PATCH] Track late bound names in binding patterns (#26336) --- src/compiler/checker.ts | 24 +++++++---- ...EmitComputedNameCausesImportToBePainted.js | 43 +++++++++++++++++++ ...omputedNameCausesImportToBePainted.symbols | 33 ++++++++++++++ ...tComputedNameCausesImportToBePainted.types | 33 ++++++++++++++ ...EmitComputedNameCausesImportToBePainted.ts | 15 +++++++ 5 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.js create mode 100644 tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.symbols create mode 100644 tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.types create mode 100644 tests/cases/compiler/declarationEmitComputedNameCausesImportToBePainted.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9bcd6708b22..26b93b19335 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3552,15 +3552,10 @@ namespace ts { anyType : getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; - if (getCheckFlags(propertySymbol) & CheckFlags.Late) { + if (context.tracker.trackSymbol && getCheckFlags(propertySymbol) & CheckFlags.Late) { const decl = first(propertySymbol.declarations); - if (context.tracker.trackSymbol && hasLateBindableName(decl)) { - // get symbol of the first identifier of the entityName - const firstIdentifier = getFirstIdentifier(decl.name.expression); - const name = resolveName(firstIdentifier, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); - if (name) { - context.tracker.trackSymbol(name, saveEnclosingDeclaration, SymbolFlags.Value); - } + if (hasLateBindableName(decl)) { + trackComputedName(decl.name, saveEnclosingDeclaration, context); } } const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true); @@ -3770,6 +3765,9 @@ namespace ts { function cloneBindingName(node: BindingName): BindingName { return elideInitializerAndSetEmitFlags(node); function elideInitializerAndSetEmitFlags(node: Node): Node { + if (context.tracker.trackSymbol && isComputedPropertyName(node) && isLateBindableName(node)) { + trackComputedName(node, context.enclosingDeclaration, context); + } const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags)!; const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); if (clone.kind === SyntaxKind.BindingElement) { @@ -3780,6 +3778,16 @@ namespace ts { } } + function trackComputedName(node: LateBoundName, enclosingDeclaration: Node | undefined, context: NodeBuilderContext) { + if (!context.tracker.trackSymbol) return; + // get symbol of the first identifier of the entityName + const firstIdentifier = getFirstIdentifier(node.expression); + const name = resolveName(firstIdentifier, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); + if (name) { + context.tracker.trackSymbol(name, enclosingDeclaration, SymbolFlags.Value); + } + } + function lookupSymbolChain(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, yieldModuleSymbol?: boolean) { context.tracker.trackSymbol!(symbol, context.enclosingDeclaration, meaning); // TODO: GH#18217 // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. diff --git a/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.js b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.js new file mode 100644 index 00000000000..ee97c6b2182 --- /dev/null +++ b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.js @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/declarationEmitComputedNameCausesImportToBePainted.ts] //// + +//// [context.ts] +export const Key = Symbol(); +export interface Context { + [Key]: string; +} +//// [index.ts] +import { Key, Context } from "./context"; + +export const context: Context = { + [Key]: 'bar', +} + +export const withContext = ({ [Key]: value }: Context) => value; + +//// [context.js] +"use strict"; +exports.__esModule = true; +exports.Key = Symbol(); +//// [index.js] +"use strict"; +exports.__esModule = true; +var _a; +var context_1 = require("./context"); +exports.context = (_a = {}, + _a[context_1.Key] = 'bar', + _a); +exports.withContext = function (_a) { + var _b = context_1.Key, value = _a[_b]; + return value; +}; + + +//// [context.d.ts] +export declare const Key: unique symbol; +export interface Context { + [Key]: string; +} +//// [index.d.ts] +import { Key, Context } from "./context"; +export declare const context: Context; +export declare const withContext: ({ [Key]: value }: Context) => string; diff --git a/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.symbols b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.symbols new file mode 100644 index 00000000000..aee133a4fee --- /dev/null +++ b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/context.ts === +export const Key = Symbol(); +>Key : Symbol(Key, Decl(context.ts, 0, 12)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +export interface Context { +>Context : Symbol(Context, Decl(context.ts, 0, 28)) + + [Key]: string; +>[Key] : Symbol(Context[Key], Decl(context.ts, 1, 26)) +>Key : Symbol(Key, Decl(context.ts, 0, 12)) +} +=== tests/cases/compiler/index.ts === +import { Key, Context } from "./context"; +>Key : Symbol(Key, Decl(index.ts, 0, 8)) +>Context : Symbol(Context, Decl(index.ts, 0, 13)) + +export const context: Context = { +>context : Symbol(context, Decl(index.ts, 2, 12)) +>Context : Symbol(Context, Decl(index.ts, 0, 13)) + + [Key]: 'bar', +>[Key] : Symbol([Key], Decl(index.ts, 2, 33)) +>Key : Symbol(Key, Decl(index.ts, 0, 8)) +} + +export const withContext = ({ [Key]: value }: Context) => value; +>withContext : Symbol(withContext, Decl(index.ts, 6, 12)) +>Key : Symbol(Key, Decl(index.ts, 0, 8)) +>value : Symbol(value, Decl(index.ts, 6, 29)) +>Context : Symbol(Context, Decl(index.ts, 0, 13)) +>value : Symbol(value, Decl(index.ts, 6, 29)) + diff --git a/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.types b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.types new file mode 100644 index 00000000000..8198bf9c791 --- /dev/null +++ b/tests/baselines/reference/declarationEmitComputedNameCausesImportToBePainted.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/context.ts === +export const Key = Symbol(); +>Key : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +export interface Context { + [Key]: string; +>[Key] : string +>Key : unique symbol +} +=== tests/cases/compiler/index.ts === +import { Key, Context } from "./context"; +>Key : unique symbol +>Context : any + +export const context: Context = { +>context : Context +>{ [Key]: 'bar',} : { [Key]: string; } + + [Key]: 'bar', +>[Key] : string +>Key : unique symbol +>'bar' : "bar" +} + +export const withContext = ({ [Key]: value }: Context) => value; +>withContext : ({ [Key]: value }: Context) => string +>({ [Key]: value }: Context) => value : ({ [Key]: value }: Context) => string +>Key : unique symbol +>value : string +>value : string + diff --git a/tests/cases/compiler/declarationEmitComputedNameCausesImportToBePainted.ts b/tests/cases/compiler/declarationEmitComputedNameCausesImportToBePainted.ts new file mode 100644 index 00000000000..c5ffd0c3a74 --- /dev/null +++ b/tests/cases/compiler/declarationEmitComputedNameCausesImportToBePainted.ts @@ -0,0 +1,15 @@ +// @declaration: true +// @lib: es6 +// @filename: context.ts +export const Key = Symbol(); +export interface Context { + [Key]: string; +} +// @filename: index.ts +import { Key, Context } from "./context"; + +export const context: Context = { + [Key]: 'bar', +} + +export const withContext = ({ [Key]: value }: Context) => value; \ No newline at end of file