From 83f5d3f25ccfd184164ec1572f2b4394789b3c3b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 21 Oct 2016 18:44:13 -0700 Subject: [PATCH 01/30] Move emit helpers into related transformers --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 23 +- src/compiler/emitter.ts | 290 ++-------- src/compiler/factory.ts | 617 +++++++++++---------- src/compiler/transformers/es2015.ts | 39 +- src/compiler/transformers/es2017.ts | 137 +++-- src/compiler/transformers/generators.ts | 159 +++++- src/compiler/transformers/jsx.ts | 553 +++++++++--------- src/compiler/transformers/module/es2015.ts | 22 +- src/compiler/transformers/module/module.ts | 16 +- src/compiler/transformers/module/system.ts | 22 +- src/compiler/transformers/ts.ts | 158 ++++-- src/compiler/types.ts | 26 +- src/compiler/utilities.ts | 147 ----- 14 files changed, 1104 insertions(+), 1107 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4fa14658494..e6617c88eb3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19034,7 +19034,7 @@ namespace ts { function isNameOfModuleOrEnumDeclaration(node: Identifier) { const parent = node.parent; - return isModuleOrEnumDeclaration(parent) && node === parent.name; + return parent && isModuleOrEnumDeclaration(parent) && node === parent.name; } // When resolved as an expression identifier, if the given node references an exported entity, return the declaration diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 13813182d32..ac881200837 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -538,7 +538,7 @@ namespace ts { */ export function append(to: T[] | undefined, value: T | undefined): T[] | undefined { if (value === undefined) return to; - if (to === undefined) to = []; + if (to === undefined) return [value]; to.push(value); return to; } @@ -559,6 +559,16 @@ namespace ts { return to; } + /** + * Stable sort of an array. Elements equal to each other maintain their relative position in the array. + */ + export function stableSort(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) { + return array + .map((_, i) => i) // create array of indices + .sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position + .map(i => array[i]); // get sorted array + } + export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) { while (pos < end) { if (array1[pos] !== array2[pos]) { @@ -2022,6 +2032,17 @@ namespace ts { } /** Remove an item from an array, moving everything to its right one space left. */ + export function orderedRemoveItem(array: T[], item: T): boolean { + for (let i = 0; i < array.length; i++) { + if (array[i] === item) { + orderedRemoveItemAt(array, i); + return true; + } + } + return false; + } + + /** Remove an item by index from an array, moving everything to its right one space left. */ export function orderedRemoveItemAt(array: T[], index: number): void { // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. for (let i = index; i < array.length - 1; i++) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1051c9adca9..ed16ec06159 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -21,153 +21,6 @@ namespace ts { const delimiters = createDelimiterMap(); const brackets = createBracketsMap(); - // emit output for the __extends helper function - const extendsHelper = ` -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -};`; - - // Emit output for the __assign helper function. - // This is typically used for JSX spread attributes, - // and can be used for object literal spread properties. - const assignHelper = ` -var __assign = (this && this.__assign) || Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; -};`; - - // emit output for the __decorate helper function - const decorateHelper = ` -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -};`; - - // emit output for the __metadata helper function - const metadataHelper = ` -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -};`; - - // emit output for the __param helper function - const paramHelper = ` -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -};`; - - // emit output for the __awaiter helper function - const awaiterHelper = ` -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments)).next()); - }); -};`; - - // The __generator helper is used by down-level transformations to emulate the runtime - // semantics of an ES2015 generator function. When called, this helper returns an - // object that implements the Iterator protocol, in that it has `next`, `return`, and - // `throw` methods that step through the generator when invoked. - // - // parameters: - // thisArg The value to use as the `this` binding for the transformed generator body. - // body A function that acts as the transformed generator body. - // - // variables: - // _ Persistent state for the generator that is shared between the helper and the - // generator body. The state object has the following members: - // sent() - A method that returns or throws the current completion value. - // label - The next point at which to resume evaluation of the generator body. - // trys - A stack of protected regions (try/catch/finally blocks). - // ops - A stack of pending instructions when inside of a finally block. - // f A value indicating whether the generator is executing. - // y An iterator to delegate for a yield*. - // t A temporary variable that holds one of the following values (note that these - // cases do not overlap): - // - The completion value when resuming from a `yield` or `yield*`. - // - The error value for a catch block. - // - The current protected region (array of try/catch/finally/end labels). - // - The verb (`next`, `throw`, or `return` method) to delegate to the expression - // of a `yield*`. - // - The result of evaluating the verb delegated to the expression of a `yield*`. - // - // functions: - // verb(n) Creates a bound callback to the `step` function for opcode `n`. - // step(op) Evaluates opcodes in a generator body until execution is suspended or - // completed. - // - // The __generator helper understands a limited set of instructions: - // 0: next(value?) - Start or resume the generator with the specified value. - // 1: throw(error) - Resume the generator with an exception. If the generator is - // suspended inside of one or more protected regions, evaluates - // any intervening finally blocks between the current label and - // the nearest catch block or function boundary. If uncaught, the - // exception is thrown to the caller. - // 2: return(value?) - Resume the generator as if with a return. If the generator is - // suspended inside of one or more protected regions, evaluates any - // intervening finally blocks. - // 3: break(label) - Jump to the specified label. If the label is outside of the - // current protected region, evaluates any intervening finally - // blocks. - // 4: yield(value?) - Yield execution to the caller with an optional value. When - // resumed, the generator will continue at the next label. - // 5: yield*(value) - Delegates evaluation to the supplied iterator. When - // delegation completes, the generator will continue at the next - // label. - // 6: catch(error) - Handles an exception thrown from within the generator body. If - // the current label is inside of one or more protected regions, - // evaluates any intervening finally blocks between the current - // label and the nearest catch block or function boundary. If - // uncaught, the exception is thrown to the caller. - // 7: endfinally - Ends a finally block, resuming the last instruction prior to - // entering a finally block. - // - // For examples of how these are used, see the comments in ./transformers/generators.ts - const generatorHelper = ` -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; - return { next: verb(0), "throw": verb(1), "return": verb(2) }; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [0, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -};`; - - // emit output for the __export helper function - const exportStarHelper = ` -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -}`; - // emit output for the UMD helper function. const umdHelper = ` (function (dependencies, factory) { @@ -179,15 +32,6 @@ function __export(m) { } })`; - const superHelper = ` -const _super = name => super[name];`; - - const advancedSuperHelper = ` -const _super = (function (geti, seti) { - const cache = Object.create(null); - return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); -})(name => super[name], (name, value) => super[name] = value);`; - const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -224,11 +68,7 @@ const _super = (function (geti, seti) { let currentSourceFile: SourceFile; let currentText: string; let currentFileIdentifiers: Map; - let extendsEmitted: boolean; - let assignEmitted: boolean; - let decorateEmitted: boolean; - let paramEmitted: boolean; - let awaiterEmitted: boolean; + let bundledHelpers: Map; let isOwnFileEmit: boolean; let emitSkipped = false; @@ -293,12 +133,13 @@ const _super = (function (geti, seti) { nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; generatedNameSet = createMap(); + bundledHelpers = isBundledEmit ? createMap() : undefined; isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files if (isBundledEmit && moduleKind) { for (const sourceFile of sourceFiles) { - emitEmitHelpers(sourceFile); + emitHelpers(sourceFile, /*isBundle*/ true); } } @@ -333,11 +174,6 @@ const _super = (function (geti, seti) { tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; - extendsEmitted = false; - assignEmitted = false; - decorateEmitted = false; - paramEmitted = false; - awaiterEmitted = false; isOwnFileEmit = false; } @@ -2141,85 +1977,39 @@ const _super = (function (geti, seti) { return statements.length; } - function emitHelpers(node: Node) { - const emitFlags = getEmitFlags(node); - let helpersEmitted = false; - if (emitFlags & EmitFlags.EmitEmitHelpers) { - helpersEmitted = emitEmitHelpers(currentSourceFile); - } - - if (emitFlags & EmitFlags.EmitExportStar) { - writeLines(exportStarHelper); - helpersEmitted = true; - } - - if (emitFlags & EmitFlags.EmitSuperHelper) { - writeLines(superHelper); - helpersEmitted = true; - } - - if (emitFlags & EmitFlags.EmitAdvancedSuperHelper) { - writeLines(advancedSuperHelper); - helpersEmitted = true; - } - - return helpersEmitted; - } - - function emitEmitHelpers(node: SourceFile) { - // Only emit helpers if the user did not say otherwise. - if (compilerOptions.noEmitHelpers) { - return false; - } - - // Don't emit helpers if we can import them. - if (compilerOptions.importHelpers - && (isExternalModule(node) || compilerOptions.isolatedModules)) { - return false; - } + function emitHelpers(node: Node, isBundle?: boolean) { + const sourceFile = isSourceFile(node) ? node : currentSourceFile; + const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined); + const shouldBundle = isSourceFile(node) && !isOwnFileEmit; let helpersEmitted = false; + const helpers = getEmitHelpers(node); + if (helpers) { + for (const helper of stableSort(helpers, compareEmitHelpers)) { + if (!helper.scoped) { + // Skip the helper if it can be skipped and the noEmitHelpers compiler + // option is set, or if it can be imported and the importHelpers compiler + // option is set. + if (shouldSkip) continue; - // Only Emit __extends function when target ES5. - // For target ES6 and above, we can emit classDeclaration as is. - if ((languageVersion < ScriptTarget.ES2015) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { - writeLines(extendsHelper); - extendsEmitted = true; - helpersEmitted = true; - } + // Skip the helper if it can be bundled but hasn't already been emitted and we + // are emitting a bundled module. + if (shouldBundle) { + if (helper.name in bundledHelpers) { + continue; + } - if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttributes)) { - writeLines(assignHelper); - assignEmitted = true; - } + bundledHelpers[helper.name] = true; + } + } + else if (isBundle) { + // Skip the helper if it is scoped and we are emitting bundled helpers + continue; + } - if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { - writeLines(decorateHelper); - if (compilerOptions.emitDecoratorMetadata) { - writeLines(metadataHelper); + writeLines(helper.text); + helpersEmitted = true; } - - decorateEmitted = true; - helpersEmitted = true; - } - - if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { - writeLines(paramHelper); - paramEmitted = true; - helpersEmitted = true; - } - - // Only emit __awaiter function when target ES5/ES6. - // Only emit __generator function when target ES5. - // For target ES2017 and above, we can emit async/await as is. - if ((languageVersion < ScriptTarget.ES2017) && (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions)) { - writeLines(awaiterHelper); - if (languageVersion < ScriptTarget.ES2015) { - writeLines(generatorHelper); - } - - awaiterEmitted = true; - helpersEmitted = true; } if (helpersEmitted) { @@ -2230,9 +2020,10 @@ const _super = (function (geti, seti) { } function writeLines(text: string): void { - const lines = text.split(/\r\n|\r|\n/g); + const lines = text.split(/\r\n?|\n/g); + const indentation = guessIndentation(lines); for (let i = 0; i < lines.length; i++) { - const line = lines[i]; + const line = indentation ? lines[i].slice(indentation) : lines[i]; if (line.length) { if (i > 0) { writeLine(); @@ -2242,6 +2033,21 @@ const _super = (function (geti, seti) { } } + function guessIndentation(lines: string[]) { + let indentation: number; + for (const line of lines) { + for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { + if (!isWhiteSpace(line.charCodeAt(i))) { + if (indentation === undefined || i < indentation) { + indentation = i; + break; + } + } + } + } + return indentation; + } + // // Helpers // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c557e3514a5..04b7079d094 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1440,7 +1440,6 @@ namespace ts { if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames; if (node.imports !== undefined) updated.imports = node.imports; if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations; - if (node.externalHelpersModuleName !== undefined) updated.externalHelpersModuleName = node.externalHelpersModuleName; return updateNode(updated, node); } @@ -1683,278 +1682,23 @@ namespace ts { // Helpers - export function createHelperName(externalHelpersModuleName: Identifier | undefined, name: string) { + export interface EmitHelperState { + currentSourceFile: SourceFile; + compilerOptions: CompilerOptions; + requestedHelpers?: EmitHelper[]; + } + + export function getHelperName(helperState: EmitHelperState, name: string) { + const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions); return externalHelpersModuleName ? createPropertyAccess(externalHelpersModuleName, name) : createIdentifier(name); } - export function createExtendsHelper(externalHelpersModuleName: Identifier | undefined, name: Identifier) { - return createCall( - createHelperName(externalHelpersModuleName, "__extends"), - /*typeArguments*/ undefined, - [ - name, - createIdentifier("_super") - ] - ); - } - - export function createAssignHelper(externalHelpersModuleName: Identifier | undefined, attributesSegments: Expression[]) { - return createCall( - createHelperName(externalHelpersModuleName, "__assign"), - /*typeArguments*/ undefined, - attributesSegments - ); - } - - export function createParamHelper(externalHelpersModuleName: Identifier | undefined, expression: Expression, parameterOffset: number, location?: TextRange) { - return createCall( - createHelperName(externalHelpersModuleName, "__param"), - /*typeArguments*/ undefined, - [ - createLiteral(parameterOffset), - expression - ], - location - ); - } - - export function createMetadataHelper(externalHelpersModuleName: Identifier | undefined, metadataKey: string, metadataValue: Expression) { - return createCall( - createHelperName(externalHelpersModuleName, "__metadata"), - /*typeArguments*/ undefined, - [ - createLiteral(metadataKey), - metadataValue - ] - ); - } - - export function createDecorateHelper(externalHelpersModuleName: Identifier | undefined, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { - const argumentsArray: Expression[] = []; - argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); - argumentsArray.push(target); - if (memberName) { - argumentsArray.push(memberName); - if (descriptor) { - argumentsArray.push(descriptor); - } + export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) { + if (!contains(helperState.requestedHelpers, helper)) { + helperState.requestedHelpers = append(helperState.requestedHelpers, helper); } - - return createCall(createHelperName(externalHelpersModuleName, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); - } - - export function createAwaiterHelper(externalHelpersModuleName: Identifier | undefined, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { - const generatorFunc = createFunctionExpression( - /*modifiers*/ undefined, - createToken(SyntaxKind.AsteriskToken), - /*name*/ undefined, - /*typeParameters*/ undefined, - /*parameters*/ [], - /*type*/ undefined, - body - ); - - // Mark this node as originally an async function - (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; - - return createCall( - createHelperName(externalHelpersModuleName, "__awaiter"), - /*typeArguments*/ undefined, - [ - createThis(), - hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), - promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), - generatorFunc - ] - ); - } - - export function createHasOwnProperty(target: LeftHandSideExpression, propertyName: Expression) { - return createCall( - createPropertyAccess(target, "hasOwnProperty"), - /*typeArguments*/ undefined, - [propertyName] - ); - } - - function createObjectCreate(prototype: Expression) { - return createCall( - createPropertyAccess(createIdentifier("Object"), "create"), - /*typeArguments*/ undefined, - [prototype] - ); - } - - function createGeti(target: LeftHandSideExpression) { - // name => super[name] - return createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createElementAccess(target, createIdentifier("name")) - ); - } - - function createSeti(target: LeftHandSideExpression) { - // (name, value) => super[name] = value - return createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [ - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name"), - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "value") - ], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createAssignment( - createElementAccess( - target, - createIdentifier("name") - ), - createIdentifier("value") - ) - ); - } - - export function createAdvancedAsyncSuperHelper() { - // const _super = (function (geti, seti) { - // const cache = Object.create(null); - // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); - // })(name => super[name], (name, value) => super[name] = value); - - // const cache = Object.create(null); - const createCache = createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "cache", - /*type*/ undefined, - createObjectCreate(createNull()) - ) - ]) - ); - - // get value() { return geti(name); } - const getter = createGetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - "value", - /*parameters*/ [], - /*type*/ undefined, - createBlock([ - createReturn( - createCall( - createIdentifier("geti"), - /*typeArguments*/ undefined, - [createIdentifier("name")] - ) - ) - ]) - ); - - // set value(v) { seti(name, v); } - const setter = createSetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - "value", - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "v")], - createBlock([ - createStatement( - createCall( - createIdentifier("seti"), - /*typeArguments*/ undefined, - [ - createIdentifier("name"), - createIdentifier("v") - ] - ) - ) - ]) - ); - - // return name => cache[name] || ... - const getOrCreateAccessorsForName = createReturn( - createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")], - /*type*/ undefined, - createToken(SyntaxKind.EqualsGreaterThanToken), - createLogicalOr( - createElementAccess( - createIdentifier("cache"), - createIdentifier("name") - ), - createParen( - createAssignment( - createElementAccess( - createIdentifier("cache"), - createIdentifier("name") - ), - createObjectLiteral([ - getter, - setter - ]) - ) - ) - ) - ) - ); - - // const _super = (function (geti, seti) { - // const cache = Object.create(null); - // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); - // })(name => super[name], (name, value) => super[name] = value); - return createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "_super", - /*type*/ undefined, - createCall( - createParen( - createFunctionExpression( - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - [ - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "geti"), - createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "seti") - ], - /*type*/ undefined, - createBlock([ - createCache, - getOrCreateAccessorsForName - ]) - ) - ), - /*typeArguments*/ undefined, - [ - createGeti(createSuper()), - createSeti(createSuper()) - ] - ) - ) - ]) - ); - } - - export function createSimpleAsyncSuperHelper() { - return createVariableStatement( - /*modifiers*/ undefined, - createConstDeclarationList([ - createVariableDeclaration( - "_super", - /*type*/ undefined, - createGeti(createSuper()) - ) - ]) - ); } export interface CallBinding { @@ -2356,11 +2100,11 @@ namespace ts { /** * Ensures "use strict" directive is added * - * @param node source file + * @param statements An array of statements */ - export function ensureUseStrict(node: SourceFile): SourceFile { + export function ensureUseStrict(statements: NodeArray): NodeArray { let foundUseStrict = false; - for (const statement of node.statements) { + for (const statement of statements) { if (isPrologueDirective(statement)) { if (isUseStrictPrologue(statement as ExpressionStatement)) { foundUseStrict = true; @@ -2371,13 +2115,15 @@ namespace ts { break; } } + if (!foundUseStrict) { - const statements: Statement[] = []; - statements.push(startOnNewLine(createStatement(createLiteral("use strict")))); - // add "use strict" as the first statement - return updateSourceFileNode(node, statements.concat(node.statements)); + return createNodeArray([ + startOnNewLine(createStatement(createLiteral("use strict"))), + ...statements + ], statements); } - return node; + + return statements; } /** @@ -2796,12 +2542,21 @@ namespace ts { } function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode) { - const { flags, commentRange, sourceMapRange, tokenSourceMapRanges } = sourceEmitNode; - if (!destEmitNode && (flags || commentRange || sourceMapRange || tokenSourceMapRanges)) destEmitNode = {}; + const { + flags, + commentRange, + sourceMapRange, + tokenSourceMapRanges, + constantValue, + helpers + } = sourceEmitNode; + if (!destEmitNode) destEmitNode = {}; if (flags) destEmitNode.flags = flags; if (commentRange) destEmitNode.commentRange = commentRange; if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges); + if (constantValue !== undefined) destEmitNode.constantValue = constantValue; + if (helpers) destEmitNode.helpers = addRange(destEmitNode.helpers, helpers); return destEmitNode; } @@ -2879,6 +2634,16 @@ namespace ts { return node; } + /** + * Gets a custom text range to use when emitting source maps. + * + * @param node The node. + */ + export function getSourceMapRange(node: Node) { + const emitNode = node.emitNode; + return (emitNode && emitNode.sourceMapRange) || node; + } + /** * Sets a custom text range to use when emitting source maps. * @@ -2890,6 +2655,18 @@ namespace ts { return node; } + /** + * Gets the TextRange to use for source maps for a token of a node. + * + * @param node The node. + * @param token The token. + */ + export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { + const emitNode = node.emitNode; + const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; + return tokenSourceMapRanges && tokenSourceMapRanges[token]; + } + /** * Sets the TextRange to use for source maps for a token of a node. * @@ -2904,14 +2681,6 @@ namespace ts { return node; } - /** - * Sets a custom text range to use when emitting comments. - */ - export function setCommentRange(node: T, range: TextRange) { - getOrCreateEmitNode(node).commentRange = range; - return node; - } - /** * Gets a custom text range to use when emitting comments. * @@ -2923,25 +2692,11 @@ namespace ts { } /** - * Gets a custom text range to use when emitting source maps. - * - * @param node The node. + * Sets a custom text range to use when emitting comments. */ - export function getSourceMapRange(node: Node) { - const emitNode = node.emitNode; - return (emitNode && emitNode.sourceMapRange) || node; - } - - /** - * Gets the TextRange to use for source maps for a token of a node. - * - * @param node The node. - * @param token The token. - */ - export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { - const emitNode = node.emitNode; - const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges[token]; + export function setCommentRange(node: T, range: TextRange) { + getOrCreateEmitNode(node).commentRange = range; + return node; } /** @@ -2961,6 +2716,102 @@ namespace ts { return node; } + export function getExternalHelpersModuleName(node: SourceFile) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = parseNode && parseNode.emitNode; + return emitNode && emitNode.externalHelpersModuleName; + } + + export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) { + if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = getOrCreateEmitNode(parseNode); + return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + } + } + + /** + * Adds an EmitHelper to a node. + */ + export function addEmitHelper(node: T, helper: EmitHelper): T { + const emitNode = getOrCreateEmitNode(node); + emitNode.helpers = append(emitNode.helpers, helper); + return node; + } + + /** + * Adds an EmitHelper to a node. + */ + export function addEmitHelpers(node: T, helpers: EmitHelper[] | undefined): T { + if (some(helpers)) { + const emitNode = getOrCreateEmitNode(node); + for (const helper of helpers) { + if (!contains(emitNode.helpers, helper)) { + emitNode.helpers = append(emitNode.helpers, helper); + } + } + } + return node; + } + + /** + * Removes an EmitHelper from a node. + */ + export function removeEmitHelper(node: Node, helper: EmitHelper): boolean { + const emitNode = node.emitNode; + if (emitNode) { + const helpers = emitNode.helpers; + if (helpers) { + return orderedRemoveItem(helpers, helper); + } + } + return false; + } + + /** + * Gets the EmitHelpers of a node. + */ + export function getEmitHelpers(node: Node): EmitHelper[] | undefined { + const emitNode = node.emitNode; + return emitNode && emitNode.helpers; + } + + /** + * Moves matching emit helpers from a source node to a target node. + */ + export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) { + const sourceEmitNode = source.emitNode; + const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers; + if (!some(sourceEmitHelpers)) return; + + const targetEmitNode = getOrCreateEmitNode(target); + let helpersRemoved = 0; + for (let i = 0; i < sourceEmitHelpers.length; i++) { + const helper = sourceEmitHelpers[i]; + if (predicate(helper)) { + helpersRemoved++; + if (!contains(targetEmitNode.helpers, helper)) { + targetEmitNode.helpers = append(targetEmitNode.helpers, helper); + } + } + else if (helpersRemoved > 0) { + sourceEmitHelpers[i - helpersRemoved] = helper; + } + } + + if (helpersRemoved > 0) { + sourceEmitHelpers.length -= helpersRemoved; + } + } + + export function compareEmitHelpers(x: EmitHelper, y: EmitHelper) { + if (x === y) return Comparison.EqualTo; + if (x.priority === y.priority) return Comparison.EqualTo; + if (x.priority === undefined) return Comparison.GreaterThan; + if (y.priority === undefined) return Comparison.LessThan; + return compareValues(x.priority, y.priority); + } + export function setTextRange(node: T, location: TextRange): T { if (location) { node.pos = location.pos; @@ -3055,4 +2906,164 @@ namespace ts { function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } + + export interface ExternalModuleInfo { + externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules + externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers + exportSpecifiers: Map; // export specifiers by name + exportedBindings: Map; // exported names of local declarations + exportedNames: Identifier[]; // all exported names local to module + exportEquals: ExportAssignment | undefined; // an export= declaration if one was present + hasExportStarsToExportValues: boolean; // whether this module contains export* + } + + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { + const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; + const exportSpecifiers = createMap(); + const exportedBindings = createMap(); + const uniqueExports = createMap(); + let hasExportDefault = false; + let exportEquals: ExportAssignment = undefined; + let hasExportStarsToExportValues = false; + + const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile); + const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), + createLiteral(externalHelpersModuleNameText)); + + if (externalHelpersImportDeclaration) { + externalImports.push(externalHelpersImportDeclaration); + } + + for (const node of sourceFile.statements) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + // import "mod" + // import x from "mod" + // import * as x from "mod" + // import { x, y } from "mod" + externalImports.push(node); + break; + + case SyntaxKind.ImportEqualsDeclaration: + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference) { + // import x = require("mod") + externalImports.push(node); + } + + break; + + case SyntaxKind.ExportDeclaration: + if ((node).moduleSpecifier) { + if (!(node).exportClause) { + // export * from "mod" + externalImports.push(node); + hasExportStarsToExportValues = true; + } + else { + // export { x, y } from "mod" + externalImports.push(node); + } + } + else { + // export { x, y } + for (const specifier of (node).exportClause.elements) { + if (!uniqueExports[specifier.name.text]) { + const name = specifier.propertyName || specifier.name; + multiMapAdd(exportSpecifiers, name.text, specifier); + + const decl = resolver.getReferencedImportDeclaration(name) + || resolver.getReferencedValueDeclaration(name); + + if (decl) { + multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + } + + uniqueExports[specifier.name.text] = specifier.name; + } + } + } + break; + + case SyntaxKind.ExportAssignment: + if ((node).isExportEquals && !exportEquals) { + // export = x + exportEquals = node; + } + break; + + case SyntaxKind.VariableStatement: + if (hasModifier(node, ModifierFlags.Export)) { + for (const decl of (node).declarationList.declarations) { + collectExportedVariableInfo(decl, uniqueExports); + } + } + break; + + case SyntaxKind.FunctionDeclaration: + if (hasModifier(node, ModifierFlags.Export)) { + if (hasModifier(node, ModifierFlags.Default)) { + // export default function() { } + if (!hasExportDefault) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + hasExportDefault = true; + } + } + else { + // export function x() { } + const name = (node).name; + if (!uniqueExports[name.text]) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + uniqueExports[name.text] = name; + } + } + } + break; + + case SyntaxKind.ClassDeclaration: + if (hasModifier(node, ModifierFlags.Export)) { + if (hasModifier(node, ModifierFlags.Default)) { + // export default class { } + if (!hasExportDefault) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + hasExportDefault = true; + } + } + else { + // export class x { } + const name = (node).name; + if (!uniqueExports[name.text]) { + multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + uniqueExports[name.text] = name; + } + } + } + break; + } + } + + let exportedNames: Identifier[]; + for (const key in uniqueExports) { + exportedNames = ts.append(exportedNames, uniqueExports[key]); + } + + return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration }; + } + + function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map) { + if (isBindingPattern(decl.name)) { + for (const element of decl.name.elements) { + if (!isOmittedExpression(element)) { + collectExportedVariableInfo(element, uniqueExports); + } + } + } + else if (!isGeneratedIdentifier(decl.name)) { + if (!uniqueExports[decl.name.text]) { + uniqueExports[decl.name.text] = decl.name; + } + } + } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 25c15305c4a..60d1ad95682 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3,7 +3,6 @@ /*@internal*/ namespace ts { - const enum ES2015SubstitutionFlags { /** Enables substitutions for captured `this` */ CapturedThis = 1 << 0, @@ -170,6 +169,7 @@ namespace ts { hoistVariableDeclaration, } = context; + const compilerOptions = context.getCompilerOptions(); const resolver = context.getEmitResolver(); const previousOnSubstituteNode = context.onSubstituteNode; const previousOnEmitNode = context.onEmitNode; @@ -187,6 +187,7 @@ namespace ts { let enclosingNonArrowFunction: FunctionLikeDeclaration; let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement; let isInConstructorWithCapturedSuper: boolean; + let helperState: EmitHelperState; /** * Used to track if we are emitting body of the converted loop @@ -209,7 +210,15 @@ namespace ts { currentSourceFile = node; currentText = node.text; - return visitNode(node, visitor, isSourceFile); + helperState = { currentSourceFile, compilerOptions }; + + const visited = visitNode(node, visitor, isSourceFile); + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + currentText = undefined; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -778,7 +787,7 @@ namespace ts { if (extendsClauseElement) { statements.push( createStatement( - createExtendsHelper(currentSourceFile.externalHelpersModuleName, getLocalName(node)), + createExtendsHelper(helperState, getLocalName(node)), /*location*/ extendsClauseElement ) ); @@ -3274,4 +3283,28 @@ namespace ts { return isIdentifier(expression) && expression === parameter.name; } } + + function createExtendsHelper(helperState: EmitHelperState, name: Identifier) { + requestEmitHelper(helperState, extendsHelper); + return createCall( + getHelperName(helperState, "__extends"), + /*typeArguments*/ undefined, + [ + name, + createIdentifier("_super") + ] + ); + } + + const extendsHelper: EmitHelper = { + name: "typescript:extends", + scoped: false, + priority: 0, + text: ` + var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + };` + }; } diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 7800a41e147..14d97ade2ac 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -5,13 +5,12 @@ namespace ts { type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; + const enum ES2017SubstitutionFlags { + /** Enables substitutions for async methods with `super` calls. */ + AsyncMethodsWithSuper = 1 << 0 + } + export function transformES2017(context: TransformationContext) { - - const enum ES2017SubstitutionFlags { - /** Enables substitutions for async methods with `super` calls. */ - AsyncMethodsWithSuper = 1 << 0 - } - const { startLexicalEnvironment, endLexicalEnvironment, @@ -22,7 +21,9 @@ namespace ts { const languageVersion = getEmitScriptTarget(compilerOptions); // These variables contain state that changes as we descend into the tree. - let currentSourceFileExternalHelpersModuleName: Identifier; + let currentSourceFile: SourceFile; + let helperState: EmitHelperState; + /** * Keeps track of whether expression substitution has been enabled for specific edge cases. * They are persisted between each SourceFile transformation and should not be reset. @@ -58,9 +59,16 @@ namespace ts { return node; } - currentSourceFileExternalHelpersModuleName = node.externalHelpersModuleName; + currentSourceFile = node; + helperState = { currentSourceFile, compilerOptions }; - return visitEachChild(node, visitor, context); + const visited = visitEachChild(node, visitor, context); + + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -107,11 +115,11 @@ namespace ts { } /** - * Visits an await expression. + * Visits an AwaitExpression node. * * This function will be called any time a ES2017 await expression is encountered. * - * @param node The await expression node. + * @param node The node to visit. */ function visitAwaitExpression(node: AwaitExpression): Expression { return setOriginalNode( @@ -125,17 +133,15 @@ namespace ts { } /** - * Visits a method declaration of a class. + * Visits a MethodDeclaration node. * * This function will be called when one of the following conditions are met: * - The node is marked as async * - * @param node The method node. + * @param node The node to visit. */ function visitMethodDeclaration(node: MethodDeclaration) { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const method = createMethod( /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), @@ -150,25 +156,20 @@ namespace ts { // While we emit the source map for the node after skipping decorators and modifiers, // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); setOriginalNode(method, node); - return method; } /** - * Visits a function declaration. + * Visits a FunctionDeclaration node. * * This function will be called when one of the following conditions are met: * - The node is marked async * - * @param node The function node. + * @param node The node to visit. */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const func = createFunctionDeclaration( /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), @@ -180,23 +181,21 @@ namespace ts { transformFunctionBody(node), /*location*/ node ); - setOriginalNode(func, node); + setOriginalNode(func, node); return func; } /** - * Visits a function expression node. + * Visits a FunctionExpression node. * * This function will be called when one of the following conditions are met: * - The node is marked async * - * @param node The function expression node. + * @param node The node to visit. */ function visitFunctionExpression(node: FunctionExpression): Expression { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); if (nodeIsMissing(node.body)) { return createOmittedExpression(); } @@ -213,19 +212,19 @@ namespace ts { ); setOriginalNode(func, node); - return func; } /** - * @remarks + * Visits an ArrowFunction. + * * This function will be called when one of the following conditions are met: * - The node is marked async + * + * @param node The node to visit. */ function visitArrowFunction(node: ArrowFunction) { - if (!isAsyncFunctionLike(node)) { - return node; - } + Debug.assert(hasModifier(node, ModifierFlags.Async)); const func = createArrowFunction( visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, @@ -237,7 +236,6 @@ namespace ts { ); setOriginalNode(func, node); - return func; } @@ -280,7 +278,7 @@ namespace ts { statements.push( createReturn( createAwaiterHelper( - currentSourceFileExternalHelpersModuleName, + helperState, hasLexicalArguments, promiseConstructor, transformFunctionBodyWorker(node.body, statementOffset) @@ -295,11 +293,11 @@ namespace ts { if (languageVersion >= ScriptTarget.ES2015) { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) { enableSubstitutionForAsyncMethodsWithSuper(); - setEmitFlags(block, EmitFlags.EmitAdvancedSuperHelper); + addEmitHelper(block, advancedAsyncSuperHelper); } else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) { enableSubstitutionForAsyncMethodsWithSuper(); - setEmitFlags(block, EmitFlags.EmitSuperHelper); + addEmitHelper(block, asyncSuperHelper); } } @@ -307,7 +305,7 @@ namespace ts { } else { return createAwaiterHelper( - currentSourceFileExternalHelpersModuleName, + helperState, hasLexicalArguments, promiseConstructor, transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) @@ -507,4 +505,63 @@ namespace ts { && resolver.getNodeCheckFlags(currentSuperContainer) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); } } + + function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + const generatorFunc = createFunctionExpression( + /*modifiers*/ undefined, + createToken(SyntaxKind.AsteriskToken), + /*name*/ undefined, + /*typeParameters*/ undefined, + /*parameters*/ [], + /*type*/ undefined, + body + ); + + // Mark this node as originally an async function + (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; + + requestEmitHelper(helperState, awaiterHelper); + return createCall( + getHelperName(helperState, "__awaiter"), + /*typeArguments*/ undefined, + [ + createThis(), + hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), + promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), + generatorFunc + ] + ); + } + + const awaiterHelper: EmitHelper = { + name: "typescript:awaiter", + scoped: false, + priority: 5, + text: ` + var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); + };` + }; + + const asyncSuperHelper: EmitHelper = { + name: "typescript:async-super", + scoped: true, + text: ` + const _super = name => super[name];` + }; + + const advancedAsyncSuperHelper: EmitHelper = { + name: "typescript:advanced-async-super", + scoped: true, + text: ` + const _super = (function (geti, seti) { + const cache = Object.create(null); + return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + })(name => super[name], (name, value) => super[name] = value);` + }; } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7c9cde59fde..78c9e42c1bf 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -242,6 +242,7 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; let renamedCatchVariableDeclarations: Map; + let helperState: EmitHelperState; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -291,17 +292,20 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { - if (isDeclarationFile(node)) { + if (isDeclarationFile(node) + || (node.transformFlags & TransformFlags.ContainsGenerator) === 0) { return node; } - if (node.transformFlags & TransformFlags.ContainsGenerator) { - currentSourceFile = node; - node = visitEachChild(node, visitor, context); - currentSourceFile = undefined; - } + currentSourceFile = node; + helperState = { currentSourceFile, compilerOptions }; - return node; + const visited = visitEachChild(node, visitor, context); + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + helperState = undefined; + return visited; } /** @@ -2585,28 +2589,24 @@ namespace ts { withBlockStack = undefined; const buildResult = buildStatements(); - return createCall( - createHelperName(currentSourceFile.externalHelpersModuleName, "__generator"), - /*typeArguments*/ undefined, - [ - createThis(), - setEmitFlags( - createFunctionExpression( - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)], - /*type*/ undefined, - createBlock( - buildResult, - /*location*/ undefined, - /*multiLine*/ buildResult.length > 0 - ) - ), - EmitFlags.ReuseTempVariableScope - ) - ] + return createGeneratorHelper( + helperState, + setEmitFlags( + createFunctionExpression( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)], + /*type*/ undefined, + createBlock( + buildResult, + /*location*/ undefined, + /*multiLine*/ buildResult.length > 0 + ) + ), + EmitFlags.ReuseTempVariableScope + ) ); } @@ -3082,4 +3082,105 @@ namespace ts { ); } } + + function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) { + requestEmitHelper(helperState, generatorHelper); + return createCall( + getHelperName(helperState, "__generator"), + /*typeArguments*/ undefined, + [createThis(), body]); + } + + // The __generator helper is used by down-level transformations to emulate the runtime + // semantics of an ES2015 generator function. When called, this helper returns an + // object that implements the Iterator protocol, in that it has `next`, `return`, and + // `throw` methods that step through the generator when invoked. + // + // parameters: + // thisArg The value to use as the `this` binding for the transformed generator body. + // body A function that acts as the transformed generator body. + // + // variables: + // _ Persistent state for the generator that is shared between the helper and the + // generator body. The state object has the following members: + // sent() - A method that returns or throws the current completion value. + // label - The next point at which to resume evaluation of the generator body. + // trys - A stack of protected regions (try/catch/finally blocks). + // ops - A stack of pending instructions when inside of a finally block. + // f A value indicating whether the generator is executing. + // y An iterator to delegate for a yield*. + // t A temporary variable that holds one of the following values (note that these + // cases do not overlap): + // - The completion value when resuming from a `yield` or `yield*`. + // - The error value for a catch block. + // - The current protected region (array of try/catch/finally/end labels). + // - The verb (`next`, `throw`, or `return` method) to delegate to the expression + // of a `yield*`. + // - The result of evaluating the verb delegated to the expression of a `yield*`. + // + // functions: + // verb(n) Creates a bound callback to the `step` function for opcode `n`. + // step(op) Evaluates opcodes in a generator body until execution is suspended or + // completed. + // + // The __generator helper understands a limited set of instructions: + // 0: next(value?) - Start or resume the generator with the specified value. + // 1: throw(error) - Resume the generator with an exception. If the generator is + // suspended inside of one or more protected regions, evaluates + // any intervening finally blocks between the current label and + // the nearest catch block or function boundary. If uncaught, the + // exception is thrown to the caller. + // 2: return(value?) - Resume the generator as if with a return. If the generator is + // suspended inside of one or more protected regions, evaluates any + // intervening finally blocks. + // 3: break(label) - Jump to the specified label. If the label is outside of the + // current protected region, evaluates any intervening finally + // blocks. + // 4: yield(value?) - Yield execution to the caller with an optional value. When + // resumed, the generator will continue at the next label. + // 5: yield*(value) - Delegates evaluation to the supplied iterator. When + // delegation completes, the generator will continue at the next + // label. + // 6: catch(error) - Handles an exception thrown from within the generator body. If + // the current label is inside of one or more protected regions, + // evaluates any intervening finally blocks between the current + // label and the nearest catch block or function boundary. If + // uncaught, the exception is thrown to the caller. + // 7: endfinally - Ends a finally block, resuming the last instruction prior to + // entering a finally block. + // + // For examples of how these are used, see the comments in ./transformers/generators.ts + const generatorHelper: EmitHelper = { + name: "typescript:generator", + scoped: false, + priority: 6, + text: ` + var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; + return { next: verb(0), "throw": verb(1), "return": verb(2) }; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + };` + }; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 95a4016bb0a..8867896e51e 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -3,11 +3,11 @@ /*@internal*/ namespace ts { - const entities: Map = createEntitiesMap(); - export function transformJsx(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; + let helperState: EmitHelperState; + return transformSourceFile; /** @@ -21,9 +21,14 @@ namespace ts { } currentSourceFile = node; - node = visitEachChild(node, visitor, context); + helperState = { currentSourceFile, compilerOptions }; + + const visited = visitEachChild(node, visitor, context); + addEmitHelpers(visited, helperState.requestedHelpers); + currentSourceFile = undefined; - return node; + helperState = undefined; + return visited; } function visitor(node: Node): VisitResult { @@ -109,8 +114,10 @@ namespace ts { // Either emit one big object literal (no spread attribs), or // a call to the __assign helper. - objectProperties = singleOrUndefined(segments) - || createAssignHelper(currentSourceFile.externalHelpersModuleName, segments); + objectProperties = singleOrUndefined(segments); + if (!objectProperties) { + objectProperties = createAssignHelper(helperState, segments); + } } const element = createReactCreateElement( @@ -275,261 +282,283 @@ namespace ts { } } - function createEntitiesMap(): Map { - return createMap({ - "quot": 0x0022, - "amp": 0x0026, - "apos": 0x0027, - "lt": 0x003C, - "gt": 0x003E, - "nbsp": 0x00A0, - "iexcl": 0x00A1, - "cent": 0x00A2, - "pound": 0x00A3, - "curren": 0x00A4, - "yen": 0x00A5, - "brvbar": 0x00A6, - "sect": 0x00A7, - "uml": 0x00A8, - "copy": 0x00A9, - "ordf": 0x00AA, - "laquo": 0x00AB, - "not": 0x00AC, - "shy": 0x00AD, - "reg": 0x00AE, - "macr": 0x00AF, - "deg": 0x00B0, - "plusmn": 0x00B1, - "sup2": 0x00B2, - "sup3": 0x00B3, - "acute": 0x00B4, - "micro": 0x00B5, - "para": 0x00B6, - "middot": 0x00B7, - "cedil": 0x00B8, - "sup1": 0x00B9, - "ordm": 0x00BA, - "raquo": 0x00BB, - "frac14": 0x00BC, - "frac12": 0x00BD, - "frac34": 0x00BE, - "iquest": 0x00BF, - "Agrave": 0x00C0, - "Aacute": 0x00C1, - "Acirc": 0x00C2, - "Atilde": 0x00C3, - "Auml": 0x00C4, - "Aring": 0x00C5, - "AElig": 0x00C6, - "Ccedil": 0x00C7, - "Egrave": 0x00C8, - "Eacute": 0x00C9, - "Ecirc": 0x00CA, - "Euml": 0x00CB, - "Igrave": 0x00CC, - "Iacute": 0x00CD, - "Icirc": 0x00CE, - "Iuml": 0x00CF, - "ETH": 0x00D0, - "Ntilde": 0x00D1, - "Ograve": 0x00D2, - "Oacute": 0x00D3, - "Ocirc": 0x00D4, - "Otilde": 0x00D5, - "Ouml": 0x00D6, - "times": 0x00D7, - "Oslash": 0x00D8, - "Ugrave": 0x00D9, - "Uacute": 0x00DA, - "Ucirc": 0x00DB, - "Uuml": 0x00DC, - "Yacute": 0x00DD, - "THORN": 0x00DE, - "szlig": 0x00DF, - "agrave": 0x00E0, - "aacute": 0x00E1, - "acirc": 0x00E2, - "atilde": 0x00E3, - "auml": 0x00E4, - "aring": 0x00E5, - "aelig": 0x00E6, - "ccedil": 0x00E7, - "egrave": 0x00E8, - "eacute": 0x00E9, - "ecirc": 0x00EA, - "euml": 0x00EB, - "igrave": 0x00EC, - "iacute": 0x00ED, - "icirc": 0x00EE, - "iuml": 0x00EF, - "eth": 0x00F0, - "ntilde": 0x00F1, - "ograve": 0x00F2, - "oacute": 0x00F3, - "ocirc": 0x00F4, - "otilde": 0x00F5, - "ouml": 0x00F6, - "divide": 0x00F7, - "oslash": 0x00F8, - "ugrave": 0x00F9, - "uacute": 0x00FA, - "ucirc": 0x00FB, - "uuml": 0x00FC, - "yacute": 0x00FD, - "thorn": 0x00FE, - "yuml": 0x00FF, - "OElig": 0x0152, - "oelig": 0x0153, - "Scaron": 0x0160, - "scaron": 0x0161, - "Yuml": 0x0178, - "fnof": 0x0192, - "circ": 0x02C6, - "tilde": 0x02DC, - "Alpha": 0x0391, - "Beta": 0x0392, - "Gamma": 0x0393, - "Delta": 0x0394, - "Epsilon": 0x0395, - "Zeta": 0x0396, - "Eta": 0x0397, - "Theta": 0x0398, - "Iota": 0x0399, - "Kappa": 0x039A, - "Lambda": 0x039B, - "Mu": 0x039C, - "Nu": 0x039D, - "Xi": 0x039E, - "Omicron": 0x039F, - "Pi": 0x03A0, - "Rho": 0x03A1, - "Sigma": 0x03A3, - "Tau": 0x03A4, - "Upsilon": 0x03A5, - "Phi": 0x03A6, - "Chi": 0x03A7, - "Psi": 0x03A8, - "Omega": 0x03A9, - "alpha": 0x03B1, - "beta": 0x03B2, - "gamma": 0x03B3, - "delta": 0x03B4, - "epsilon": 0x03B5, - "zeta": 0x03B6, - "eta": 0x03B7, - "theta": 0x03B8, - "iota": 0x03B9, - "kappa": 0x03BA, - "lambda": 0x03BB, - "mu": 0x03BC, - "nu": 0x03BD, - "xi": 0x03BE, - "omicron": 0x03BF, - "pi": 0x03C0, - "rho": 0x03C1, - "sigmaf": 0x03C2, - "sigma": 0x03C3, - "tau": 0x03C4, - "upsilon": 0x03C5, - "phi": 0x03C6, - "chi": 0x03C7, - "psi": 0x03C8, - "omega": 0x03C9, - "thetasym": 0x03D1, - "upsih": 0x03D2, - "piv": 0x03D6, - "ensp": 0x2002, - "emsp": 0x2003, - "thinsp": 0x2009, - "zwnj": 0x200C, - "zwj": 0x200D, - "lrm": 0x200E, - "rlm": 0x200F, - "ndash": 0x2013, - "mdash": 0x2014, - "lsquo": 0x2018, - "rsquo": 0x2019, - "sbquo": 0x201A, - "ldquo": 0x201C, - "rdquo": 0x201D, - "bdquo": 0x201E, - "dagger": 0x2020, - "Dagger": 0x2021, - "bull": 0x2022, - "hellip": 0x2026, - "permil": 0x2030, - "prime": 0x2032, - "Prime": 0x2033, - "lsaquo": 0x2039, - "rsaquo": 0x203A, - "oline": 0x203E, - "frasl": 0x2044, - "euro": 0x20AC, - "image": 0x2111, - "weierp": 0x2118, - "real": 0x211C, - "trade": 0x2122, - "alefsym": 0x2135, - "larr": 0x2190, - "uarr": 0x2191, - "rarr": 0x2192, - "darr": 0x2193, - "harr": 0x2194, - "crarr": 0x21B5, - "lArr": 0x21D0, - "uArr": 0x21D1, - "rArr": 0x21D2, - "dArr": 0x21D3, - "hArr": 0x21D4, - "forall": 0x2200, - "part": 0x2202, - "exist": 0x2203, - "empty": 0x2205, - "nabla": 0x2207, - "isin": 0x2208, - "notin": 0x2209, - "ni": 0x220B, - "prod": 0x220F, - "sum": 0x2211, - "minus": 0x2212, - "lowast": 0x2217, - "radic": 0x221A, - "prop": 0x221D, - "infin": 0x221E, - "ang": 0x2220, - "and": 0x2227, - "or": 0x2228, - "cap": 0x2229, - "cup": 0x222A, - "int": 0x222B, - "there4": 0x2234, - "sim": 0x223C, - "cong": 0x2245, - "asymp": 0x2248, - "ne": 0x2260, - "equiv": 0x2261, - "le": 0x2264, - "ge": 0x2265, - "sub": 0x2282, - "sup": 0x2283, - "nsub": 0x2284, - "sube": 0x2286, - "supe": 0x2287, - "oplus": 0x2295, - "otimes": 0x2297, - "perp": 0x22A5, - "sdot": 0x22C5, - "lceil": 0x2308, - "rceil": 0x2309, - "lfloor": 0x230A, - "rfloor": 0x230B, - "lang": 0x2329, - "rang": 0x232A, - "loz": 0x25CA, - "spades": 0x2660, - "clubs": 0x2663, - "hearts": 0x2665, - "diams": 0x2666 - }); + const entities = createMap({ + "quot": 0x0022, + "amp": 0x0026, + "apos": 0x0027, + "lt": 0x003C, + "gt": 0x003E, + "nbsp": 0x00A0, + "iexcl": 0x00A1, + "cent": 0x00A2, + "pound": 0x00A3, + "curren": 0x00A4, + "yen": 0x00A5, + "brvbar": 0x00A6, + "sect": 0x00A7, + "uml": 0x00A8, + "copy": 0x00A9, + "ordf": 0x00AA, + "laquo": 0x00AB, + "not": 0x00AC, + "shy": 0x00AD, + "reg": 0x00AE, + "macr": 0x00AF, + "deg": 0x00B0, + "plusmn": 0x00B1, + "sup2": 0x00B2, + "sup3": 0x00B3, + "acute": 0x00B4, + "micro": 0x00B5, + "para": 0x00B6, + "middot": 0x00B7, + "cedil": 0x00B8, + "sup1": 0x00B9, + "ordm": 0x00BA, + "raquo": 0x00BB, + "frac14": 0x00BC, + "frac12": 0x00BD, + "frac34": 0x00BE, + "iquest": 0x00BF, + "Agrave": 0x00C0, + "Aacute": 0x00C1, + "Acirc": 0x00C2, + "Atilde": 0x00C3, + "Auml": 0x00C4, + "Aring": 0x00C5, + "AElig": 0x00C6, + "Ccedil": 0x00C7, + "Egrave": 0x00C8, + "Eacute": 0x00C9, + "Ecirc": 0x00CA, + "Euml": 0x00CB, + "Igrave": 0x00CC, + "Iacute": 0x00CD, + "Icirc": 0x00CE, + "Iuml": 0x00CF, + "ETH": 0x00D0, + "Ntilde": 0x00D1, + "Ograve": 0x00D2, + "Oacute": 0x00D3, + "Ocirc": 0x00D4, + "Otilde": 0x00D5, + "Ouml": 0x00D6, + "times": 0x00D7, + "Oslash": 0x00D8, + "Ugrave": 0x00D9, + "Uacute": 0x00DA, + "Ucirc": 0x00DB, + "Uuml": 0x00DC, + "Yacute": 0x00DD, + "THORN": 0x00DE, + "szlig": 0x00DF, + "agrave": 0x00E0, + "aacute": 0x00E1, + "acirc": 0x00E2, + "atilde": 0x00E3, + "auml": 0x00E4, + "aring": 0x00E5, + "aelig": 0x00E6, + "ccedil": 0x00E7, + "egrave": 0x00E8, + "eacute": 0x00E9, + "ecirc": 0x00EA, + "euml": 0x00EB, + "igrave": 0x00EC, + "iacute": 0x00ED, + "icirc": 0x00EE, + "iuml": 0x00EF, + "eth": 0x00F0, + "ntilde": 0x00F1, + "ograve": 0x00F2, + "oacute": 0x00F3, + "ocirc": 0x00F4, + "otilde": 0x00F5, + "ouml": 0x00F6, + "divide": 0x00F7, + "oslash": 0x00F8, + "ugrave": 0x00F9, + "uacute": 0x00FA, + "ucirc": 0x00FB, + "uuml": 0x00FC, + "yacute": 0x00FD, + "thorn": 0x00FE, + "yuml": 0x00FF, + "OElig": 0x0152, + "oelig": 0x0153, + "Scaron": 0x0160, + "scaron": 0x0161, + "Yuml": 0x0178, + "fnof": 0x0192, + "circ": 0x02C6, + "tilde": 0x02DC, + "Alpha": 0x0391, + "Beta": 0x0392, + "Gamma": 0x0393, + "Delta": 0x0394, + "Epsilon": 0x0395, + "Zeta": 0x0396, + "Eta": 0x0397, + "Theta": 0x0398, + "Iota": 0x0399, + "Kappa": 0x039A, + "Lambda": 0x039B, + "Mu": 0x039C, + "Nu": 0x039D, + "Xi": 0x039E, + "Omicron": 0x039F, + "Pi": 0x03A0, + "Rho": 0x03A1, + "Sigma": 0x03A3, + "Tau": 0x03A4, + "Upsilon": 0x03A5, + "Phi": 0x03A6, + "Chi": 0x03A7, + "Psi": 0x03A8, + "Omega": 0x03A9, + "alpha": 0x03B1, + "beta": 0x03B2, + "gamma": 0x03B3, + "delta": 0x03B4, + "epsilon": 0x03B5, + "zeta": 0x03B6, + "eta": 0x03B7, + "theta": 0x03B8, + "iota": 0x03B9, + "kappa": 0x03BA, + "lambda": 0x03BB, + "mu": 0x03BC, + "nu": 0x03BD, + "xi": 0x03BE, + "omicron": 0x03BF, + "pi": 0x03C0, + "rho": 0x03C1, + "sigmaf": 0x03C2, + "sigma": 0x03C3, + "tau": 0x03C4, + "upsilon": 0x03C5, + "phi": 0x03C6, + "chi": 0x03C7, + "psi": 0x03C8, + "omega": 0x03C9, + "thetasym": 0x03D1, + "upsih": 0x03D2, + "piv": 0x03D6, + "ensp": 0x2002, + "emsp": 0x2003, + "thinsp": 0x2009, + "zwnj": 0x200C, + "zwj": 0x200D, + "lrm": 0x200E, + "rlm": 0x200F, + "ndash": 0x2013, + "mdash": 0x2014, + "lsquo": 0x2018, + "rsquo": 0x2019, + "sbquo": 0x201A, + "ldquo": 0x201C, + "rdquo": 0x201D, + "bdquo": 0x201E, + "dagger": 0x2020, + "Dagger": 0x2021, + "bull": 0x2022, + "hellip": 0x2026, + "permil": 0x2030, + "prime": 0x2032, + "Prime": 0x2033, + "lsaquo": 0x2039, + "rsaquo": 0x203A, + "oline": 0x203E, + "frasl": 0x2044, + "euro": 0x20AC, + "image": 0x2111, + "weierp": 0x2118, + "real": 0x211C, + "trade": 0x2122, + "alefsym": 0x2135, + "larr": 0x2190, + "uarr": 0x2191, + "rarr": 0x2192, + "darr": 0x2193, + "harr": 0x2194, + "crarr": 0x21B5, + "lArr": 0x21D0, + "uArr": 0x21D1, + "rArr": 0x21D2, + "dArr": 0x21D3, + "hArr": 0x21D4, + "forall": 0x2200, + "part": 0x2202, + "exist": 0x2203, + "empty": 0x2205, + "nabla": 0x2207, + "isin": 0x2208, + "notin": 0x2209, + "ni": 0x220B, + "prod": 0x220F, + "sum": 0x2211, + "minus": 0x2212, + "lowast": 0x2217, + "radic": 0x221A, + "prop": 0x221D, + "infin": 0x221E, + "ang": 0x2220, + "and": 0x2227, + "or": 0x2228, + "cap": 0x2229, + "cup": 0x222A, + "int": 0x222B, + "there4": 0x2234, + "sim": 0x223C, + "cong": 0x2245, + "asymp": 0x2248, + "ne": 0x2260, + "equiv": 0x2261, + "le": 0x2264, + "ge": 0x2265, + "sub": 0x2282, + "sup": 0x2283, + "nsub": 0x2284, + "sube": 0x2286, + "supe": 0x2287, + "oplus": 0x2295, + "otimes": 0x2297, + "perp": 0x22A5, + "sdot": 0x22C5, + "lceil": 0x2308, + "rceil": 0x2309, + "lfloor": 0x230A, + "rfloor": 0x230B, + "lang": 0x2329, + "rang": 0x232A, + "loz": 0x25CA, + "spades": 0x2660, + "clubs": 0x2663, + "hearts": 0x2665, + "diams": 0x2666 + }); + + function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) { + requestEmitHelper(helperState, assignHelper); + return createCall( + getHelperName(helperState, "__assign"), + /*typeArguments*/ undefined, + attributesSegments + ); } + + const assignHelper: EmitHelper = { + name: "typescript:assign", + scoped: false, + priority: 1, + text: ` + var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + };` + }; } \ No newline at end of file diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index 93aa108617a..daef9cb5a4d 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -13,7 +13,27 @@ namespace ts { } if (isExternalModule(node) || compilerOptions.isolatedModules) { - return visitEachChild(node, visitor, context); + const externalHelpersModuleName = getExternalHelpersModuleName(node); + if (externalHelpersModuleName) { + const statements: Statement[] = []; + const statementOffset = addPrologueDirectives(statements, node.statements); + append(statements, + createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), + createLiteral(externalHelpersModuleNameText) + ) + ); + + addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + return updateSourceFileNode( + node, + createNodeArray(statements, node.statements)); + } + else { + return visitEachChild(node, visitor, context); + } } return node; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 87d02b44025..b946d93f32d 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -82,13 +82,14 @@ namespace ts { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); + append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true)); addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); addRange(statements, endLexicalEnvironment()); addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false); const updated = updateSourceFileNode(node, createNodeArray(statements, node.statements)); if (currentModuleInfo.hasExportStarsToExportValues) { - setEmitFlags(updated, EmitFlags.EmitExportStar | getEmitFlags(node)); + addEmitHelper(updated, exportStarHelper); } return updated; @@ -257,6 +258,7 @@ namespace ts { const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); // Visit each statement of the module body. + append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true)); addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); // End the lexical environment for the module body @@ -270,7 +272,7 @@ namespace ts { if (currentModuleInfo.hasExportStarsToExportValues) { // If we have any `export * from ...` declarations // we need to inform the emitter to add the __export helper. - setEmitFlags(body, EmitFlags.EmitExportStar); + addEmitHelper(body, exportStarHelper); } return body; @@ -1312,4 +1314,14 @@ namespace ts { } } } + + // emit output for the __export helper function + const exportStarHelper: EmitHelper = { + name: "typescript:export-star", + scoped: true, + text: ` + function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + }` + }; } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index adc9385935b..4b24d5c1641 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -82,6 +82,7 @@ namespace ts { // Add the body of the module. const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports); + const moduleBodyBlock = createSystemModuleBody(node, dependencyGroups); const moduleBodyFunction = createFunctionExpression( /*modifiers*/ undefined, /*asteriskToken*/ undefined, @@ -92,7 +93,7 @@ namespace ts { createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, contextObject) ], /*type*/ undefined, - createSystemModuleBody(node, dependencyGroups) + moduleBodyBlock ); // Write the call to `System.register` @@ -115,7 +116,9 @@ namespace ts { ], node.statements) ); - setEmitFlags(updated, getEmitFlags(node) & ~EmitFlags.EmitEmitHelpers); + if (!(compilerOptions.outFile || compilerOptions.out)) { + moveEmitHelpers(updated, moduleBodyBlock, helper => !helper.scoped); + } if (noSubstitution) { noSubstitutionMap[id] = noSubstitution; @@ -236,6 +239,9 @@ namespace ts { ) ); + // Visit the synthetic external helpers import declaration if present + visitNode(moduleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true); + // Visit the statements of the source file, emitting any transformations into // the `executeStatements` array. We do this *before* we fill the `setters` array // as we both emit transformations as well as aggregate some data used when creating @@ -280,9 +286,7 @@ namespace ts { ) ); - const body = createBlock(statements, /*location*/ undefined, /*multiLine*/ true); - setEmitFlags(body, EmitFlags.EmitEmitHelpers); - return body; + return createBlock(statements, /*location*/ undefined, /*multiLine*/ true); } /** @@ -394,7 +398,13 @@ namespace ts { if (localNames) { condition = createLogicalAnd( condition, - createLogicalNot(createHasOwnProperty(localNames, n)) + createLogicalNot( + createCall( + createPropertyAccess(localNames, "hasOwnProperty"), + /*typeArguments*/ undefined, + [n] + ) + ) ); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index fe264876b25..02c7b107b25 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -48,7 +48,7 @@ namespace ts { let currentNamespaceContainerName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentScopeFirstDeclarationsOfName: Map; - let currentExternalHelpersModuleName: Identifier; + let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -80,7 +80,23 @@ namespace ts { return node; } - return visitNode(node, visitor, isSourceFile); + currentSourceFile = node; + currentScope = node; + currentScopeFirstDeclarationsOfName = createMap(); + helperState = { currentSourceFile, compilerOptions }; + + let visited = visitEachChild(node, sourceElementVisitor, context); + if (compilerOptions.alwaysStrict) { + visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements)); + } + + addEmitHelpers(visited, helperState.requestedHelpers); + + currentSourceFile = undefined; + currentScope = undefined; + currentScopeFirstDeclarationsOfName = undefined; + helperState = undefined; + return visited; } /** @@ -122,10 +138,7 @@ namespace ts { * @param node The node to visit. */ function visitorWorker(node: Node): VisitResult { - if (node.kind === SyntaxKind.SourceFile) { - return visitSourceFile(node); - } - else if (node.transformFlags & TransformFlags.TypeScript) { + if (node.transformFlags & TransformFlags.TypeScript) { // This node is explicitly marked as TypeScript, so we should transform the node. return visitTypeScript(node); } @@ -252,7 +265,6 @@ namespace ts { return node; } - /** * Branching visitor, visits a TypeScript syntax node. * @@ -446,7 +458,6 @@ namespace ts { */ function onBeforeVisitNode(node: Node) { switch (node.kind) { - case SyntaxKind.SourceFile: case SyntaxKind.CaseBlock: case SyntaxKind.ModuleBlock: case SyntaxKind.Block: @@ -465,50 +476,6 @@ namespace ts { } } - function visitSourceFile(node: SourceFile) { - currentSourceFile = node; - - // ensure "use strict" is emitted in all scenarios in alwaysStrict mode - if (compilerOptions.alwaysStrict) { - node = ensureUseStrict(node); - } - - // If the source file requires any helpers and is an external module, and - // the importHelpers compiler option is enabled, emit a synthesized import - // statement for the helpers library. - if (node.flags & NodeFlags.EmitHelperFlags - && compilerOptions.importHelpers - && (isExternalModule(node) || compilerOptions.isolatedModules)) { - startLexicalEnvironment(); - const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); - const externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText); - const externalHelpersModuleImport = createImportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), - createLiteral(externalHelpersModuleNameText)); - - externalHelpersModuleImport.parent = node; - externalHelpersModuleImport.flags &= ~NodeFlags.Synthesized; - statements.push(externalHelpersModuleImport); - - currentExternalHelpersModuleName = externalHelpersModuleName; - addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); - addRange(statements, endLexicalEnvironment()); - currentExternalHelpersModuleName = undefined; - - node = updateSourceFileNode(node, createNodeArray(statements, node.statements)); - node.externalHelpersModuleName = externalHelpersModuleName; - } - else { - node = visitEachChild(node, sourceElementVisitor, context); - } - - setEmitFlags(node, EmitFlags.EmitEmitHelpers | getEmitFlags(node)); - return node; - } - /** * Tests whether we should emit a __decorate call for a class declaration. */ @@ -1418,7 +1385,7 @@ namespace ts { : undefined; const helper = createDecorateHelper( - currentExternalHelpersModuleName, + helperState, decoratorExpressions, prefix, memberName, @@ -1456,7 +1423,7 @@ namespace ts { const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); - const decorate = createDecorateHelper(currentExternalHelpersModuleName, decoratorExpressions, localName); + const decorate = createDecorateHelper(helperState, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); setEmitFlags(expression, EmitFlags.NoComments); setSourceMapRange(expression, moveRangePastDecorators(node)); @@ -1484,7 +1451,7 @@ namespace ts { expressions = []; for (const decorator of decorators) { const helper = createParamHelper( - currentExternalHelpersModuleName, + helperState, transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); @@ -1514,13 +1481,13 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { if (shouldAddTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:type", serializeTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node))); } if (shouldAddParamTypesMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:paramtypes", serializeParameterTypesOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node))); } if (shouldAddReturnTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:returntype", serializeReturnTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node))); } } } @@ -1538,7 +1505,7 @@ namespace ts { (properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node)))); } if (properties) { - decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); + decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); } } } @@ -3404,4 +3371,77 @@ namespace ts { : undefined; } } + + function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) { + requestEmitHelper(helperState, paramHelper); + return createCall( + getHelperName(helperState, "__param"), + /*typeArguments*/ undefined, + [ + createLiteral(parameterOffset), + expression + ], + location + ); + } + + function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) { + requestEmitHelper(helperState, metadataHelper); + return createCall( + getHelperName(helperState, "__metadata"), + /*typeArguments*/ undefined, + [ + createLiteral(metadataKey), + metadataValue + ] + ); + } + + function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } + + requestEmitHelper(helperState, decorateHelper); + return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); + } + + const decorateHelper: EmitHelper = { + name: "typescript:decorate", + scoped: false, + priority: 2, + text: ` + var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + };` + }; + + const metadataHelper: EmitHelper = { + name: "typescript:metadata", + scoped: false, + priority: 3, + text: ` + var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + };` + }; + + const paramHelper: EmitHelper = { + name: "typescript:param", + scoped: false, + priority: 4, + text: ` + var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + };` + }; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cfe7cc97ec5..d8043d1fc01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2088,8 +2088,6 @@ namespace ts { /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; - // The synthesized identifier for an imported external helpers module. - /* @internal */ externalHelpersModuleName?: Identifier; } export interface ScriptReferenceHost { @@ -3476,20 +3474,18 @@ namespace ts { /* @internal */ export interface EmitNode { - flags?: EmitFlags; - commentRange?: TextRange; - sourceMapRange?: TextRange; - tokenSourceMapRanges?: Map; annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. - constantValue?: number; + flags?: EmitFlags; // Flags that customize emit + commentRange?: TextRange; // The text range to use when emitting leading or trailing comments + sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings + tokenSourceMapRanges?: Map; // The text range to use when emitting source mappings for tokens + constantValue?: number; // The constant value of an expression + externalHelpersModuleName?: Identifier; // The local name for an imported helpers module + helpers?: EmitHelper[]; // Emit helpers for the node } /* @internal */ export const enum EmitFlags { - EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. - EmitExportStar = 1 << 1, // The export * helper should be written to this node. - EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. - EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. SingleLine = 1 << 5, // The contents of this node should be emitted on a single line. AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node. @@ -3517,6 +3513,14 @@ namespace ts { HasEndOfDeclarationMarker = 1 << 25, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } + /* @internal */ + export interface EmitHelper { + readonly name: string; // A unique name for this helper. + readonly scoped: boolean; // Indicates whether ther helper MUST be emitted in the current scope. + readonly text: string; // ES3-compatible raw script text. + readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node. + } + /* @internal */ export const enum EmitContext { SourceFile, // Emitting a SourceFile diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bec92b9a5ca..44e2d4b1135 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3493,153 +3493,6 @@ namespace ts { return positionIsSynthesized(range.pos) ? -1 : skipTrivia(sourceFile.text, range.pos); } - export interface ExternalModuleInfo { - externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules - exportSpecifiers: Map; // export specifiers by name - exportedBindings: Map; // exported names of local declarations - exportedNames: Identifier[]; // all exported names local to module - exportEquals: ExportAssignment | undefined; // an export= declaration if one was present - hasExportStarsToExportValues: boolean; // whether this module contains export* - } - - export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { - const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; - const exportSpecifiers = createMap(); - const exportedBindings = createMap(); - const uniqueExports = createMap(); - let hasExportDefault = false; - let exportEquals: ExportAssignment = undefined; - let hasExportStarsToExportValues = false; - for (const node of sourceFile.statements) { - switch (node.kind) { - case SyntaxKind.ImportDeclaration: - // import "mod" - // import x from "mod" - // import * as x from "mod" - // import { x, y } from "mod" - externalImports.push(node); - break; - - case SyntaxKind.ImportEqualsDeclaration: - if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference) { - // import x = require("mod") - externalImports.push(node); - } - - break; - - case SyntaxKind.ExportDeclaration: - if ((node).moduleSpecifier) { - if (!(node).exportClause) { - // export * from "mod" - externalImports.push(node); - hasExportStarsToExportValues = true; - } - else { - // export { x, y } from "mod" - externalImports.push(node); - } - } - else { - // export { x, y } - for (const specifier of (node).exportClause.elements) { - if (!uniqueExports[specifier.name.text]) { - const name = specifier.propertyName || specifier.name; - multiMapAdd(exportSpecifiers, name.text, specifier); - - const decl = resolver.getReferencedImportDeclaration(name) - || resolver.getReferencedValueDeclaration(name); - - if (decl) { - multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); - } - - uniqueExports[specifier.name.text] = specifier.name; - } - } - } - break; - - case SyntaxKind.ExportAssignment: - if ((node).isExportEquals && !exportEquals) { - // export = x - exportEquals = node; - } - break; - - case SyntaxKind.VariableStatement: - if (hasModifier(node, ModifierFlags.Export)) { - for (const decl of (node).declarationList.declarations) { - collectExportedVariableInfo(decl, uniqueExports); - } - } - break; - - case SyntaxKind.FunctionDeclaration: - if (hasModifier(node, ModifierFlags.Export)) { - if (hasModifier(node, ModifierFlags.Default)) { - // export default function() { } - if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); - hasExportDefault = true; - } - } - else { - // export function x() { } - const name = (node).name; - if (!uniqueExports[name.text]) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = name; - } - } - } - break; - - case SyntaxKind.ClassDeclaration: - if (hasModifier(node, ModifierFlags.Export)) { - if (hasModifier(node, ModifierFlags.Default)) { - // export default class { } - if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); - hasExportDefault = true; - } - } - else { - // export class x { } - const name = (node).name; - if (!uniqueExports[name.text]) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = name; - } - } - } - break; - } - } - - let exportedNames: Identifier[]; - for (const key in uniqueExports) { - exportedNames = ts.append(exportedNames, uniqueExports[key]); - } - - return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames }; - } - - function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map) { - if (isBindingPattern(decl.name)) { - for (const element of decl.name.elements) { - if (!isOmittedExpression(element)) { - collectExportedVariableInfo(element, uniqueExports); - } - } - } - else if (!isGeneratedIdentifier(decl.name)) { - if (!uniqueExports[decl.name.text]) { - uniqueExports[decl.name.text] = decl.name; - } - } - } - /** * Determines whether a name was originally the declaration name of an enum or namespace * declaration. From c1505a0ed9b1281ac64955e7359406f53bc39386 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 26 Oct 2016 18:38:59 -0700 Subject: [PATCH 02/30] Added RawExpression to move the UMD helper out of the emitter --- src/compiler/emitter.ts | 21 ++------ src/compiler/factory.ts | 15 +++++- src/compiler/transformers/module/module.ts | 14 +++++- src/compiler/types.ts | 56 +++++++++++++--------- src/compiler/utilities.ts | 5 +- 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ed16ec06159..a4096d91574 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -20,18 +20,6 @@ namespace ts { export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult { const delimiters = createDelimiterMap(); const brackets = createBracketsMap(); - - // emit output for the UMD helper function. - const umdHelper = ` -(function (dependencies, factory) { - if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); if (v !== undefined) module.exports = v; - } - else if (typeof define === 'function' && define.amd) { - define(dependencies, factory); - } -})`; - const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -674,6 +662,8 @@ namespace ts { // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: return emitPartiallyEmittedExpression(node); + case SyntaxKind.RawExpression: + return writeLines((node).text); } } @@ -711,12 +701,7 @@ namespace ts { // function emitIdentifier(node: Identifier) { - if (getEmitFlags(node) & EmitFlags.UMDDefine) { - writeLines(umdHelper); - } - else { - write(getTextOfNode(node, /*includeTrivia*/ false)); - } + write(getTextOfNode(node, /*includeTrivia*/ false)); } // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 04b7079d094..046e6f24c26 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1504,6 +1504,19 @@ namespace ts { return node; } + /** + * Creates a node that emits a string of raw text in an expression position. Raw text is never + * transformed, should be ES3 compliant, and should have the same precedence as + * PrimaryExpression. + * + * @param text The raw text of the node. + */ + export function createRawExpression(text: string) { + const node = createNode(SyntaxKind.RawExpression); + node.text = text; + return node; + } + // Compound nodes export function createComma(left: Expression, right: Expression) { @@ -1621,7 +1634,7 @@ namespace ts { // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - // Set the parent that is in parse tree + // Set the parent that is in parse tree // this makes sure that parent chain is intact for checker to traverse complete scope tree react.parent = getParseTreeNode(parent); return react; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index b946d93f32d..22e4e844a69 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -112,8 +112,7 @@ namespace ts { * @param node The SourceFile node. */ function transformUMDModule(node: SourceFile) { - const define = createIdentifier("define"); - setEmitFlags(define, EmitFlags.UMDDefine); + const define = createRawExpression(umdHelper); return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); } @@ -1324,4 +1323,15 @@ namespace ts { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; }` }; + + // emit output for the UMD helper function. + const umdHelper = ` + (function (dependencies, factory) { + if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); if (v !== undefined) module.exports = v; + } + else if (typeof define === 'function' && define.amd) { + define(dependencies, factory); + } + })`; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d8043d1fc01..526c4e15cb8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -364,6 +364,7 @@ namespace ts { PartiallyEmittedExpression, MergeDeclarationMarker, EndOfDeclarationMarker, + RawExpression, // Enum value count Count, @@ -423,7 +424,7 @@ namespace ts { ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node + HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node BlockScoped = Let | Const, @@ -1453,6 +1454,16 @@ namespace ts { kind: SyntaxKind.EndOfDeclarationMarker; } + /** + * Emits a string of raw text in an expression position. Raw text is never transformed, should + * be ES3 compliant, and should have the same precedence as PrimaryExpression. + */ + /* @internal */ + export interface RawExpression extends PrimaryExpression { + kind: SyntaxKind.RawExpression; + text: string; + } + /** * Marks the beginning of a merged transformed declaration. */ @@ -3486,31 +3497,30 @@ namespace ts { /* @internal */ export const enum EmitFlags { - UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. - SingleLine = 1 << 5, // The contents of this node should be emitted on a single line. - AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node. - NoSubstitution = 1 << 7, // Disables further substitution of an expression. - CapturesThis = 1 << 8, // The function captures a lexical `this` - NoLeadingSourceMap = 1 << 9, // Do not emit a leading source map location for this node. - NoTrailingSourceMap = 1 << 10, // Do not emit a trailing source map location for this node. + SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. + AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. + NoSubstitution = 1 << 2, // Disables further substitution of an expression. + CapturesThis = 1 << 3, // The function captures a lexical `this` + NoLeadingSourceMap = 1 << 4, // Do not emit a leading source map location for this node. + NoTrailingSourceMap = 1 << 5, // Do not emit a trailing source map location for this node. NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node. - NoNestedSourceMaps = 1 << 11, // Do not emit source map locations for children of this node. - NoTokenLeadingSourceMaps = 1 << 12, // Do not emit leading source map location for token nodes. - NoTokenTrailingSourceMaps = 1 << 13, // Do not emit trailing source map location for token nodes. + NoNestedSourceMaps = 1 << 6, // Do not emit source map locations for children of this node. + NoTokenLeadingSourceMaps = 1 << 7, // Do not emit leading source map location for token nodes. + NoTokenTrailingSourceMaps = 1 << 8, // Do not emit trailing source map location for token nodes. NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node. - NoLeadingComments = 1 << 14, // Do not emit leading comments for this node. - NoTrailingComments = 1 << 15, // Do not emit trailing comments for this node. + NoLeadingComments = 1 << 9, // Do not emit leading comments for this node. + NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. - NoNestedComments = 1 << 16, - ExportName = 1 << 17, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - LocalName = 1 << 18, // Ensure an export prefix is not added for an identifier that points to an exported declaration. - Indented = 1 << 19, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - NoIndentation = 1 << 20, // Do not indent the node. - AsyncFunctionBody = 1 << 21, - ReuseTempVariableScope = 1 << 22, // Reuse the existing temp variable scope during emit. - CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - NoHoisting = 1 << 24, // Do not hoist this declaration in --module system - HasEndOfDeclarationMarker = 1 << 25, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + NoNestedComments = 1 << 11, + ExportName = 1 << 12, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 13, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1 << 15, // Do not indent the node. + AsyncFunctionBody = 1 << 16, + ReuseTempVariableScope = 1 << 17, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1 << 18, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1 << 19, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1 << 20, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 44e2d4b1135..0ea82dfceb4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2087,6 +2087,7 @@ namespace ts { case SyntaxKind.TemplateExpression: case SyntaxKind.ParenthesizedExpression: case SyntaxKind.OmittedExpression: + case SyntaxKind.RawExpression: return 19; case SyntaxKind.TaggedTemplateExpression: @@ -3796,7 +3797,8 @@ namespace ts { || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.SuperKeyword - || kind === SyntaxKind.NonNullExpression; + || kind === SyntaxKind.NonNullExpression + || kind === SyntaxKind.RawExpression; } export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { @@ -3826,6 +3828,7 @@ namespace ts { || kind === SyntaxKind.SpreadElementExpression || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression + || kind === SyntaxKind.RawExpression || isUnaryExpressionKind(kind); } From 116c87819a737c9c31d97989d1982444e2885727 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 2 Nov 2016 11:07:33 -0700 Subject: [PATCH 03/30] Test case for property used in destructuring variable declaration --- .../reference/unusedLocalProperty.errors.txt | 18 +++++++++++++ .../reference/unusedLocalProperty.js | 25 +++++++++++++++++++ tests/cases/compiler/unusedLocalProperty.ts | 12 +++++++++ 3 files changed, 55 insertions(+) create mode 100644 tests/baselines/reference/unusedLocalProperty.errors.txt create mode 100644 tests/baselines/reference/unusedLocalProperty.js create mode 100644 tests/cases/compiler/unusedLocalProperty.ts diff --git a/tests/baselines/reference/unusedLocalProperty.errors.txt b/tests/baselines/reference/unusedLocalProperty.errors.txt new file mode 100644 index 00000000000..c9a4b3cb6d9 --- /dev/null +++ b/tests/baselines/reference/unusedLocalProperty.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/unusedLocalProperty.ts(3,25): error TS6138: Property 'species' is declared but never used. + + +==== tests/cases/compiler/unusedLocalProperty.ts (1 errors) ==== + declare var console: { log(msg: any): void; } + class Animal { + constructor(private species: string) { + ~~~~~~~ +!!! error TS6138: Property 'species' is declared but never used. + } + + printSpecies() { + let { species } = this; + console.log(species); + } + } + + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalProperty.js b/tests/baselines/reference/unusedLocalProperty.js new file mode 100644 index 00000000000..a124b752125 --- /dev/null +++ b/tests/baselines/reference/unusedLocalProperty.js @@ -0,0 +1,25 @@ +//// [unusedLocalProperty.ts] +declare var console: { log(msg: any): void; } +class Animal { + constructor(private species: string) { + } + + printSpecies() { + let { species } = this; + console.log(species); + } +} + + + +//// [unusedLocalProperty.js] +var Animal = (function () { + function Animal(species) { + this.species = species; + } + Animal.prototype.printSpecies = function () { + var species = this.species; + console.log(species); + }; + return Animal; +}()); diff --git a/tests/cases/compiler/unusedLocalProperty.ts b/tests/cases/compiler/unusedLocalProperty.ts new file mode 100644 index 00000000000..fdd33116135 --- /dev/null +++ b/tests/cases/compiler/unusedLocalProperty.ts @@ -0,0 +1,12 @@ +//@noUnusedLocals:true +declare var console: { log(msg: any): void; } +class Animal { + constructor(private species: string) { + } + + printSpecies() { + let { species } = this; + console.log(species); + } +} + From 13e8f7fadaa702bc871e98edc592c11a3d2c439a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 2 Nov 2016 11:08:34 -0700 Subject: [PATCH 04/30] Mark property referenced in the destructuring as referenced Fixes #11324 --- src/compiler/checker.ts | 28 ++++++++++------- .../reference/unusedLocalProperty.errors.txt | 18 ----------- .../reference/unusedLocalProperty.symbols | 29 ++++++++++++++++++ .../reference/unusedLocalProperty.types | 30 +++++++++++++++++++ 4 files changed, 76 insertions(+), 29 deletions(-) delete mode 100644 tests/baselines/reference/unusedLocalProperty.errors.txt create mode 100644 tests/baselines/reference/unusedLocalProperty.symbols create mode 100644 tests/baselines/reference/unusedLocalProperty.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf0c048ecbf..cfba4999212 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11504,6 +11504,21 @@ namespace ts { diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo)); } + function markPropertyAsReferenced(prop: Symbol) { + if (prop && + noUnusedIdentifiers && + (prop.flags & SymbolFlags.ClassMember) && + prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) { + if (prop.flags & SymbolFlags.Instantiated) { + getSymbolLinks(prop).target.isReferenced = true; + + } + else { + prop.isReferenced = true; + } + } + } + function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { const type = checkNonNullExpression(left); if (isTypeAny(type) || type === silentNeverType) { @@ -11523,17 +11538,7 @@ namespace ts { return unknownType; } - if (noUnusedIdentifiers && - (prop.flags & SymbolFlags.ClassMember) && - prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) { - if (prop.flags & SymbolFlags.Instantiated) { - getSymbolLinks(prop).target.isReferenced = true; - - } - else { - prop.isReferenced = true; - } - } + markPropertyAsReferenced(prop); getNodeLinks(node).resolvedSymbol = prop; @@ -16323,6 +16328,7 @@ namespace ts { const parentType = getTypeForBindingElementParent(parent); const name = node.propertyName || node.name; const property = getPropertyOfType(parentType, getTextOfPropertyName(name)); + markPropertyAsReferenced(property); if (parent.initializer && property && getParentOfSymbol(property)) { checkClassPropertyAccess(parent, parent.initializer, parentType, property); } diff --git a/tests/baselines/reference/unusedLocalProperty.errors.txt b/tests/baselines/reference/unusedLocalProperty.errors.txt deleted file mode 100644 index c9a4b3cb6d9..00000000000 --- a/tests/baselines/reference/unusedLocalProperty.errors.txt +++ /dev/null @@ -1,18 +0,0 @@ -tests/cases/compiler/unusedLocalProperty.ts(3,25): error TS6138: Property 'species' is declared but never used. - - -==== tests/cases/compiler/unusedLocalProperty.ts (1 errors) ==== - declare var console: { log(msg: any): void; } - class Animal { - constructor(private species: string) { - ~~~~~~~ -!!! error TS6138: Property 'species' is declared but never used. - } - - printSpecies() { - let { species } = this; - console.log(species); - } - } - - \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalProperty.symbols b/tests/baselines/reference/unusedLocalProperty.symbols new file mode 100644 index 00000000000..6a3343bd545 --- /dev/null +++ b/tests/baselines/reference/unusedLocalProperty.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/unusedLocalProperty.ts === +declare var console: { log(msg: any): void; } +>console : Symbol(console, Decl(unusedLocalProperty.ts, 0, 11)) +>log : Symbol(log, Decl(unusedLocalProperty.ts, 0, 22)) +>msg : Symbol(msg, Decl(unusedLocalProperty.ts, 0, 27)) + +class Animal { +>Animal : Symbol(Animal, Decl(unusedLocalProperty.ts, 0, 45)) + + constructor(private species: string) { +>species : Symbol(Animal.species, Decl(unusedLocalProperty.ts, 2, 16)) + } + + printSpecies() { +>printSpecies : Symbol(Animal.printSpecies, Decl(unusedLocalProperty.ts, 3, 5)) + + let { species } = this; +>species : Symbol(species, Decl(unusedLocalProperty.ts, 6, 13)) +>this : Symbol(Animal, Decl(unusedLocalProperty.ts, 0, 45)) + + console.log(species); +>console.log : Symbol(log, Decl(unusedLocalProperty.ts, 0, 22)) +>console : Symbol(console, Decl(unusedLocalProperty.ts, 0, 11)) +>log : Symbol(log, Decl(unusedLocalProperty.ts, 0, 22)) +>species : Symbol(species, Decl(unusedLocalProperty.ts, 6, 13)) + } +} + + diff --git a/tests/baselines/reference/unusedLocalProperty.types b/tests/baselines/reference/unusedLocalProperty.types new file mode 100644 index 00000000000..3cb8747957d --- /dev/null +++ b/tests/baselines/reference/unusedLocalProperty.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/unusedLocalProperty.ts === +declare var console: { log(msg: any): void; } +>console : { log(msg: any): void; } +>log : (msg: any) => void +>msg : any + +class Animal { +>Animal : Animal + + constructor(private species: string) { +>species : string + } + + printSpecies() { +>printSpecies : () => void + + let { species } = this; +>species : string +>this : this + + console.log(species); +>console.log(species) : void +>console.log : (msg: any) => void +>console : { log(msg: any): void; } +>log : (msg: any) => void +>species : string + } +} + + From e8c3d62d99023b8d1c2eb188bce29f5c7f00402e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 3 Nov 2016 08:14:40 -0700 Subject: [PATCH 05/30] Lock tslint version to 4.0.0-dev.0, because 4.0.0-dev.1 complains about unnecessary semicolons following properties --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1f943f89788..3bf9dbcff7b 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "travis-fold": "latest", "ts-node": "latest", "tsd": "latest", - "tslint": "next", + "tslint": "4.0.0-dev.0", "typescript": "next" }, "scripts": { From 83abd048b55f67e02e8fcbf69a9ed934056146bd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 10:01:27 -0700 Subject: [PATCH 06/30] Correct assignability for keyof types and type parameters --- src/compiler/checker.ts | 21 +++++++++++++++++++++ src/compiler/types.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 006b87cb542..b47f6264e3e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6825,6 +6825,27 @@ namespace ts { } } + if (target.flags & TypeFlags.TypeParameter) { + // Given a type parameter K with a constraint keyof T, a type S is + // assignable to K if S is assignable to keyof T. + let constraint = getConstraintOfTypeParameter(target); + if (constraint && constraint.flags & TypeFlags.Index) { + if (result = isRelatedTo(source, constraint, reportErrors)) { + return result; + } + } + } + else if (target.flags & TypeFlags.Index) { + // Given a type parameter T with a constraint C, a type S is assignable to + // keyof T if S is assignable to keyof C. + let constraint = getConstraintOfTypeParameter((target).type); + if (constraint) { + if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) { + return result; + } + } + } + if (source.flags & TypeFlags.TypeParameter) { let constraint = getConstraintOfTypeParameter(source); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e4c92c502a4..b865beca76b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2709,7 +2709,7 @@ namespace ts { EnumLike = Enum | EnumLiteral, UnionOrIntersection = Union | Intersection, StructuredType = Object | Union | Intersection, - StructuredOrTypeParameter = StructuredType | TypeParameter, + StructuredOrTypeParameter = StructuredType | TypeParameter | Index, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never From 4019265fe1bf8d7a0ef27fd1727304daff26c428 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 10:01:42 -0700 Subject: [PATCH 07/30] Update tests --- .../types/keyof/keyofAndIndexedAccess.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index b63386a68a3..1e874aaf80f 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -7,6 +7,10 @@ class Shape { visible: boolean; } +class TaggedShape extends Shape { + tag: string; +} + class Item { name: string; price: number; @@ -149,6 +153,17 @@ function f32(key: K) { return shape[key]; // Shape[K] } +function f33(shape: S, key: K) { + let name = getProperty(shape, "name"); + let prop = getProperty(shape, key); + return prop; +} + +function f34(ts: TaggedShape) { + let tag1 = f33(ts, "tag"); + let tag2 = getProperty(ts, "tag"); +} + class C { public x: string; protected y: string; @@ -164,4 +179,36 @@ function f40(c: C) { let x: X = c["x"]; let y: Y = c["y"]; let z: Z = c["z"]; +} + +// Repros from #12011 + +class Base { + get(prop: K) { + return this[prop]; + } + set(prop: K, value: this[K]) { + this[prop] = value; + } +} + +class Person extends Base { + parts: number; + constructor(parts: number) { + super(); + this.set("parts", parts); + } + getParts() { + return this.get("parts") + } +} + +class OtherPerson { + parts: number; + constructor(parts: number) { + setProperty(this, "parts", parts); + } + getParts() { + return getProperty(this, "parts") + } } \ No newline at end of file From d9b0b637e3b014de2462dde173904d05d1720790 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 10:01:53 -0700 Subject: [PATCH 08/30] Accept new baselines --- .../reference/keyofAndIndexedAccess.js | 120 +++ .../reference/keyofAndIndexedAccess.symbols | 718 +++++++++++------- .../reference/keyofAndIndexedAccess.types | 151 ++++ 3 files changed, 696 insertions(+), 293 deletions(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 5d986f3e892..dd59a5a1b4c 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -7,6 +7,10 @@ class Shape { visible: boolean; } +class TaggedShape extends Shape { + tag: string; +} + class Item { name: string; price: number; @@ -149,6 +153,17 @@ function f32(key: K) { return shape[key]; // Shape[K] } +function f33(shape: S, key: K) { + let name = getProperty(shape, "name"); + let prop = getProperty(shape, key); + return prop; +} + +function f34(ts: TaggedShape) { + let tag1 = f33(ts, "tag"); + let tag2 = getProperty(ts, "tag"); +} + class C { public x: string; protected y: string; @@ -164,14 +179,58 @@ function f40(c: C) { let x: X = c["x"]; let y: Y = c["y"]; let z: Z = c["z"]; +} + +// Repros from #12011 + +class Base { + get(prop: K) { + return this[prop]; + } + set(prop: K, value: this[K]) { + this[prop] = value; + } +} + +class Person extends Base { + parts: number; + constructor(parts: number) { + super(); + this.set("parts", parts); + } + getParts() { + return this.get("parts") + } +} + +class OtherPerson { + parts: number; + constructor(parts: number) { + setProperty(this, "parts", parts); + } + getParts() { + return getProperty(this, "parts") + } } //// [keyofAndIndexedAccess.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; var Shape = (function () { function Shape() { } return Shape; }()); +var TaggedShape = (function (_super) { + __extends(TaggedShape, _super); + function TaggedShape() { + return _super.apply(this, arguments) || this; + } + return TaggedShape; +}(Shape)); var Item = (function () { function Item() { } @@ -249,6 +308,15 @@ function f32(key) { var shape = { name: "foo", width: 5, height: 10, visible: true }; return shape[key]; // Shape[K] } +function f33(shape, key) { + var name = getProperty(shape, "name"); + var prop = getProperty(shape, key); + return prop; +} +function f34(ts) { + var tag1 = f33(ts, "tag"); + var tag2 = getProperty(ts, "tag"); +} var C = (function () { function C() { } @@ -261,6 +329,39 @@ function f40(c) { var y = c["y"]; var z = c["z"]; } +// Repros from #12011 +var Base = (function () { + function Base() { + } + Base.prototype.get = function (prop) { + return this[prop]; + }; + Base.prototype.set = function (prop, value) { + this[prop] = value; + }; + return Base; +}()); +var Person = (function (_super) { + __extends(Person, _super); + function Person(parts) { + var _this = _super.call(this) || this; + _this.set("parts", parts); + return _this; + } + Person.prototype.getParts = function () { + return this.get("parts"); + }; + return Person; +}(Base)); +var OtherPerson = (function () { + function OtherPerson(parts) { + setProperty(this, "parts", parts); + } + OtherPerson.prototype.getParts = function () { + return getProperty(this, "parts"); + }; + return OtherPerson; +}()); //// [keyofAndIndexedAccess.d.ts] @@ -270,6 +371,9 @@ declare class Shape { height: number; visible: boolean; } +declare class TaggedShape extends Shape { + tag: string; +} declare class Item { name: string; price: number; @@ -342,9 +446,25 @@ declare function pluck(array: T[], key: K): T[K][]; declare function f30(shapes: Shape[]): void; declare function f31(key: K): Shape[K]; declare function f32(key: K): Shape[K]; +declare function f33(shape: S, key: K): S[K]; +declare function f34(ts: TaggedShape): void; declare class C { x: string; protected y: string; private z; } declare function f40(c: C): void; +declare class Base { + get(prop: K): this[K]; + set(prop: K, value: this[K]): void; +} +declare class Person extends Base { + parts: number; + constructor(parts: number); + getParts(): number; +} +declare class OtherPerson { + parts: number; + constructor(parts: number); + getParts(): number; +} diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index d3e967ed125..a76cf32b8d2 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -16,562 +16,694 @@ class Shape { >visible : Symbol(Shape.visible, Decl(keyofAndIndexedAccess.ts, 4, 19)) } +class TaggedShape extends Shape { +>TaggedShape : Symbol(TaggedShape, Decl(keyofAndIndexedAccess.ts, 6, 1)) +>Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) + + tag: string; +>tag : Symbol(TaggedShape.tag, Decl(keyofAndIndexedAccess.ts, 8, 33)) +} + class Item { ->Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 6, 1)) +>Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 10, 1)) name: string; ->name : Symbol(Item.name, Decl(keyofAndIndexedAccess.ts, 8, 12)) +>name : Symbol(Item.name, Decl(keyofAndIndexedAccess.ts, 12, 12)) price: number; ->price : Symbol(Item.price, Decl(keyofAndIndexedAccess.ts, 9, 17)) +>price : Symbol(Item.price, Decl(keyofAndIndexedAccess.ts, 13, 17)) } class Options { ->Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 11, 1)) +>Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 15, 1)) visible: "yes" | "no"; ->visible : Symbol(Options.visible, Decl(keyofAndIndexedAccess.ts, 13, 15)) +>visible : Symbol(Options.visible, Decl(keyofAndIndexedAccess.ts, 17, 15)) } type Dictionary = { [x: string]: T }; ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 17, 16)) ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 17, 24)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 17, 16)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 21, 16)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 21, 24)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 21, 16)) const enum E { A, B, C } ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) ->A : Symbol(E.A, Decl(keyofAndIndexedAccess.ts, 19, 14)) ->B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 19, 17)) ->C : Symbol(E.C, Decl(keyofAndIndexedAccess.ts, 19, 20)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) +>A : Symbol(E.A, Decl(keyofAndIndexedAccess.ts, 23, 14)) +>B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 23, 17)) +>C : Symbol(E.C, Decl(keyofAndIndexedAccess.ts, 23, 20)) type K00 = keyof any; // string | number ->K00 : Symbol(K00, Decl(keyofAndIndexedAccess.ts, 19, 24)) +>K00 : Symbol(K00, Decl(keyofAndIndexedAccess.ts, 23, 24)) type K01 = keyof string; // number | "toString" | "charAt" | ... ->K01 : Symbol(K01, Decl(keyofAndIndexedAccess.ts, 21, 21)) +>K01 : Symbol(K01, Decl(keyofAndIndexedAccess.ts, 25, 21)) type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ... ->K02 : Symbol(K02, Decl(keyofAndIndexedAccess.ts, 22, 24)) +>K02 : Symbol(K02, Decl(keyofAndIndexedAccess.ts, 26, 24)) type K03 = keyof boolean; // "valueOf" ->K03 : Symbol(K03, Decl(keyofAndIndexedAccess.ts, 23, 24)) +>K03 : Symbol(K03, Decl(keyofAndIndexedAccess.ts, 27, 24)) type K04 = keyof void; // never ->K04 : Symbol(K04, Decl(keyofAndIndexedAccess.ts, 24, 25)) +>K04 : Symbol(K04, Decl(keyofAndIndexedAccess.ts, 28, 25)) type K05 = keyof undefined; // never ->K05 : Symbol(K05, Decl(keyofAndIndexedAccess.ts, 25, 22)) +>K05 : Symbol(K05, Decl(keyofAndIndexedAccess.ts, 29, 22)) type K06 = keyof null; // never ->K06 : Symbol(K06, Decl(keyofAndIndexedAccess.ts, 26, 27)) +>K06 : Symbol(K06, Decl(keyofAndIndexedAccess.ts, 30, 27)) type K07 = keyof never; // never ->K07 : Symbol(K07, Decl(keyofAndIndexedAccess.ts, 27, 22)) +>K07 : Symbol(K07, Decl(keyofAndIndexedAccess.ts, 31, 22)) type K10 = keyof Shape; // "name" | "width" | "height" | "visible" ->K10 : Symbol(K10, Decl(keyofAndIndexedAccess.ts, 28, 23)) +>K10 : Symbol(K10, Decl(keyofAndIndexedAccess.ts, 32, 23)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type K11 = keyof Shape[]; // number | "length" | "toString" | ... ->K11 : Symbol(K11, Decl(keyofAndIndexedAccess.ts, 30, 23)) +>K11 : Symbol(K11, Decl(keyofAndIndexedAccess.ts, 34, 23)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type K12 = keyof Dictionary; // string | number ->K12 : Symbol(K12, Decl(keyofAndIndexedAccess.ts, 31, 25)) ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) +>K12 : Symbol(K12, Decl(keyofAndIndexedAccess.ts, 35, 25)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type K13 = keyof {}; // never ->K13 : Symbol(K13, Decl(keyofAndIndexedAccess.ts, 32, 35)) +>K13 : Symbol(K13, Decl(keyofAndIndexedAccess.ts, 36, 35)) type K14 = keyof Object; // "constructor" | "toString" | ... ->K14 : Symbol(K14, Decl(keyofAndIndexedAccess.ts, 33, 20)) +>K14 : Symbol(K14, Decl(keyofAndIndexedAccess.ts, 37, 20)) >Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ... ->K15 : Symbol(K15, Decl(keyofAndIndexedAccess.ts, 34, 24)) ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) +>K15 : Symbol(K15, Decl(keyofAndIndexedAccess.ts, 38, 24)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) type K16 = keyof [string, number]; // number | "0" | "1" | "length" | "toString" | ... ->K16 : Symbol(K16, Decl(keyofAndIndexedAccess.ts, 35, 19)) +>K16 : Symbol(K16, Decl(keyofAndIndexedAccess.ts, 39, 19)) type K17 = keyof (Shape | Item); // "name" ->K17 : Symbol(K17, Decl(keyofAndIndexedAccess.ts, 36, 34)) +>K17 : Symbol(K17, Decl(keyofAndIndexedAccess.ts, 40, 34)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 6, 1)) +>Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 10, 1)) type K18 = keyof (Shape & Item); // "name" | "width" | "height" | "visible" | "price" ->K18 : Symbol(K18, Decl(keyofAndIndexedAccess.ts, 37, 32)) +>K18 : Symbol(K18, Decl(keyofAndIndexedAccess.ts, 41, 32)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 6, 1)) +>Item : Symbol(Item, Decl(keyofAndIndexedAccess.ts, 10, 1)) type KeyOf = keyof T; ->KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 38, 32)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 40, 11)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 40, 11)) +>KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 42, 32)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 44, 11)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 44, 11)) type K20 = KeyOf; // "name" | "width" | "height" | "visible" ->K20 : Symbol(K20, Decl(keyofAndIndexedAccess.ts, 40, 24)) ->KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 38, 32)) +>K20 : Symbol(K20, Decl(keyofAndIndexedAccess.ts, 44, 24)) +>KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 42, 32)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type K21 = KeyOf>; // string | number ->K21 : Symbol(K21, Decl(keyofAndIndexedAccess.ts, 42, 24)) ->KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 38, 32)) ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) +>K21 : Symbol(K21, Decl(keyofAndIndexedAccess.ts, 46, 24)) +>KeyOf : Symbol(KeyOf, Decl(keyofAndIndexedAccess.ts, 42, 32)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type NAME = "name"; ->NAME : Symbol(NAME, Decl(keyofAndIndexedAccess.ts, 43, 36)) +>NAME : Symbol(NAME, Decl(keyofAndIndexedAccess.ts, 47, 36)) type WIDTH_OR_HEIGHT = "width" | "height"; ->WIDTH_OR_HEIGHT : Symbol(WIDTH_OR_HEIGHT, Decl(keyofAndIndexedAccess.ts, 45, 19)) +>WIDTH_OR_HEIGHT : Symbol(WIDTH_OR_HEIGHT, Decl(keyofAndIndexedAccess.ts, 49, 19)) type Q10 = Shape["name"]; // string ->Q10 : Symbol(Q10, Decl(keyofAndIndexedAccess.ts, 46, 42)) +>Q10 : Symbol(Q10, Decl(keyofAndIndexedAccess.ts, 50, 42)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type Q11 = Shape["width" | "height"]; // number ->Q11 : Symbol(Q11, Decl(keyofAndIndexedAccess.ts, 48, 25)) +>Q11 : Symbol(Q11, Decl(keyofAndIndexedAccess.ts, 52, 25)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type Q12 = Shape["name" | "visible"]; // string | boolean ->Q12 : Symbol(Q12, Decl(keyofAndIndexedAccess.ts, 49, 37)) +>Q12 : Symbol(Q12, Decl(keyofAndIndexedAccess.ts, 53, 37)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type Q20 = Shape[NAME]; // string ->Q20 : Symbol(Q20, Decl(keyofAndIndexedAccess.ts, 50, 37)) +>Q20 : Symbol(Q20, Decl(keyofAndIndexedAccess.ts, 54, 37)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->NAME : Symbol(NAME, Decl(keyofAndIndexedAccess.ts, 43, 36)) +>NAME : Symbol(NAME, Decl(keyofAndIndexedAccess.ts, 47, 36)) type Q21 = Shape[WIDTH_OR_HEIGHT]; // number ->Q21 : Symbol(Q21, Decl(keyofAndIndexedAccess.ts, 52, 23)) +>Q21 : Symbol(Q21, Decl(keyofAndIndexedAccess.ts, 56, 23)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->WIDTH_OR_HEIGHT : Symbol(WIDTH_OR_HEIGHT, Decl(keyofAndIndexedAccess.ts, 45, 19)) +>WIDTH_OR_HEIGHT : Symbol(WIDTH_OR_HEIGHT, Decl(keyofAndIndexedAccess.ts, 49, 19)) type Q30 = [string, number][0]; // string ->Q30 : Symbol(Q30, Decl(keyofAndIndexedAccess.ts, 53, 34)) +>Q30 : Symbol(Q30, Decl(keyofAndIndexedAccess.ts, 57, 34)) type Q31 = [string, number][1]; // number ->Q31 : Symbol(Q31, Decl(keyofAndIndexedAccess.ts, 55, 31)) +>Q31 : Symbol(Q31, Decl(keyofAndIndexedAccess.ts, 59, 31)) type Q32 = [string, number][2]; // string | number ->Q32 : Symbol(Q32, Decl(keyofAndIndexedAccess.ts, 56, 31)) +>Q32 : Symbol(Q32, Decl(keyofAndIndexedAccess.ts, 60, 31)) type Q33 = [string, number][E.A]; // string ->Q33 : Symbol(Q33, Decl(keyofAndIndexedAccess.ts, 57, 31)) ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) ->A : Symbol(E.A, Decl(keyofAndIndexedAccess.ts, 19, 14)) +>Q33 : Symbol(Q33, Decl(keyofAndIndexedAccess.ts, 61, 31)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) +>A : Symbol(E.A, Decl(keyofAndIndexedAccess.ts, 23, 14)) type Q34 = [string, number][E.B]; // number ->Q34 : Symbol(Q34, Decl(keyofAndIndexedAccess.ts, 58, 33)) ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) ->B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 19, 17)) +>Q34 : Symbol(Q34, Decl(keyofAndIndexedAccess.ts, 62, 33)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) +>B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 23, 17)) type Q35 = [string, number][E.C]; // string | number ->Q35 : Symbol(Q35, Decl(keyofAndIndexedAccess.ts, 59, 33)) ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) ->C : Symbol(E.C, Decl(keyofAndIndexedAccess.ts, 19, 20)) +>Q35 : Symbol(Q35, Decl(keyofAndIndexedAccess.ts, 63, 33)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) +>C : Symbol(E.C, Decl(keyofAndIndexedAccess.ts, 23, 20)) type Q36 = [string, number]["0"]; // string ->Q36 : Symbol(Q36, Decl(keyofAndIndexedAccess.ts, 60, 33)) +>Q36 : Symbol(Q36, Decl(keyofAndIndexedAccess.ts, 64, 33)) type Q37 = [string, number]["1"]; // string ->Q37 : Symbol(Q37, Decl(keyofAndIndexedAccess.ts, 61, 33)) +>Q37 : Symbol(Q37, Decl(keyofAndIndexedAccess.ts, 65, 33)) type Q40 = (Shape | Options)["visible"]; // boolean | "yes" | "no" ->Q40 : Symbol(Q40, Decl(keyofAndIndexedAccess.ts, 62, 33)) +>Q40 : Symbol(Q40, Decl(keyofAndIndexedAccess.ts, 66, 33)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 11, 1)) +>Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 15, 1)) type Q41 = (Shape & Options)["visible"]; // true & "yes" | true & "no" | false & "yes" | false & "no" ->Q41 : Symbol(Q41, Decl(keyofAndIndexedAccess.ts, 64, 40)) +>Q41 : Symbol(Q41, Decl(keyofAndIndexedAccess.ts, 68, 40)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 11, 1)) +>Options : Symbol(Options, Decl(keyofAndIndexedAccess.ts, 15, 1)) type Q50 = Dictionary["howdy"]; // Shape ->Q50 : Symbol(Q50, Decl(keyofAndIndexedAccess.ts, 65, 40)) ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) +>Q50 : Symbol(Q50, Decl(keyofAndIndexedAccess.ts, 69, 40)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type Q51 = Dictionary[123]; // Shape ->Q51 : Symbol(Q51, Decl(keyofAndIndexedAccess.ts, 67, 38)) ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) +>Q51 : Symbol(Q51, Decl(keyofAndIndexedAccess.ts, 71, 38)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) type Q52 = Dictionary[E.B]; // Shape ->Q52 : Symbol(Q52, Decl(keyofAndIndexedAccess.ts, 68, 34)) ->Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 15, 1)) +>Q52 : Symbol(Q52, Decl(keyofAndIndexedAccess.ts, 72, 34)) +>Dictionary : Symbol(Dictionary, Decl(keyofAndIndexedAccess.ts, 19, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 17, 40)) ->B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 19, 17)) +>E : Symbol(E, Decl(keyofAndIndexedAccess.ts, 21, 40)) +>B : Symbol(E.B, Decl(keyofAndIndexedAccess.ts, 23, 17)) declare let cond: boolean; ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) function getProperty(obj: T, key: K) { ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 73, 21)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 73, 23)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 73, 21)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 73, 43)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 73, 21)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 73, 50)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 73, 23)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 77, 23)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 77, 43)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 77, 50)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 77, 23)) return obj[key]; ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 73, 43)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 73, 50)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 77, 43)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 77, 50)) } function setProperty(obj: T, key: K, value: T[K]) { ->setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 75, 1)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 77, 23)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 77, 43)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 77, 50)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 77, 23)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 77, 58)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 77, 21)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 77, 23)) +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 81, 21)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 81, 23)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 81, 21)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 81, 43)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 81, 21)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 81, 50)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 81, 23)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 81, 58)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 81, 21)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 81, 23)) obj[key] = value; ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 77, 43)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 77, 50)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 77, 58)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 81, 43)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 81, 50)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 81, 58)) } function f10(shape: Shape) { ->f10 : Symbol(f10, Decl(keyofAndIndexedAccess.ts, 79, 1)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) +>f10 : Symbol(f10, Decl(keyofAndIndexedAccess.ts, 83, 1)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) let name = getProperty(shape, "name"); // string ->name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 82, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) +>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 86, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) let widthOrHeight = getProperty(shape, cond ? "width" : "height"); // number ->widthOrHeight : Symbol(widthOrHeight, Decl(keyofAndIndexedAccess.ts, 83, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>widthOrHeight : Symbol(widthOrHeight, Decl(keyofAndIndexedAccess.ts, 87, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) let nameOrVisible = getProperty(shape, cond ? "name" : "visible"); // string | boolean ->nameOrVisible : Symbol(nameOrVisible, Decl(keyofAndIndexedAccess.ts, 84, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>nameOrVisible : Symbol(nameOrVisible, Decl(keyofAndIndexedAccess.ts, 88, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) setProperty(shape, "name", "rectangle"); ->setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 75, 1)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) setProperty(shape, cond ? "width" : "height", 10); ->setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 75, 1)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) setProperty(shape, cond ? "name" : "visible", true); // Technically not safe ->setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 75, 1)) ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 81, 13)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 85, 13)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) } function f11(a: Shape[]) { ->f11 : Symbol(f11, Decl(keyofAndIndexedAccess.ts, 88, 1)) ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 90, 13)) +>f11 : Symbol(f11, Decl(keyofAndIndexedAccess.ts, 92, 1)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 94, 13)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) let len = getProperty(a, "length"); // number ->len : Symbol(len, Decl(keyofAndIndexedAccess.ts, 91, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 90, 13)) +>len : Symbol(len, Decl(keyofAndIndexedAccess.ts, 95, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 94, 13)) let shape = getProperty(a, 1000); // Shape ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 92, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 90, 13)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 96, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 94, 13)) setProperty(a, 1000, getProperty(a, 1001)); ->setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 75, 1)) ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 90, 13)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 90, 13)) +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 94, 13)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 94, 13)) } function f12(t: [Shape, boolean]) { ->f12 : Symbol(f12, Decl(keyofAndIndexedAccess.ts, 94, 1)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>f12 : Symbol(f12, Decl(keyofAndIndexedAccess.ts, 98, 1)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) let len = getProperty(t, "length"); ->len : Symbol(len, Decl(keyofAndIndexedAccess.ts, 97, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>len : Symbol(len, Decl(keyofAndIndexedAccess.ts, 101, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) let s1 = getProperty(t, 0); // Shape ->s1 : Symbol(s1, Decl(keyofAndIndexedAccess.ts, 98, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>s1 : Symbol(s1, Decl(keyofAndIndexedAccess.ts, 102, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) let s2 = getProperty(t, "0"); // Shape ->s2 : Symbol(s2, Decl(keyofAndIndexedAccess.ts, 99, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>s2 : Symbol(s2, Decl(keyofAndIndexedAccess.ts, 103, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) let b1 = getProperty(t, 1); // boolean ->b1 : Symbol(b1, Decl(keyofAndIndexedAccess.ts, 100, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>b1 : Symbol(b1, Decl(keyofAndIndexedAccess.ts, 104, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) let b2 = getProperty(t, "1"); // boolean ->b2 : Symbol(b2, Decl(keyofAndIndexedAccess.ts, 101, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>b2 : Symbol(b2, Decl(keyofAndIndexedAccess.ts, 105, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) let x1 = getProperty(t, 2); // Shape | boolean ->x1 : Symbol(x1, Decl(keyofAndIndexedAccess.ts, 102, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 96, 13)) +>x1 : Symbol(x1, Decl(keyofAndIndexedAccess.ts, 106, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 100, 13)) } function f13(foo: any, bar: any) { ->f13 : Symbol(f13, Decl(keyofAndIndexedAccess.ts, 103, 1)) ->foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 105, 13)) ->bar : Symbol(bar, Decl(keyofAndIndexedAccess.ts, 105, 22)) +>f13 : Symbol(f13, Decl(keyofAndIndexedAccess.ts, 107, 1)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 109, 13)) +>bar : Symbol(bar, Decl(keyofAndIndexedAccess.ts, 109, 22)) let x = getProperty(foo, "x"); // any ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 106, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 105, 13)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 110, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 109, 13)) let y = getProperty(foo, 100); // any ->y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 107, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 105, 13)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 111, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 109, 13)) let z = getProperty(foo, bar); // any ->z : Symbol(z, Decl(keyofAndIndexedAccess.ts, 108, 7)) ->getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 71, 26)) ->foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 105, 13)) ->bar : Symbol(bar, Decl(keyofAndIndexedAccess.ts, 105, 22)) +>z : Symbol(z, Decl(keyofAndIndexedAccess.ts, 112, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 109, 13)) +>bar : Symbol(bar, Decl(keyofAndIndexedAccess.ts, 109, 22)) } class Component { ->Component : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 109, 1)) ->PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 111, 16)) +>Component : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 113, 1)) +>PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 115, 16)) props: PropType; ->props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 111, 27)) ->PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 111, 16)) +>props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 115, 27)) +>PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 115, 16)) getProperty(key: K) { ->getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 113, 16)) ->PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 111, 16)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 113, 42)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 113, 16)) +>getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 117, 16)) +>PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 115, 16)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 117, 42)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 117, 16)) return this.props[key]; ->this.props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 111, 27)) ->this : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 109, 1)) ->props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 111, 27)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 113, 42)) +>this.props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 115, 27)) +>this : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 113, 1)) +>props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 115, 27)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 117, 42)) } setProperty(key: K, value: PropType[K]) { ->setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 116, 16)) ->PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 111, 16)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 116, 42)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 116, 16)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 116, 49)) ->PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 111, 16)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 116, 16)) +>setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 120, 16)) +>PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 115, 16)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 120, 42)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 120, 16)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 120, 49)) +>PropType : Symbol(PropType, Decl(keyofAndIndexedAccess.ts, 115, 16)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 120, 16)) this.props[key] = value; ->this.props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 111, 27)) ->this : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 109, 1)) ->props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 111, 27)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 116, 42)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 116, 49)) +>this.props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 115, 27)) +>this : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 113, 1)) +>props : Symbol(Component.props, Decl(keyofAndIndexedAccess.ts, 115, 27)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 120, 42)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 120, 49)) } } function f20(component: Component) { ->f20 : Symbol(f20, Decl(keyofAndIndexedAccess.ts, 119, 1)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->Component : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 109, 1)) +>f20 : Symbol(f20, Decl(keyofAndIndexedAccess.ts, 123, 1)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>Component : Symbol(Component, Decl(keyofAndIndexedAccess.ts, 113, 1)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) let name = component.getProperty("name"); // string ->name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 122, 7)) ->component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) +>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 126, 7)) +>component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) let widthOrHeight = component.getProperty(cond ? "width" : "height"); // number ->widthOrHeight : Symbol(widthOrHeight, Decl(keyofAndIndexedAccess.ts, 123, 7)) ->component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>widthOrHeight : Symbol(widthOrHeight, Decl(keyofAndIndexedAccess.ts, 127, 7)) +>component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) let nameOrVisible = component.getProperty(cond ? "name" : "visible"); // string | boolean ->nameOrVisible : Symbol(nameOrVisible, Decl(keyofAndIndexedAccess.ts, 124, 7)) ->component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 112, 20)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>nameOrVisible : Symbol(nameOrVisible, Decl(keyofAndIndexedAccess.ts, 128, 7)) +>component.getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>getProperty : Symbol(Component.getProperty, Decl(keyofAndIndexedAccess.ts, 116, 20)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) component.setProperty("name", "rectangle"); ->component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) +>component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) component.setProperty(cond ? "width" : "height", 10) ->component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) component.setProperty(cond ? "name" : "visible", true); // Technically not safe ->component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 121, 13)) ->setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 115, 5)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>component.setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>component : Symbol(component, Decl(keyofAndIndexedAccess.ts, 125, 13)) +>setProperty : Symbol(Component.setProperty, Decl(keyofAndIndexedAccess.ts, 119, 5)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) } function pluck(array: T[], key: K) { ->pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 128, 1)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 130, 15)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 130, 17)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 130, 15)) ->array : Symbol(array, Decl(keyofAndIndexedAccess.ts, 130, 37)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 130, 15)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 130, 48)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 130, 17)) +>pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 132, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 134, 15)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 134, 17)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 134, 15)) +>array : Symbol(array, Decl(keyofAndIndexedAccess.ts, 134, 37)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 134, 15)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 134, 48)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 134, 17)) return array.map(x => x[key]); >array.map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->array : Symbol(array, Decl(keyofAndIndexedAccess.ts, 130, 37)) +>array : Symbol(array, Decl(keyofAndIndexedAccess.ts, 134, 37)) >map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 131, 21)) ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 131, 21)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 130, 48)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 135, 21)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 135, 21)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 134, 48)) } function f30(shapes: Shape[]) { ->f30 : Symbol(f30, Decl(keyofAndIndexedAccess.ts, 132, 1)) ->shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 134, 13)) +>f30 : Symbol(f30, Decl(keyofAndIndexedAccess.ts, 136, 1)) +>shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 138, 13)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) let names = pluck(shapes, "name"); // string[] ->names : Symbol(names, Decl(keyofAndIndexedAccess.ts, 135, 7)) ->pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 128, 1)) ->shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 134, 13)) +>names : Symbol(names, Decl(keyofAndIndexedAccess.ts, 139, 7)) +>pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 132, 1)) +>shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 138, 13)) let widths = pluck(shapes, "width"); // number[] ->widths : Symbol(widths, Decl(keyofAndIndexedAccess.ts, 136, 7)) ->pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 128, 1)) ->shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 134, 13)) +>widths : Symbol(widths, Decl(keyofAndIndexedAccess.ts, 140, 7)) +>pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 132, 1)) +>shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 138, 13)) let nameOrVisibles = pluck(shapes, cond ? "name" : "visible"); // (string | boolean)[] ->nameOrVisibles : Symbol(nameOrVisibles, Decl(keyofAndIndexedAccess.ts, 137, 7)) ->pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 128, 1)) ->shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 134, 13)) ->cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 71, 11)) +>nameOrVisibles : Symbol(nameOrVisibles, Decl(keyofAndIndexedAccess.ts, 141, 7)) +>pluck : Symbol(pluck, Decl(keyofAndIndexedAccess.ts, 132, 1)) +>shapes : Symbol(shapes, Decl(keyofAndIndexedAccess.ts, 138, 13)) +>cond : Symbol(cond, Decl(keyofAndIndexedAccess.ts, 75, 11)) } function f31(key: K) { ->f31 : Symbol(f31, Decl(keyofAndIndexedAccess.ts, 138, 1)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 140, 13)) +>f31 : Symbol(f31, Decl(keyofAndIndexedAccess.ts, 142, 1)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 144, 13)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 140, 36)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 140, 13)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 144, 36)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 144, 13)) const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 141, 9)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 145, 9)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 141, 26)) ->width : Symbol(width, Decl(keyofAndIndexedAccess.ts, 141, 39)) ->height : Symbol(height, Decl(keyofAndIndexedAccess.ts, 141, 49)) ->visible : Symbol(visible, Decl(keyofAndIndexedAccess.ts, 141, 61)) +>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 145, 26)) +>width : Symbol(width, Decl(keyofAndIndexedAccess.ts, 145, 39)) +>height : Symbol(height, Decl(keyofAndIndexedAccess.ts, 145, 49)) +>visible : Symbol(visible, Decl(keyofAndIndexedAccess.ts, 145, 61)) return shape[key]; // Shape[K] ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 141, 9)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 140, 36)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 145, 9)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 144, 36)) } function f32(key: K) { ->f32 : Symbol(f32, Decl(keyofAndIndexedAccess.ts, 143, 1)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 145, 13)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 145, 43)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 145, 13)) +>f32 : Symbol(f32, Decl(keyofAndIndexedAccess.ts, 147, 1)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 149, 13)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 149, 43)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 149, 13)) const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 146, 9)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 150, 9)) >Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) ->name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 146, 26)) ->width : Symbol(width, Decl(keyofAndIndexedAccess.ts, 146, 39)) ->height : Symbol(height, Decl(keyofAndIndexedAccess.ts, 146, 49)) ->visible : Symbol(visible, Decl(keyofAndIndexedAccess.ts, 146, 61)) +>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 150, 26)) +>width : Symbol(width, Decl(keyofAndIndexedAccess.ts, 150, 39)) +>height : Symbol(height, Decl(keyofAndIndexedAccess.ts, 150, 49)) +>visible : Symbol(visible, Decl(keyofAndIndexedAccess.ts, 150, 61)) return shape[key]; // Shape[K] ->shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 146, 9)) ->key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 145, 43)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 150, 9)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 149, 43)) +} + +function f33(shape: S, key: K) { +>f33 : Symbol(f33, Decl(keyofAndIndexedAccess.ts, 152, 1)) +>S : Symbol(S, Decl(keyofAndIndexedAccess.ts, 154, 13)) +>Shape : Symbol(Shape, Decl(keyofAndIndexedAccess.ts, 0, 0)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 154, 29)) +>S : Symbol(S, Decl(keyofAndIndexedAccess.ts, 154, 13)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 154, 49)) +>S : Symbol(S, Decl(keyofAndIndexedAccess.ts, 154, 13)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 154, 58)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 154, 29)) + + let name = getProperty(shape, "name"); +>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 155, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 154, 49)) + + let prop = getProperty(shape, key); +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 156, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>shape : Symbol(shape, Decl(keyofAndIndexedAccess.ts, 154, 49)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 154, 58)) + + return prop; +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 156, 7)) +} + +function f34(ts: TaggedShape) { +>f34 : Symbol(f34, Decl(keyofAndIndexedAccess.ts, 158, 1)) +>ts : Symbol(ts, Decl(keyofAndIndexedAccess.ts, 160, 13)) +>TaggedShape : Symbol(TaggedShape, Decl(keyofAndIndexedAccess.ts, 6, 1)) + + let tag1 = f33(ts, "tag"); +>tag1 : Symbol(tag1, Decl(keyofAndIndexedAccess.ts, 161, 7)) +>f33 : Symbol(f33, Decl(keyofAndIndexedAccess.ts, 152, 1)) +>ts : Symbol(ts, Decl(keyofAndIndexedAccess.ts, 160, 13)) + + let tag2 = getProperty(ts, "tag"); +>tag2 : Symbol(tag2, Decl(keyofAndIndexedAccess.ts, 162, 7)) +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>ts : Symbol(ts, Decl(keyofAndIndexedAccess.ts, 160, 13)) } class C { ->C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 148, 1)) +>C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 163, 1)) public x: string; ->x : Symbol(C.x, Decl(keyofAndIndexedAccess.ts, 150, 9)) +>x : Symbol(C.x, Decl(keyofAndIndexedAccess.ts, 165, 9)) protected y: string; ->y : Symbol(C.y, Decl(keyofAndIndexedAccess.ts, 151, 21)) +>y : Symbol(C.y, Decl(keyofAndIndexedAccess.ts, 166, 21)) private z: string; ->z : Symbol(C.z, Decl(keyofAndIndexedAccess.ts, 152, 24)) +>z : Symbol(C.z, Decl(keyofAndIndexedAccess.ts, 167, 24)) } // Indexed access expressions have always permitted access to private and protected members. // For consistency we also permit such access in indexed access types. function f40(c: C) { ->f40 : Symbol(f40, Decl(keyofAndIndexedAccess.ts, 154, 1)) ->c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 158, 13)) ->C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 148, 1)) +>f40 : Symbol(f40, Decl(keyofAndIndexedAccess.ts, 169, 1)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 173, 13)) +>C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 163, 1)) type X = C["x"]; ->X : Symbol(X, Decl(keyofAndIndexedAccess.ts, 158, 20)) ->C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 148, 1)) +>X : Symbol(X, Decl(keyofAndIndexedAccess.ts, 173, 20)) +>C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 163, 1)) type Y = C["y"]; ->Y : Symbol(Y, Decl(keyofAndIndexedAccess.ts, 159, 20)) ->C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 148, 1)) +>Y : Symbol(Y, Decl(keyofAndIndexedAccess.ts, 174, 20)) +>C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 163, 1)) type Z = C["z"]; ->Z : Symbol(Z, Decl(keyofAndIndexedAccess.ts, 160, 20)) ->C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 148, 1)) +>Z : Symbol(Z, Decl(keyofAndIndexedAccess.ts, 175, 20)) +>C : Symbol(C, Decl(keyofAndIndexedAccess.ts, 163, 1)) let x: X = c["x"]; ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 162, 7)) ->X : Symbol(X, Decl(keyofAndIndexedAccess.ts, 158, 20)) ->c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 158, 13)) ->"x" : Symbol(C.x, Decl(keyofAndIndexedAccess.ts, 150, 9)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 177, 7)) +>X : Symbol(X, Decl(keyofAndIndexedAccess.ts, 173, 20)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 173, 13)) +>"x" : Symbol(C.x, Decl(keyofAndIndexedAccess.ts, 165, 9)) let y: Y = c["y"]; ->y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 163, 7)) ->Y : Symbol(Y, Decl(keyofAndIndexedAccess.ts, 159, 20)) ->c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 158, 13)) ->"y" : Symbol(C.y, Decl(keyofAndIndexedAccess.ts, 151, 21)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 178, 7)) +>Y : Symbol(Y, Decl(keyofAndIndexedAccess.ts, 174, 20)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 173, 13)) +>"y" : Symbol(C.y, Decl(keyofAndIndexedAccess.ts, 166, 21)) let z: Z = c["z"]; ->z : Symbol(z, Decl(keyofAndIndexedAccess.ts, 164, 7)) ->Z : Symbol(Z, Decl(keyofAndIndexedAccess.ts, 160, 20)) ->c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 158, 13)) ->"z" : Symbol(C.z, Decl(keyofAndIndexedAccess.ts, 152, 24)) +>z : Symbol(z, Decl(keyofAndIndexedAccess.ts, 179, 7)) +>Z : Symbol(Z, Decl(keyofAndIndexedAccess.ts, 175, 20)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 173, 13)) +>"z" : Symbol(C.z, Decl(keyofAndIndexedAccess.ts, 167, 24)) +} + +// Repros from #12011 + +class Base { +>Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 180, 1)) + + get(prop: K) { +>get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 184, 12)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 185, 8)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 185, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 185, 8)) + + return this[prop]; +>this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 180, 1)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 185, 30)) + } + set(prop: K, value: this[K]) { +>set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 187, 5)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 188, 8)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 188, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 188, 8)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 188, 38)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 188, 8)) + + this[prop] = value; +>this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 180, 1)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 188, 30)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 188, 38)) + } +} + +class Person extends Base { +>Person : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 191, 1)) +>Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 180, 1)) + + parts: number; +>parts : Symbol(Person.parts, Decl(keyofAndIndexedAccess.ts, 193, 27)) + + constructor(parts: number) { +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 195, 16)) + + super(); +>super : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 180, 1)) + + this.set("parts", parts); +>this.set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 187, 5)) +>this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 191, 1)) +>set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 187, 5)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 195, 16)) + } + getParts() { +>getParts : Symbol(Person.getParts, Decl(keyofAndIndexedAccess.ts, 198, 5)) + + return this.get("parts") +>this.get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 184, 12)) +>this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 191, 1)) +>get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 184, 12)) + } +} + +class OtherPerson { +>OtherPerson : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 202, 1)) + + parts: number; +>parts : Symbol(OtherPerson.parts, Decl(keyofAndIndexedAccess.ts, 204, 19)) + + constructor(parts: number) { +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 206, 16)) + + setProperty(this, "parts", parts); +>setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 79, 1)) +>this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 202, 1)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 206, 16)) + } + getParts() { +>getParts : Symbol(OtherPerson.getParts, Decl(keyofAndIndexedAccess.ts, 208, 5)) + + return getProperty(this, "parts") +>getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 75, 26)) +>this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 202, 1)) + } } diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 74c06a2535d..a5395db8292 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -16,6 +16,14 @@ class Shape { >visible : boolean } +class TaggedShape extends Shape { +>TaggedShape : TaggedShape +>Shape : Shape + + tag: string; +>tag : string +} + class Item { >Item : Item @@ -626,6 +634,55 @@ function f32(key: K) { >key : K } +function f33(shape: S, key: K) { +>f33 : (shape: S, key: K) => S[K] +>S : S +>Shape : Shape +>K : K +>S : S +>shape : S +>S : S +>key : K +>K : K + + let name = getProperty(shape, "name"); +>name : string +>getProperty(shape, "name") : string +>getProperty : (obj: T, key: K) => T[K] +>shape : S +>"name" : "name" + + let prop = getProperty(shape, key); +>prop : S[K] +>getProperty(shape, key) : S[K] +>getProperty : (obj: T, key: K) => T[K] +>shape : S +>key : K + + return prop; +>prop : S[K] +} + +function f34(ts: TaggedShape) { +>f34 : (ts: TaggedShape) => void +>ts : TaggedShape +>TaggedShape : TaggedShape + + let tag1 = f33(ts, "tag"); +>tag1 : string +>f33(ts, "tag") : string +>f33 : (shape: S, key: K) => S[K] +>ts : TaggedShape +>"tag" : "tag" + + let tag2 = getProperty(ts, "tag"); +>tag2 : string +>getProperty(ts, "tag") : string +>getProperty : (obj: T, key: K) => T[K] +>ts : TaggedShape +>"tag" : "tag" +} + class C { >C : C @@ -679,3 +736,97 @@ function f40(c: C) { >c : C >"z" : "z" } + +// Repros from #12011 + +class Base { +>Base : Base + + get(prop: K) { +>get : (prop: K) => this[K] +>K : K +>prop : K +>K : K + + return this[prop]; +>this[prop] : this[K] +>this : this +>prop : K + } + set(prop: K, value: this[K]) { +>set : (prop: K, value: this[K]) => void +>K : K +>prop : K +>K : K +>value : this[K] +>K : K + + this[prop] = value; +>this[prop] = value : this[K] +>this[prop] : this[K] +>this : this +>prop : K +>value : this[K] + } +} + +class Person extends Base { +>Person : Person +>Base : Base + + parts: number; +>parts : number + + constructor(parts: number) { +>parts : number + + super(); +>super() : void +>super : typeof Base + + this.set("parts", parts); +>this.set("parts", parts) : void +>this.set : (prop: K, value: this[K]) => void +>this : this +>set : (prop: K, value: this[K]) => void +>"parts" : "parts" +>parts : number + } + getParts() { +>getParts : () => number + + return this.get("parts") +>this.get("parts") : number +>this.get : (prop: K) => this[K] +>this : this +>get : (prop: K) => this[K] +>"parts" : "parts" + } +} + +class OtherPerson { +>OtherPerson : OtherPerson + + parts: number; +>parts : number + + constructor(parts: number) { +>parts : number + + setProperty(this, "parts", parts); +>setProperty(this, "parts", parts) : void +>setProperty : (obj: T, key: K, value: T[K]) => void +>this : this +>"parts" : "parts" +>parts : number + } + getParts() { +>getParts : () => number + + return getProperty(this, "parts") +>getProperty(this, "parts") : number +>getProperty : (obj: T, key: K) => T[K] +>this : this +>"parts" : "parts" + } +} From 6a5de5d00ee092322a1cb6d68809c37366090ee4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 10:55:58 -0700 Subject: [PATCH 09/30] Fix linting errors --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b47f6264e3e..28b14502af9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6828,7 +6828,7 @@ namespace ts { if (target.flags & TypeFlags.TypeParameter) { // Given a type parameter K with a constraint keyof T, a type S is // assignable to K if S is assignable to keyof T. - let constraint = getConstraintOfTypeParameter(target); + const constraint = getConstraintOfTypeParameter(target); if (constraint && constraint.flags & TypeFlags.Index) { if (result = isRelatedTo(source, constraint, reportErrors)) { return result; @@ -6838,7 +6838,7 @@ namespace ts { else if (target.flags & TypeFlags.Index) { // Given a type parameter T with a constraint C, a type S is assignable to // keyof T if S is assignable to keyof C. - let constraint = getConstraintOfTypeParameter((target).type); + const constraint = getConstraintOfTypeParameter((target).type); if (constraint) { if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) { return result; From cbec19afd76855b48c95ba09498ac21d5a17b544 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 3 Nov 2016 17:21:36 -0700 Subject: [PATCH 10/30] Ensure transformFlags are correct before visiting a node. --- src/compiler/visitor.ts | 2 + tests/baselines/reference/callWithSpread.js | 24 ++- .../reference/callWithSpread.symbols | 115 +++++++++--- .../baselines/reference/callWithSpread.types | 177 ++++++++++++++---- .../functionCalls/callWithSpread.ts | 10 +- 5 files changed, 257 insertions(+), 71 deletions(-) diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 609031a8221..45b82a6a08a 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -553,6 +553,7 @@ namespace ts { return undefined; } + aggregateTransformFlags(node); const visited = visitor(node); if (visited === node) { return node; @@ -621,6 +622,7 @@ namespace ts { // Visit each original node. for (let i = 0; i < count; i++) { const node = nodes[i + start]; + aggregateTransformFlags(node); const visited = node !== undefined ? visitor(node) : undefined; if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { diff --git a/tests/baselines/reference/callWithSpread.js b/tests/baselines/reference/callWithSpread.js index 161eb36dbcf..69f7e4e30b0 100644 --- a/tests/baselines/reference/callWithSpread.js +++ b/tests/baselines/reference/callWithSpread.js @@ -1,6 +1,6 @@ //// [callWithSpread.ts] interface X { - foo(x: number, y: number, ...z: string[]); + foo(x: number, y: number, ...z: string[]): X; } function foo(x: number, y: number, ...z: string[]) { @@ -19,10 +19,18 @@ obj.foo(1, 2, "abc"); obj.foo(1, 2, ...a); obj.foo(1, 2, ...a, "abc"); +obj.foo(1, 2, ...a).foo(1, 2, "abc"); +obj.foo(1, 2, ...a).foo(1, 2, ...a); +obj.foo(1, 2, ...a).foo(1, 2, ...a, "abc"); + (obj.foo)(1, 2, "abc"); (obj.foo)(1, 2, ...a); (obj.foo)(1, 2, ...a, "abc"); +((obj.foo)(1, 2, ...a).foo)(1, 2, "abc"); +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a); +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a, "abc"); + xa[1].foo(1, 2, "abc"); xa[1].foo(1, 2, ...a); xa[1].foo(1, 2, ...a, "abc"); @@ -72,13 +80,19 @@ foo.apply(void 0, [1, 2].concat(a, ["abc"])); obj.foo(1, 2, "abc"); obj.foo.apply(obj, [1, 2].concat(a)); obj.foo.apply(obj, [1, 2].concat(a, ["abc"])); +obj.foo.apply(obj, [1, 2].concat(a)).foo(1, 2, "abc"); +(_a = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_a, [1, 2].concat(a)); +(_b = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_b, [1, 2].concat(a, ["abc"])); (obj.foo)(1, 2, "abc"); obj.foo.apply(obj, [1, 2].concat(a)); obj.foo.apply(obj, [1, 2].concat(a, ["abc"])); +(obj.foo.apply(obj, [1, 2].concat(a)).foo)(1, 2, "abc"); +(_c = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_c, [1, 2].concat(a)); +(_d = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_d, [1, 2].concat(a, ["abc"])); xa[1].foo(1, 2, "abc"); -(_a = xa[1]).foo.apply(_a, [1, 2].concat(a)); -(_b = xa[1]).foo.apply(_b, [1, 2].concat(a, ["abc"])); -(_c = xa[1]).foo.apply(_c, [1, 2, "abc"]); +(_e = xa[1]).foo.apply(_e, [1, 2].concat(a)); +(_f = xa[1]).foo.apply(_f, [1, 2].concat(a, ["abc"])); +(_g = xa[1]).foo.apply(_g, [1, 2, "abc"]); var C = (function () { function C(x, y) { var z = []; @@ -109,4 +123,4 @@ var D = (function (_super) { }; return D; }(C)); -var _a, _b, _c; +var _a, _b, _c, _d, _e, _f, _g; diff --git a/tests/baselines/reference/callWithSpread.symbols b/tests/baselines/reference/callWithSpread.symbols index bc2d9bfebd9..42331422df7 100644 --- a/tests/baselines/reference/callWithSpread.symbols +++ b/tests/baselines/reference/callWithSpread.symbols @@ -2,11 +2,12 @@ interface X { >X : Symbol(X, Decl(callWithSpread.ts, 0, 0)) - foo(x: number, y: number, ...z: string[]); + foo(x: number, y: number, ...z: string[]): X; >foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) >x : Symbol(x, Decl(callWithSpread.ts, 1, 8)) >y : Symbol(y, Decl(callWithSpread.ts, 1, 18)) >z : Symbol(z, Decl(callWithSpread.ts, 1, 29)) +>X : Symbol(X, Decl(callWithSpread.ts, 0, 0)) } function foo(x: number, y: number, ...z: string[]) { @@ -58,6 +59,32 @@ obj.foo(1, 2, ...a, "abc"); >foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) >a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +obj.foo(1, 2, ...a).foo(1, 2, "abc"); +>obj.foo(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) + +obj.foo(1, 2, ...a).foo(1, 2, ...a); +>obj.foo(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) + +obj.foo(1, 2, ...a).foo(1, 2, ...a, "abc"); +>obj.foo(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) + (obj.foo)(1, 2, "abc"); >obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) >obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) @@ -75,6 +102,32 @@ obj.foo(1, 2, ...a, "abc"); >foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) >a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +((obj.foo)(1, 2, ...a).foo)(1, 2, "abc"); +>(obj.foo)(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) + +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a); +>(obj.foo)(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) + +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a, "abc"); +>(obj.foo)(1, 2, ...a).foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj.foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>obj : Symbol(obj, Decl(callWithSpread.ts, 9, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) +>foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) +>a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) + xa[1].foo(1, 2, "abc"); >xa[1].foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) >xa : Symbol(xa, Decl(callWithSpread.ts, 10, 3)) @@ -99,60 +152,60 @@ xa[1].foo(1, 2, ...a, "abc"); >foo : Symbol(X.foo, Decl(callWithSpread.ts, 0, 13)) class C { ->C : Symbol(C, Decl(callWithSpread.ts, 28, 40)) +>C : Symbol(C, Decl(callWithSpread.ts, 36, 40)) constructor(x: number, y: number, ...z: string[]) { ->x : Symbol(x, Decl(callWithSpread.ts, 31, 16)) ->y : Symbol(y, Decl(callWithSpread.ts, 31, 26)) ->z : Symbol(z, Decl(callWithSpread.ts, 31, 37)) +>x : Symbol(x, Decl(callWithSpread.ts, 39, 16)) +>y : Symbol(y, Decl(callWithSpread.ts, 39, 26)) +>z : Symbol(z, Decl(callWithSpread.ts, 39, 37)) this.foo(x, y); ->this.foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->this : Symbol(C, Decl(callWithSpread.ts, 28, 40)) ->foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->x : Symbol(x, Decl(callWithSpread.ts, 31, 16)) ->y : Symbol(y, Decl(callWithSpread.ts, 31, 26)) +>this.foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>this : Symbol(C, Decl(callWithSpread.ts, 36, 40)) +>foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>x : Symbol(x, Decl(callWithSpread.ts, 39, 16)) +>y : Symbol(y, Decl(callWithSpread.ts, 39, 26)) this.foo(x, y, ...z); ->this.foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->this : Symbol(C, Decl(callWithSpread.ts, 28, 40)) ->foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->x : Symbol(x, Decl(callWithSpread.ts, 31, 16)) ->y : Symbol(y, Decl(callWithSpread.ts, 31, 26)) ->z : Symbol(z, Decl(callWithSpread.ts, 31, 37)) +>this.foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>this : Symbol(C, Decl(callWithSpread.ts, 36, 40)) +>foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>x : Symbol(x, Decl(callWithSpread.ts, 39, 16)) +>y : Symbol(y, Decl(callWithSpread.ts, 39, 26)) +>z : Symbol(z, Decl(callWithSpread.ts, 39, 37)) } foo(x: number, y: number, ...z: string[]) { ->foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->x : Symbol(x, Decl(callWithSpread.ts, 35, 8)) ->y : Symbol(y, Decl(callWithSpread.ts, 35, 18)) ->z : Symbol(z, Decl(callWithSpread.ts, 35, 29)) +>foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>x : Symbol(x, Decl(callWithSpread.ts, 43, 8)) +>y : Symbol(y, Decl(callWithSpread.ts, 43, 18)) +>z : Symbol(z, Decl(callWithSpread.ts, 43, 29)) } } class D extends C { ->D : Symbol(D, Decl(callWithSpread.ts, 37, 1)) ->C : Symbol(C, Decl(callWithSpread.ts, 28, 40)) +>D : Symbol(D, Decl(callWithSpread.ts, 45, 1)) +>C : Symbol(C, Decl(callWithSpread.ts, 36, 40)) constructor() { super(1, 2); ->super : Symbol(C, Decl(callWithSpread.ts, 28, 40)) +>super : Symbol(C, Decl(callWithSpread.ts, 36, 40)) super(1, 2, ...a); ->super : Symbol(C, Decl(callWithSpread.ts, 28, 40)) +>super : Symbol(C, Decl(callWithSpread.ts, 36, 40)) >a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) } foo() { ->foo : Symbol(D.foo, Decl(callWithSpread.ts, 43, 5)) +>foo : Symbol(D.foo, Decl(callWithSpread.ts, 51, 5)) super.foo(1, 2); ->super.foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->super : Symbol(C, Decl(callWithSpread.ts, 28, 40)) ->foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) +>super.foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>super : Symbol(C, Decl(callWithSpread.ts, 36, 40)) +>foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) super.foo(1, 2, ...a); ->super.foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) ->super : Symbol(C, Decl(callWithSpread.ts, 28, 40)) ->foo : Symbol(C.foo, Decl(callWithSpread.ts, 34, 5)) +>super.foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) +>super : Symbol(C, Decl(callWithSpread.ts, 36, 40)) +>foo : Symbol(C.foo, Decl(callWithSpread.ts, 42, 5)) >a : Symbol(a, Decl(callWithSpread.ts, 7, 3)) } } diff --git a/tests/baselines/reference/callWithSpread.types b/tests/baselines/reference/callWithSpread.types index c9403f1bb69..5af9d2fef84 100644 --- a/tests/baselines/reference/callWithSpread.types +++ b/tests/baselines/reference/callWithSpread.types @@ -2,11 +2,12 @@ interface X { >X : X - foo(x: number, y: number, ...z: string[]); ->foo : (x: number, y: number, ...z: string[]) => any + foo(x: number, y: number, ...z: string[]): X; +>foo : (x: number, y: number, ...z: string[]) => X >x : number >y : number >z : string[] +>X : X } function foo(x: number, y: number, ...z: string[]) { @@ -55,29 +56,80 @@ foo(1, 2, ...a, "abc"); >"abc" : "abc" obj.foo(1, 2, "abc"); ->obj.foo(1, 2, "abc") : any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>obj.foo(1, 2, "abc") : X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >"abc" : "abc" obj.foo(1, 2, ...a); ->obj.foo(1, 2, ...a) : any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>obj.foo(1, 2, ...a) : X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string >a : string[] obj.foo(1, 2, ...a, "abc"); ->obj.foo(1, 2, ...a, "abc") : any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>obj.foo(1, 2, ...a, "abc") : X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>"abc" : "abc" + +obj.foo(1, 2, ...a).foo(1, 2, "abc"); +>obj.foo(1, 2, ...a).foo(1, 2, "abc") : X +>obj.foo(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>obj.foo(1, 2, ...a) : X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>"abc" : "abc" + +obj.foo(1, 2, ...a).foo(1, 2, ...a); +>obj.foo(1, 2, ...a).foo(1, 2, ...a) : X +>obj.foo(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>obj.foo(1, 2, ...a) : X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] + +obj.foo(1, 2, ...a).foo(1, 2, ...a, "abc"); +>obj.foo(1, 2, ...a).foo(1, 2, ...a, "abc") : X +>obj.foo(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>obj.foo(1, 2, ...a) : X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string @@ -85,32 +137,89 @@ obj.foo(1, 2, ...a, "abc"); >"abc" : "abc" (obj.foo)(1, 2, "abc"); ->(obj.foo)(1, 2, "abc") : any ->(obj.foo) : (x: number, y: number, ...z: string[]) => any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>(obj.foo)(1, 2, "abc") : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >"abc" : "abc" (obj.foo)(1, 2, ...a); ->(obj.foo)(1, 2, ...a) : any ->(obj.foo) : (x: number, y: number, ...z: string[]) => any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>(obj.foo)(1, 2, ...a) : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string >a : string[] (obj.foo)(1, 2, ...a, "abc"); ->(obj.foo)(1, 2, ...a, "abc") : any ->(obj.foo) : (x: number, y: number, ...z: string[]) => any ->obj.foo : (x: number, y: number, ...z: string[]) => any +>(obj.foo)(1, 2, ...a, "abc") : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X >obj : X ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>"abc" : "abc" + +((obj.foo)(1, 2, ...a).foo)(1, 2, "abc"); +>((obj.foo)(1, 2, ...a).foo)(1, 2, "abc") : X +>((obj.foo)(1, 2, ...a).foo) : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a) : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>"abc" : "abc" + +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a); +>((obj.foo)(1, 2, ...a).foo)(1, 2, ...a) : X +>((obj.foo)(1, 2, ...a).foo) : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a) : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] + +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a, "abc"); +>((obj.foo)(1, 2, ...a).foo)(1, 2, ...a, "abc") : X +>((obj.foo)(1, 2, ...a).foo) : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a).foo : (x: number, y: number, ...z: string[]) => X +>(obj.foo)(1, 2, ...a) : X +>(obj.foo) : (x: number, y: number, ...z: string[]) => X +>obj.foo : (x: number, y: number, ...z: string[]) => X +>obj : X +>foo : (x: number, y: number, ...z: string[]) => X +>1 : 1 +>2 : 2 +>...a : string +>a : string[] +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string @@ -118,35 +227,35 @@ obj.foo(1, 2, ...a, "abc"); >"abc" : "abc" xa[1].foo(1, 2, "abc"); ->xa[1].foo(1, 2, "abc") : any ->xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1].foo(1, 2, "abc") : X +>xa[1].foo : (x: number, y: number, ...z: string[]) => X >xa[1] : X >xa : X[] >1 : 1 ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >"abc" : "abc" xa[1].foo(1, 2, ...a); ->xa[1].foo(1, 2, ...a) : any ->xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1].foo(1, 2, ...a) : X +>xa[1].foo : (x: number, y: number, ...z: string[]) => X >xa[1] : X >xa : X[] >1 : 1 ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string >a : string[] xa[1].foo(1, 2, ...a, "abc"); ->xa[1].foo(1, 2, ...a, "abc") : any ->xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1].foo(1, 2, ...a, "abc") : X +>xa[1].foo : (x: number, y: number, ...z: string[]) => X >xa[1] : X >xa : X[] >1 : 1 ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >1 : 1 >2 : 2 >...a : string @@ -158,11 +267,11 @@ xa[1].foo(1, 2, ...a, "abc"); >(xa[1].foo) : Function >xa[1].foo : Function >Function : Function ->xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1].foo : (x: number, y: number, ...z: string[]) => X >xa[1] : X >xa : X[] >1 : 1 ->foo : (x: number, y: number, ...z: string[]) => any +>foo : (x: number, y: number, ...z: string[]) => X >...[1, 2, "abc"] : string | number >[1, 2, "abc"] : (string | number)[] >1 : 1 diff --git a/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts b/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts index 245028db3d4..b1e2ee75784 100644 --- a/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts +++ b/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts @@ -1,5 +1,5 @@ interface X { - foo(x: number, y: number, ...z: string[]); + foo(x: number, y: number, ...z: string[]): X; } function foo(x: number, y: number, ...z: string[]) { @@ -18,10 +18,18 @@ obj.foo(1, 2, "abc"); obj.foo(1, 2, ...a); obj.foo(1, 2, ...a, "abc"); +obj.foo(1, 2, ...a).foo(1, 2, "abc"); +obj.foo(1, 2, ...a).foo(1, 2, ...a); +obj.foo(1, 2, ...a).foo(1, 2, ...a, "abc"); + (obj.foo)(1, 2, "abc"); (obj.foo)(1, 2, ...a); (obj.foo)(1, 2, ...a, "abc"); +((obj.foo)(1, 2, ...a).foo)(1, 2, "abc"); +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a); +((obj.foo)(1, 2, ...a).foo)(1, 2, ...a, "abc"); + xa[1].foo(1, 2, "abc"); xa[1].foo(1, 2, ...a); xa[1].foo(1, 2, ...a, "abc"); From 1c004bf317a3e9103572668191055ba1c86f1788 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 3 Nov 2016 21:13:41 -0700 Subject: [PATCH 11/30] Port #12027, #11980 and #11932 to master (#12037) * add test for the fix for overwrite emitting error * cr feedback --- src/compiler/core.ts | 12 +++++++++ src/compiler/diagnosticMessages.json | 4 +++ src/compiler/program.ts | 25 ++++++++++++++----- src/compiler/types.ts | 1 + src/harness/fourslash.ts | 13 +++++++++- .../unittests/tsserverProjectSystem.ts | 24 ++++++++++++++++++ src/server/project.ts | 4 +++ ...sWhenEmitBlockingErrorOnOtherFile.baseline | 3 ++- .../declarationFileOverwriteError.errors.txt | 2 ++ ...rationFileOverwriteErrorWithOut.errors.txt | 2 ++ .../exportDefaultInJsFile01.errors.txt | 2 ++ .../exportDefaultInJsFile02.errors.txt | 2 ++ ...tionAmbientVarDeclarationSyntax.errors.txt | 2 ++ ...CompilationEmitBlockedCorrectly.errors.txt | 2 ++ .../jsFileCompilationEnumSyntax.errors.txt | 2 ++ ...onsWithJsFileReferenceWithNoOut.errors.txt | 2 ++ ...mpilationExportAssignmentSyntax.errors.txt | 2 ++ ...tionHeritageClauseSyntaxOfClass.errors.txt | 2 ++ ...leCompilationImportEqualsSyntax.errors.txt | 2 ++ ...sFileCompilationInterfaceSyntax.errors.txt | 2 ++ .../jsFileCompilationModuleSyntax.errors.txt | 2 ++ ...onsWithJsFileReferenceWithNoOut.errors.txt | 2 ++ ...ileCompilationOptionalParameter.errors.txt | 2 ++ ...lationPublicMethodSyntaxOfClass.errors.txt | 2 ++ ...pilationPublicParameterModifier.errors.txt | 2 ++ ...ationReturnTypeSyntaxOfFunction.errors.txt | 2 ++ .../jsFileCompilationSyntaxError.errors.txt | 2 ++ ...sFileCompilationTypeAliasSyntax.errors.txt | 2 ++ ...ilationTypeArgumentSyntaxOfCall.errors.txt | 2 ++ ...jsFileCompilationTypeAssertions.errors.txt | 2 ++ ...sFileCompilationTypeOfParameter.errors.txt | 2 ++ ...ationTypeParameterSyntaxOfClass.errors.txt | 2 ++ ...onTypeParameterSyntaxOfFunction.errors.txt | 2 ++ ...sFileCompilationTypeSyntaxOfVar.errors.txt | 2 ++ ...hDeclarationEmitPathSameAsInput.errors.txt | 2 ++ ...lationWithJsEmitPathSameAsInput.errors.txt | 2 ++ ...sFileCompilationWithMapFileAsJs.errors.txt | 2 ++ ...hMapFileAsJsWithInlineSourceMap.errors.txt | 2 ++ ...rationFileNameSameAsInputJsFile.errors.txt | 2 ++ ...ithOutFileNameSameAsInputJsFile.errors.txt | 2 ++ .../jsFileCompilationWithoutOut.errors.txt | 2 ++ ...eNameDtsNotSpecifiedWithAllowJs.errors.txt | 2 ++ ...eNameDtsNotSpecifiedWithAllowJs.errors.txt | 2 ++ 43 files changed, 148 insertions(+), 8 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index dd71877930a..8175730159b 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1127,6 +1127,18 @@ namespace ts { }; } + export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain): Diagnostic { + return { + file: undefined, + start: undefined, + length: undefined, + + code: chain.code, + category: chain.category, + messageText: chain.next ? chain : chain.messageText + }; + } + export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage, ...args: any[]): DiagnosticMessageChain; export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage): DiagnosticMessageChain { let text = getLocaleSpecificMessage(message); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 5290782c156..9a353e6fb36 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3158,5 +3158,9 @@ "Implement inherited abstract class": { "category": "Message", "code": 90007 + }, + "Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig": { + "category": "Error", + "code": 90009 } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 82d274e0487..dcb9cce0adb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -239,11 +239,11 @@ namespace ts { const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); const fileName = diagnostic.file.fileName; const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)); - output += `${ relativeFileName }(${ line + 1 },${ character + 1 }): `; + output += `${relativeFileName}(${line + 1},${character + 1}): `; } const category = DiagnosticCategory[diagnostic.category].toLowerCase(); - output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()) }${ host.getNewLine() }`; + output += `${category} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`; } return output; } @@ -1685,13 +1685,24 @@ namespace ts { const emitFilePath = toPath(emitFileName, currentDirectory, getCanonicalFileName); // Report error if the output overwrites input file if (filesByName.contains(emitFilePath)) { - createEmitBlockingDiagnostics(emitFileName, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file); + if (options.noEmitOverwritenFiles && !options.out && !options.outDir && !options.outFile) { + blockEmittingOfFile(emitFileName); + } + else { + let chain: DiagnosticMessageChain; + if (!options.configFilePath) { + // The program is from either an inferred project or an external project + chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig); + } + chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName); + blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain)); + } } // Report error if multiple files write into same file if (emitFilesSeen.contains(emitFilePath)) { // Already seen the same emit file - report error - createEmitBlockingDiagnostics(emitFileName, Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files); + blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName)); } else { emitFilesSeen.set(emitFilePath, true); @@ -1700,9 +1711,11 @@ namespace ts { } } - function createEmitBlockingDiagnostics(emitFileName: string, message: DiagnosticMessage) { + function blockEmittingOfFile(emitFileName: string, diag?: Diagnostic) { hasEmitBlockingDiagnostics.set(toPath(emitFileName, currentDirectory, getCanonicalFileName), true); - programDiagnostics.add(createCompilerDiagnostic(message, emitFileName)); + if (diag) { + programDiagnostics.add(diag); + } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b865beca76b..5536a818df5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3058,6 +3058,7 @@ namespace ts { moduleResolution?: ModuleResolutionKind; newLine?: NewLineKind; noEmit?: boolean; + /*@internal*/noEmitOverwritenFiles?: boolean; noEmitHelpers?: boolean; noEmitOnError?: boolean; noErrorTruncation?: boolean; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index b008ca65370..ae32fe3db16 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1253,7 +1253,18 @@ namespace FourSlash { resultString += "Diagnostics:" + Harness.IO.newLine(); const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram()); for (const diagnostic of diagnostics) { - resultString += " " + diagnostic.messageText + Harness.IO.newLine(); + if (typeof diagnostic.messageText !== "string") { + let chainedMessage = diagnostic.messageText; + let indentation = " "; + while (chainedMessage) { + resultString += indentation + chainedMessage.messageText + Harness.IO.newLine(); + chainedMessage = chainedMessage.next; + indentation = indentation + " "; + } + } + else { + resultString += " " + diagnostic.messageText + Harness.IO.newLine(); + } } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 870926a8c55..15cfa3f8c8e 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -2505,4 +2505,28 @@ namespace ts.projectSystem { checkProjectActualFiles(projectService.inferredProjects[0], [f.path]); }); }); + + describe("No overwrite emit error", () => { + it("for inferred project", () => { + const f1 = { + path: "/a/b/f1.js", + content: "function test1() { }" + }; + const host = createServerHost([f1, libFile]); + const session = createSession(host); + openFilesForSession([f1], session); + + const projectService = session.getProjectService(); + checkNumberOfProjects(projectService, { inferredProjects: 1 }); + const projectName = projectService.inferredProjects[0].getProjectName(); + + const diags = session.executeCommand({ + type: "request", + command: server.CommandNames.CompilerOptionsDiagnosticsFull, + seq: 2, + arguments: { projectFileName: projectName } + }).response; + assert.isTrue(diags.length === 0); + }); + }); } \ No newline at end of file diff --git a/src/server/project.ts b/src/server/project.ts index 457f5e2b261..563b0456910 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -161,6 +161,10 @@ namespace ts.server { this.compilerOptions.allowNonTsExtensions = true; } + if (this.projectKind === ProjectKind.Inferred) { + this.compilerOptions.noEmitOverwritenFiles = true; + } + if (languageServiceEnabled) { this.enableLanguageService(); } diff --git a/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline b/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline index a088006d145..f8b479e6d7b 100644 --- a/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline +++ b/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline @@ -1,6 +1,7 @@ EmitSkipped: true Diagnostics: - Cannot write file '/tests/cases/fourslash/b.js' because it would overwrite input file. + Cannot write file '/tests/cases/fourslash/b.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig EmitSkipped: false FileName : /tests/cases/fourslash/a.js diff --git a/tests/baselines/reference/declarationFileOverwriteError.errors.txt b/tests/baselines/reference/declarationFileOverwriteError.errors.txt index 1974976dee1..211469778b1 100644 --- a/tests/baselines/reference/declarationFileOverwriteError.errors.txt +++ b/tests/baselines/reference/declarationFileOverwriteError.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.d.ts' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/a.d.ts' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.d.ts (0 errors) ==== declare class c { diff --git a/tests/baselines/reference/declarationFileOverwriteErrorWithOut.errors.txt b/tests/baselines/reference/declarationFileOverwriteErrorWithOut.errors.txt index 658465de9c2..ea4a93b3b12 100644 --- a/tests/baselines/reference/declarationFileOverwriteErrorWithOut.errors.txt +++ b/tests/baselines/reference/declarationFileOverwriteErrorWithOut.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/out.d.ts' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/out.d.ts' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/out.d.ts (0 errors) ==== declare class c { diff --git a/tests/baselines/reference/exportDefaultInJsFile01.errors.txt b/tests/baselines/reference/exportDefaultInJsFile01.errors.txt index 34035e1e14e..0565c8bd61e 100644 --- a/tests/baselines/reference/exportDefaultInJsFile01.errors.txt +++ b/tests/baselines/reference/exportDefaultInJsFile01.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/conformance/salsa/myFile01.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/conformance/salsa/myFile01.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/conformance/salsa/myFile01.js (0 errors) ==== export default "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/exportDefaultInJsFile02.errors.txt b/tests/baselines/reference/exportDefaultInJsFile02.errors.txt index 5e17306e066..a74fbacfcfd 100644 --- a/tests/baselines/reference/exportDefaultInJsFile02.errors.txt +++ b/tests/baselines/reference/exportDefaultInJsFile02.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/conformance/salsa/myFile02.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/conformance/salsa/myFile02.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/conformance/salsa/myFile02.js (0 errors) ==== export default "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/jsFileCompilationAmbientVarDeclarationSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationAmbientVarDeclarationSyntax.errors.txt index 19bfd5d1c02..039834ff702 100644 --- a/tests/baselines/reference/jsFileCompilationAmbientVarDeclarationSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationAmbientVarDeclarationSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,1): error TS8009: 'declare' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== declare var v; ~~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationEmitBlockedCorrectly.errors.txt b/tests/baselines/reference/jsFileCompilationEmitBlockedCorrectly.errors.txt index 0aa9d5733d5..75bcf88b10a 100644 --- a/tests/baselines/reference/jsFileCompilationEmitBlockedCorrectly.errors.txt +++ b/tests/baselines/reference/jsFileCompilationEmitBlockedCorrectly.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig error TS5056: Cannot write file 'tests/cases/compiler/a.js' because it would be overwritten by multiple input files. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5056: Cannot write file 'tests/cases/compiler/a.js' because it would be overwritten by multiple input files. ==== tests/cases/compiler/a.ts (0 errors) ==== class c { diff --git a/tests/baselines/reference/jsFileCompilationEnumSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationEnumSyntax.errors.txt index 538dfb38972..c6da8931d5e 100644 --- a/tests/baselines/reference/jsFileCompilationEnumSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationEnumSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,6): error TS8015: 'enum declarations' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== enum E { } ~ diff --git a/tests/baselines/reference/jsFileCompilationErrorOnDeclarationsWithJsFileReferenceWithNoOut.errors.txt b/tests/baselines/reference/jsFileCompilationErrorOnDeclarationsWithJsFileReferenceWithNoOut.errors.txt index 641731936f8..54e91c5d196 100644 --- a/tests/baselines/reference/jsFileCompilationErrorOnDeclarationsWithJsFileReferenceWithNoOut.errors.txt +++ b/tests/baselines/reference/jsFileCompilationErrorOnDeclarationsWithJsFileReferenceWithNoOut.errors.txt @@ -1,9 +1,11 @@ error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. error TS5055: Cannot write file 'tests/cases/compiler/c.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. !!! error TS5055: Cannot write file 'tests/cases/compiler/c.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/jsFileCompilationExportAssignmentSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationExportAssignmentSyntax.errors.txt index f537941c55c..babbeea8004 100644 --- a/tests/baselines/reference/jsFileCompilationExportAssignmentSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationExportAssignmentSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,1): error TS8003: 'export=' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== export = b; ~~~~~~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationHeritageClauseSyntaxOfClass.errors.txt b/tests/baselines/reference/jsFileCompilationHeritageClauseSyntaxOfClass.errors.txt index 33b8a45f1aa..3b30663308f 100644 --- a/tests/baselines/reference/jsFileCompilationHeritageClauseSyntaxOfClass.errors.txt +++ b/tests/baselines/reference/jsFileCompilationHeritageClauseSyntaxOfClass.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,9): error TS8005: 'implements clauses' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== class C implements D { } ~~~~~~~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationImportEqualsSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationImportEqualsSyntax.errors.txt index b4161367037..a064f53e92f 100644 --- a/tests/baselines/reference/jsFileCompilationImportEqualsSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationImportEqualsSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,1): error TS8002: 'import ... =' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== import a = b; ~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationInterfaceSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationInterfaceSyntax.errors.txt index 17fb8fcb187..9e53438c732 100644 --- a/tests/baselines/reference/jsFileCompilationInterfaceSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationInterfaceSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,11): error TS8006: 'interface declarations' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== interface I { } ~ diff --git a/tests/baselines/reference/jsFileCompilationModuleSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationModuleSyntax.errors.txt index 662028c53ed..6952c0055c0 100644 --- a/tests/baselines/reference/jsFileCompilationModuleSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationModuleSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,8): error TS8007: 'module declarations' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== module M { } ~ diff --git a/tests/baselines/reference/jsFileCompilationNoErrorWithoutDeclarationsWithJsFileReferenceWithNoOut.errors.txt b/tests/baselines/reference/jsFileCompilationNoErrorWithoutDeclarationsWithJsFileReferenceWithNoOut.errors.txt index f2f4142b993..888310414f2 100644 --- a/tests/baselines/reference/jsFileCompilationNoErrorWithoutDeclarationsWithJsFileReferenceWithNoOut.errors.txt +++ b/tests/baselines/reference/jsFileCompilationNoErrorWithoutDeclarationsWithJsFileReferenceWithNoOut.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/c.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/c.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/jsFileCompilationOptionalParameter.errors.txt b/tests/baselines/reference/jsFileCompilationOptionalParameter.errors.txt index 295f827dd2d..15bfe2281d9 100644 --- a/tests/baselines/reference/jsFileCompilationOptionalParameter.errors.txt +++ b/tests/baselines/reference/jsFileCompilationOptionalParameter.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,13): error TS8009: '?' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== function F(p?) { } ~ diff --git a/tests/baselines/reference/jsFileCompilationPublicMethodSyntaxOfClass.errors.txt b/tests/baselines/reference/jsFileCompilationPublicMethodSyntaxOfClass.errors.txt index d67c9baea70..af95c136205 100644 --- a/tests/baselines/reference/jsFileCompilationPublicMethodSyntaxOfClass.errors.txt +++ b/tests/baselines/reference/jsFileCompilationPublicMethodSyntaxOfClass.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(2,5): error TS8009: 'public' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== class C { public foo() { diff --git a/tests/baselines/reference/jsFileCompilationPublicParameterModifier.errors.txt b/tests/baselines/reference/jsFileCompilationPublicParameterModifier.errors.txt index bc4913f6217..9314e1da807 100644 --- a/tests/baselines/reference/jsFileCompilationPublicParameterModifier.errors.txt +++ b/tests/baselines/reference/jsFileCompilationPublicParameterModifier.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,23): error TS8012: 'parameter modifiers' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== class C { constructor(public x) { }} ~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationReturnTypeSyntaxOfFunction.errors.txt b/tests/baselines/reference/jsFileCompilationReturnTypeSyntaxOfFunction.errors.txt index 6dad5a29485..acae950d081 100644 --- a/tests/baselines/reference/jsFileCompilationReturnTypeSyntaxOfFunction.errors.txt +++ b/tests/baselines/reference/jsFileCompilationReturnTypeSyntaxOfFunction.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,15): error TS8010: 'types' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== function F(): number { } ~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationSyntaxError.errors.txt b/tests/baselines/reference/jsFileCompilationSyntaxError.errors.txt index d98a1e7e7bb..eb4c114d3d0 100644 --- a/tests/baselines/reference/jsFileCompilationSyntaxError.errors.txt +++ b/tests/baselines/reference/jsFileCompilationSyntaxError.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (0 errors) ==== /** * @type {number} diff --git a/tests/baselines/reference/jsFileCompilationTypeAliasSyntax.errors.txt b/tests/baselines/reference/jsFileCompilationTypeAliasSyntax.errors.txt index 2b5112e1ce2..74978eddf8c 100644 --- a/tests/baselines/reference/jsFileCompilationTypeAliasSyntax.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeAliasSyntax.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,6): error TS8008: 'type aliases' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== type a = b; ~ diff --git a/tests/baselines/reference/jsFileCompilationTypeArgumentSyntaxOfCall.errors.txt b/tests/baselines/reference/jsFileCompilationTypeArgumentSyntaxOfCall.errors.txt index 8ac59196ed9..f53ec55d2b1 100644 --- a/tests/baselines/reference/jsFileCompilationTypeArgumentSyntaxOfCall.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeArgumentSyntaxOfCall.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,5): error TS8011: 'type arguments' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== Foo(); ~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationTypeAssertions.errors.txt b/tests/baselines/reference/jsFileCompilationTypeAssertions.errors.txt index 662eea4d405..be1e6d32436 100644 --- a/tests/baselines/reference/jsFileCompilationTypeAssertions.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeAssertions.errors.txt @@ -1,9 +1,11 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,10): error TS17008: JSX element 'string' has no corresponding closing tag. tests/cases/compiler/a.js(1,27): error TS1005: 'undefined; ~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationTypeOfParameter.errors.txt b/tests/baselines/reference/jsFileCompilationTypeOfParameter.errors.txt index 680b32a64f1..811c0793ffe 100644 --- a/tests/baselines/reference/jsFileCompilationTypeOfParameter.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeOfParameter.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,15): error TS8010: 'types' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== function F(a: number) { } ~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfClass.errors.txt b/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfClass.errors.txt index 161c832ffd5..0fe902aa661 100644 --- a/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfClass.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfClass.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,9): error TS8004: 'type parameter declarations' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== class C { } ~ diff --git a/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfFunction.errors.txt b/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfFunction.errors.txt index d7626f68564..bfc4c7be26a 100644 --- a/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfFunction.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeParameterSyntaxOfFunction.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,12): error TS8004: 'type parameter declarations' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== function F() { } ~ diff --git a/tests/baselines/reference/jsFileCompilationTypeSyntaxOfVar.errors.txt b/tests/baselines/reference/jsFileCompilationTypeSyntaxOfVar.errors.txt index 2c6ac3771be..65333751780 100644 --- a/tests/baselines/reference/jsFileCompilationTypeSyntaxOfVar.errors.txt +++ b/tests/baselines/reference/jsFileCompilationTypeSyntaxOfVar.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig tests/cases/compiler/a.js(1,8): error TS8010: 'types' can only be used in a .ts file. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.js (1 errors) ==== var v: () => number; ~~~~~~~~~~~~ diff --git a/tests/baselines/reference/jsFileCompilationWithDeclarationEmitPathSameAsInput.errors.txt b/tests/baselines/reference/jsFileCompilationWithDeclarationEmitPathSameAsInput.errors.txt index 9e9c2adb0ef..4170e818fd7 100644 --- a/tests/baselines/reference/jsFileCompilationWithDeclarationEmitPathSameAsInput.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithDeclarationEmitPathSameAsInput.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.d.ts' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/a.d.ts' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/jsFileCompilationWithJsEmitPathSameAsInput.errors.txt b/tests/baselines/reference/jsFileCompilationWithJsEmitPathSameAsInput.errors.txt index 4cc2cc2ab45..8004efd02ef 100644 --- a/tests/baselines/reference/jsFileCompilationWithJsEmitPathSameAsInput.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithJsEmitPathSameAsInput.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig error TS5056: Cannot write file 'tests/cases/compiler/a.js' because it would be overwritten by multiple input files. !!! error TS5055: Cannot write file 'tests/cases/compiler/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5056: Cannot write file 'tests/cases/compiler/a.js' because it would be overwritten by multiple input files. ==== tests/cases/compiler/a.ts (0 errors) ==== class c { diff --git a/tests/baselines/reference/jsFileCompilationWithMapFileAsJs.errors.txt b/tests/baselines/reference/jsFileCompilationWithMapFileAsJs.errors.txt index 06186b9f0b2..069c562f29b 100644 --- a/tests/baselines/reference/jsFileCompilationWithMapFileAsJs.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithMapFileAsJs.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig error TS6054: File 'tests/cases/compiler/b.js.map' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'. !!! error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS6054: File 'tests/cases/compiler/b.js.map' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'. ==== tests/cases/compiler/a.ts (0 errors) ==== diff --git a/tests/baselines/reference/jsFileCompilationWithMapFileAsJsWithInlineSourceMap.errors.txt b/tests/baselines/reference/jsFileCompilationWithMapFileAsJsWithInlineSourceMap.errors.txt index 06186b9f0b2..069c562f29b 100644 --- a/tests/baselines/reference/jsFileCompilationWithMapFileAsJsWithInlineSourceMap.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithMapFileAsJsWithInlineSourceMap.errors.txt @@ -1,8 +1,10 @@ error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig error TS6054: File 'tests/cases/compiler/b.js.map' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'. !!! error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS6054: File 'tests/cases/compiler/b.js.map' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'. ==== tests/cases/compiler/a.ts (0 errors) ==== diff --git a/tests/baselines/reference/jsFileCompilationWithOutDeclarationFileNameSameAsInputJsFile.errors.txt b/tests/baselines/reference/jsFileCompilationWithOutDeclarationFileNameSameAsInputJsFile.errors.txt index 10c7b8c90dd..0a4f0cdf1a4 100644 --- a/tests/baselines/reference/jsFileCompilationWithOutDeclarationFileNameSameAsInputJsFile.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithOutDeclarationFileNameSameAsInputJsFile.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/b.d.ts' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/b.d.ts' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/jsFileCompilationWithOutFileNameSameAsInputJsFile.errors.txt b/tests/baselines/reference/jsFileCompilationWithOutFileNameSameAsInputJsFile.errors.txt index 0f6852b21a0..3f72a8113ef 100644 --- a/tests/baselines/reference/jsFileCompilationWithOutFileNameSameAsInputJsFile.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithOutFileNameSameAsInputJsFile.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/jsFileCompilationWithoutOut.errors.txt b/tests/baselines/reference/jsFileCompilationWithoutOut.errors.txt index 0f6852b21a0..3f72a8113ef 100644 --- a/tests/baselines/reference/jsFileCompilationWithoutOut.errors.txt +++ b/tests/baselines/reference/jsFileCompilationWithoutOut.errors.txt @@ -1,7 +1,9 @@ error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5055: Cannot write file 'tests/cases/compiler/b.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== tests/cases/compiler/a.ts (0 errors) ==== class c { } diff --git a/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/amd/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt b/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/amd/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt index ac25edc1bd5..668f037a03b 100644 --- a/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/amd/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt +++ b/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/amd/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt @@ -1,9 +1,11 @@ error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. error TS5055: Cannot write file 'SameNameDTsNotSpecifiedWithAllowJs/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. !!! error TS5055: Cannot write file 'SameNameDTsNotSpecifiedWithAllowJs/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== SameNameDTsNotSpecifiedWithAllowJs/a.d.ts (0 errors) ==== declare var a: number; ==== SameNameDTsNotSpecifiedWithAllowJs/a.js (0 errors) ==== diff --git a/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/node/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt b/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/node/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt index ac25edc1bd5..668f037a03b 100644 --- a/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/node/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt +++ b/tests/baselines/reference/project/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs/node/jsFileCompilationSameNameDtsNotSpecifiedWithAllowJs.errors.txt @@ -1,9 +1,11 @@ error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. error TS5055: Cannot write file 'SameNameDTsNotSpecifiedWithAllowJs/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig !!! error TS5053: Option 'allowJs' cannot be specified with option 'declaration'. !!! error TS5055: Cannot write file 'SameNameDTsNotSpecifiedWithAllowJs/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig ==== SameNameDTsNotSpecifiedWithAllowJs/a.d.ts (0 errors) ==== declare var a: number; ==== SameNameDTsNotSpecifiedWithAllowJs/a.js (0 errors) ==== From ed4fead087ab0fea1811e4b42b6d8e599ee13f1e Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 4 Nov 2016 21:54:22 -0700 Subject: [PATCH 12/30] add missing bind calls to properly set parent on token nodes (#12057) --- src/compiler/binder.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c1011e57568..1d721e2db90 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1234,9 +1234,11 @@ namespace ts { const postExpressionLabel = createBranchLabel(); bindCondition(node.condition, trueLabel, falseLabel); currentFlow = finishFlowLabel(trueLabel); + bind(node.questionToken); bind(node.whenTrue); addAntecedent(postExpressionLabel, currentFlow); currentFlow = finishFlowLabel(falseLabel); + bind(node.colonToken); bind(node.whenFalse); addAntecedent(postExpressionLabel, currentFlow); currentFlow = finishFlowLabel(postExpressionLabel); From 61b9da548a6eb8d6b11849d129996625968833f4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 08:20:02 -0700 Subject: [PATCH 13/30] Cache generic signature instantiations --- src/compiler/checker.ts | 8 +++++++- src/compiler/types.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f2c642124a..9647077d943 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4232,7 +4232,7 @@ namespace ts { for (const baseSig of baseSignatures) { const typeParamCount = baseSig.typeParameters ? baseSig.typeParameters.length : 0; if (typeParamCount === typeArgCount) { - const sig = typeParamCount ? getSignatureInstantiation(baseSig, typeArguments) : cloneSignature(baseSig); + const sig = typeParamCount ? createSignatureInstantiation(baseSig, typeArguments) : cloneSignature(baseSig); sig.typeParameters = classType.localTypeParameters; sig.resolvedReturnType = classType; result.push(sig); @@ -4982,6 +4982,12 @@ namespace ts { } function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { + const instantiations = signature.instantiations || (signature.instantiations = createMap()); + const id = getTypeListId(typeArguments); + return instantiations[id] || (instantiations[id] = createSignatureInstantiation(signature, typeArguments)); + } + + function createSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { return instantiateSignature(signature, createTypeMapper(signature.typeParameters, typeArguments), /*eraseTypeParameters*/ true); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5536a818df5..37a3cc466a0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2923,6 +2923,8 @@ namespace ts { isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison /* @internal */ typePredicate?: TypePredicate; + /* @internal */ + instantiations?: Map; // Generic signature instantiation cache } export const enum IndexKind { From 4c1e4169bded1a3e6fbfb72edf8ae3a47e78b5ad Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 08:23:42 -0700 Subject: [PATCH 14/30] Accept new baselines --- tests/baselines/reference/promisePermutations.errors.txt | 4 ---- tests/baselines/reference/promisePermutations2.errors.txt | 4 ---- tests/baselines/reference/promisePermutations3.errors.txt | 4 ---- 3 files changed, 12 deletions(-) diff --git a/tests/baselines/reference/promisePermutations.errors.txt b/tests/baselines/reference/promisePermutations.errors.txt index 65fa0981643..13fc0718596 100644 --- a/tests/baselines/reference/promisePermutations.errors.txt +++ b/tests/baselines/reference/promisePermutations.errors.txt @@ -25,8 +25,6 @@ tests/cases/compiler/promisePermutations.ts(106,19): error TS2345: Argument of t Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations.ts(109,19): error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. - Types of parameters 'cb' and 'value' are incompatible. - Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations.ts(110,19): error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. @@ -229,8 +227,6 @@ tests/cases/compiler/promisePermutations.ts(160,21): error TS2345: Argument of t var s7a = r7.then(testFunction7, testFunction7, testFunction7); // error ~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. -!!! error TS2345: Types of parameters 'cb' and 'value' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type '(a: T) => T'. var s7b = r7.then(testFunction7P, testFunction7P, testFunction7P); // error ~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. diff --git a/tests/baselines/reference/promisePermutations2.errors.txt b/tests/baselines/reference/promisePermutations2.errors.txt index 0fc6f04911a..b21a3a39208 100644 --- a/tests/baselines/reference/promisePermutations2.errors.txt +++ b/tests/baselines/reference/promisePermutations2.errors.txt @@ -25,8 +25,6 @@ tests/cases/compiler/promisePermutations2.ts(105,19): error TS2345: Argument of Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations2.ts(108,19): error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. - Types of parameters 'cb' and 'value' are incompatible. - Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations2.ts(109,19): error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. @@ -228,8 +226,6 @@ tests/cases/compiler/promisePermutations2.ts(159,21): error TS2345: Argument of var s7a = r7.then(testFunction7, testFunction7, testFunction7); // error ~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. -!!! error TS2345: Types of parameters 'cb' and 'value' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type '(a: T) => T'. var s7b = r7.then(testFunction7P, testFunction7P, testFunction7P); // error ~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. diff --git a/tests/baselines/reference/promisePermutations3.errors.txt b/tests/baselines/reference/promisePermutations3.errors.txt index a1a1b3f493f..1a33d1083aa 100644 --- a/tests/baselines/reference/promisePermutations3.errors.txt +++ b/tests/baselines/reference/promisePermutations3.errors.txt @@ -28,8 +28,6 @@ tests/cases/compiler/promisePermutations3.ts(105,19): error TS2345: Argument of Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations3.ts(108,19): error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. - Types of parameters 'cb' and 'value' are incompatible. - Type 'string' is not assignable to type '(a: T) => T'. tests/cases/compiler/promisePermutations3.ts(109,19): error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. Types of parameters 'cb' and 'value' are incompatible. Type 'string' is not assignable to type '(a: T) => T'. @@ -240,8 +238,6 @@ tests/cases/compiler/promisePermutations3.ts(165,21): error TS2345: Argument of var s7a = r7.then(testFunction7, testFunction7, testFunction7); // error ~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => IPromise' is not assignable to parameter of type '(value: string) => IPromise'. -!!! error TS2345: Types of parameters 'cb' and 'value' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type '(a: T) => T'. var s7b = r7.then(testFunction7P, testFunction7P, testFunction7P); // error ~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(cb: (a: T) => T) => Promise' is not assignable to parameter of type '(value: string) => Promise'. From da7f11fe4bc7a2b95705601590df17a720f8e072 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 17:36:00 -0700 Subject: [PATCH 15/30] Properly instantiate aliasTypeArguments --- src/compiler/checker.ts | 25 ++++++++++++++++--------- src/compiler/types.ts | 1 - 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f2c642124a..d0028896baa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4164,8 +4164,8 @@ namespace ts { else { mapper = createTypeMapper(typeParameters, typeArguments); members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); - callSignatures = instantiateList(source.declaredCallSignatures, mapper, instantiateSignature); - constructSignatures = instantiateList(source.declaredConstructSignatures, mapper, instantiateSignature); + callSignatures = instantiateSignatures(source.declaredCallSignatures, mapper); + constructSignatures = instantiateSignatures(source.declaredConstructSignatures, mapper); stringIndexInfo = instantiateIndexInfo(source.declaredStringIndexInfo, mapper); numberIndexInfo = instantiateIndexInfo(source.declaredNumberIndexInfo, mapper); } @@ -4363,8 +4363,8 @@ namespace ts { const symbol = type.symbol; if (type.target) { const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper, /*mappingThisOnly*/ false); - const callSignatures = instantiateList(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper, instantiateSignature); - const constructSignatures = instantiateList(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper, instantiateSignature); + const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper); + const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper); const stringIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.String), type.mapper); const numberIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.Number), type.mapper); setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); @@ -6037,6 +6037,14 @@ namespace ts { return items; } + function instantiateTypes(types: Type[], mapper: TypeMapper) { + return instantiateList(types, mapper, instantiateType); + } + + function instantiateSignatures(signatures: Signature[], mapper: TypeMapper) { + return instantiateList(signatures, mapper, instantiateSignature); + } + function createUnaryTypeMapper(source: Type, target: Type): TypeMapper { return t => t === source ? target : t; } @@ -6063,7 +6071,6 @@ namespace ts { count == 2 ? createBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) : createArrayTypeMapper(sources, targets); mapper.mappedTypes = sources; - mapper.targetTypes = targets; return mapper; } @@ -6190,7 +6197,7 @@ namespace ts { result.target = type; result.mapper = mapper; result.aliasSymbol = type.aliasSymbol; - result.aliasTypeArguments = mapper.targetTypes; + result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper); mapper.instantiations[type.id] = result; return result; } @@ -6266,14 +6273,14 @@ namespace ts { instantiateAnonymousType(type, mapper) : type; } if ((type).objectFlags & ObjectFlags.Reference) { - return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); + return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper)); } } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { - return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes); + return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes); + return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } if (type.flags & TypeFlags.Index) { return getIndexType(instantiateType((type).type, mapper)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5536a818df5..f8c77a821a3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2940,7 +2940,6 @@ namespace ts { export interface TypeMapper { (t: TypeParameter): Type; mappedTypes?: Type[]; // Types mapped by this mapper - targetTypes?: Type[]; // Types substituted for mapped types instantiations?: Type[]; // Cache of instantiations created using this type mapper. context?: InferenceContext; // The inference context this mapper was created from. // Only inference mappers have this set (in createInferenceMapper). From adfa271e4487b0e86932f936b53536262db84c35 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 17:36:13 -0700 Subject: [PATCH 16/30] Add regression test --- .../reference/instantiatedTypeAliasDisplay.js | 21 ++++++ .../instantiatedTypeAliasDisplay.symbols | 60 +++++++++++++++++ .../instantiatedTypeAliasDisplay.types | 66 +++++++++++++++++++ .../compiler/instantiatedTypeAliasDisplay.ts | 15 +++++ 4 files changed, 162 insertions(+) create mode 100644 tests/baselines/reference/instantiatedTypeAliasDisplay.js create mode 100644 tests/baselines/reference/instantiatedTypeAliasDisplay.symbols create mode 100644 tests/baselines/reference/instantiatedTypeAliasDisplay.types create mode 100644 tests/cases/compiler/instantiatedTypeAliasDisplay.ts diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.js b/tests/baselines/reference/instantiatedTypeAliasDisplay.js new file mode 100644 index 00000000000..86b17262e0f --- /dev/null +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.js @@ -0,0 +1,21 @@ +//// [instantiatedTypeAliasDisplay.ts] +// Repros from #12066 + +interface X { + a: A; +} +interface Y { + b: B; +} +type Z = X | Y; + +declare function f1(): Z; +declare function f2(a: A, b: B, c: C, d: D): Z; + +const x1 = f1(); // Z +const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> + +//// [instantiatedTypeAliasDisplay.js] +// Repros from #12066 +var x1 = f1(); // Z +var x2 = f2({}, {}, {}, {}); // Z<{}, string[]> diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols b/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols new file mode 100644 index 00000000000..3c6f7d2b725 --- /dev/null +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols @@ -0,0 +1,60 @@ +=== tests/cases/compiler/instantiatedTypeAliasDisplay.ts === +// Repros from #12066 + +interface X { +>X : Symbol(X, Decl(instantiatedTypeAliasDisplay.ts, 0, 0)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 2, 12)) + + a: A; +>a : Symbol(X.a, Decl(instantiatedTypeAliasDisplay.ts, 2, 16)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 2, 12)) +} +interface Y { +>Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 4, 1)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 5, 12)) + + b: B; +>b : Symbol(Y.b, Decl(instantiatedTypeAliasDisplay.ts, 5, 16)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 5, 12)) +} +type Z = X | Y; +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 8, 7)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 8, 9)) +>X : Symbol(X, Decl(instantiatedTypeAliasDisplay.ts, 0, 0)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 8, 7)) +>Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 4, 1)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 8, 9)) + +declare function f1(): Z; +>f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 8, 27)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 10, 20)) +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 10, 20)) + +declare function f2(a: A, b: B, c: C, d: D): Z; +>f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 10, 39)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 11, 22)) +>C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 11, 25)) +>D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 11, 28)) +>E : Symbol(E, Decl(instantiatedTypeAliasDisplay.ts, 11, 31)) +>a : Symbol(a, Decl(instantiatedTypeAliasDisplay.ts, 11, 35)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) +>b : Symbol(b, Decl(instantiatedTypeAliasDisplay.ts, 11, 40)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 11, 22)) +>c : Symbol(c, Decl(instantiatedTypeAliasDisplay.ts, 11, 46)) +>C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 11, 25)) +>d : Symbol(d, Decl(instantiatedTypeAliasDisplay.ts, 11, 52)) +>D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 11, 28)) +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) + +const x1 = f1(); // Z +>x1 : Symbol(x1, Decl(instantiatedTypeAliasDisplay.ts, 13, 5)) +>f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 8, 27)) + +const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> +>x2 : Symbol(x2, Decl(instantiatedTypeAliasDisplay.ts, 14, 5)) +>f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 10, 39)) + diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.types b/tests/baselines/reference/instantiatedTypeAliasDisplay.types new file mode 100644 index 00000000000..44885777e74 --- /dev/null +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.types @@ -0,0 +1,66 @@ +=== tests/cases/compiler/instantiatedTypeAliasDisplay.ts === +// Repros from #12066 + +interface X { +>X : X +>A : A + + a: A; +>a : A +>A : A +} +interface Y { +>Y : Y +>B : B + + b: B; +>b : B +>B : B +} +type Z = X | Y; +>Z : Z +>A : A +>B : B +>X : X +>A : A +>Y : Y +>B : B + +declare function f1(): Z; +>f1 : () => Z +>A : A +>Z : Z +>A : A + +declare function f2(a: A, b: B, c: C, d: D): Z; +>f2 : (a: A, b: B, c: C, d: D) => Z +>A : A +>B : B +>C : C +>D : D +>E : E +>a : A +>A : A +>b : B +>B : B +>c : C +>C : C +>d : D +>D : D +>Z : Z +>A : A + +const x1 = f1(); // Z +>x1 : Z +>f1() : Z +>f1 : () => Z + +const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> +>x2 : Z<{}, string[]> +>f2({}, {}, {}, {}) : Z<{}, string[]> +>f2 : (a: A, b: B, c: C, d: D) => Z +>{} : {} +>{} : {} +>{} : {} +>{} : {} + diff --git a/tests/cases/compiler/instantiatedTypeAliasDisplay.ts b/tests/cases/compiler/instantiatedTypeAliasDisplay.ts new file mode 100644 index 00000000000..d2abaaf8afe --- /dev/null +++ b/tests/cases/compiler/instantiatedTypeAliasDisplay.ts @@ -0,0 +1,15 @@ +// Repros from #12066 + +interface X { + a: A; +} +interface Y { + b: B; +} +type Z = X | Y; + +declare function f1(): Z; +declare function f2(a: A, b: B, c: C, d: D): Z; + +const x1 = f1(); // Z +const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> \ No newline at end of file From 2bf4bad0e16a3e3d4546afe47d8ef4fb837778f4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 6 Nov 2016 16:00:41 -0800 Subject: [PATCH 17/30] Revert incorrect logic from #11392 --- src/compiler/checker.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d0028896baa..1a93909834b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2229,14 +2229,8 @@ namespace ts { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } - else if (!(flags & TypeFormatFlags.InTypeAlias) && ((getObjectFlags(type) & ObjectFlags.Anonymous && !(type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol && + else if (!(flags & TypeFormatFlags.InTypeAlias) && (getObjectFlags(type) & ObjectFlags.Anonymous || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { - // We emit inferred type as type-alias at the current localtion if all the following is true - // the input type is has alias symbol that is accessible - // the input type is a union, intersection or anonymous type that is fully instantiated (if not we want to keep dive into) - // e.g.: export type Bar = () => [X, Y]; - // export type Foo = Bar; - // export const y = (x: Foo) => 1 // we want to emit as ...x: () => [any, string]) const typeArguments = type.aliasTypeArguments; writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags); } From cc9daca38d06719612803e90bdf8ca4fcb706b67 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 6 Nov 2016 16:02:32 -0800 Subject: [PATCH 18/30] Accept new baselines --- .../declarationEmitTypeAliasWithTypeParameters1.js | 2 +- .../declarationEmitTypeAliasWithTypeParameters1.types | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.js b/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.js index c41c4690391..6079de35964 100644 --- a/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.js +++ b/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.js @@ -12,4 +12,4 @@ exports.y = function (x) { return 1; }; //// [declarationEmitTypeAliasWithTypeParameters1.d.ts] export declare type Bar = () => [X, Y]; export declare type Foo = Bar; -export declare const y: (x: () => [any, string]) => number; +export declare const y: (x: Bar) => number; diff --git a/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.types b/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.types index 3ff9f9bde1d..a8b0f146f8e 100644 --- a/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.types +++ b/tests/baselines/reference/declarationEmitTypeAliasWithTypeParameters1.types @@ -8,15 +8,15 @@ export type Bar = () => [X, Y]; >Y : Y export type Foo = Bar; ->Foo : () => [any, Y] +>Foo : Bar >Y : Y >Bar : Bar >Y : Y export const y = (x: Foo) => 1 ->y : (x: () => [any, string]) => number ->(x: Foo) => 1 : (x: () => [any, string]) => number ->x : () => [any, string] ->Foo : () => [any, Y] +>y : (x: Bar) => number +>(x: Foo) => 1 : (x: Bar) => number +>x : Bar +>Foo : Bar >1 : 1 From d5c67312f619f3283c2c1a448b965d6698da41a5 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 7 Nov 2016 12:41:22 -0800 Subject: [PATCH 19/30] Add `realpath` implementation for lshost --- src/server/lsHost.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 628de71bb7a..f1e80d95880 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -13,6 +13,7 @@ namespace ts.server { private readonly resolveModuleName: typeof resolveModuleName; readonly trace: (s: string) => void; + readonly realpath?: (path: string) => string; constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) { this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); @@ -39,6 +40,10 @@ namespace ts.server { } return primaryResult; }; + + if (this.host.realpath) { + this.realpath = path => this.host.realpath(path); + } } public startRecordingFilesWithChangedResolutions() { From 4ffdea838a65cf948f4ace6e659fc7936c7ea9a7 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 7 Nov 2016 13:36:08 -0800 Subject: [PATCH 20/30] Ports #12051 and #12032 into master (#12090) * use local registry to check if typings package exist (#12014) use local registry to check if typings package exist * enable sending telemetry events to tsserver client (#12035) enable sending telemetry events --- Jakefile.js | 2 + .../unittests/tsserverProjectSystem.ts | 33 +-- src/harness/unittests/typingsInstaller.ts | 261 ++++++++++-------- src/server/editorServices.ts | 4 +- src/server/protocol.ts | 26 ++ src/server/server.ts | 77 ++++-- src/server/session.ts | 6 +- src/server/shared.ts | 24 ++ src/server/tsconfig.json | 1 + src/server/tsconfig.library.json | 1 + src/server/types.d.ts | 26 +- .../typingsInstaller/nodeTypingsInstaller.ts | 141 +++++----- src/server/typingsInstaller/tsconfig.json | 1 + .../typingsInstaller/typingsInstaller.ts | 135 ++++----- src/server/utilities.ts | 2 + 15 files changed, 415 insertions(+), 325 deletions(-) create mode 100644 src/server/shared.ts diff --git a/Jakefile.js b/Jakefile.js index 8ce70eb353f..fc2b6359355 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -171,6 +171,7 @@ var servicesSources = [ var serverCoreSources = [ "types.d.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "typingsCache.ts", @@ -193,6 +194,7 @@ var cancellationTokenSources = [ var typingsInstallerSources = [ "../types.d.ts", + "../shared.ts", "typingsInstaller.ts", "nodeTypingsInstaller.ts" ].map(function (f) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 15cfa3f8c8e..ee6139b1215 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -18,7 +18,6 @@ namespace ts.projectSystem { }; export interface PostExecAction { - readonly requestKind: TI.RequestKind; readonly success: boolean; readonly callback: TI.RequestCompletedAction; } @@ -47,9 +46,14 @@ namespace ts.projectSystem { export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller { protected projectService: server.ProjectService; - constructor(readonly globalTypingsCacheLocation: string, throttleLimit: number, readonly installTypingHost: server.ServerHost, log?: TI.Log) { - super(globalTypingsCacheLocation, safeList.path, throttleLimit, log); - this.init(); + constructor( + readonly globalTypingsCacheLocation: string, + throttleLimit: number, + installTypingHost: server.ServerHost, + readonly typesRegistry = createMap(), + telemetryEnabled?: boolean, + log?: TI.Log) { + super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, telemetryEnabled, log); } safeFileList = safeList.path; @@ -63,9 +67,8 @@ namespace ts.projectSystem { } } - checkPendingCommands(expected: TI.RequestKind[]) { - assert.equal(this.postExecActions.length, expected.length, `Expected ${expected.length} post install actions`); - this.postExecActions.forEach((act, i) => assert.equal(act.requestKind, expected[i], "Unexpected post install action")); + checkPendingCommands(expectedCount: number) { + assert.equal(this.postExecActions.length, expectedCount, `Expected ${expectedCount} post install actions`); } onProjectClosed() { @@ -79,15 +82,8 @@ namespace ts.projectSystem { return this.installTypingHost; } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { - switch (requestKind) { - case TI.NpmViewRequest: - case TI.NpmInstallRequest: - break; - default: - assert.isTrue(false, `request ${requestKind} is not supported`); - } - this.addPostExecAction(requestKind, "success", cb); + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + this.addPostExecAction("success", cb); } sendResponse(response: server.SetTypings | server.InvalidateCachedTypings) { @@ -99,12 +95,11 @@ namespace ts.projectSystem { this.install(request); } - addPostExecAction(requestKind: TI.RequestKind, stdout: string | string[], cb: TI.RequestCompletedAction) { + addPostExecAction(stdout: string | string[], cb: TI.RequestCompletedAction) { const out = typeof stdout === "string" ? stdout : createNpmPackageJsonString(stdout); const action: PostExecAction = { success: !!out, - callback: cb, - requestKind + callback: cb }; this.postExecActions.push(action); } diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 356992d5717..2dda506ad58 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -8,44 +8,44 @@ namespace ts.projectSystem { interface InstallerParams { globalTypingsCacheLocation?: string; throttleLimit?: number; + typesRegistry?: Map; + } + + function createTypesRegistry(...list: string[]): Map { + const map = createMap(); + for (const l of list) { + map[l] = undefined; + } + return map; } class Installer extends TestTypingsInstaller { - constructor(host: server.ServerHost, p?: InstallerParams, log?: TI.Log) { + constructor(host: server.ServerHost, p?: InstallerParams, telemetryEnabled?: boolean, log?: TI.Log) { super( (p && p.globalTypingsCacheLocation) || "/a/data", (p && p.throttleLimit) || 5, host, + (p && p.typesRegistry), + telemetryEnabled, log); } - installAll(expectedView: typeof TI.NpmViewRequest[], expectedInstall: typeof TI.NpmInstallRequest[]) { - this.checkPendingCommands(expectedView); - this.executePendingCommands(); - this.checkPendingCommands(expectedInstall); + installAll(expectedCount: number) { + this.checkPendingCommands(expectedCount); this.executePendingCommands(); } } - describe("typingsInstaller", () => { - function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void { - switch (requestKind) { - case TI.NpmInstallRequest: - self.addPostExecAction(requestKind, installedTypings, success => { - for (const file of typingFiles) { - host.createFileOrFolder(file, /*createParentDirectory*/ true); - } - cb(success); - }); - break; - case TI.NpmViewRequest: - self.addPostExecAction(requestKind, installedTypings, cb); - break; - default: - assert.isTrue(false, `unexpected request kind ${requestKind}`); - break; + function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void { + self.addPostExecAction(installedTypings, success => { + for (const file of typingFiles) { + host.createFileOrFolder(file, /*createParentDirectory*/ true); } - } + cb(success); + }); + } + + describe("typingsInstaller", () => { it("configured projects (typings installed) 1", () => { const file1 = { path: "/a/b/app.js", @@ -79,12 +79,12 @@ namespace ts.projectSystem { const host = createServerHost([file1, tsconfig, packageJson]); const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/jquery"]; const typingFiles = [jquery]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -95,7 +95,7 @@ namespace ts.projectSystem { const p = projectService.configuredProjects[0]; checkProjectActualFiles(p, [file1.path]); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkProjectActualFiles(p, [file1.path, jquery.path]); @@ -123,12 +123,12 @@ namespace ts.projectSystem { const host = createServerHost([file1, packageJson]); const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/jquery"]; const typingFiles = [jquery]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -139,7 +139,7 @@ namespace ts.projectSystem { const p = projectService.inferredProjects[0]; checkProjectActualFiles(p, [file1.path]); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { inferredProjects: 1 }); checkProjectActualFiles(p, [file1.path, jquery.path]); @@ -167,7 +167,7 @@ namespace ts.projectSystem { options: {}, rootFiles: [toExternalFile(file1.path)] }); - installer.checkPendingCommands([]); + installer.checkPendingCommands(/*expectedCount*/ 0); // by default auto discovery will kick in if project contain only .js/.d.ts files // in this case project contain only ts files - no auto discovery projectService.checkNumberOfProjects({ externalProjects: 1 }); @@ -181,7 +181,7 @@ namespace ts.projectSystem { const host = createServerHost([file1]); const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery") }); } enqueueInstallTypingsRequest() { assert(false, "auto discovery should not be enabled"); @@ -196,7 +196,7 @@ namespace ts.projectSystem { rootFiles: [toExternalFile(file1.path)], typingOptions: { include: ["jquery"] } }); - installer.checkPendingCommands([]); + installer.checkPendingCommands(/*expectedCount*/ 0); // by default auto discovery will kick in if project contain only .js/.d.ts files // in this case project contain only ts files - no auto discovery even if typing options is set projectService.checkNumberOfProjects({ externalProjects: 1 }); @@ -215,16 +215,16 @@ namespace ts.projectSystem { let enqueueIsCalled = false; const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery") }); } enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray) { enqueueIsCalled = true; super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { - const installedTypings = ["@types/jquery"]; + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + const installedTypings = ["@types/node"]; const typingFiles = [jquery]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -234,11 +234,11 @@ namespace ts.projectSystem { projectFileName, options: {}, rootFiles: [toExternalFile(file1.path)], - typingOptions: { enableAutoDiscovery: true, include: ["node"] } + typingOptions: { enableAutoDiscovery: true, include: ["jquery"] } }); assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true"); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); // autoDiscovery is set in typing options - use it even if project contains only .ts files projectService.checkNumberOfProjects({ externalProjects: 1 }); @@ -273,12 +273,12 @@ namespace ts.projectSystem { const host = createServerHost([file1, file2, file3]); const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("lodash", "react") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings = ["@types/lodash", "@types/react"]; const typingFiles = [lodash, react]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -295,7 +295,7 @@ namespace ts.projectSystem { projectService.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path, file3.path]); - installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest], ); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]); @@ -317,16 +317,16 @@ namespace ts.projectSystem { let enqueueIsCalled = false; const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery") }); } enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray) { enqueueIsCalled = true; super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings: string[] = []; const typingFiles: FileOrFolder[] = []; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -343,7 +343,7 @@ namespace ts.projectSystem { projectService.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path]); - installer.checkPendingCommands([]); + installer.checkPendingCommands(/*expectedCount*/ 0); checkNumberOfProjects(projectService, { externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path]); @@ -396,12 +396,12 @@ namespace ts.projectSystem { const host = createServerHost([file1, file2, file3, packageJson]); const installer = new (class extends Installer { constructor() { - super(host); + super(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"]; const typingFiles = [commander, express, jquery, moment]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -418,10 +418,7 @@ namespace ts.projectSystem { projectService.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path, file3.path]); - installer.installAll( - [TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest], - [TI.NpmInstallRequest] - ); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { externalProjects: 1 }); checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]); @@ -475,11 +472,11 @@ namespace ts.projectSystem { const host = createServerHost([lodashJs, commanderJs, file3, packageJson]); const installer = new (class extends Installer { constructor() { - super(host, { throttleLimit: 3 }); + super(host, { throttleLimit: 3, typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -495,18 +492,7 @@ namespace ts.projectSystem { const p = projectService.externalProjects[0]; projectService.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path]); - // expected 3 view requests in the queue - installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]); - assert.equal(installer.pendingRunRequests.length, 2, "expected 2 pending requests"); - - // push view requests - installer.executePendingCommands(); - // expected 2 remaining view requests in the queue - installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest]); - // push view requests - installer.executePendingCommands(); - // expected one install request - installer.checkPendingCommands([TI.NpmInstallRequest]); + installer.checkPendingCommands(/*expectedCount*/ 1); installer.executePendingCommands(); // expected all typings file to exist for (const f of typingFiles) { @@ -565,22 +551,17 @@ namespace ts.projectSystem { const host = createServerHost([lodashJs, commanderJs, file3]); const installer = new (class extends Installer { constructor() { - super(host, { throttleLimit: 3 }); + super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { - if (requestKind === TI.NpmInstallRequest) { - let typingFiles: (FileOrFolder & { typings: string })[] = []; - if (args.indexOf("@types/commander") >= 0) { - typingFiles = [commander, jquery, lodash, cordova]; - } - else { - typingFiles = [grunt, gulp]; - } - executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, requestKind, cb); + installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { + let typingFiles: (FileOrFolder & { typings: string })[] = []; + if (args.indexOf("@types/commander") >= 0) { + typingFiles = [commander, jquery, lodash, cordova]; } else { - executeCommand(this, host, [], [], requestKind, cb); + typingFiles = [grunt, gulp]; } + executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, cb); } })(); @@ -594,8 +575,8 @@ namespace ts.projectSystem { typingOptions: { include: ["jquery", "cordova"] } }); - installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]); - assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request"); + installer.checkPendingCommands(/*expectedCount*/ 1); + assert.equal(installer.pendingRunRequests.length, 0, "expect no throttled requests"); // Create project #2 with 2 typings const projectFileName2 = "/a/app/test2.csproj"; @@ -605,7 +586,7 @@ namespace ts.projectSystem { rootFiles: [toExternalFile(file3.path)], typingOptions: { include: ["grunt", "gulp"] } }); - assert.equal(installer.pendingRunRequests.length, 3, "expect three throttled request"); + assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request"); const p1 = projectService.externalProjects[0]; const p2 = projectService.externalProjects[1]; @@ -613,18 +594,14 @@ namespace ts.projectSystem { checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path]); checkProjectActualFiles(p2, [file3.path]); - installer.executePendingCommands(); - // expected one view request from the first project and two - from the second one - installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]); + + // expected one install request from the second project + installer.checkPendingCommands(/*expectedCount*/ 1); assert.equal(installer.pendingRunRequests.length, 0, "expected no throttled requests"); installer.executePendingCommands(); - // should be two install requests from both projects - installer.checkPendingCommands([TI.NpmInstallRequest, TI.NpmInstallRequest]); - installer.executePendingCommands(); - checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]); checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path]); }); @@ -653,12 +630,12 @@ namespace ts.projectSystem { const host = createServerHost([app, jsconfig, jquery, jqueryPackage]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: "/tmp" }); + super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/jquery"]; const typingFiles = [jqueryDTS]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -669,7 +646,7 @@ namespace ts.projectSystem { const p = projectService.configuredProjects[0]; checkProjectActualFiles(p, [app.path]); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkProjectActualFiles(p, [app.path, jqueryDTS.path]); @@ -699,12 +676,12 @@ namespace ts.projectSystem { const host = createServerHost([app, jsconfig, bowerJson]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: "/tmp" }); + super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/jquery"]; const typingFiles = [jqueryDTS]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -715,7 +692,7 @@ namespace ts.projectSystem { const p = projectService.configuredProjects[0]; checkProjectActualFiles(p, [app.path]); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkProjectActualFiles(p, [app.path, jqueryDTS.path]); @@ -742,23 +719,23 @@ namespace ts.projectSystem { const host = createServerHost([f, brokenPackageJson]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: cachePath }); + super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/commander"]; const typingFiles = [commander]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); const service = createProjectService(host, { typingsInstaller: installer }); service.openClientFile(f.path); - installer.checkPendingCommands([]); + installer.checkPendingCommands(/*expectedCount*/ 0); host.reloadFS([f, fixedPackageJson]); host.triggerFileWatcherCallback(fixedPackageJson.path, /*removed*/ false); - // expected one view and one install request - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + // expected install request + installer.installAll(/*expectedCount*/ 1); service.checkNumberOfProjects({ inferredProjects: 1 }); checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]); @@ -783,12 +760,12 @@ namespace ts.projectSystem { const host = createServerHost([file]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: cachePath }); + super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { const installedTypings = ["@types/node", "@types/commander"]; const typingFiles = [node, commander]; - executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + executeCommand(this, host, installedTypings, typingFiles, cb); } })(); const service = createProjectService(host, { typingsInstaller: installer }); @@ -797,7 +774,7 @@ namespace ts.projectSystem { service.checkNumberOfProjects({ inferredProjects: 1 }); checkProjectActualFiles(service.inferredProjects[0], [file.path]); - installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/1); assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created"); assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created"); @@ -822,14 +799,10 @@ namespace ts.projectSystem { const host = createServerHost([f1]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: "/tmp" }); + super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("foo") }); } - executeRequest(requestKind: TI.RequestKind, _requestId: number, args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { - if (requestKind === TI.NpmViewRequest) { - // args should have only non-scoped packages - scoped packages are not yet supported - assert.deepEqual(args, ["foo"]); - } - executeCommand(this, host, ["foo"], [], requestKind, cb); + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + executeCommand(this, host, ["foo"], [], cb); } })(); const projectService = createProjectService(host, { typingsInstaller: installer }); @@ -844,7 +817,7 @@ namespace ts.projectSystem { ["foo", "foo", "foo", "@bar/router", "@bar/common", "@bar/common"] ); - installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + installer.installAll(/*expectedCount*/ 1); }); it("cached unresolved typings are not recomputed if program structure did not change", () => { @@ -934,16 +907,16 @@ namespace ts.projectSystem { const host = createServerHost([f1, packageJson]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) }); + super(host, { globalTypingsCacheLocation: "/tmp" }, /*telemetryEnabled*/ false, { isEnabled: () => true, writeLine: msg => messages.push(msg) }); } - executeRequest() { + installWorker(_requestId: number, _args: string[], _cwd: string, _cb: server.typingsInstaller.RequestCompletedAction) { assert(false, "runCommand should not be invoked"); } })(); const projectService = createProjectService(host, { typingsInstaller: installer }); projectService.openClientFile(f1.path); - installer.checkPendingCommands([]); + installer.checkPendingCommands(/*expectedCount*/ 0); assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); }); }); @@ -978,4 +951,50 @@ namespace ts.projectSystem { assert.deepEqual(result.newTypingNames, ["bar"]); }); }); + + describe("telemetry events", () => { + it ("should be received", () => { + const f1 = { + path: "/a/app.js", + content: "" + }; + const package = { + path: "/a/package.json", + content: JSON.stringify({ dependencies: { "commander": "1.0.0" } }) + }; + const cachePath = "/a/cache/"; + const commander = { + path: cachePath + "node_modules/@types/commander/index.d.ts", + content: "export let x: number" + }; + const host = createServerHost([f1, package]); + let seenTelemetryEvent = false; + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") }, /*telemetryEnabled*/ true); + } + installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/commander"]; + const typingFiles = [commander]; + executeCommand(this, host, installedTypings, typingFiles, cb); + } + sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.TypingsInstallEvent) { + if (response.kind === server.EventInstall) { + assert.deepEqual(response.packagesToInstall, ["@types/commander"]); + seenTelemetryEvent = true; + return; + } + super.sendResponse(response); + } + })(); + const projectService = createProjectService(host, { typingsInstaller: installer }); + projectService.openClientFile(f1.path); + + installer.installAll(/*expectedCount*/ 1); + + assert.isTrue(seenTelemetryEvent); + checkNumberOfProjects(projectService, { inferredProjects: 1 }); + checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]); + }); + }); } \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a02becd87c4..bf760106b74 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -286,10 +286,10 @@ namespace ts.server { return; } switch (response.kind) { - case "set": + case ActionSet: this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.unresolvedImports, response.typings); break; - case "invalidate": + case ActionInvalidate: this.typingsCache.deleteTypingsForProject(response.projectName); break; } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 7da95e49575..d13caf7f01b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2057,6 +2057,32 @@ namespace ts.server.protocol { childItems?: NavigationTree[]; } + export type TelemetryEventName = "telemetry"; + + export interface TelemetryEvent extends Event { + event: TelemetryEventName; + body: TelemetryEventBody; + } + + export interface TelemetryEventBody { + telemetryEventName: string; + payload: any; + } + + export type TypingsInstalledTelemetryEventName = "typingsInstalled"; + + export interface TypingsInstalledTelemetryEventBody extends TelemetryEventBody { + telemetryEventName: TypingsInstalledTelemetryEventName; + payload: TypingsInstalledTelemetryEventPayload; + } + + export interface TypingsInstalledTelemetryEventPayload { + /** + * Comma separated list of installed typing packages + */ + installedPackages: string; + } + export interface NavBarResponse extends Response { body?: NavigationBarItem[]; } diff --git a/src/server/server.ts b/src/server/server.ts index 5057a13dbd1..3b33554aaba 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -1,4 +1,5 @@ /// +/// /// // used in fs.writeSync /* tslint:disable:no-null-keyword */ @@ -196,8 +197,10 @@ namespace ts.server { private socket: NodeSocket; private projectService: ProjectService; private throttledOperations: ThrottledOperations; + private telemetrySender: EventSender; constructor( + private readonly telemetryEnabled: boolean, private readonly logger: server.Logger, host: ServerHost, eventPort: number, @@ -226,15 +229,22 @@ namespace ts.server { this.socket.write(formatMessage({ seq, type: "event", event, body }, this.logger, Buffer.byteLength, this.newLine), "utf8"); } + setTelemetrySender(telemetrySender: EventSender) { + this.telemetrySender = telemetrySender; + } + attach(projectService: ProjectService) { this.projectService = projectService; if (this.logger.hasLevel(LogLevel.requestTime)) { this.logger.info("Binding..."); } - const args: string[] = ["--globalTypingsCacheLocation", this.globalTypingsCacheLocation]; + const args: string[] = [Arguments.GlobalCacheLocation, this.globalTypingsCacheLocation]; + if (this.telemetryEnabled) { + args.push(Arguments.EnableTelemetry); + } if (this.logger.loggingEnabled() && this.logger.getLogFileName()) { - args.push("--logFile", combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`)); + args.push(Arguments.LogFile, combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`)); } const execArgv: string[] = []; { @@ -280,12 +290,25 @@ namespace ts.server { }); } - private handleMessage(response: SetTypings | InvalidateCachedTypings) { + private handleMessage(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent) { if (this.logger.hasLevel(LogLevel.verbose)) { this.logger.info(`Received response: ${JSON.stringify(response)}`); } + if (response.kind === EventInstall) { + if (this.telemetrySender) { + const body: protocol.TypingsInstalledTelemetryEventBody = { + telemetryEventName: "typingsInstalled", + payload: { + installedPackages: response.packagesToInstall.join(",") + } + }; + const eventName: protocol.TelemetryEventName = "telemetry"; + this.telemetrySender.event(body, eventName); + } + return; + } this.projectService.updateTypingsForProject(response); - if (response.kind == "set" && this.socket) { + if (response.kind == ActionSet && this.socket) { this.sendEvent(0, "setTypings", response); } } @@ -300,18 +323,25 @@ namespace ts.server { useSingleInferredProject: boolean, disableAutomaticTypingAcquisition: boolean, globalTypingsCacheLocation: string, + telemetryEnabled: boolean, logger: server.Logger) { - super( - host, - cancellationToken, - useSingleInferredProject, - disableAutomaticTypingAcquisition - ? nullTypingsInstaller - : new NodeTypingsInstaller(logger, host, installerEventPort, globalTypingsCacheLocation, host.newLine), - Buffer.byteLength, - process.hrtime, - logger, - canUseEvents); + const typingsInstaller = disableAutomaticTypingAcquisition + ? undefined + : new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, host.newLine); + + super( + host, + cancellationToken, + useSingleInferredProject, + typingsInstaller || nullTypingsInstaller, + Buffer.byteLength, + process.hrtime, + logger, + canUseEvents); + + if (telemetryEnabled && typingsInstaller) { + typingsInstaller.setTelemetrySender(this); + } } exit() { @@ -538,17 +568,17 @@ namespace ts.server { let eventPort: number; { - const index = sys.args.indexOf("--eventPort"); - if (index >= 0 && index < sys.args.length - 1) { - const v = parseInt(sys.args[index + 1]); - if (!isNaN(v)) { - eventPort = v; - } + const str = findArgument("--eventPort"); + const v = str && parseInt(str); + if (!isNaN(v)) { + eventPort = v; } } - const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0; - const disableAutomaticTypingAcquisition = sys.args.indexOf("--disableAutomaticTypingAcquisition") >= 0; + const useSingleInferredProject = hasArgument("--useSingleInferredProject"); + const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition"); + const telemetryEnabled = hasArgument(Arguments.EnableTelemetry); + const ioSession = new IOSession( sys, cancellationToken, @@ -557,6 +587,7 @@ namespace ts.server { useSingleInferredProject, disableAutomaticTypingAcquisition, getGlobalTypingsCacheLocation(), + telemetryEnabled, logger); process.on("uncaughtException", function (err: Error) { ioSession.logError(err, "unknown"); diff --git a/src/server/session.ts b/src/server/session.ts index 545701f1449..b250393b7ff 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -73,6 +73,10 @@ namespace ts.server { project: Project; } + export interface EventSender { + event(payload: any, eventName: string): void; + } + function allEditsBeforePos(edits: ts.TextChange[], pos: number) { for (const edit of edits) { if (textSpanEnd(edit.span) >= pos) { @@ -165,7 +169,7 @@ namespace ts.server { return `Content-Length: ${1 + len}\r\n\r\n${json}${newLine}`; } - export class Session { + export class Session implements EventSender { private readonly gcTimer: GcTimer; protected projectService: ProjectService; private errorTimer: any; /*NodeJS.Timer | number*/ diff --git a/src/server/shared.ts b/src/server/shared.ts new file mode 100644 index 00000000000..81a1f7fb55b --- /dev/null +++ b/src/server/shared.ts @@ -0,0 +1,24 @@ +/// + +namespace ts.server { + export const ActionSet: ActionSet = "action::set"; + export const ActionInvalidate: ActionInvalidate = "action::invalidate"; + export const EventInstall: EventInstall = "event::install"; + + export namespace Arguments { + export const GlobalCacheLocation = "--globalTypingsCacheLocation"; + export const LogFile = "--logFile"; + export const EnableTelemetry = "--enableTelemetry"; + } + + export function hasArgument(argumentName: string) { + return sys.args.indexOf(argumentName) >= 0; + } + + export function findArgument(argumentName: string) { + const index = sys.args.indexOf(argumentName); + return index >= 0 && index < sys.args.length - 1 + ? sys.args[index + 1] + : undefined; + } +} \ No newline at end of file diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index a99994d97c5..85c88679164 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -18,6 +18,7 @@ "files": [ "../services/shims.ts", "../services/utilities.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "scriptInfo.ts", diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index e269629b558..5483cc8ec28 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -15,6 +15,7 @@ "files": [ "../services/shims.ts", "../services/utilities.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "scriptInfo.ts", diff --git a/src/server/types.d.ts b/src/server/types.d.ts index ec2befe8fa9..95f9519cc38 100644 --- a/src/server/types.d.ts +++ b/src/server/types.d.ts @@ -41,23 +41,33 @@ declare namespace ts.server { readonly kind: "closeProject"; } - export type SetRequest = "set"; - export type InvalidateRequest = "invalidate"; + export type ActionSet = "action::set"; + export type ActionInvalidate = "action::invalidate"; + export type EventInstall = "event::install"; + export interface TypingInstallerResponse { - readonly projectName: string; - readonly kind: SetRequest | InvalidateRequest; + readonly kind: ActionSet | ActionInvalidate | EventInstall; } - export interface SetTypings extends TypingInstallerResponse { + export interface ProjectResponse extends TypingInstallerResponse { + readonly projectName: string; + } + + export interface SetTypings extends ProjectResponse { readonly typingOptions: ts.TypingOptions; readonly compilerOptions: ts.CompilerOptions; readonly typings: string[]; readonly unresolvedImports: SortedReadonlyArray; - readonly kind: SetRequest; + readonly kind: ActionSet; } - export interface InvalidateCachedTypings extends TypingInstallerResponse { - readonly kind: InvalidateRequest; + export interface InvalidateCachedTypings extends ProjectResponse { + readonly kind: ActionInvalidate; + } + + export interface TypingsInstallEvent extends TypingInstallerResponse { + readonly packagesToInstall: ReadonlyArray; + readonly kind: EventInstall; } export interface InstallTypingHost extends JsTyping.TypingResolutionHost { diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index 89419264930..7020b6aa4f8 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -33,42 +33,81 @@ namespace ts.server.typingsInstaller { } } - type HttpGet = { - (url: string, callback: (response: HttpResponse) => void): NodeJS.EventEmitter; - }; - - interface HttpResponse extends NodeJS.ReadableStream { - statusCode: number; - statusMessage: string; - destroy(): void; + interface TypesRegistryFile { + entries: MapLike; } + function loadTypesRegistryFile(typesRegistryFilePath: string, host: InstallTypingHost, log: Log): Map { + if (!host.fileExists(typesRegistryFilePath)) { + if (log.isEnabled()) { + log.writeLine(`Types registry file '${typesRegistryFilePath}' does not exist`); + } + return createMap(); + } + try { + const content = JSON.parse(host.readFile(typesRegistryFilePath)); + return createMap(content.entries); + } + catch (e) { + if (log.isEnabled()) { + log.writeLine(`Error when loading types registry file '${typesRegistryFilePath}': ${(e).message}, ${(e).stack}`); + } + return createMap(); + } + } + + const TypesRegistryPackageName = "types-registry"; + function getTypesRegistryFileLocation(globalTypingsCacheLocation: string): string { + return combinePaths(normalizeSlashes(globalTypingsCacheLocation), `node_modules/${TypesRegistryPackageName}/index.json`); + } + + type Exec = { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any }; + type ExecSync = { + (command: string, options: { cwd: string, stdio: "ignore" }): any + }; + export class NodeTypingsInstaller extends TypingsInstaller { private readonly exec: Exec; - private readonly httpGet: HttpGet; private readonly npmPath: string; - readonly installTypingHost: InstallTypingHost = sys; + readonly typesRegistry: Map; - constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) { + constructor(globalTypingsCacheLocation: string, throttleLimit: number, telemetryEnabled: boolean, log: Log) { super( + sys, globalTypingsCacheLocation, toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), throttleLimit, + telemetryEnabled, log); if (this.log.isEnabled()) { this.log.writeLine(`Process id: ${process.pid}`); } this.npmPath = getNPMLocation(process.argv[0]); - this.exec = require("child_process").exec; - this.httpGet = require("http").get; + let execSync: ExecSync; + ({ exec: this.exec, execSync } = require("child_process")); + + this.ensurePackageDirectoryExists(globalTypingsCacheLocation); + + try { + if (this.log.isEnabled()) { + this.log.writeLine(`Updating ${TypesRegistryPackageName} npm package...`); + } + execSync(`${this.npmPath} install ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation, stdio: "ignore" }); + } + catch (e) { + if (this.log.isEnabled()) { + this.log.writeLine(`Error updating ${TypesRegistryPackageName} package: ${(e).message}`); + } + } + + this.typesRegistry = loadTypesRegistryFile(getTypesRegistryFileLocation(globalTypingsCacheLocation), this.installTypingHost, this.log); } - init() { - super.init(); + listen() { process.on("message", (req: DiscoverTypings | CloseProject) => { switch (req.kind) { case "discover": @@ -90,66 +129,26 @@ namespace ts.server.typingsInstaller { } } - protected executeRequest(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void { + protected installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void { if (this.log.isEnabled()) { - this.log.writeLine(`#${requestId} executing ${requestKind}, arguments'${JSON.stringify(args)}'.`); - } - switch (requestKind) { - case NpmViewRequest: { - // const command = `${self.npmPath} view @types/${typing} --silent name`; - // use http request to global npm registry instead of running npm view - Debug.assert(args.length === 1); - const url = `http://registry.npmjs.org/@types%2f${args[0]}`; - const start = Date.now(); - this.httpGet(url, response => { - let ok = false; - if (this.log.isEnabled()) { - this.log.writeLine(`${requestKind} #${requestId} request to ${url}:: status code ${response.statusCode}, status message '${response.statusMessage}', took ${Date.now() - start} ms`); - } - switch (response.statusCode) { - case 200: // OK - case 301: // redirect - Moved - treat package as present - case 302: // redirect - Found - treat package as present - ok = true; - break; - } - response.destroy(); - onRequestCompleted(ok); - }).on("error", (err: Error) => { - if (this.log.isEnabled()) { - this.log.writeLine(`${requestKind} #${requestId} query to npm registry failed with error ${err.message}, stack ${err.stack}`); - } - onRequestCompleted(/*success*/ false); - }); - } - break; - case NpmInstallRequest: { - const command = `${this.npmPath} install ${args.join(" ")} --save-dev`; - const start = Date.now(); - this.exec(command, { cwd }, (_err, stdout, stderr) => { - if (this.log.isEnabled()) { - this.log.writeLine(`${requestKind} #${requestId} took: ${Date.now() - start} ms${sys.newLine}stdout: ${stdout}${sys.newLine}stderr: ${stderr}`); - } - // treat any output on stdout as success - onRequestCompleted(!!stdout); - }); - } - break; - default: - Debug.assert(false, `Unknown request kind ${requestKind}`); + this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(args)}'.`); } + const command = `${this.npmPath} install ${args.join(" ")} --save-dev`; + const start = Date.now(); + this.exec(command, { cwd }, (_err, stdout, stderr) => { + if (this.log.isEnabled()) { + this.log.writeLine(`npm install #${requestId} took: ${Date.now() - start} ms${sys.newLine}stdout: ${stdout}${sys.newLine}stderr: ${stderr}`); + } + // treat any output on stdout as success + onRequestCompleted(!!stdout); + }); } } - function findArgument(argumentName: string) { - const index = sys.args.indexOf(argumentName); - return index >= 0 && index < sys.args.length - 1 - ? sys.args[index + 1] - : undefined; - } + const logFilePath = findArgument(server.Arguments.LogFile); + const globalTypingsCacheLocation = findArgument(server.Arguments.GlobalCacheLocation); + const telemetryEnabled = hasArgument(server.Arguments.EnableTelemetry); - const logFilePath = findArgument("--logFile"); - const globalTypingsCacheLocation = findArgument("--globalTypingsCacheLocation"); const log = new FileLog(logFilePath); if (log.isEnabled()) { process.on("uncaughtException", (e: Error) => { @@ -162,6 +161,6 @@ namespace ts.server.typingsInstaller { } process.exit(0); }); - const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, log); - installer.init(); + const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, telemetryEnabled, log); + installer.listen(); } \ No newline at end of file diff --git a/src/server/typingsInstaller/tsconfig.json b/src/server/typingsInstaller/tsconfig.json index c9b4d8f0ad1..c6031b19aae 100644 --- a/src/server/typingsInstaller/tsconfig.json +++ b/src/server/typingsInstaller/tsconfig.json @@ -17,6 +17,7 @@ }, "files": [ "../types.d.ts", + "../shared.ts", "typingsInstaller.ts", "nodeTypingsInstaller.ts" ] diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index df043fc26ae..a602a9f1a26 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -2,6 +2,7 @@ /// /// /// +/// namespace ts.server.typingsInstaller { interface NpmConfig { @@ -63,14 +64,8 @@ namespace ts.server.typingsInstaller { return PackageNameValidationResult.Ok; } - export const NpmViewRequest: "npm view" = "npm view"; - export const NpmInstallRequest: "npm install" = "npm install"; - - export type RequestKind = typeof NpmViewRequest | typeof NpmInstallRequest; - export type RequestCompletedAction = (success: boolean) => void; type PendingRequest = { - requestKind: RequestKind; requestId: number; args: string[]; cwd: string; @@ -87,19 +82,18 @@ namespace ts.server.typingsInstaller { private installRunCount = 1; private inFlightRequestCount = 0; - abstract readonly installTypingHost: InstallTypingHost; + abstract readonly typesRegistry: Map; constructor( + readonly installTypingHost: InstallTypingHost, readonly globalCachePath: string, readonly safeListPath: Path, readonly throttleLimit: number, + readonly telemetryEnabled: boolean, protected readonly log = nullLog) { if (this.log.isEnabled()) { this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`); } - } - - init() { this.processCacheLocation(this.globalCachePath); } @@ -224,7 +218,7 @@ namespace ts.server.typingsInstaller { this.knownCachesSet[cacheLocation] = true; } - private filterTypings(typingsToInstall: string[]) { + private filterAndMapToScopedName(typingsToInstall: string[]) { if (typingsToInstall.length === 0) { return typingsToInstall; } @@ -235,7 +229,14 @@ namespace ts.server.typingsInstaller { } const validationResult = validatePackageName(typing); if (validationResult === PackageNameValidationResult.Ok) { - result.push(typing); + if (typing in this.typesRegistry) { + result.push(`@types/${typing}`); + } + else { + if (this.log.isEnabled()) { + this.log.writeLine(`Entry for package '${typing}' does not exist in local types registry - skipping...`); + } + } } else { // add typing name to missing set so we won't process it again @@ -267,19 +268,8 @@ namespace ts.server.typingsInstaller { return result; } - private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) { - if (this.log.isEnabled()) { - this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`); - } - typingsToInstall = this.filterTypings(typingsToInstall); - if (typingsToInstall.length === 0) { - if (this.log.isEnabled()) { - this.log.writeLine(`All typings are known to be missing or invalid - no need to go any further`); - } - return; - } - - const npmConfigPath = combinePaths(cachePath, "package.json"); + protected ensurePackageDirectoryExists(directory: string) { + const npmConfigPath = combinePaths(directory, "package.json"); if (this.log.isEnabled()) { this.log.writeLine(`Npm config file: ${npmConfigPath}`); } @@ -287,23 +277,50 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Npm config file: '${npmConfigPath}' is missing, creating new one...`); } - this.ensureDirectoryExists(cachePath, this.installTypingHost); + this.ensureDirectoryExists(directory, this.installTypingHost); this.installTypingHost.writeFile(npmConfigPath, "{}"); } + } + + private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) { + if (this.log.isEnabled()) { + this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`); + } + const scopedTypings = this.filterAndMapToScopedName(typingsToInstall); + if (scopedTypings.length === 0) { + if (this.log.isEnabled()) { + this.log.writeLine(`All typings are known to be missing or invalid - no need to go any further`); + } + return; + } + + this.ensurePackageDirectoryExists(cachePath); + + const requestId = this.installRunCount; + this.installRunCount++; + + this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => { + if (this.telemetryEnabled) { + this.sendResponse({ + kind: EventInstall, + packagesToInstall: scopedTypings + }); + } + + if (!ok) { + return; + } - this.runInstall(cachePath, typingsToInstall, installedTypings => { // TODO: watch project directory if (this.log.isEnabled()) { - this.log.writeLine(`Requested to install typings ${JSON.stringify(typingsToInstall)}, installed typings ${JSON.stringify(installedTypings)}`); + this.log.writeLine(`Requested to install typings ${JSON.stringify(scopedTypings)}, installed typings ${JSON.stringify(scopedTypings)}`); } - const installedPackages: Map = createMap(); const installedTypingFiles: string[] = []; - for (const t of installedTypings) { + for (const t of scopedTypings) { const packageName = getBaseFileName(t); if (!packageName) { continue; } - installedPackages[packageName] = true; const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost); if (!typingFile) { continue; @@ -316,53 +333,11 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`); } - for (const toInstall of typingsToInstall) { - if (!installedPackages[toInstall]) { - if (this.log.isEnabled()) { - this.log.writeLine(`New missing typing package '${toInstall}'`); - } - this.missingTypingsSet[toInstall] = true; - } - } this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles))); }); } - private runInstall(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void { - const requestId = this.installRunCount; - - this.installRunCount++; - let execInstallCmdCount = 0; - const filteredTypings: string[] = []; - for (const typing of typingsToInstall) { - filterExistingTypings(this, typing); - } - - function filterExistingTypings(self: TypingsInstaller, typing: string) { - self.execAsync(NpmViewRequest, requestId, [typing], cachePath, ok => { - if (ok) { - filteredTypings.push(typing); - } - execInstallCmdCount++; - if (execInstallCmdCount === typingsToInstall.length) { - installFilteredTypings(self, filteredTypings); - } - }); - } - - function installFilteredTypings(self: TypingsInstaller, filteredTypings: string[]) { - if (filteredTypings.length === 0) { - postInstallAction([]); - return; - } - const scopedTypings = filteredTypings.map(t => "@types/" + t); - self.execAsync(NpmInstallRequest, requestId, scopedTypings, cachePath, ok => { - postInstallAction(ok ? scopedTypings : []); - }); - } - } - private ensureDirectoryExists(directory: string, host: InstallTypingHost): void { const directoryName = getDirectoryPath(directory); if (!host.directoryExists(directoryName)) { @@ -389,7 +364,7 @@ namespace ts.server.typingsInstaller { this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`); } if (!isInvoked) { - this.sendResponse({ projectName: projectName, kind: "invalidate" }); + this.sendResponse({ projectName: projectName, kind: server.ActionInvalidate }); isInvoked = true; } }); @@ -405,12 +380,12 @@ namespace ts.server.typingsInstaller { compilerOptions: request.compilerOptions, typings, unresolvedImports: request.unresolvedImports, - kind: "set" + kind: server.ActionSet }; } - private execAsync(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void { - this.pendingRunRequests.unshift({ requestKind, requestId, args, cwd, onRequestCompleted }); + private installTypingsAsync(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void { + this.pendingRunRequests.unshift({ requestId, args, cwd, onRequestCompleted }); this.executeWithThrottling(); } @@ -418,7 +393,7 @@ namespace ts.server.typingsInstaller { while (this.inFlightRequestCount < this.throttleLimit && this.pendingRunRequests.length) { this.inFlightRequestCount++; const request = this.pendingRunRequests.pop(); - this.executeRequest(request.requestKind, request.requestId, request.args, request.cwd, ok => { + this.installWorker(request.requestId, request.args, request.cwd, ok => { this.inFlightRequestCount--; request.onRequestCompleted(ok); this.executeWithThrottling(); @@ -426,7 +401,7 @@ namespace ts.server.typingsInstaller { } } - protected abstract executeRequest(requestKind: RequestKind, requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void; - protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void; + protected abstract installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void; + protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent): void; } } \ No newline at end of file diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 8806b759e3f..9a832d22c8d 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -1,4 +1,5 @@ /// +/// namespace ts.server { export enum LogLevel { @@ -10,6 +11,7 @@ namespace ts.server { export const emptyArray: ReadonlyArray = []; + export interface Logger { close(): void; hasLevel(level: LogLevel): boolean; From 0173a3fa790ca49b74d609c410c77d363e18229d Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 7 Nov 2016 15:48:46 -0800 Subject: [PATCH 21/30] return empty file watcher in case if target directory does not exist (#12091) * return empty file watcher in case if target directory does not exist * linter --- src/compiler/sys.ts | 3 ++- .../unittests/tsserverProjectSystem.ts | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 13bbfc2ab15..9cf95d6de9b 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -439,6 +439,7 @@ namespace ts { return filter(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory)); } + const noOpFileWatcher: FileWatcher = { close: noop }; const nodeSystem: System = { args: process.argv.slice(2), newLine: _os.EOL, @@ -475,7 +476,7 @@ namespace ts { // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) let options: any; if (!directoryExists(directoryName)) { - return; + return noOpFileWatcher; } if (isNode4OrLater() && (process.platform === "win32" || process.platform === "darwin")) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index ee6139b1215..c3974f03772 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1964,6 +1964,33 @@ namespace ts.projectSystem { projectService.checkNumberOfProjects({ configuredProjects: 1 }); checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, libES2015Promise.path, app.path]); }); + + it("should handle non-existing directories in config file", () => { + const f = { + path: "/a/src/app.ts", + content: "let x = 1;" + }; + const config = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compilerOptions: {}, + include: [ + "src/**/*", + "notexistingfolder/*" + ] + }) + }; + const host = createServerHost([f, config]); + const projectService = createProjectService(host); + projectService.openClientFile(f.path); + projectService.checkNumberOfProjects({ configuredProjects: 1 }); + + projectService.closeClientFile(f.path); + projectService.checkNumberOfProjects({ configuredProjects: 0 }); + + projectService.openClientFile(f.path); + projectService.checkNumberOfProjects({ configuredProjects: 1 }); + }); }); describe("prefer typings to js", () => { From be2e8e85d63f472a5a7762e1e56316026156317c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 7 Nov 2016 15:49:19 -0800 Subject: [PATCH 22/30] property handle missing config files in external projects (#12094) --- .../unittests/tsserverProjectSystem.ts | 21 +++++++++++++++++++ src/server/editorServices.ts | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index c3974f03772..62c0957d6a3 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -2249,6 +2249,27 @@ namespace ts.projectSystem { assert.equal(diags.length, 0); }); + it("should property handle missing config files", () => { + const f1 = { + path: "/a/b/app.ts", + content: "let x = 1" + }; + const config = { + path: "/a/b/tsconfig.json", + content: "{}" + }; + const projectName = "project1"; + const host = createServerHost([f1]); + const projectService = createProjectService(host); + projectService.openExternalProject({ rootFiles: toExternalFiles([f1.path, config.path]), options: {}, projectFileName: projectName }); + + // should have one external project since config file is missing + projectService.checkNumberOfProjects({ externalProjects: 1 }); + + host.reloadFS([f1, config]); + projectService.openExternalProject({ rootFiles: toExternalFiles([f1.path, config.path]), options: {}, projectFileName: projectName }); + projectService.checkNumberOfProjects({ configuredProjects: 1 }); + }); }); describe("add the missing module file for inferred project", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bf760106b74..269eea06464 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1288,7 +1288,9 @@ namespace ts.server { for (const file of proj.rootFiles) { const normalized = toNormalizedPath(file.fileName); if (getBaseFileName(normalized) === "tsconfig.json") { - (tsConfigFiles || (tsConfigFiles = [])).push(normalized); + if (this.host.fileExists(normalized)) { + (tsConfigFiles || (tsConfigFiles = [])).push(normalized); + } } else { rootFiles.push(file); From ddc4ae7eac9526b5dbd6624f89a2f0308b32b425 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 7 Nov 2016 16:03:04 -0800 Subject: [PATCH 23/30] Reuse subtree transform flags for incrementally parsed nodes (#12088) --- src/compiler/binder.ts | 66 +++++++++++++++++++++- src/compiler/visitor.ts | 60 -------------------- src/harness/unittests/incrementalParser.ts | 24 +++++--- src/services/services.ts | 2 - 4 files changed, 81 insertions(+), 71 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1d721e2db90..112a53e88ec 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -560,6 +560,7 @@ namespace ts { skipTransformFlagAggregation = true; bindChildrenWorker(node); skipTransformFlagAggregation = false; + subtreeTransformFlags |= node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind); } else { const savedSubtreeTransformFlags = subtreeTransformFlags; @@ -895,8 +896,8 @@ namespace ts { const enclosingLabeledStatement = node.parent.kind === SyntaxKind.LabeledStatement ? lastOrUndefined(activeLabels) : undefined; - // if do statement is wrapped in labeled statement then target labels for break/continue with or without - // label should be the same + // if do statement is wrapped in labeled statement then target labels for break/continue with or without + // label should be the same const preConditionLabel = enclosingLabeledStatement ? enclosingLabeledStatement.continueTarget : createBranchLabel(); const postDoLabel = enclosingLabeledStatement ? enclosingLabeledStatement.breakTarget : createBranchLabel(); addAntecedent(preDoLabel, currentFlow); @@ -3206,4 +3207,65 @@ namespace ts { node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; return transformFlags & ~excludeFlags; } + + /** + * Gets the transform flags to exclude when unioning the transform flags of a subtree. + * + * NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`. + * For performance reasons, `computeTransformFlagsForNode` uses local constant values rather + * than calling this function. + */ + /* @internal */ + export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) { + if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) { + return TransformFlags.TypeExcludes; + } + + switch (kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.ArrayLiteralExpression: + return TransformFlags.ArrayLiteralOrCallOrNewExcludes; + case SyntaxKind.ModuleDeclaration: + return TransformFlags.ModuleExcludes; + case SyntaxKind.Parameter: + return TransformFlags.ParameterExcludes; + case SyntaxKind.ArrowFunction: + return TransformFlags.ArrowFunctionExcludes; + case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionDeclaration: + return TransformFlags.FunctionExcludes; + case SyntaxKind.VariableDeclarationList: + return TransformFlags.VariableDeclarationListExcludes; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + return TransformFlags.ClassExcludes; + case SyntaxKind.Constructor: + return TransformFlags.ConstructorExcludes; + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return TransformFlags.MethodOrAccessorExcludes; + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.NeverKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.TypeParameter: + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + return TransformFlags.TypeExcludes; + case SyntaxKind.ObjectLiteralExpression: + return TransformFlags.ObjectLiteralExcludes; + default: + return TransformFlags.NodeExcludes; + } + } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 45b82a6a08a..34cc2e07436 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -1270,66 +1270,6 @@ namespace ts { return transformFlags | aggregateTransformFlagsForNode(child); } - /** - * Gets the transform flags to exclude when unioning the transform flags of a subtree. - * - * NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`. - * For performance reasons, `computeTransformFlagsForNode` uses local constant values rather - * than calling this function. - */ - function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) { - if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) { - return TransformFlags.TypeExcludes; - } - - switch (kind) { - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.ArrayLiteralExpression: - return TransformFlags.ArrayLiteralOrCallOrNewExcludes; - case SyntaxKind.ModuleDeclaration: - return TransformFlags.ModuleExcludes; - case SyntaxKind.Parameter: - return TransformFlags.ParameterExcludes; - case SyntaxKind.ArrowFunction: - return TransformFlags.ArrowFunctionExcludes; - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - return TransformFlags.FunctionExcludes; - case SyntaxKind.VariableDeclarationList: - return TransformFlags.VariableDeclarationListExcludes; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return TransformFlags.ClassExcludes; - case SyntaxKind.Constructor: - return TransformFlags.ConstructorExcludes; - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return TransformFlags.MethodOrAccessorExcludes; - case SyntaxKind.AnyKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.TypeParameter: - case SyntaxKind.PropertySignature: - case SyntaxKind.MethodSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - return TransformFlags.TypeExcludes; - case SyntaxKind.ObjectLiteralExpression: - return TransformFlags.ObjectLiteralExcludes; - default: - return TransformFlags.NodeExcludes; - } - } - export namespace Debug { export const failNotOptional = shouldAssert(AssertionLevel.Normal) ? (message?: string) => assert(false, message || "Node not optional.") diff --git a/src/harness/unittests/incrementalParser.ts b/src/harness/unittests/incrementalParser.ts index 0082207e699..6088e5fe082 100644 --- a/src/harness/unittests/incrementalParser.ts +++ b/src/harness/unittests/incrementalParser.ts @@ -44,10 +44,10 @@ namespace ts { // NOTE: 'reusedElements' is the expected count of elements reused from the old tree to the new // tree. It may change as we tweak the parser. If the count increases then that should always - // be a good thing. If it decreases, that's not great (less reusability), but that may be - // unavoidable. If it does decrease an investigation should be done to make sure that things + // be a good thing. If it decreases, that's not great (less reusability), but that may be + // unavoidable. If it does decrease an investigation should be done to make sure that things // are still ok and we're still appropriately reusing most of the tree. - function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile): SourceFile { + function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile) { oldTree = oldTree || createTree(oldText, /*version:*/ "."); Utils.assertInvariants(oldTree, /*parent:*/ undefined); @@ -76,7 +76,7 @@ namespace ts { assert.equal(actualReusedCount, expectedReusedElements, actualReusedCount + " !== " + expectedReusedElements); } - return incrementalNewTree; + return { oldTree, newTree, incrementalNewTree }; } function reusedElements(oldNode: SourceFile, newNode: SourceFile): number { @@ -103,7 +103,7 @@ namespace ts { for (let i = 0; i < repeat; i++) { const oldText = ScriptSnapshot.fromString(source); const newTextAndChange = withDelete(oldText, index, 1); - const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree); + const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree; source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength()); oldTree = newTree; @@ -116,7 +116,7 @@ namespace ts { for (let i = 0; i < repeat; i++) { const oldText = ScriptSnapshot.fromString(source); const newTextAndChange = withInsert(oldText, index + i, toInsert.charAt(i)); - const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree); + const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree; source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength()); oldTree = newTree; @@ -639,7 +639,7 @@ module m3 { }\ }); it("Unterminated comment after keyword converted to identifier", () => { - // 'public' as a keyword should be incrementally unusable (because it has an + // 'public' as a keyword should be incrementally unusable (because it has an // unterminated comment). When we convert it to an identifier, that shouldn't // change anything, and we should still get the same errors. const source = "return; a.public /*"; @@ -796,6 +796,16 @@ module m3 { }\ compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); }); + it("Reuse transformFlags of subtree during bind", () => { + const source = `class Greeter { constructor(element: HTMLElement) { } }`; + const oldText = ScriptSnapshot.fromString(source); + const newTextAndChange = withChange(oldText, 15, 0, "\n"); + const { oldTree, incrementalNewTree } = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1); + bindSourceFile(oldTree, {}); + bindSourceFile(incrementalNewTree, {}); + assert.equal(oldTree.transformFlags, incrementalNewTree.transformFlags); + }); + // Simulated typing tests. it("Type extends clause 1", () => { diff --git a/src/services/services.ts b/src/services/services.ts index 8abcf8f7684..56e604abeb3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -48,7 +48,6 @@ namespace ts { public jsDocComments: JSDoc[]; public original: Node; public transformFlags: TransformFlags; - public excludeTransformFlags: TransformFlags; private _children: Node[]; constructor(kind: SyntaxKind, pos: number, end: number) { @@ -56,7 +55,6 @@ namespace ts { this.end = end; this.flags = NodeFlags.None; this.transformFlags = undefined; - this.excludeTransformFlags = undefined; this.parent = undefined; this.kind = kind; } From 2bf38ab6cd6a2321b2422a503573d492115d3457 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 7 Nov 2016 21:09:17 -0800 Subject: [PATCH 24/30] Port fix for https://github.com/Microsoft/TypeScript/issues/12069 (#12095) --- src/lib/dom.generated.d.ts | 4 ++-- src/lib/webworker.generated.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 9a87020d4f0..fd77f369fd8 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -7586,7 +7586,7 @@ declare var IDBCursorWithValue: { interface IDBDatabase extends EventTarget { readonly name: string; - readonly objectStoreNames: string[]; + readonly objectStoreNames: DOMStringList; onabort: (this: this, ev: Event) => any; onerror: (this: this, ev: ErrorEvent) => any; version: number; @@ -7652,7 +7652,7 @@ declare var IDBKeyRange: { } interface IDBObjectStore { - readonly indexNames: string[]; + readonly indexNames: DOMStringList; keyPath: string | string[]; readonly name: string; readonly transaction: IDBTransaction; diff --git a/src/lib/webworker.generated.d.ts b/src/lib/webworker.generated.d.ts index 4aeba696306..130510334d6 100644 --- a/src/lib/webworker.generated.d.ts +++ b/src/lib/webworker.generated.d.ts @@ -341,7 +341,7 @@ declare var IDBCursorWithValue: { interface IDBDatabase extends EventTarget { readonly name: string; - readonly objectStoreNames: string[]; + readonly objectStoreNames: DOMStringList; onabort: (this: this, ev: Event) => any; onerror: (this: this, ev: ErrorEvent) => any; version: number; @@ -407,7 +407,7 @@ declare var IDBKeyRange: { } interface IDBObjectStore { - readonly indexNames: string[]; + readonly indexNames: DOMStringList; keyPath: string | string[]; readonly name: string; readonly transaction: IDBTransaction; From 9e3d6efb199520ac66529b933e549e432cd1776c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 7 Nov 2016 21:13:11 -0800 Subject: [PATCH 25/30] reduce set of files being watched, increase polling interval (#12054) (#12092) --- src/compiler/program.ts | 7 ++++++- src/compiler/sys.ts | 10 +++++++--- src/compiler/types.ts | 1 + src/server/project.ts | 12 +++++++++--- src/server/types.d.ts | 2 +- src/server/typingsInstaller/typingsInstaller.ts | 2 +- src/server/utilities.ts | 2 +- src/services/jsTyping.ts | 2 +- 8 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index dcb9cce0adb..1b36d99d7c9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -420,6 +420,7 @@ namespace ts { getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(), getFileProcessingDiagnostics: () => fileProcessingDiagnostics, getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives, + isSourceFileFromExternalLibrary, dropDiagnosticsProducingTypeChecker }; @@ -722,13 +723,17 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], + isSourceFileFromExternalLibrary, writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, }; } + function isSourceFileFromExternalLibrary(file: SourceFile): boolean { + return sourceFilesFoundSearchingNodeModules[file.path]; + } + function getDiagnosticsProducingTypeChecker() { return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true)); } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 9cf95d6de9b..a1e82a66595 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -17,7 +17,11 @@ namespace ts { readFile(path: string, encoding?: string): string; getFileSize?(path: string): number; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - watchFile?(path: string, callback: FileWatcherCallback): FileWatcher; + /** + * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that + * use native OS file watching + */ + watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; @@ -449,7 +453,7 @@ namespace ts { }, readFile, writeFile, - watchFile: (fileName, callback) => { + watchFile: (fileName, callback, pollingInterval) => { if (useNonPollingWatchers) { const watchedFile = watchedFileSet.addFile(fileName, callback); return { @@ -457,7 +461,7 @@ namespace ts { }; } else { - _fs.watchFile(fileName, { persistent: true, interval: 250 }, fileChanged); + _fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged); return { close: () => _fs.unwatchFile(fileName, fileChanged) }; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 37a3cc466a0..aa0b3e505f6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2192,6 +2192,7 @@ namespace ts { /* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection; /* @internal */ getResolvedTypeReferenceDirectives(): Map; + /* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean; // For testing purposes only. /* @internal */ structureIsReused?: boolean; } diff --git a/src/server/project.ts b/src/server/project.ts index 563b0456910..d01df728248 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -301,7 +301,7 @@ namespace ts.server { return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles); } - getFileNames() { + getFileNames(excludeFilesFromExternalLibraries?: boolean) { if (!this.program) { return []; } @@ -317,8 +317,14 @@ namespace ts.server { } return rootFiles; } - const sourceFiles = this.program.getSourceFiles(); - return sourceFiles.map(sourceFile => asNormalizedPath(sourceFile.fileName)); + const result: NormalizedPath[] = []; + for (const f of this.program.getSourceFiles()) { + if (excludeFilesFromExternalLibraries && this.program.isSourceFileFromExternalLibrary(f)) { + continue; + } + result.push(asNormalizedPath(f.fileName)); + } + return result; } getAllEmittableFiles() { diff --git a/src/server/types.d.ts b/src/server/types.d.ts index 95f9519cc38..aebc3121252 100644 --- a/src/server/types.d.ts +++ b/src/server/types.d.ts @@ -73,6 +73,6 @@ declare namespace ts.server { export interface InstallTypingHost extends JsTyping.TypingResolutionHost { writeFile(path: string, content: string): void; createDirectory(path: string): void; - watchFile?(path: string, callback: FileWatcherCallback): FileWatcher; + watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; } } \ No newline at end of file diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index a602a9f1a26..da97dbcd194 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -367,7 +367,7 @@ namespace ts.server.typingsInstaller { this.sendResponse({ projectName: projectName, kind: server.ActionInvalidate }); isInvoked = true; } - }); + }, /*pollingInterval*/ 2000); watchers.push(w); } this.projectWatchers[projectName] = watchers; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 9a832d22c8d..ac809652119 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -50,7 +50,7 @@ namespace ts.server { export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray, cachePath?: string): DiscoverTypings { return { projectName: project.getProjectName(), - fileNames: project.getFileNames(), + fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true), compilerOptions: project.getCompilerOptions(), typingOptions, unresolvedImports, diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 0f635c15174..316dcd355c2 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -88,7 +88,7 @@ namespace ts.JsTyping { exclude = typingOptions.exclude || []; const possibleSearchDirs = map(fileNames, getDirectoryPath); - if (projectRootPath !== undefined) { + if (projectRootPath) { possibleSearchDirs.push(projectRootPath); } searchDirs = deduplicate(possibleSearchDirs); From 84f8f8bba89ff459180852179c31ea7e9105736d Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 7 Nov 2016 21:58:53 -0800 Subject: [PATCH 26/30] Update authors for release-2.1 --- .mailmap | 43 ++++++++++++++++++++++++++++++++++++------- AUTHORS.md | 32 +++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/.mailmap b/.mailmap index e66f669b8ee..cc101c0517e 100644 --- a/.mailmap +++ b/.mailmap @@ -1,32 +1,38 @@  -Alexander # Alexander Kuvaev AbubakerB # Abubaker Bashir +Alexander # Alexander Kuvaev Adam Freidin Adam Freidin Adi Dahiya Adi Dahiya Ahmad Farid ahmad-farid +Alexander Rusakov Alex Eagle +Anatoly Ressin Anders Hejlsberg unknown unknown +Andrej Baran Andrew Z Allen Andy Hanson Andy Anil Anar Anton Tolmachev Arnavion # Arnav Singh -Arthur Ozga Arthur Ozga +Arthur Ozga Arthur Ozga Arthur Ozga Arthur Ozga Arthur Ozga Asad Saeeduddin Schmavery # Avery Morin Basarat Ali Syed Basarat Syed basarat Bill Ticehurst Bill Ticehurst Ben Duffield +Ben Mosher Blake Embrey Bowden Kelly Brett Mayen Bryan Forbes Caitlin Potter ChrisBubernak unknown # Chris Bubernak +Christophe Vidal Chuck Jazdzewski Colby Russell Colin Snover Cyrus Najmabadi CyrusNajmabadi unknown +Dafrok # Dafrok Zhang Dan Corder Dan Quirk Dan Quirk nknown Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser @@ -36,17 +42,22 @@ Denis Nedelyaev Dick van den Brink unknown unknown Dirk Baeumer Dirk Bäumer # Dirk Bäumer Dirk Holtwick +Dom Chen Doug Ilijev Erik Edrosa +erictsangx # Eric Tsang Ethan Rubio Evan Martin Evan Sebastian Eyas # Eyas Sharaiha +Fabian Cook falsandtru # @falsandtru Frank Wallis -František Žiačik František Žiačik +František Žiacik František Žiacik +Gabe Moothart Gabriel Isenberg Gilad Peleg +Godfrey Chan Graeme Wicksted Guillaume Salles Guy Bedford guybedford @@ -55,6 +66,7 @@ Iain Monro Ingvar Stepanyan impinball # Isiah Meadows Ivo Gabe de Wolff +Jakub Młokosiewicz James Whitney Jason Freeman Jason Freeman Jason Killian @@ -69,11 +81,14 @@ Jonathan Park Jonathan Turner Jonathan Turner Jonathan Toland Jesse Schalken +Josh Abernathy joshaber Josh Kalderimis Josh Soref Juan Luis Boya García Julian Williams -Herrington Darkholme +Justin Bay +Justin Johansson +Herrington Darkholme (´·?·`) # Herrington Darkholme Kagami Sascha Rosylight SaschaNaz Kanchalai Tanglertsampan Yui Kanchalai Tanglertsampan Yui T @@ -82,23 +97,29 @@ Kanchalai Tanglertsampan Yui Kanchalai Tanglertsampan yui T Keith Mashinter kmashint Ken Howard +Kevin Lang kimamula # Kenji Imamula Kyle Kelley Lorant Pinter Lucien Greathouse +Lukas Elmer Lukas Elmer Martin Vseticka Martin Všeticka MartyIX +gcnew # Marin Marinov vvakame # Masahiro Wakame Matt McCutchen Max Deepfield Micah Zoltu +Michael Mohamed Hegazy Nathan Shively-Sanders Nathan Yee Nima Zahedi +Noah Chen Noj Vek mihailik # Oleg Mihailik Oleksandr Chekhovskyi Paul van Brenk Paul van Brenk unknown unknown unknown +Omer Sheikh Oskar Segersva¨rd pcan # Piero Cangianiello pcbro <2bux89+dk3zspjmuh16o@sharklasers.com> # @pcbro @@ -109,21 +130,26 @@ progre # @progre Prayag Verma Punya Biswal Rado Kirov -Ron Buckton Ron Buckton +Ron Buckton Ron Buckton rbuckton +Rostislav Galimsky Richard Knoll Richard Knoll Rowan Wyborn Ryan Cavanaugh Ryan Cavanaugh Ryan Cavanaugh Ryohei Ikegami Sarangan Rajamanickam Sébastien Arod +Sergey Shandar Sheetal Nandi Shengping Zhong shyyko.serhiy@gmail.com # Shyyko Serhiy +Sam El-Husseini Simon Hürlimann +Slawomir Sadziak Solal Pirelli Stan Thomas Stanislav Sysoev Steve Lucco steveluc +Sudheesh Singanamalla Tarik # Tarik Ozket Tetsuharu OHZEKI # Tetsuharu Ohzeki Tien Nguyen tien unknown #Tien Hoanhtien @@ -133,6 +159,7 @@ Tingan Ho togru # togru Tomas Grubliauskas ToddThomson # Todd Thomson +Torben Fitschen TruongSinh Tran-Nguyen vilicvane # Vilic Vane Vladimir Matveev vladima v2m @@ -140,7 +167,9 @@ Wesley Wigham Wesley Wigham York Yao york yao yaoyao Yuichi Nukiyama YuichiNukiyama Zev Spitz -Zhengbo Li zhengbli Zhengbo Li Zhengbo Li tinza123 unknown Zhengbo Li +Zhengbo Li zhengbli Zhengbo Li Zhengbo Li tinza123 unknown Zhengbo Li zhengbli zhongsp # Patrick Zhong T18970237136 # @T18970237136 -JBerger \ No newline at end of file +JBerger +bootstraponline # @bootstraponline +yortus # @yortus \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md index 08039127d9d..50f1ea12c2b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -5,7 +5,10 @@ TypeScript is authored by: * Ahmad Farid * Alex Eagle * Alexander Kuvaev +* Alexander Rusakov +* Anatoly Ressin * Anders Hejlsberg +* Andrej Baran * Andrew Z Allen * Andy Hanson * Anil Anar @@ -16,17 +19,21 @@ TypeScript is authored by: * Avery Morin * Basarat Ali Syed * Ben Duffield +* Ben Mosher * Bill Ticehurst * Blake Embrey +* @bootstraponline * Bowden Kelly * Brett Mayen * Bryan Forbes * Caitlin Potter * Chris Bubernak +* Christophe Vidal * Chuck Jazdzewski * Colby Russell * Colin Snover * Cyrus Najmabadi +* Dafrok Zhang * Dan Corder * Dan Quirk * Daniel Rosenwasser @@ -36,17 +43,22 @@ TypeScript is authored by: * Dick van den Brink * Dirk Bäumer * Dirk Holtwick +* Dom Chen * Doug Ilijev +* Eric Tsang * Erik Edrosa * Ethan Rubio * Evan Martin * Evan Sebastian * Eyas Sharaiha +* Fabian Cook * @falsandtru * Frank Wallis -* František Žiačik +* František Žiacik +* Gabe Moothart * Gabriel Isenberg * Gilad Peleg +* Godfrey Chan * Graeme Wicksted * Guillaume Salles * Guy Bedford @@ -56,6 +68,7 @@ TypeScript is authored by: * Ingvar Stepanyan * Isiah Meadows * Ivo Gabe de Wolff +* Jakub Młokosiewicz * James Whitney * Jason Freeman * Jason Killian @@ -71,30 +84,39 @@ TypeScript is authored by: * Jonathan Park * Jonathan Toland * Jonathan Turner +* Josh Abernathy * Josh Kalderimis * Josh Soref * Juan Luis Boya García * Julian Williams +* Justin Bay +* Justin Johansson * Kagami Sascha Rosylight * Kanchalai Tanglertsampan * Keith Mashinter * Ken Howard * Kenji Imamula +* Kevin Lang * Kyle Kelley * Lorant Pinter * Lucien Greathouse +* Lukas Elmer +* Marin Marinov * Martin Vseticka * Masahiro Wakame * Matt McCutchen * Max Deepfield * Micah Zoltu +* Michael * Mohamed Hegazy * Nathan Shively-Sanders * Nathan Yee * Nima Zahedi +* Noah Chen * Noj Vek * Oleg Mihailik * Oleksandr Chekhovskyi +* Omer Sheikh * Oskar Segersva¨rd * Patrick Zhong * Paul van Brenk @@ -109,21 +131,27 @@ TypeScript is authored by: * Rado Kirov * Richard Knoll * Ron Buckton +* Rostislav Galimsky * Rowan Wyborn * Ryan Cavanaugh * Ryohei Ikegami +* Sam El-Husseini * Sarangan Rajamanickam +* Sergey Shandar * Sheetal Nandi * Shengping Zhong * Shyyko Serhiy * Simon Hürlimann +* Slawomir Sadziak * Solal Pirelli * Stan Thomas * Stanislav Sysoev * Steve Lucco +* Sudheesh Singanamalla * Sébastien Arod * @T18970237136 * Tarik Ozket +* Tetsuharu Ohzeki * Tien Hoanhtien * Tim Perry * Tim Viiding-Spader @@ -131,11 +159,13 @@ TypeScript is authored by: * Todd Thomson * togru * Tomas Grubliauskas +* Torben Fitschen * TruongSinh Tran-Nguyen * Vilic Vane * Vladimir Matveev * Wesley Wigham * York Yao +* @yortus * Yuichi Nukiyama * Zev Spitz * Zhengbo Li \ No newline at end of file From be0358cc0cff88b0e5b05e656735da80a052c0f6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 8 Nov 2016 06:09:41 -0800 Subject: [PATCH 27/30] Include declaration file emit --- .../reference/instantiatedTypeAliasDisplay.js | 15 ++++ .../instantiatedTypeAliasDisplay.symbols | 75 ++++++++++--------- .../instantiatedTypeAliasDisplay.types | 1 + .../compiler/instantiatedTypeAliasDisplay.ts | 2 + 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.js b/tests/baselines/reference/instantiatedTypeAliasDisplay.js index 86b17262e0f..9c2b91b613e 100644 --- a/tests/baselines/reference/instantiatedTypeAliasDisplay.js +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.js @@ -1,4 +1,5 @@ //// [instantiatedTypeAliasDisplay.ts] + // Repros from #12066 interface X { @@ -19,3 +20,17 @@ const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> // Repros from #12066 var x1 = f1(); // Z var x2 = f2({}, {}, {}, {}); // Z<{}, string[]> + + +//// [instantiatedTypeAliasDisplay.d.ts] +interface X { + a: A; +} +interface Y { + b: B; +} +declare type Z = X | Y; +declare function f1(): Z; +declare function f2(a: A, b: B, c: C, d: D): Z; +declare const x1: Z; +declare const x2: Z<{}, string[]>; diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols b/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols index 3c6f7d2b725..fa8140cfcd7 100644 --- a/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.symbols @@ -1,60 +1,61 @@ === tests/cases/compiler/instantiatedTypeAliasDisplay.ts === + // Repros from #12066 interface X { >X : Symbol(X, Decl(instantiatedTypeAliasDisplay.ts, 0, 0)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 2, 12)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 3, 12)) a: A; ->a : Symbol(X.a, Decl(instantiatedTypeAliasDisplay.ts, 2, 16)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 2, 12)) +>a : Symbol(X.a, Decl(instantiatedTypeAliasDisplay.ts, 3, 16)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 3, 12)) } interface Y { ->Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 4, 1)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 5, 12)) +>Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 5, 1)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 6, 12)) b: B; ->b : Symbol(Y.b, Decl(instantiatedTypeAliasDisplay.ts, 5, 16)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 5, 12)) +>b : Symbol(Y.b, Decl(instantiatedTypeAliasDisplay.ts, 6, 16)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 6, 12)) } type Z = X | Y; ->Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 8, 7)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 8, 9)) +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 8, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 9, 7)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 9, 9)) >X : Symbol(X, Decl(instantiatedTypeAliasDisplay.ts, 0, 0)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 8, 7)) ->Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 4, 1)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 8, 9)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 9, 7)) +>Y : Symbol(Y, Decl(instantiatedTypeAliasDisplay.ts, 5, 1)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 9, 9)) declare function f1(): Z; ->f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 8, 27)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 10, 20)) ->Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 10, 20)) +>f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 9, 27)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 8, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) declare function f2(a: A, b: B, c: C, d: D): Z; ->f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 10, 39)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 11, 22)) ->C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 11, 25)) ->D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 11, 28)) ->E : Symbol(E, Decl(instantiatedTypeAliasDisplay.ts, 11, 31)) ->a : Symbol(a, Decl(instantiatedTypeAliasDisplay.ts, 11, 35)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) ->b : Symbol(b, Decl(instantiatedTypeAliasDisplay.ts, 11, 40)) ->B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 11, 22)) ->c : Symbol(c, Decl(instantiatedTypeAliasDisplay.ts, 11, 46)) ->C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 11, 25)) ->d : Symbol(d, Decl(instantiatedTypeAliasDisplay.ts, 11, 52)) ->D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 11, 28)) ->Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 7, 1)) ->A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 11, 20)) +>f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 11, 39)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 12, 20)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 12, 22)) +>C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 12, 25)) +>D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 12, 28)) +>E : Symbol(E, Decl(instantiatedTypeAliasDisplay.ts, 12, 31)) +>a : Symbol(a, Decl(instantiatedTypeAliasDisplay.ts, 12, 35)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 12, 20)) +>b : Symbol(b, Decl(instantiatedTypeAliasDisplay.ts, 12, 40)) +>B : Symbol(B, Decl(instantiatedTypeAliasDisplay.ts, 12, 22)) +>c : Symbol(c, Decl(instantiatedTypeAliasDisplay.ts, 12, 46)) +>C : Symbol(C, Decl(instantiatedTypeAliasDisplay.ts, 12, 25)) +>d : Symbol(d, Decl(instantiatedTypeAliasDisplay.ts, 12, 52)) +>D : Symbol(D, Decl(instantiatedTypeAliasDisplay.ts, 12, 28)) +>Z : Symbol(Z, Decl(instantiatedTypeAliasDisplay.ts, 8, 1)) +>A : Symbol(A, Decl(instantiatedTypeAliasDisplay.ts, 12, 20)) const x1 = f1(); // Z ->x1 : Symbol(x1, Decl(instantiatedTypeAliasDisplay.ts, 13, 5)) ->f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 8, 27)) +>x1 : Symbol(x1, Decl(instantiatedTypeAliasDisplay.ts, 14, 5)) +>f1 : Symbol(f1, Decl(instantiatedTypeAliasDisplay.ts, 9, 27)) const x2 = f2({}, {}, {}, {}); // Z<{}, string[]> ->x2 : Symbol(x2, Decl(instantiatedTypeAliasDisplay.ts, 14, 5)) ->f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 10, 39)) +>x2 : Symbol(x2, Decl(instantiatedTypeAliasDisplay.ts, 15, 5)) +>f2 : Symbol(f2, Decl(instantiatedTypeAliasDisplay.ts, 11, 39)) diff --git a/tests/baselines/reference/instantiatedTypeAliasDisplay.types b/tests/baselines/reference/instantiatedTypeAliasDisplay.types index 44885777e74..89320e1dba6 100644 --- a/tests/baselines/reference/instantiatedTypeAliasDisplay.types +++ b/tests/baselines/reference/instantiatedTypeAliasDisplay.types @@ -1,4 +1,5 @@ === tests/cases/compiler/instantiatedTypeAliasDisplay.ts === + // Repros from #12066 interface X { diff --git a/tests/cases/compiler/instantiatedTypeAliasDisplay.ts b/tests/cases/compiler/instantiatedTypeAliasDisplay.ts index d2abaaf8afe..8f7500e7830 100644 --- a/tests/cases/compiler/instantiatedTypeAliasDisplay.ts +++ b/tests/cases/compiler/instantiatedTypeAliasDisplay.ts @@ -1,3 +1,5 @@ +// @declaration: true + // Repros from #12066 interface X { From 9a9f45f0fbafebb0b52b524db8448d8330c5984b Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 8 Nov 2016 15:32:53 -0800 Subject: [PATCH 28/30] use createHash from ServerHost instead of explicit require (#12043) * use createHash from ServerHost instead of explicit require * added missing method in ServerHost stub --- src/harness/harnessLanguageService.ts | 4 ++++ src/harness/unittests/cachingInServerLSHost.ts | 3 ++- src/harness/unittests/session.ts | 3 ++- src/harness/unittests/tsserverProjectSystem.ts | 4 ++++ src/server/builder.ts | 13 +------------ src/server/editorServices.ts | 2 ++ 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 889d4f28ce9..a49f8926729 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -720,6 +720,10 @@ namespace Harness.LanguageService { clearImmediate(timeoutId: any): void { clearImmediate(timeoutId); } + + createHash(s: string) { + return s; + } } export class ServerLanguageServiceAdapter implements LanguageServiceAdapter { diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 1100bb3663b..4286d3555d8 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -43,7 +43,8 @@ namespace ts { setTimeout, clearTimeout, setImmediate: typeof setImmediate !== "undefined" ? setImmediate : action => setTimeout(action, 0), - clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout + clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout, + createHash: s => s }; } diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 05f62c4b9f6..8800e84474b 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -24,7 +24,8 @@ namespace ts.server { setTimeout() { return 0; }, clearTimeout: noop, setImmediate: () => 0, - clearImmediate: noop + clearImmediate: noop, + createHash: s => s }; const nullCancellationToken: HostCancellationToken = { isCancellationRequested: () => false }; const mockLogger: Logger = { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 62c0957d6a3..17b1dd3e585 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -434,6 +434,10 @@ namespace ts.projectSystem { }; } + createHash(s: string): string { + return s; + } + triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); const callbacks = this.watchedDirectories[path]; diff --git a/src/server/builder.ts b/src/server/builder.ts index 548a3ac854c..5354e7ed508 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -5,15 +5,6 @@ namespace ts.server { - interface Hash { - update(data: any, input_encoding?: string): Hash; - digest(encoding: string): any; - } - - const crypto: { - createHash(algorithm: string): Hash - } = require("crypto"); - export function shouldEmitFile(scriptInfo: ScriptInfo) { return !scriptInfo.hasMixedContent; } @@ -49,9 +40,7 @@ namespace ts.server { } private computeHash(text: string): string { - return crypto.createHash("md5") - .update(text) - .digest("base64"); + return this.project.projectService.host.createHash(text); } private getSourceFile(): SourceFile { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 269eea06464..d7dcfc810b4 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -250,6 +250,8 @@ namespace ts.server { readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller, private readonly eventHandler?: ProjectServiceEventHandler) { + Debug.assert(!!host.createHash, "'ServerHost.createHash' is required for ProjectService"); + this.toCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); this.directoryWatchers = new DirectoryWatchers(this); this.throttledOperations = new ThrottledOperations(host); From 28cc9385035a65f5dcd74a4f9b47e7022678302f Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 8 Nov 2016 16:10:34 -0800 Subject: [PATCH 29/30] (signature help) type parameter lists are never variadic (#12112) --- src/harness/fourslash.ts | 10 +++++++++- src/services/signatureHelp.ts | 5 ++++- tests/cases/fourslash/fourslash.ts | 1 + .../signatureHelpTypeParametersNotVariadic.ts | 8 ++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/signatureHelpTypeParametersNotVariadic.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ae32fe3db16..6ff4dccdee3 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1026,7 +1026,7 @@ namespace FourSlash { ts.displayPartsToString(help.suffixDisplayParts), expected); } - public verifyCurrentParameterIsletiable(isVariable: boolean) { + public verifyCurrentParameterIsVariable(isVariable: boolean) { const signature = this.getActiveSignatureHelpItem(); assert.isOk(signature); assert.equal(isVariable, signature.isVariadic); @@ -1053,6 +1053,10 @@ namespace FourSlash { assert.equal(this.getActiveSignatureHelpItem().parameters.length, expectedCount); } + public verifyCurrentSignatureHelpIsVariadic(expected: boolean) { + assert.equal(this.getActiveSignatureHelpItem().isVariadic, expected); + } + public verifyCurrentSignatureHelpDocComment(docComment: string) { const actualDocComment = this.getActiveSignatureHelpItem().documentation; assert.equal(ts.displayPartsToString(actualDocComment), docComment, this.assertionMessageAtLastKnownMarker("current signature help doc comment")); @@ -3231,6 +3235,10 @@ namespace FourSlashInterface { this.state.verifySignatureHelpCount(expected); } + public signatureHelpCurrentArgumentListIsVariadic(expected: boolean) { + this.state.verifyCurrentSignatureHelpIsVariadic(expected); + } + public signatureHelpArgumentCountIs(expected: number) { this.state.verifySignatureHelpArgumentCount(expected); } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 211e55b23ba..44c2ede9fbb 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -557,7 +557,9 @@ namespace ts.SignatureHelp { addRange(prefixDisplayParts, callTargetDisplayParts); } + let isVariadic: boolean; if (isTypeParameterList) { + isVariadic = false; // type parameter lists are not variadic prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); const typeParameters = candidateSignature.typeParameters; signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; @@ -567,6 +569,7 @@ namespace ts.SignatureHelp { addRange(suffixDisplayParts, parameterParts); } else { + isVariadic = candidateSignature.hasRestParameter; const typeParameterParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); addRange(prefixDisplayParts, typeParameterParts); @@ -582,7 +585,7 @@ namespace ts.SignatureHelp { addRange(suffixDisplayParts, returnTypeParts); return { - isVariadic: candidateSignature.hasRestParameter, + isVariadic, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 0a72d295295..295b8e422b9 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -194,6 +194,7 @@ declare namespace FourSlashInterface { currentSignatureHelpDocCommentIs(docComment: string): void; signatureHelpCountIs(expected: number): void; signatureHelpArgumentCountIs(expected: number): void; + signatureHelpCurrentArgumentListIsVariadic(expected: boolean); currentSignatureParameterCountIs(expected: number): void; currentSignatureTypeParameterCountIs(expected: number): void; currentSignatureHelpIs(expected: string): void; diff --git a/tests/cases/fourslash/signatureHelpTypeParametersNotVariadic.ts b/tests/cases/fourslash/signatureHelpTypeParametersNotVariadic.ts new file mode 100644 index 00000000000..30b0bac7e71 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpTypeParametersNotVariadic.ts @@ -0,0 +1,8 @@ +/// + +//// declare function f(a: any, ...b: any[]): any; +//// f(1, 2); + +goTo.marker("1"); +verify.signatureHelpArgumentCountIs(0); +verify.signatureHelpCurrentArgumentListIsVariadic(false); \ No newline at end of file From 12cd0bfb69b88fffc5135a4bccd3eb6b6c4c35b1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 8 Nov 2016 16:32:41 -0800 Subject: [PATCH 30/30] Remove EmitHelperState, general helper cleanup. --- src/compiler/factory.ts | 57 ++-- src/compiler/transformer.ts | 178 +++++------ src/compiler/transformers/es2015.ts | 246 ++++++++------- src/compiler/transformers/es2017.ts | 141 ++++----- src/compiler/transformers/generators.ts | 13 +- src/compiler/transformers/jsx.ts | 13 +- src/compiler/transformers/module/es2015.ts | 60 +++- src/compiler/transformers/module/module.ts | 10 +- src/compiler/transformers/module/system.ts | 10 +- src/compiler/transformers/ts.ts | 345 +++++++++------------ src/compiler/types.ts | 135 +++++++- src/compiler/utilities.ts | 20 +- src/compiler/visitor.ts | 117 ++++--- 13 files changed, 725 insertions(+), 620 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index cd4f4561fd9..f8b71a733cd 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1695,23 +1695,8 @@ namespace ts { // Helpers - export interface EmitHelperState { - currentSourceFile: SourceFile; - compilerOptions: CompilerOptions; - requestedHelpers?: EmitHelper[]; - } - - export function getHelperName(helperState: EmitHelperState, name: string) { - const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions); - return externalHelpersModuleName - ? createPropertyAccess(externalHelpersModuleName, name) - : createIdentifier(name); - } - - export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) { - if (!contains(helperState.requestedHelpers, helper)) { - helperState.requestedHelpers = append(helperState.requestedHelpers, helper); - } + export function getHelperName(name: string) { + return setEmitFlags(createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode); } export interface CallBinding { @@ -2061,6 +2046,10 @@ namespace ts { // Utilities + export function convertToFunctionBody(node: ConciseBody) { + return isBlock(node) ? node : createBlock([createReturn(node, /*location*/ node)], /*location*/ node); + } + function isUseStrictPrologue(node: ExpressionStatement): boolean { return (node.expression as StringLiteral).text === "use strict"; } @@ -2110,6 +2099,13 @@ namespace ts { return statementOffset; } + export function startsWithUseStrict(statements: Statement[]) { + const firstStatement = firstOrUndefined(statements); + return firstStatement !== undefined + && isPrologueDirective(firstStatement) + && isUseStrictPrologue(firstStatement); + } + /** * Ensures "use strict" directive is added * @@ -2606,7 +2602,7 @@ namespace ts { * * @param node The node. */ - function getOrCreateEmitNode(node: Node) { + export function getOrCreateEmitNode(node: Node) { if (!node.emitNode) { if (isParseTreeNode(node)) { // To avoid holding onto transformation artifacts, we keep track of any @@ -2735,14 +2731,25 @@ namespace ts { return emitNode && emitNode.externalHelpersModuleName; } - export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) { + export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions) { if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) { - const parseNode = getOriginalNode(node, isSourceFile); - const emitNode = getOrCreateEmitNode(parseNode); - return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + const externalHelpersModuleName = getExternalHelpersModuleName(node); + if (externalHelpersModuleName) { + return externalHelpersModuleName; + } + + const helpers = getEmitHelpers(node); + if (helpers) { + for (const helper of helpers) { + if (!helper.scoped) { + const parseNode = getOriginalNode(node, isSourceFile); + const emitNode = getOrCreateEmitNode(parseNode); + return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText)); + } + } + } } } - /** * Adds an EmitHelper to a node. */ @@ -2930,7 +2937,7 @@ namespace ts { hasExportStarsToExportValues: boolean; // whether this module contains export* } - export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo { + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMap(); const exportedBindings = createMap(); @@ -2940,7 +2947,7 @@ namespace ts { let exportEquals: ExportAssignment = undefined; let hasExportStarsToExportValues = false; - const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile); + const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions); const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index ee9c081e6eb..1ae123ce5ca 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -26,84 +26,6 @@ namespace ts { EmitNotifications = 1 << 1, } - export interface TransformationResult { - /** - * Gets the transformed source files. - */ - transformed: SourceFile[]; - - /** - * Emits the substitute for a node, if one is available; otherwise, emits the node. - * - * @param emitContext The current emit context. - * @param node The node to substitute. - * @param emitCallback A callback used to emit the node or its substitute. - */ - emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; - - /** - * Emits a node with possible notification. - * - * @param emitContext The current emit context. - * @param node The node to emit. - * @param emitCallback A callback used to emit the node. - */ - emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; - } - - export interface TransformationContext extends LexicalEnvironment { - getCompilerOptions(): CompilerOptions; - getEmitResolver(): EmitResolver; - getEmitHost(): EmitHost; - - /** - * Hoists a function declaration to the containing scope. - */ - hoistFunctionDeclaration(node: FunctionDeclaration): void; - - /** - * Hoists a variable declaration to the containing scope. - */ - hoistVariableDeclaration(node: Identifier): void; - - /** - * Enables expression substitutions in the pretty printer for the provided SyntaxKind. - */ - enableSubstitution(kind: SyntaxKind): void; - - /** - * Determines whether expression substitutions are enabled for the provided node. - */ - isSubstitutionEnabled(node: Node): boolean; - - /** - * Hook used by transformers to substitute expressions just before they - * are emitted by the pretty printer. - */ - onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; - - /** - * Enables before/after emit notifications in the pretty printer for the provided - * SyntaxKind. - */ - enableEmitNotification(kind: SyntaxKind): void; - - /** - * Determines whether before/after emit notifications should be raised in the pretty - * printer when it emits a node. - */ - isEmitNotificationEnabled(node: Node): boolean; - - /** - * Hook used to allow transformers to capture state before or after - * the printer emits a node. - */ - onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; - } - - /* @internal */ - export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; - export function getTransformers(compilerOptions: CompilerOptions) { const jsx = compilerOptions.jsx; const languageVersion = getEmitScriptTarget(compilerOptions); @@ -149,14 +71,18 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { - const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; - const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); + let scopeModificationDisabled = false; + + let lexicalEnvironmentVariableDeclarations: VariableDeclaration[]; + let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[]; + let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; + let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; let lexicalEnvironmentStackOffset = 0; - let hoistedVariableDeclarations: VariableDeclaration[]; - let hoistedFunctionDeclarations: FunctionDeclaration[]; - let lexicalEnvironmentDisabled: boolean; + let lexicalEnvironmentSuspended = false; + + let emitHelpers: EmitHelper[]; // The transformation context is provided to each transformer as part of transformer // initialization. @@ -164,10 +90,14 @@ namespace ts { getCompilerOptions: () => host.getCompilerOptions(), getEmitResolver: () => resolver, getEmitHost: () => host, + startLexicalEnvironment, + suspendLexicalEnvironment, + resumeLexicalEnvironment, + endLexicalEnvironment, hoistVariableDeclaration, hoistFunctionDeclaration, - startLexicalEnvironment, - endLexicalEnvironment, + requestEmitHelper, + readEmitHelpers, onSubstituteNode: (_emitContext, node) => node, enableSubstitution, isSubstitutionEnabled, @@ -183,7 +113,7 @@ namespace ts { const transformed = map(sourceFiles, transformSourceFile); // Disable modification of the lexical environment. - lexicalEnvironmentDisabled = true; + scopeModificationDisabled = true; return { transformed, @@ -278,13 +208,13 @@ namespace ts { * Records a hoisted variable declaration for the provided name within a lexical environment. */ function hoistVariableDeclaration(name: Identifier): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); const decl = createVariableDeclaration(name); - if (!hoistedVariableDeclarations) { - hoistedVariableDeclarations = [decl]; + if (!lexicalEnvironmentVariableDeclarations) { + lexicalEnvironmentVariableDeclarations = [decl]; } else { - hoistedVariableDeclarations.push(decl); + lexicalEnvironmentVariableDeclarations.push(decl); } } @@ -292,31 +222,46 @@ namespace ts { * Records a hoisted function declaration within a lexical environment. */ function hoistFunctionDeclaration(func: FunctionDeclaration): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase."); - if (!hoistedFunctionDeclarations) { - hoistedFunctionDeclarations = [func]; + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + if (!lexicalEnvironmentFunctionDeclarations) { + lexicalEnvironmentFunctionDeclarations = [func]; } else { - hoistedFunctionDeclarations.push(func); + lexicalEnvironmentFunctionDeclarations.push(func); } } + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + function suspendLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot suspend a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended."); + lexicalEnvironmentSuspended = true; + } + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + function resumeLexicalEnvironment(): void { + Debug.assert(!scopeModificationDisabled, "Cannot resume a lexical environment during the print phase."); + Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended suspended."); + lexicalEnvironmentSuspended = false; + } + /** * Starts a new lexical environment. Any existing hoisted variable or function declarations * are pushed onto a stack, and the related storage variables are reset. */ function startLexicalEnvironment(): void { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot start a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); // Save the current lexical environment. Rather than resizing the array we adjust the // stack size variable. This allows us to reuse existing array slots we've // already allocated between transformations to avoid allocation and GC overhead during // transformation. - lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations; - lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations; + lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations; + lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations; lexicalEnvironmentStackOffset++; - hoistedVariableDeclarations = undefined; - hoistedFunctionDeclarations = undefined; + lexicalEnvironmentVariableDeclarations = undefined; + lexicalEnvironmentFunctionDeclarations = undefined; } /** @@ -324,18 +269,19 @@ namespace ts { * any hoisted declarations added in this environment are returned. */ function endLexicalEnvironment(): Statement[] { - Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!scopeModificationDisabled, "Cannot end a lexical environment during the print phase."); + Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); let statements: Statement[]; - if (hoistedVariableDeclarations || hoistedFunctionDeclarations) { - if (hoistedFunctionDeclarations) { - statements = [...hoistedFunctionDeclarations]; + if (lexicalEnvironmentVariableDeclarations || lexicalEnvironmentFunctionDeclarations) { + if (lexicalEnvironmentFunctionDeclarations) { + statements = [...lexicalEnvironmentFunctionDeclarations]; } - if (hoistedVariableDeclarations) { + if (lexicalEnvironmentVariableDeclarations) { const statement = createVariableStatement( /*modifiers*/ undefined, - createVariableDeclarationList(hoistedVariableDeclarations) + createVariableDeclarationList(lexicalEnvironmentVariableDeclarations) ); if (!statements) { @@ -349,9 +295,27 @@ namespace ts { // Restore the previous lexical environment. lexicalEnvironmentStackOffset--; - hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; - hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; + lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; + if (lexicalEnvironmentStackOffset === 0) { + lexicalEnvironmentVariableDeclarationsStack = []; + lexicalEnvironmentFunctionDeclarationsStack = []; + } + return statements; } + + function requestEmitHelper(helper: EmitHelper): void { + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + Debug.assert(!helper.scoped, "Cannot request a scoped emit helper."); + emitHelpers = append(emitHelpers, helper); + } + + function readEmitHelpers(): EmitHelper[] | undefined { + Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase."); + const helpers = emitHelpers; + emitHelpers = undefined; + return helpers; + } } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 60d1ad95682..733e47eaa67 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -165,11 +165,11 @@ namespace ts { export function transformES2015(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; - const compilerOptions = context.getCompilerOptions(); const resolver = context.getEmitResolver(); const previousOnSubstituteNode = context.onSubstituteNode; const previousOnEmitNode = context.onEmitNode; @@ -187,7 +187,6 @@ namespace ts { let enclosingNonArrowFunction: FunctionLikeDeclaration; let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement; let isInConstructorWithCapturedSuper: boolean; - let helperState: EmitHelperState; /** * Used to track if we are emitting body of the converted loop @@ -210,14 +209,12 @@ namespace ts { currentSourceFile = node; currentText = node.text; - helperState = { currentSourceFile, compilerOptions }; - const visited = visitNode(node, visitor, isSourceFile); - addEmitHelpers(visited, helperState.requestedHelpers); + const visited = saveStateAndInvoke(node, visitSourceFile); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; currentText = undefined; - helperState = undefined; return visited; } @@ -264,6 +261,47 @@ namespace ts { return visited; } + function onBeforeVisitNode(node: Node) { + if (currentNode) { + if (isBlockScope(currentNode, currentParent)) { + enclosingBlockScopeContainer = currentNode; + enclosingBlockScopeContainerParent = currentParent; + } + + if (isFunctionLike(currentNode)) { + enclosingFunction = currentNode; + if (currentNode.kind !== SyntaxKind.ArrowFunction) { + enclosingNonArrowFunction = currentNode; + if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) { + enclosingNonAsyncFunctionBody = currentNode; + } + } + } + + // keep track of the enclosing variable statement when in the context of + // variable statements, variable declarations, binding elements, and binding + // patterns. + switch (currentNode.kind) { + case SyntaxKind.VariableStatement: + enclosingVariableStatement = currentNode; + break; + + case SyntaxKind.VariableDeclarationList: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + break; + + default: + enclosingVariableStatement = undefined; + } + } + + currentParent = currentNode; + currentNode = node; + } + function returnCapturedThis(node: Node): Node { return setOriginalNode(createReturn(createIdentifier("_this")), node); } @@ -288,7 +326,7 @@ namespace ts { else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) { // we want to dive in this branch either if node has children with ES2015 specific syntax // or we are inside constructor that captures result of the super call so all returns without expression should be - // rewritten. Note: we skip expressions since returns should never appear there + // rewritten. Note: we skip expressions since returns should never appear there return visitEachChild(node, visitor, context); } else { @@ -426,6 +464,9 @@ namespace ts { case SyntaxKind.YieldExpression: return visitYieldExpression(node); + case SyntaxKind.SpreadElementExpression: + return visitSpreadElementExpression(node); + case SyntaxKind.SuperKeyword: return visitSuperKeyword(); @@ -436,9 +477,6 @@ namespace ts { case SyntaxKind.MethodDeclaration: return visitMethodDeclaration(node); - case SyntaxKind.SourceFile: - return visitSourceFileNode(node); - case SyntaxKind.VariableStatement: return visitVariableStatement(node); @@ -446,48 +484,19 @@ namespace ts { Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); } - } - function onBeforeVisitNode(node: Node) { - if (currentNode) { - if (isBlockScope(currentNode, currentParent)) { - enclosingBlockScopeContainer = currentNode; - enclosingBlockScopeContainerParent = currentParent; - } - - if (isFunctionLike(currentNode)) { - enclosingFunction = currentNode; - if (currentNode.kind !== SyntaxKind.ArrowFunction) { - enclosingNonArrowFunction = currentNode; - if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) { - enclosingNonAsyncFunctionBody = currentNode; - } - } - } - - // keep track of the enclosing variable statement when in the context of - // variable statements, variable declarations, binding elements, and binding - // patterns. - switch (currentNode.kind) { - case SyntaxKind.VariableStatement: - enclosingVariableStatement = currentNode; - break; - - case SyntaxKind.VariableDeclarationList: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - break; - - default: - enclosingVariableStatement = undefined; - } - } - - currentParent = currentNode; - currentNode = node; + function visitSourceFile(node: SourceFile): SourceFile { + const statements: Statement[] = []; + startLexicalEnvironment(); + const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); + addCaptureThisForNodeIfNeeded(statements, node); + addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + addRange(statements, endLexicalEnvironment()); + return updateSourceFileNode( + node, + createNodeArray(statements, node.statements) + ); } function visitSwitchStatement(node: SwitchStatement): SwitchStatement { @@ -787,7 +796,7 @@ namespace ts { if (extendsClauseElement) { statements.push( createStatement( - createExtendsHelper(helperState, getLocalName(node)), + createExtendsHelper(context, getLocalName(node)), /*location*/ extendsClauseElement ) ); @@ -837,11 +846,8 @@ namespace ts { // `super` call. // If this is the case, we do not include the synthetic `...args` parameter and // will instead use the `arguments` object in ES5/3. - if (constructor && !hasSynthesizedSuper) { - return visitNodes(constructor.parameters, visitor, isParameter); - } - - return []; + return visitParameterList(constructor && !hasSynthesizedSuper && constructor.parameters, visitor, context) + || []; } /** @@ -855,14 +861,14 @@ namespace ts { */ function transformConstructorBody(constructor: ConstructorDeclaration | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments, hasSynthesizedSuper: boolean) { const statements: Statement[] = []; - startLexicalEnvironment(); + resumeLexicalEnvironment(); let statementOffset = -1; if (hasSynthesizedSuper) { // If a super call has already been synthesized, // we're going to assume that we should just transform everything after that. // The assumption is that no prior step in the pipeline has added any prologue directives. - statementOffset = 1; + statementOffset = 0; } else if (constructor) { // Otherwise, try to emit all potential prologue directives first. @@ -1386,20 +1392,13 @@ namespace ts { function transformClassMethodDeclarationToStatement(receiver: LeftHandSideExpression, member: MethodDeclaration) { const commentRange = getCommentRange(member); const sourceMapRange = getSourceMapRange(member); - - const func = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined); - setEmitFlags(func, EmitFlags.NoComments); - setSourceMapRange(func, sourceMapRange); + const memberName = createMemberAccessForPropertyName(receiver, visitNode(member.name, visitor, isPropertyName), /*location*/ member.name); + const memberFunction = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined); + setEmitFlags(memberFunction, EmitFlags.NoComments); + setSourceMapRange(memberFunction, sourceMapRange); const statement = createStatement( - createAssignment( - createMemberAccessForPropertyName( - receiver, - visitNode(member.name, visitor, isPropertyName), - /*location*/ member.name - ), - func - ), + createAssignment(memberName, memberFunction), /*location*/ member ); @@ -1497,8 +1496,17 @@ namespace ts { if (node.transformFlags & TransformFlags.ContainsLexicalThis) { enableSubstitutionsForCapturedThis(); } - - const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + const func = createFunctionExpression( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + transformFunctionBody(node), + node + ); + setOriginalNode(func, node); setEmitFlags(func, EmitFlags.CapturesThis); return func; } @@ -1509,7 +1517,17 @@ namespace ts { * @param node a FunctionExpression node. */ function visitFunctionExpression(node: FunctionExpression): Expression { - return transformFunctionLikeToExpression(node, /*location*/ node, node.name); + return updateFunctionExpression( + node, + /*modifiers*/ undefined, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1518,19 +1536,18 @@ namespace ts { * @param node a FunctionDeclaration node. */ function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { - return setOriginalNode( - createFunctionDeclaration( - /*decorators*/ undefined, - node.modifiers, - node.asteriskToken, - node.name, - /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), - /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node - ), - /*original*/ node); + return updateFunctionDeclaration( + node, + /*decorators*/ undefined, + node.modifiers, + node.name, + /*typeParameters*/ undefined, + visitParameterList(node.parameters, visitor, context), + /*type*/ undefined, + node.transformFlags & TransformFlags.ES2015 + ? transformFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) + ); } /** @@ -1552,7 +1569,7 @@ namespace ts { node.asteriskToken, name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, saveStateAndInvoke(node, transformFunctionBody), location @@ -1579,7 +1596,8 @@ namespace ts { const body = node.body; let statementOffset: number; - startLexicalEnvironment(); + resumeLexicalEnvironment(); + if (isBlock(body)) { // ensureUseStrict is false because no new prologue-directive should be added. // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array @@ -1633,15 +1651,21 @@ namespace ts { closeBraceLocation = body; } - const lexicalEnvironment = endLexicalEnvironment(); - addRange(statements, lexicalEnvironment); + const declarations = endLexicalEnvironment(); + addRange(statements, declarations); // If we added any final generated statements, this must be a multi-line block - if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) { + if (!multiLine && declarations && declarations.length) { multiLine = true; } - const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine); + const block = createBlock( + createNodeArray(statements, statementsLocation), + node.body, + multiLine); + + setOriginalNode(block, node.body); + if (!multiLine && singleLine) { setEmitFlags(block, EmitFlags.SingleLine); } @@ -1650,7 +1674,6 @@ namespace ts { setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation); } - setOriginalNode(block, node.body); return block; } @@ -1659,7 +1682,7 @@ namespace ts { * * @param node An ExpressionStatement node. */ - function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { + function visitExpressionStatement(node: ExpressionStatement): Statement { // If we are here it is most likely because our expression is a destructuring assignment. switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: @@ -1713,8 +1736,11 @@ namespace ts { */ function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { // If we are here it is because this is a destructuring assignment. - Debug.assert(isDestructuringAssignment(node)); - return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + if (isDestructuringAssignment(node)) { + return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + } + + return visitEachChild(node, visitor, context); } function visitVariableStatement(node: VariableStatement): Statement { @@ -2894,6 +2920,10 @@ namespace ts { ); } + function visitSpreadElementExpression(node: SpreadElementExpression) { + return visitNode(node.expression, visitor, isExpression); + } + /** * Transforms the expression of a SpreadElementExpression node. * @@ -3080,19 +3110,6 @@ namespace ts { : createIdentifier("_super"); } - function visitSourceFileNode(node: SourceFile): SourceFile { - const [prologue, remaining] = span(node.statements, isPrologueDirective); - const statements: Statement[] = []; - startLexicalEnvironment(); - addRange(statements, prologue); - addCaptureThisForNodeIfNeeded(statements, node); - addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); - addRange(statements, endLexicalEnvironment()); - const clone = getMutableClone(node); - clone.statements = createNodeArray(statements, /*location*/ node.statements); - return clone; - } - /** * Called by the printer just before a node is printed. * @@ -3254,8 +3271,7 @@ namespace ts { return false; } - const parameter = singleOrUndefined(constructor.parameters); - if (!parameter || !nodeIsSynthesized(parameter) || !parameter.dotDotDotToken) { + if (some(constructor.parameters)) { return false; } @@ -3280,14 +3296,14 @@ namespace ts { } const expression = (callArgument).expression; - return isIdentifier(expression) && expression === parameter.name; + return isIdentifier(expression) && expression.text === "arguments"; } } - function createExtendsHelper(helperState: EmitHelperState, name: Identifier) { - requestEmitHelper(helperState, extendsHelper); + function createExtendsHelper(context: TransformationContext, name: Identifier) { + context.requestEmitHelper(extendsHelper); return createCall( - getHelperName(helperState, "__extends"), + getHelperName("__extends"), /*typeArguments*/ undefined, [ name, diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 14d97ade2ac..eca0e9a1488 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -13,6 +13,7 @@ namespace ts { export function transformES2017(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, } = context; @@ -22,7 +23,6 @@ namespace ts { // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; - let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -50,8 +50,6 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; - return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -60,14 +58,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -142,22 +137,17 @@ namespace ts { */ function visitMethodDeclaration(node: MethodDeclaration) { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const method = createMethod( + const updated = updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(method, node); - return method; + return updated; } /** @@ -170,20 +160,17 @@ namespace ts { */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const func = createFunctionDeclaration( + const updated = updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - - setOriginalNode(func, node); - return func; + return updated; } /** @@ -199,20 +186,18 @@ namespace ts { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( - /*modifiers*/ undefined, - node.asteriskToken, + const updated = updateFunctionExpression( + node, + visitNodes(node.modifiers, visitor, isModifier), node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + transformFunctionBody(node) ); - setOriginalNode(func, node); - return func; + setOriginalNode(updated, node); + return updated; } /** @@ -225,42 +210,24 @@ namespace ts { */ function visitArrowFunction(node: ArrowFunction) { Debug.assert(hasModifier(node, ModifierFlags.Async)); - const func = createArrowFunction( + const updated = updateArrowFunction( + node, visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + transformFunctionBody(node) ); - setOriginalNode(func, node); - return func; + setOriginalNode(updated, node); + return updated; } - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformAsyncFunctionBody(node); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformAsyncFunctionBody(node); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - currentScope = body; - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody { - const nodeType = node.original ? (node.original).type : node.type; + function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; + function transformFunctionBody(node: ArrowFunction): ConciseBody; + function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + const original = getOriginalNode(node, isFunctionLike); + const nodeType = original.type; const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; const isArrowFunction = node.kind === SyntaxKind.ArrowFunction; const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; @@ -271,6 +238,7 @@ namespace ts { // passed to `__awaiter` is executed inside of the callback to the // promise constructor. + resumeLexicalEnvironment(); if (!isArrowFunction) { const statements: Statement[] = []; @@ -278,7 +246,7 @@ namespace ts { statements.push( createReturn( createAwaiterHelper( - helperState, + context, hasLexicalArguments, promiseConstructor, transformFunctionBodyWorker(node.body, statementOffset) @@ -286,6 +254,8 @@ namespace ts { ) ); + addRange(statements, endLexicalEnvironment()); + const block = createBlock(statements, /*location*/ node.body, /*multiLine*/ true); // Minor optimization, emit `_super` helper to capture `super` access in an arrow. @@ -304,37 +274,40 @@ namespace ts { return block; } else { - return createAwaiterHelper( - helperState, + const expression = createAwaiterHelper( + context, hasLexicalArguments, promiseConstructor, - transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) + transformFunctionBodyWorker(node.body) ); + + const declarations = endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(expression); + const statements = mergeLexicalEnvironment(block.statements, declarations); + return updateBlock(block, statements); + } + + return expression; } } - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { + function transformFunctionBodyWorker(body: ConciseBody, start?: number) { if (isBlock(body)) { - return transformFunctionBodyWorker(body); + return updateBlock( + body, + visitLexicalEnvironment(body.statements, visitor, context, start)); } else { startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); - const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } + const visited = convertToFunctionBody(visitNode(body, visitor, isConciseBody)); + const statements = mergeLexicalEnvironment(visited.statements, endLexicalEnvironment()); + return updateBlock(visited, statements); } } function getPromiseConstructor(type: TypeNode) { - const typeName = getEntityNameFromTypeNode(type); + const typeName = type && getEntityNameFromTypeNode(type); if (typeName && isEntityName(typeName)) { const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue @@ -506,7 +479,8 @@ namespace ts { } } - function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + function createAwaiterHelper(context: TransformationContext, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + context.requestEmitHelper(awaiterHelper); const generatorFunc = createFunctionExpression( /*modifiers*/ undefined, createToken(SyntaxKind.AsteriskToken), @@ -520,9 +494,8 @@ namespace ts { // Mark this node as originally an async function (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; - requestEmitHelper(helperState, awaiterHelper); return createCall( - getHelperName(helperState, "__awaiter"), + getHelperName("__awaiter"), /*typeArguments*/ undefined, [ createThis(), diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 78c9e42c1bf..6047a8d8edd 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -242,7 +242,6 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; let renamedCatchVariableDeclarations: Map; - let helperState: EmitHelperState; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -298,13 +297,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -2590,7 +2587,7 @@ namespace ts { const buildResult = buildStatements(); return createGeneratorHelper( - helperState, + context, setEmitFlags( createFunctionExpression( /*modifiers*/ undefined, @@ -3083,10 +3080,10 @@ namespace ts { } } - function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) { - requestEmitHelper(helperState, generatorHelper); + function createGeneratorHelper(context: TransformationContext, body: FunctionExpression) { + context.requestEmitHelper(generatorHelper); return createCall( - getHelperName(helperState, "__generator"), + getHelperName("__generator"), /*typeArguments*/ undefined, [createThis(), body]); } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 8867896e51e..e342f2b7906 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -6,7 +6,6 @@ namespace ts { export function transformJsx(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; - let helperState: EmitHelperState; return transformSourceFile; @@ -21,13 +20,11 @@ namespace ts { } currentSourceFile = node; - helperState = { currentSourceFile, compilerOptions }; const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, helperState.requestedHelpers); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - helperState = undefined; return visited; } @@ -116,7 +113,7 @@ namespace ts { // a call to the __assign helper. objectProperties = singleOrUndefined(segments); if (!objectProperties) { - objectProperties = createAssignHelper(helperState, segments); + objectProperties = createAssignHelper(context, segments); } } @@ -538,10 +535,10 @@ namespace ts { "diams": 0x2666 }); - function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) { - requestEmitHelper(helperState, assignHelper); + function createAssignHelper(context: TransformationContext, attributesSegments: Expression[]) { + context.requestEmitHelper(assignHelper); return createCall( - getHelperName(helperState, "__assign"), + getHelperName("__assign"), /*typeArguments*/ undefined, attributesSegments ); diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index daef9cb5a4d..5611f890164 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -5,6 +5,14 @@ namespace ts { export function transformES2015Module(context: TransformationContext) { const compilerOptions = context.getCompilerOptions(); + const previousOnEmitNode = context.onEmitNode; + const previousOnSubstituteNode = context.onSubstituteNode; + context.onEmitNode = onEmitNode; + context.onSubstituteNode = onSubstituteNode; + context.enableEmitNotification(SyntaxKind.SourceFile); + context.enableSubstitution(SyntaxKind.Identifier); + + let currentSourceFile: SourceFile; return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -13,7 +21,7 @@ namespace ts { } if (isExternalModule(node) || compilerOptions.isolatedModules) { - const externalHelpersModuleName = getExternalHelpersModuleName(node); + const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(node, compilerOptions); if (externalHelpersModuleName) { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, node.statements); @@ -55,5 +63,55 @@ namespace ts { // Elide `export=` as it is not legal with --module ES6 return node.isExportEquals ? undefined : node; } + + // + // Emit Notification + // + + /** + * Hook for node emit. + * + * @param emitContext A context hint for the emitter. + * @param node The node to emit. + * @param emit A callback used to emit the node in the printer. + */ + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + if (isSourceFile(node)) { + currentSourceFile = node; + previousOnEmitNode(emitContext, node, emitCallback); + currentSourceFile = undefined; + } + else { + previousOnEmitNode(emitContext, node, emitCallback); + } + } + + // + // Substitutions + // + + /** + * Hooks node substitutions. + * + * @param emitContext A context hint for the emitter. + * @param node The node to substitute. + */ + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); + if (isIdentifier(node) && emitContext === EmitContext.Expression) { + return substituteExpressionIdentifier(node); + } + return node; + } + + function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + } + return node; + } } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 22e4e844a69..7b6fcc60f1b 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -61,7 +61,7 @@ namespace ts { } currentSourceFile = node; - currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver); + currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver, compilerOptions); // Perform the transformation. const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None]; @@ -1187,6 +1187,14 @@ namespace ts { * @param node The node to substitute. */ function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + return node; + } + if (!isGeneratedIdentifier(node) && !isLocalName(node)) { const exportContainer = resolver.getReferencedExportContainer(node, isExportName(node)); if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 4b24d5c1641..b979f66efd1 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -73,7 +73,7 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver); + moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. @@ -1609,6 +1609,14 @@ namespace ts { * @param node The node to substitute. */ function substituteExpressionIdentifier(node: Identifier): Expression { + if (getEmitFlags(node) & EmitFlags.HelperName) { + const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); + if (externalHelpersModuleName) { + return createPropertyAccess(externalHelpersModuleName, node); + } + return node; + } + // When we see an identifier in an expression position that // points to an imported symbol, we should substitute a qualified // reference to the imported symbol if one is needed. diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 02c7b107b25..09bfba2263e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -21,6 +21,7 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { startLexicalEnvironment, + resumeLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, } = context; @@ -48,7 +49,6 @@ namespace ts { let currentNamespaceContainerName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentScopeFirstDeclarationsOfName: Map; - let helperState: EmitHelperState; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -81,21 +81,11 @@ namespace ts { } currentSourceFile = node; - currentScope = node; - currentScopeFirstDeclarationsOfName = createMap(); - helperState = { currentSourceFile, compilerOptions }; - let visited = visitEachChild(node, sourceElementVisitor, context); - if (compilerOptions.alwaysStrict) { - visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements)); - } - - addEmitHelpers(visited, helperState.requestedHelpers); + const visited = saveStateAndInvoke(node, visitSourceFile); + addEmitHelpers(visited, context.readEmitHelpers()); currentSourceFile = undefined; - currentScope = undefined; - currentScopeFirstDeclarationsOfName = undefined; - helperState = undefined; return visited; } @@ -123,6 +113,32 @@ namespace ts { return visited; } + /** + * Performs actions that should always occur immediately before visiting a node. + * + * @param node The node to visit. + */ + function onBeforeVisitNode(node: Node) { + switch (node.kind) { + case SyntaxKind.SourceFile: + case SyntaxKind.CaseBlock: + case SyntaxKind.ModuleBlock: + case SyntaxKind.Block: + currentScope = node; + currentScopeFirstDeclarationsOfName = undefined; + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.FunctionDeclaration: + if (hasModifier(node, ModifierFlags.Ambient)) { + break; + } + + recordEmittedDeclarationInScope(node); + break; + } + } + /** * General-purpose node visitor. * @@ -451,29 +467,10 @@ namespace ts { } } - /** - * Performs actions that should always occur immediately before visiting a node. - * - * @param node The node to visit. - */ - function onBeforeVisitNode(node: Node) { - switch (node.kind) { - case SyntaxKind.CaseBlock: - case SyntaxKind.ModuleBlock: - case SyntaxKind.Block: - currentScope = node; - currentScopeFirstDeclarationsOfName = undefined; - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.FunctionDeclaration: - if (hasModifier(node, ModifierFlags.Ambient)) { - break; - } - - recordEmittedDeclarationInScope(node); - break; - } + function visitSourceFile(node: SourceFile) { + return updateSourceFileNode( + node, + visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, compilerOptions.alwaysStrict)); } /** @@ -845,9 +842,8 @@ namespace ts { // downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array. // Instead, we'll avoid using a rest parameter and spread into the super call as // 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody". - return constructor - ? visitNodes(constructor.parameters, visitor, isParameter) - : []; + return visitParameterList(constructor && constructor.parameters, visitor, context) + || []; } /** @@ -863,7 +859,7 @@ namespace ts { let indexOfFirstStatement = 0; // The body of a constructor is a new lexical environment - startLexicalEnvironment(); + resumeLexicalEnvironment(); if (constructor) { indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements); @@ -919,15 +915,13 @@ namespace ts { // End the lexical environment. addRange(statements, endLexicalEnvironment()); - return setMultiLine( - createBlock( - createNodeArray( - statements, - /*location*/ constructor ? constructor.body.statements : node.members - ), - /*location*/ constructor ? constructor.body : /*location*/ undefined + return createBlock( + createNodeArray( + statements, + /*location*/ constructor ? constructor.body.statements : node.members ), - true + /*location*/ constructor ? constructor.body : /*location*/ undefined, + /*multiLine*/ true ); } @@ -1385,7 +1379,7 @@ namespace ts { : undefined; const helper = createDecorateHelper( - helperState, + context, decoratorExpressions, prefix, memberName, @@ -1423,7 +1417,7 @@ namespace ts { const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); - const decorate = createDecorateHelper(helperState, decoratorExpressions, localName); + const decorate = createDecorateHelper(context, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); setEmitFlags(expression, EmitFlags.NoComments); setSourceMapRange(expression, moveRangePastDecorators(node)); @@ -1451,7 +1445,7 @@ namespace ts { expressions = []; for (const decorator of decorators) { const helper = createParamHelper( - helperState, + context, transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); @@ -1481,13 +1475,13 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { if (shouldAddTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:type", serializeTypeOfNode(node))); } if (shouldAddParamTypesMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:paramtypes", serializeParameterTypesOfNode(node))); } if (shouldAddReturnTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(context, "design:returntype", serializeReturnTypeOfNode(node))); } } } @@ -1505,7 +1499,7 @@ namespace ts { (properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node)))); } if (properties) { - decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); + decoratorExpressions.push(createMetadataHelper(context, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); } } } @@ -2020,26 +2014,23 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return undefined; } - - const method = createMethod( + const updated = updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, visitPropertyNameOfClassElement(node), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); - setOriginalNode(method, node); - - return method; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2065,24 +2056,22 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createGetAccessor( + const updated = updateGetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2098,23 +2087,21 @@ namespace ts { if (!shouldEmitAccessorDeclaration(node)) { return undefined; } - - const accessor = createSetAccessor( + const updated = updateSetAccessor( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), visitPropertyNameOfClassElement(node), - visitNodes(node.parameters, visitor, isParameter), - node.body ? visitEachChild(node.body, visitor, context) : createBlock([]), - /*location*/ node + visitParameterList(node.parameters, visitor, context), + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setOriginalNode(accessor, node); - setCommentRange(accessor, node); - setSourceMapRange(accessor, moveRangePastDecorators(node)); - - return accessor; + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); + } + return updated; } /** @@ -2131,27 +2118,22 @@ namespace ts { if (!shouldEmitFunctionLikeDeclaration(node)) { return createNotEmittedStatement(node); } - - const func = createFunctionDeclaration( + const updated = updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) || createBlock([]) ); - setOriginalNode(func, node); - if (isNamespaceExport(node)) { - const statements: Statement[] = [func]; + const statements: Statement[] = [updated]; addExportMemberAssignment(statements, node); return statements; } - - return func; + return updated; } /** @@ -2166,21 +2148,16 @@ namespace ts { if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( + const updated = updateFunctionExpression( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; + return updated; } /** @@ -2189,62 +2166,15 @@ namespace ts { * - The node has type annotations */ function visitArrowFunction(node: ArrowFunction) { - const func = createArrowFunction( + const updated = updateArrowFunction( + node, visitNodes(node.modifiers, modifierVisitor, isModifier), /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; - } - - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformFunctionBodyWorker(node.body); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; - currentScope = body; - currentScopeFirstDeclarationsOfName = createMap(); - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ false); - } - - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { - if (isBlock(body)) { - return transformFunctionBodyWorker(body); - } - else { - startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); - const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } - } + return updated; } /** @@ -3316,7 +3246,7 @@ namespace ts { function trySubstituteNamespaceExportedName(node: Identifier): Expression { // If this is explicitly a local name, do not substitute. - if (enabledSubstitutions & applicableSubstitutions && !isLocalName(node)) { + if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); @@ -3372,10 +3302,20 @@ namespace ts { } } - function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) { - requestEmitHelper(helperState, paramHelper); + const paramHelper: EmitHelper = { + name: "typescript:param", + scoped: false, + priority: 4, + text: ` + var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + };` + }; + + function createParamHelper(context: TransformationContext, expression: Expression, parameterOffset: number, location?: TextRange) { + context.requestEmitHelper(paramHelper); return createCall( - getHelperName(helperState, "__param"), + getHelperName("__param"), /*typeArguments*/ undefined, [ createLiteral(parameterOffset), @@ -3385,10 +3325,20 @@ namespace ts { ); } - function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) { - requestEmitHelper(helperState, metadataHelper); + const metadataHelper: EmitHelper = { + name: "typescript:metadata", + scoped: false, + priority: 3, + text: ` + var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + };` + }; + + function createMetadataHelper(context: TransformationContext, metadataKey: string, metadataValue: Expression) { + context.requestEmitHelper(metadataHelper); return createCall( - getHelperName(helperState, "__metadata"), + getHelperName("__metadata"), /*typeArguments*/ undefined, [ createLiteral(metadataKey), @@ -3397,20 +3347,6 @@ namespace ts { ); } - function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { - const argumentsArray: Expression[] = []; - argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); - argumentsArray.push(target); - if (memberName) { - argumentsArray.push(memberName); - if (descriptor) { - argumentsArray.push(descriptor); - } - } - - requestEmitHelper(helperState, decorateHelper); - return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); - } const decorateHelper: EmitHelper = { name: "typescript:decorate", @@ -3425,23 +3361,18 @@ namespace ts { };` }; - const metadataHelper: EmitHelper = { - name: "typescript:metadata", - scoped: false, - priority: 3, - text: ` - var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - };` - }; + function createDecorateHelper(context: TransformationContext, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { + context.requestEmitHelper(decorateHelper); + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } - const paramHelper: EmitHelper = { - name: "typescript:param", - scoped: false, - priority: 4, - text: ` - var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } - };` - }; + return createCall(getHelperName("__decorate"), /*typeArguments*/ undefined, argumentsArray, location); + } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 69ac1d76c9e..178e0bf6de7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1503,6 +1503,11 @@ namespace ts { expression: Expression; } + /* @internal */ + export interface PrologueDirective extends ExpressionStatement { + expression: StringLiteral; + } + export interface IfStatement extends Statement { kind: SyntaxKind.IfStatement; expression: Expression; @@ -3541,15 +3546,16 @@ namespace ts { NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. NoNestedComments = 1 << 11, - ExportName = 1 << 12, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - LocalName = 1 << 13, // Ensure an export prefix is not added for an identifier that points to an exported declaration. - Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - NoIndentation = 1 << 15, // Do not indent the node. - AsyncFunctionBody = 1 << 16, - ReuseTempVariableScope = 1 << 17, // Reuse the existing temp variable scope during emit. - CustomPrologue = 1 << 18, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - NoHoisting = 1 << 19, // Do not hoist this declaration in --module system - HasEndOfDeclarationMarker = 1 << 20, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + HelperName = 1 << 12, + ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1 << 16, // Do not indent the node. + AsyncFunctionBody = 1 << 17, + ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1 << 20, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration } /* @internal */ @@ -3568,16 +3574,123 @@ namespace ts { Unspecified, // Emitting an otherwise unspecified node } - /** Additional context provided to `visitEachChild` */ /* @internal */ - export interface LexicalEnvironment { + export interface EmitHost extends ScriptReferenceHost { + getSourceFiles(): SourceFile[]; + + /* @internal */ + isSourceFileFromExternalLibrary(file: SourceFile): boolean; + + getCommonSourceDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; + + isEmitBlocked(emitFileName: string): boolean; + + writeFile: WriteFileCallback; + } + + /* @internal */ + export interface TransformationContext { + getCompilerOptions(): CompilerOptions; + getEmitResolver(): EmitResolver; + getEmitHost(): EmitHost; + /** Starts a new lexical environment. */ startLexicalEnvironment(): void; + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + suspendLexicalEnvironment(): void; + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + resumeLexicalEnvironment(): void; + /** Ends a lexical environment, returning any declarations. */ endLexicalEnvironment(): Statement[]; + + /** + * Hoists a function declaration to the containing scope. + */ + hoistFunctionDeclaration(node: FunctionDeclaration): void; + + /** + * Hoists a variable declaration to the containing scope. + */ + hoistVariableDeclaration(node: Identifier): void; + + /** + * Records a request for a non-scoped emit helper in the current context. + */ + requestEmitHelper(helper: EmitHelper): void; + + /** + * Gets and resets the requested non-scoped emit helpers. + */ + readEmitHelpers(): EmitHelper[] | undefined; + + /** + * Enables expression substitutions in the pretty printer for the provided SyntaxKind. + */ + enableSubstitution(kind: SyntaxKind): void; + + /** + * Determines whether expression substitutions are enabled for the provided node. + */ + isSubstitutionEnabled(node: Node): boolean; + + /** + * Hook used by transformers to substitute expressions just before they + * are emitted by the pretty printer. + */ + onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; + + /** + * Enables before/after emit notifications in the pretty printer for the provided + * SyntaxKind. + */ + enableEmitNotification(kind: SyntaxKind): void; + + /** + * Determines whether before/after emit notifications should be raised in the pretty + * printer when it emits a node. + */ + isEmitNotificationEnabled(node: Node): boolean; + + /** + * Hook used to allow transformers to capture state before or after + * the printer emits a node. + */ + onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; } + /* @internal */ + export interface TransformationResult { + /** + * Gets the transformed source files. + */ + transformed: SourceFile[]; + + /** + * Emits the substitute for a node, if one is available; otherwise, emits the node. + * + * @param emitContext The current emit context. + * @param node The node to substitute. + * @param emitCallback A callback used to emit the node or its substitute. + */ + emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + + /** + * Emits a node with possible notification. + * + * @param emitContext The current emit context. + * @param node The node to emit. + * @param emitCallback A callback used to emit the node. + */ + emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + } + + /* @internal */ + export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; export interface TextSpan { start: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 79e15566e1d..5bdcf27769f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -28,21 +28,6 @@ namespace ts { string(): string; } - export interface EmitHost extends ScriptReferenceHost { - getSourceFiles(): SourceFile[]; - - /* @internal */ - isSourceFileFromExternalLibrary(file: SourceFile): boolean; - - getCommonSourceDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; - - isEmitBlocked(emitFileName: string): boolean; - - writeFile: WriteFileCallback; - } - // Pool writers to avoid needing to allocate them for every symbol we write. const stringWriters: StringSymbolWriter[] = []; export function getSingleLineStringWriter(): StringSymbolWriter { @@ -595,8 +580,9 @@ namespace ts { return n.kind === SyntaxKind.CallExpression && (n).expression.kind === SyntaxKind.SuperKeyword; } - export function isPrologueDirective(node: Node): boolean { - return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; + export function isPrologueDirective(node: Node): node is PrologueDirective { + return node.kind === SyntaxKind.ExpressionStatement + && (node).expression.kind === SyntaxKind.StringLiteral; } export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 609031a8221..dc0e69a9b0f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -653,6 +653,53 @@ namespace ts { return updated || nodes; } + /** + * Starts a new lexical environment and visits a statement list, ending the lexical environment + * and merging hoisted declarations upon completion. + */ + export function visitLexicalEnvironment(statements: NodeArray, visitor: (node: Node) => VisitResult, context: TransformationContext, start?: number, ensureUseStrict?: boolean) { + context.startLexicalEnvironment(); + statements = visitNodes(statements, visitor, isStatement, start); + if (ensureUseStrict && !startsWithUseStrict(statements)) { + statements = createNodeArray([createStatement(createLiteral("use strict")), ...statements], statements); + } + statements = mergeLexicalEnvironment(statements, context.endLexicalEnvironment()); + return statements; + } + + /** + * Starts a new lexical environment and visits a parameter list, suspending the lexical + * environment upon completion. + */ + export function visitParameterList(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: TransformationContext) { + context.startLexicalEnvironment(); + const updated = visitNodes(nodes, visitor, isParameterDeclaration); + context.suspendLexicalEnvironment(); + return updated; + } + + /** + * Resumes a suspended lexical environment and visits a function body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ + export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult, context: TransformationContext, optional?: boolean): FunctionBody; + /** + * Resumes a suspended lexical environment and visits a concise body, ending the lexical + * environment and merging hoisted declarations upon completion. + */ + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: TransformationContext): ConciseBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: TransformationContext, optional?: boolean): ConciseBody { + context.resumeLexicalEnvironment(); + const updated = visitNode(node, visitor, isConciseBody, optional); + const declarations = context.endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(updated); + const statements = mergeLexicalEnvironment(block.statements, declarations); + return updateBlock(block, statements); + } + return updated; + } + /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * @@ -660,8 +707,8 @@ namespace ts { * @param visitor The callback used to visit each child. * @param context A lexical environment context for the visitor. */ - export function visitEachChild(node: T, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T; - export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): Node { + export function visitEachChild(node: T, visitor: (node: Node) => VisitResult, context: TransformationContext): T; + export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult, context: TransformationContext): Node { if (node === undefined) { return undefined; } @@ -714,41 +761,33 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.Constructor: return updateConstructor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.GetAccessor: return updateGetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.SetAccessor: return updateSetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); // Binding patterns case SyntaxKind.ObjectBindingPattern: @@ -810,21 +849,17 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.ArrowFunction: return updateArrowFunction(node, visitNodes((node).modifiers, visitor, isModifier), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isConciseBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.DeleteExpression: return updateDelete(node, @@ -995,11 +1030,9 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context, /*optional*/ true)); case SyntaxKind.ClassDeclaration: return updateClassDeclaration(node, @@ -1129,11 +1162,7 @@ namespace ts { case SyntaxKind.SourceFile: context.startLexicalEnvironment(); return updateSourceFileNode(node, - createNodeArray( - concatenate( - visitNodes((node).statements, visitor, isStatement), - context.endLexicalEnvironment()), - (node).statements)); + visitLexicalEnvironment((node).statements, visitor, context)); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: @@ -1167,6 +1196,24 @@ namespace ts { // return node; } + /** + * Merges generated lexical declarations into a new statement list. + */ + export function mergeLexicalEnvironment(statements: NodeArray, declarations: Statement[]): NodeArray; + /** + * Appends generated lexical declarations to an array of statements. + */ + export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]): Statement[]; + export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]) { + if (!some(declarations)) { + return statements; + } + return isNodeArray(statements) + ? createNodeArray(concatenate(statements, declarations), statements) + : addRange(statements, declarations); + } + + /** * Merges generated lexical declarations into the FunctionBody of a non-arrow function-like declaration. *